diff --git a/src/plugins/texteditor/codeassist/codeassistant.cpp b/src/plugins/texteditor/codeassist/codeassistant.cpp index 3eecfa9530a..bf1d2be10c2 100644 --- a/src/plugins/texteditor/codeassist/codeassistant.cpp +++ b/src/plugins/texteditor/codeassist/codeassistant.cpp @@ -41,6 +41,7 @@ #include #include #include +#include #include #include @@ -157,7 +158,6 @@ void CodeAssistantPrivate::invoke(AssistKind kind, IAssistProvider *provider) m_proposal->basePosition(), m_editorWidget->position() - m_proposal->basePosition())); } else { - destroyContext(); requestProposal(ExplicitlyInvoked, kind, provider); } } @@ -166,8 +166,6 @@ bool CodeAssistantPrivate::requestActivationCharProposal() { if (m_assistKind == Completion && m_settings.m_completionTrigger != ManualCompletion) { if (CompletionAssistProvider *provider = identifyActivationSequence()) { - if (isWaitingForProposal()) - cancelCurrentRequest(); requestProposal(ActivationCharacter, Completion, provider); return true; } @@ -194,7 +192,10 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason, AssistKind kind, IAssistProvider *provider) { - QTC_ASSERT(!isWaitingForProposal(), return); + // make sure to cleanup old proposals if we cannot find a new assistant + Utils::ExecuteOnDestruction earlyReturnContextClear([this]() { destroyContext(); }); + if (isWaitingForProposal()) + cancelCurrentRequest(); if (m_editorWidget->hasBlockSelection()) return; // TODO @@ -215,6 +216,9 @@ void CodeAssistantPrivate::requestProposal(AssistReason reason, if (!assistInterface) return; + // We got an assist provider and interface so no need to reset the current context anymore + earlyReturnContextClear.reset({}); + m_assistKind = kind; m_requestProvider = provider; IAssistProcessor *processor = provider->createProcessor(); @@ -337,6 +341,15 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR return; } + if (m_proposalWidget + && basePosition == proposalCandidate->basePosition() + && m_proposalWidget->supportsModelUpdate(proposalCandidate->id())) { + m_proposal.reset(proposalCandidate.take()); + m_proposalWidget->updateModel(m_proposal->model()); + m_proposalWidget->updateProposal(prefix); + return; + } + destroyContext(); clearAbortedPosition(); @@ -471,7 +484,6 @@ void CodeAssistantPrivate::notifyChange() if (!isDisplayingProposal()) requestActivationCharProposal(); } else { - destroyContext(); requestProposal(ExplicitlyInvoked, m_assistKind, m_requestProvider); } } diff --git a/src/plugins/texteditor/codeassist/functionhintproposal.cpp b/src/plugins/texteditor/codeassist/functionhintproposal.cpp index 54447e70f6f..33d90f9b394 100644 --- a/src/plugins/texteditor/codeassist/functionhintproposal.cpp +++ b/src/plugins/texteditor/codeassist/functionhintproposal.cpp @@ -27,10 +27,12 @@ #include "ifunctionhintproposalmodel.h" #include "functionhintproposalwidget.h" +static const char functionHintId[] = "TextEditor.FunctionHintId"; + using namespace TextEditor; FunctionHintProposal::FunctionHintProposal(int cursorPos, FunctionHintProposalModelPtr model) - : IAssistProposal(cursorPos) + : IAssistProposal(functionHintId, cursorPos) , m_model(model) { setFragile(true); diff --git a/src/plugins/texteditor/codeassist/genericproposal.cpp b/src/plugins/texteditor/codeassist/genericproposal.cpp index f9ad6833712..4b2e0415ab3 100644 --- a/src/plugins/texteditor/codeassist/genericproposal.cpp +++ b/src/plugins/texteditor/codeassist/genericproposal.cpp @@ -28,16 +28,17 @@ #include "genericproposal.h" #include "genericproposalmodel.h" #include "genericproposalwidget.h" +#include "../texteditorconstants.h" namespace TextEditor { GenericProposal::GenericProposal(int cursorPos, GenericProposalModelPtr model) - : IAssistProposal(cursorPos) + : IAssistProposal(Constants::GENERIC_PROPOSAL_ID, cursorPos) , m_model(model) {} GenericProposal::GenericProposal(int cursorPos, const QList &items) - : IAssistProposal(cursorPos) + : IAssistProposal(Constants::GENERIC_PROPOSAL_ID, cursorPos) , m_model(new GenericProposalModel) { m_model->loadContent(items); @@ -45,7 +46,8 @@ GenericProposal::GenericProposal(int cursorPos, const QList #include +#include using namespace TextEditor; @@ -417,3 +418,13 @@ AssistProposalItemInterface *GenericProposalModel::proposalItem(int index) const { return m_currentItems.at(index); } + +int GenericProposalModel::indexOf( + const std::function &predicate) const +{ + for (int index = 0, end = m_currentItems.size(); index < end; ++index) { + if (predicate(m_currentItems.at(index))) + return index; + } + return -1; +} diff --git a/src/plugins/texteditor/codeassist/genericproposalmodel.h b/src/plugins/texteditor/codeassist/genericproposalmodel.h index de84d60dbe9..a4ef6fb0e68 100644 --- a/src/plugins/texteditor/codeassist/genericproposalmodel.h +++ b/src/plugins/texteditor/codeassist/genericproposalmodel.h @@ -64,6 +64,7 @@ public: virtual QString proposalPrefix() const; virtual bool keepPerfectMatch(AssistReason reason) const; virtual AssistProposalItemInterface *proposalItem(int index) const; + virtual int indexOf(const std::function &predicate) const; void loadContent(const QList &items); diff --git a/src/plugins/texteditor/codeassist/genericproposalwidget.cpp b/src/plugins/texteditor/codeassist/genericproposalwidget.cpp index e0072b039d5..ad056e41ad4 100644 --- a/src/plugins/texteditor/codeassist/genericproposalwidget.cpp +++ b/src/plugins/texteditor/codeassist/genericproposalwidget.cpp @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -414,6 +415,33 @@ void GenericProposalWidget::setIsSynchronized(bool isSync) d->m_isSynchronized = isSync; } +bool GenericProposalWidget::supportsModelUpdate(const Utils::Id &proposalId) const +{ + return proposalId == Constants::GENERIC_PROPOSAL_ID; +} + +void GenericProposalWidget::updateModel(ProposalModelPtr model) +{ + QString currentText; + if (d->m_explicitlySelected) + currentText = d->m_model->text(d->m_completionListView->currentIndex().row()); + d->m_model = model.staticCast(); + if (d->m_model->containsDuplicates()) + d->m_model->removeDuplicates(); + d->m_completionListView->setModel(new ModelAdapter(d->m_model, d->m_completionListView)); + connect(d->m_completionListView->selectionModel(), &QItemSelectionModel::currentChanged, + &d->m_infoTimer, QOverload<>::of(&QTimer::start)); + int currentRow = -1; + if (!currentText.isEmpty()) { + currentRow = d->m_model->indexOf( + Utils::equal(&AssistProposalItemInterface::text, currentText)); + } + if (currentRow >= 0) + d->m_completionListView->selectRow(currentRow); + else + d->m_explicitlySelected = false; +} + void GenericProposalWidget::showProposal(const QString &prefix) { ensurePolished(); diff --git a/src/plugins/texteditor/codeassist/genericproposalwidget.h b/src/plugins/texteditor/codeassist/genericproposalwidget.h index 7a75c6ab649..c698b106887 100644 --- a/src/plugins/texteditor/codeassist/genericproposalwidget.h +++ b/src/plugins/texteditor/codeassist/genericproposalwidget.h @@ -53,6 +53,9 @@ public: void setDisplayRect(const QRect &rect) override; void setIsSynchronized(bool isSync) override; + bool supportsModelUpdate(const Utils::Id &proposalId) const override; + void updateModel(ProposalModelPtr model) override; + void showProposal(const QString &prefix) override; void updateProposal(const QString &prefix) override; void closeProposal() override; diff --git a/src/plugins/texteditor/codeassist/iassistproposal.cpp b/src/plugins/texteditor/codeassist/iassistproposal.cpp index a5576ae6202..d1b4f131831 100644 --- a/src/plugins/texteditor/codeassist/iassistproposal.cpp +++ b/src/plugins/texteditor/codeassist/iassistproposal.cpp @@ -59,8 +59,9 @@ using namespace TextEditor; \sa IAssistProposalWidget, IAssistModel */ -IAssistProposal::IAssistProposal(int basePosition) - : m_basePosition(basePosition) +IAssistProposal::IAssistProposal(Utils::Id id, int basePosition) + : m_id(id) + , m_basePosition(basePosition) {} IAssistProposal::~IAssistProposal() = default; diff --git a/src/plugins/texteditor/codeassist/iassistproposal.h b/src/plugins/texteditor/codeassist/iassistproposal.h index e7ac9131965..d4784a3f60d 100644 --- a/src/plugins/texteditor/codeassist/iassistproposal.h +++ b/src/plugins/texteditor/codeassist/iassistproposal.h @@ -30,6 +30,8 @@ #include +#include + namespace TextEditor { class IAssistProposalWidget; @@ -38,7 +40,7 @@ class TextEditorWidget; class TEXTEDITOR_EXPORT IAssistProposal { public: - IAssistProposal(int basePosition); + IAssistProposal(Utils::Id id,int basePosition); virtual ~IAssistProposal(); int basePosition() const; @@ -52,7 +54,11 @@ public: void setFragile(bool fragile); void setSupportsPrefix(bool supportsPrefix); + + Utils::Id id() const { return m_id; } + protected: + Utils::Id m_id; int m_basePosition; bool m_isFragile = false; bool m_supportsPrefix = true; diff --git a/src/plugins/texteditor/codeassist/iassistproposalwidget.h b/src/plugins/texteditor/codeassist/iassistproposalwidget.h index d178fc4b5ed..f76464b4736 100644 --- a/src/plugins/texteditor/codeassist/iassistproposalwidget.h +++ b/src/plugins/texteditor/codeassist/iassistproposalwidget.h @@ -32,6 +32,8 @@ #include +namespace Utils { class Id; } + namespace TextEditor { class CodeAssistant; @@ -58,6 +60,8 @@ public: virtual void closeProposal() = 0; virtual bool proposalIsVisible() const { return isVisible(); } + virtual bool supportsModelUpdate(const Utils::Id &/*proposalId*/) const { return false; } + virtual void updateModel(ProposalModelPtr) {} int basePosition() const; void setBasePosition(int basePosition); diff --git a/src/plugins/texteditor/texteditorconstants.h b/src/plugins/texteditor/texteditorconstants.h index abab979b611..c3f65a54c9e 100644 --- a/src/plugins/texteditor/texteditorconstants.h +++ b/src/plugins/texteditor/texteditorconstants.h @@ -236,6 +236,7 @@ const char SNIPPET_EDITOR_ID[] = "TextEditor.SnippetEditor"; const char TEXT_SNIPPET_GROUP_ID[] = "Text"; const char GLOBAL_SETTINGS_ID[] = "Global"; +const char GENERIC_PROPOSAL_ID[] = "TextEditor.GenericProposalId"; /** * Delay before tooltip will be shown near completion assistant proposal