From b4e2ab36a7e381f41a162b4cfbef324d4c437ae5 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Fri, 18 Aug 2017 12:21:45 +0200 Subject: [PATCH] Clang: Remember selected function signature hint ...when typing more arguments: struct Foo {}; void f(int, int); void f(Foo, Foo); void f(char, char); void c() { f( // 1. Trigger completion with Ctrl+Space // 2. Chose item "f(Foo, Foo)" // 3. Type: Foo(), // OK, signature hint "f(Foo, Foo)" is displayed again } FunctionHintProposalWidget and IFunctionHintProposalModel are instantiated for each calculation, so remember the selected hint in the CodeAssist. Keep the latest 20 entries. Task-number: QTCREATORBUG-11688 Change-Id: I579fc6d8a35dd8fa398e4b3170ddc05a85252d1a Reviewed-by: Tim Jenssen --- .../clangcodemodel/clangfunctionhintmodel.cpp | 9 ++ .../clangcodemodel/clangfunctionhintmodel.h | 1 + .../texteditor/codeassist/codeassistant.cpp | 25 ++++++ .../texteditor/codeassist/codeassistant.h | 3 + .../codeassist/functionhintproposalwidget.cpp | 85 ++++++++++++++++++- .../codeassist/functionhintproposalwidget.h | 3 + .../codeassist/iassistproposalwidget.cpp | 10 +++ .../codeassist/iassistproposalwidget.h | 6 ++ .../codeassist/ifunctionhintproposalmodel.cpp | 7 ++ .../codeassist/ifunctionhintproposalmodel.h | 3 + 10 files changed, 151 insertions(+), 1 deletion(-) diff --git a/src/plugins/clangcodemodel/clangfunctionhintmodel.cpp b/src/plugins/clangcodemodel/clangfunctionhintmodel.cpp index 67d58d9cb68..1641f24a714 100644 --- a/src/plugins/clangcodemodel/clangfunctionhintmodel.cpp +++ b/src/plugins/clangcodemodel/clangfunctionhintmodel.cpp @@ -61,6 +61,15 @@ QString ClangFunctionHintModel::text(int index) const return signatureWithEmphasizedCurrentParameter; } +QString ClangFunctionHintModel::id(int index) const +{ + QString chunks; + for (const ClangBackEnd::CodeCompletionChunk &chunk : m_functionSymbols.at(index).chunks()) + chunks += chunk.text(); + + return chunks; +} + int ClangFunctionHintModel::activeArgument(const QString &prefix) const { int activeArgumentNumber = 0; diff --git a/src/plugins/clangcodemodel/clangfunctionhintmodel.h b/src/plugins/clangcodemodel/clangfunctionhintmodel.h index edfb2f6d74c..9a848025a4d 100644 --- a/src/plugins/clangcodemodel/clangfunctionhintmodel.h +++ b/src/plugins/clangcodemodel/clangfunctionhintmodel.h @@ -40,6 +40,7 @@ public: void reset() override; int size() const override; QString text(int index) const override; + QString id(int index) const override; int activeArgument(const QString &prefix) const override; private: diff --git a/src/plugins/texteditor/codeassist/codeassistant.cpp b/src/plugins/texteditor/codeassist/codeassistant.cpp index b5368b87b1e..5de47c3b092 100644 --- a/src/plugins/texteditor/codeassist/codeassistant.cpp +++ b/src/plugins/texteditor/codeassist/codeassistant.cpp @@ -74,6 +74,9 @@ public: bool hasContext() const; void destroyContext(); + QVariant userData() const; + void setUserData(const QVariant &data); + CompletionAssistProvider *identifyActivationSequence(); void stopAutomaticProposalTimer(); @@ -105,6 +108,7 @@ private: CompletionSettings m_settings; int m_abortedBasePosition; static const QChar m_null; + QVariant m_userData; }; // -------------------- @@ -340,6 +344,7 @@ void CodeAssistantPrivate::displayProposal(IAssistProposal *newProposal, AssistR m_proposalWidget->setAssistant(q); m_proposalWidget->setReason(reason); m_proposalWidget->setKind(m_assistKind); + m_proposalWidget->setBasePosition(basePosition); m_proposalWidget->setUnderlyingWidget(m_editorWidget); m_proposalWidget->setModel(proposalCandidateModel.take()); m_proposalWidget->setDisplayRect(m_editorWidget->cursorRect(basePosition)); @@ -465,6 +470,16 @@ void CodeAssistantPrivate::destroyContext() } } +QVariant CodeAssistantPrivate::userData() const +{ + return m_userData; +} + +void CodeAssistantPrivate::setUserData(const QVariant &data) +{ + m_userData = data; +} + void CodeAssistantPrivate::startAutomaticProposalTimer() { if (m_settings.m_completionTrigger == AutomaticCompletion) @@ -572,6 +587,16 @@ void CodeAssistant::destroyContext() d->destroyContext(); } +QVariant CodeAssistant::userData() const +{ + return d->userData(); +} + +void CodeAssistant::setUserData(const QVariant &data) +{ + d->setUserData(data); +} + void CodeAssistant::invoke(AssistKind kind, IAssistProvider *provider) { d->invoke(kind, provider); diff --git a/src/plugins/texteditor/codeassist/codeassistant.h b/src/plugins/texteditor/codeassist/codeassistant.h index dca761f4ac8..64e507f49b8 100644 --- a/src/plugins/texteditor/codeassist/codeassistant.h +++ b/src/plugins/texteditor/codeassist/codeassistant.h @@ -53,6 +53,9 @@ public: bool hasContext() const; void destroyContext(); + QVariant userData() const; + void setUserData(const QVariant &data); + void invoke(AssistKind assistKind, IAssistProvider *provider = 0); signals: diff --git a/src/plugins/texteditor/codeassist/functionhintproposalwidget.cpp b/src/plugins/texteditor/codeassist/functionhintproposalwidget.cpp index 4ad414d44ec..dc952e19946 100644 --- a/src/plugins/texteditor/codeassist/functionhintproposalwidget.cpp +++ b/src/plugins/texteditor/codeassist/functionhintproposalwidget.cpp @@ -27,6 +27,7 @@ #include "ifunctionhintproposalmodel.h" #include "codeassistant.h" +#include #include #include #include @@ -42,6 +43,56 @@ namespace TextEditor { +static const int maxSelectedFunctionHints = 20; + +class SelectedFunctionHints +{ +public: + void insert(int basePosition, const QString &hintId) + { + if (basePosition < 0 || hintId.isEmpty()) + return; + + const int index = indexOf(basePosition); + + // Add new item + if (index == -1) { + if (m_items.size() + 1 > maxSelectedFunctionHints) + m_items.removeLast(); + m_items.prepend(FunctionHintItem(basePosition, hintId)); + return; + } + + // Update existing item + m_items[index].hintId = hintId; + } + + QString hintId(int basePosition) const + { + const int index = indexOf(basePosition); + return index == -1 ? QString() : m_items.at(index).hintId; + } + +private: + int indexOf(int basePosition) const + { + return Utils::indexOf(m_items, [&](const FunctionHintItem &item) { + return item.basePosition == basePosition; + }); + } + +private: + struct FunctionHintItem { + FunctionHintItem(int basePosition, const QString &hintId) + : basePosition(basePosition), hintId(hintId) {} + + int basePosition = -1; + QString hintId; + }; + + QList m_items; +}; + // ------------------------- // HintProposalWidgetPrivate // ------------------------- @@ -158,7 +209,7 @@ void FunctionHintProposalWidget::showProposal(const QString &prefix) QTC_ASSERT(d->m_totalHints != 0, abort(); return; ); d->m_pager->setVisible(d->m_totalHints > 1); - d->m_currentHint = 0; + d->m_currentHint = loadSelectedHint(); if (!updateAndCheck(prefix)) return; @@ -184,6 +235,32 @@ void FunctionHintProposalWidget::abort() deleteLater(); } +static SelectedFunctionHints selectedFunctionHints(CodeAssistant &codeAssistant) +{ + const QVariant variant = codeAssistant.userData(); + return variant.value(); +} + +int FunctionHintProposalWidget::loadSelectedHint() const +{ + const QString hintId = selectedFunctionHints(*d->m_assistant).hintId(basePosition()); + + for (int i = 0; i < d->m_model->size(); ++i) { + if (d->m_model->id(i) == hintId) + return i; + } + + return 0; +} + +void FunctionHintProposalWidget::storeSelectedHint() +{ + SelectedFunctionHints table = selectedFunctionHints(*d->m_assistant); + table.insert(basePosition(), d->m_model->id(d->m_currentHint)); + + d->m_assistant->setUserData(QVariant::fromValue(table)); +} + bool FunctionHintProposalWidget::eventFilter(QObject *obj, QEvent *e) { switch (e->type()) { @@ -257,6 +334,8 @@ bool FunctionHintProposalWidget::eventFilter(QObject *obj, QEvent *e) void FunctionHintProposalWidget::nextPage() { d->m_currentHint = (d->m_currentHint + 1) % d->m_totalHints; + + storeSelectedHint(); updateContent(); } @@ -266,6 +345,8 @@ void FunctionHintProposalWidget::previousPage() d->m_currentHint = d->m_totalHints - 1; else --d->m_currentHint; + + storeSelectedHint(); updateContent(); } @@ -322,3 +403,5 @@ void FunctionHintProposalWidget::updatePosition() } } // TextEditor + +Q_DECLARE_METATYPE(TextEditor::SelectedFunctionHints) diff --git a/src/plugins/texteditor/codeassist/functionhintproposalwidget.h b/src/plugins/texteditor/codeassist/functionhintproposalwidget.h index d42d1e3e692..21a85395dce 100644 --- a/src/plugins/texteditor/codeassist/functionhintproposalwidget.h +++ b/src/plugins/texteditor/codeassist/functionhintproposalwidget.h @@ -63,6 +63,9 @@ private: void updatePosition(); void abort(); + int loadSelectedHint() const; + void storeSelectedHint(); + private: FunctionHintProposalWidgetPrivate *d; }; diff --git a/src/plugins/texteditor/codeassist/iassistproposalwidget.cpp b/src/plugins/texteditor/codeassist/iassistproposalwidget.cpp index 6e3d7d34e05..4be7453bd0a 100644 --- a/src/plugins/texteditor/codeassist/iassistproposalwidget.cpp +++ b/src/plugins/texteditor/codeassist/iassistproposalwidget.cpp @@ -55,6 +55,16 @@ IAssistProposalWidget::IAssistProposalWidget() IAssistProposalWidget::~IAssistProposalWidget() {} +int IAssistProposalWidget::basePosition() const +{ + return m_basePosition; +} + +void IAssistProposalWidget::setBasePosition(int basePosition) +{ + m_basePosition = basePosition; +} + /*! \fn void TextEditor::IAssistProposalWidget::setAssistant(CodeAssistant *assistant) diff --git a/src/plugins/texteditor/codeassist/iassistproposalwidget.h b/src/plugins/texteditor/codeassist/iassistproposalwidget.h index 2f2f6e8bf6c..fd86bb5722b 100644 --- a/src/plugins/texteditor/codeassist/iassistproposalwidget.h +++ b/src/plugins/texteditor/codeassist/iassistproposalwidget.h @@ -57,10 +57,16 @@ public: virtual void updateProposal(const QString &prefix) = 0; virtual void closeProposal() = 0; + int basePosition() const; + void setBasePosition(int basePosition); + signals: void prefixExpanded(const QString &newPrefix); void proposalItemActivated(AssistProposalItemInterface *proposalItem); void explicitlyAborted(); + +protected: + int m_basePosition = -1; }; } // TextEditor diff --git a/src/plugins/texteditor/codeassist/ifunctionhintproposalmodel.cpp b/src/plugins/texteditor/codeassist/ifunctionhintproposalmodel.cpp index 2a28dfc5a1d..f1685c58d6a 100644 --- a/src/plugins/texteditor/codeassist/ifunctionhintproposalmodel.cpp +++ b/src/plugins/texteditor/codeassist/ifunctionhintproposalmodel.cpp @@ -25,6 +25,8 @@ #include "ifunctionhintproposalmodel.h" +#include + using namespace TextEditor; IFunctionHintProposalModel::IFunctionHintProposalModel() @@ -32,3 +34,8 @@ IFunctionHintProposalModel::IFunctionHintProposalModel() IFunctionHintProposalModel::~IFunctionHintProposalModel() {} + +QString IFunctionHintProposalModel::id(int /*index*/) const +{ + return QString(); +} diff --git a/src/plugins/texteditor/codeassist/ifunctionhintproposalmodel.h b/src/plugins/texteditor/codeassist/ifunctionhintproposalmodel.h index 1af6bf12196..fe329e7c822 100644 --- a/src/plugins/texteditor/codeassist/ifunctionhintproposalmodel.h +++ b/src/plugins/texteditor/codeassist/ifunctionhintproposalmodel.h @@ -29,6 +29,8 @@ #include +QT_FORWARD_DECLARE_CLASS(QString); + namespace TextEditor { class TEXTEDITOR_EXPORT IFunctionHintProposalModel : public IAssistProposalModel @@ -38,6 +40,7 @@ public: ~IFunctionHintProposalModel(); virtual int activeArgument(const QString &prefix) const = 0; + virtual QString id(int index) const; }; } // TextEditor