From 14067cca8657566e83a205480735c6db8677874a Mon Sep 17 00:00:00 2001 From: David Schulz Date: Mon, 31 May 2021 12:47:35 +0200 Subject: [PATCH] LanguageClient: filter completion results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Use the optional filter text or the label of a completion item to filter out results that do not match the current completion prefix. Fixes completing code with language servers that do not provide server side filtering. Done with: Thorbjørn Lindeijer Change-Id: Id27b88bb4e8f0b8b68d6ee49bd1d41ec11d54c45 Reviewed-by: Thorbjørn Lindeijer Reviewed-by: Christian Stenger --- .../languageclientcompletionassist.cpp | 26 ++++++++++++++----- .../codeassist/assistproposaliteminterface.h | 1 + .../codeassist/genericproposalmodel.cpp | 2 +- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/plugins/languageclient/languageclientcompletionassist.cpp b/src/plugins/languageclient/languageclientcompletionassist.cpp index 0affc6fe8a7..3e6c4e883a9 100644 --- a/src/plugins/languageclient/languageclientcompletionassist.cpp +++ b/src/plugins/languageclient/languageclientcompletionassist.cpp @@ -61,6 +61,7 @@ public: // AssistProposalItemInterface interface QString text() const override; + QString filterText() const override; bool implicitlyApplies() const override; bool prematurelyApplies(const QChar &typedCharacter) const override; void apply(TextDocumentManipulatorInterface &manipulator, int basePosition) const override; @@ -80,6 +81,7 @@ private: CompletionItem m_item; mutable QChar m_triggeredCommitCharacter; mutable QString m_sortText; + mutable QString m_filterText; }; LanguageClientCompletionItem::LanguageClientCompletionItem(CompletionItem item) @@ -202,6 +204,15 @@ const QString &LanguageClientCompletionItem::sortText() const return m_sortText; } +QString LanguageClientCompletionItem::filterText() const +{ + if (m_filterText.isEmpty()) { + const Utils::optional filterText = m_item.filterText(); + m_filterText = filterText.has_value() ? filterText.value() : m_item.label(); + } + return m_filterText; +} + bool LanguageClientCompletionItem::operator <(const LanguageClientCompletionItem &other) const { return sortText() < other.sortText(); @@ -292,6 +303,7 @@ private: Utils::optional m_currentRequest; QMetaObject::Connection m_postponedUpdateConnection; int m_pos = -1; + int m_basePos = -1; }; LanguageClientCompletionAssistProcessor::LanguageClientCompletionAssistProcessor(Client *client) @@ -317,14 +329,13 @@ IAssistProposal *LanguageClientCompletionAssistProcessor::perform(const AssistIn { QTC_ASSERT(m_client, return nullptr); m_pos = interface->position(); + m_basePos = m_pos; + auto isIdentifierChar = [](const QChar &c) { return c.isLetterOrNumber() || c == '_'; }; + while (m_basePos > 0 && isIdentifierChar(interface->characterAt(m_basePos - 1))) + --m_basePos; if (interface->reason() == IdleEditor) { // Trigger an automatic completion request only when we are on a word with at least n "identifier" characters - const QRegularExpression regexp("^[_a-zA-Z0-9]+$"); - auto hasMatch = [®exp](const QString &txt) { return regexp.match(txt).hasMatch(); }; - int delta = 0; - while (m_pos - delta > 0 && hasMatch(interface->textAt(m_pos - delta - 1, delta + 1))) - ++delta; - if (delta < TextEditorSettings::completionSettings().m_characterThreshold) + if (m_pos - m_basePos < TextEditorSettings::completionSettings().m_characterThreshold) return nullptr; if (m_client->documentUpdatePostponed(interface->filePath())) { m_postponedUpdateConnection @@ -416,7 +427,8 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse( model->loadContent(Utils::transform(items, [](const CompletionItem &item){ return static_cast(new LanguageClientCompletionItem(item)); })); - LanguageClientCompletionProposal *proposal = new LanguageClientCompletionProposal(m_pos, model); + LanguageClientCompletionProposal *proposal = new LanguageClientCompletionProposal(m_basePos, + model); proposal->m_document = m_document; proposal->m_pos = m_pos; proposal->setFragile(true); diff --git a/src/plugins/texteditor/codeassist/assistproposaliteminterface.h b/src/plugins/texteditor/codeassist/assistproposaliteminterface.h index d4b7f3268e9..b1fd5f3848c 100644 --- a/src/plugins/texteditor/codeassist/assistproposaliteminterface.h +++ b/src/plugins/texteditor/codeassist/assistproposaliteminterface.h @@ -57,6 +57,7 @@ public: UTILS_DELETE_MOVE_AND_COPY(AssistProposalItemInterface) virtual QString text() const = 0; + virtual QString filterText() const { return text(); } virtual bool implicitlyApplies() const = 0; virtual bool prematurelyApplies(const QChar &typedCharacter) const = 0; virtual void apply(TextDocumentManipulatorInterface &manipulator, int basePosition) const = 0; diff --git a/src/plugins/texteditor/codeassist/genericproposalmodel.cpp b/src/plugins/texteditor/codeassist/genericproposalmodel.cpp index 7236f90beb0..c5fec55a339 100644 --- a/src/plugins/texteditor/codeassist/genericproposalmodel.cpp +++ b/src/plugins/texteditor/codeassist/genericproposalmodel.cpp @@ -305,7 +305,7 @@ void GenericProposalModel::filter(const QString &prefix) const QString lowerPrefix = prefix.toLower(); const bool checkInfix = prefix.size() >= 3; for (const auto &item : qAsConst(m_originalItems)) { - const QString &text = item->text(); + const QString &text = item->filterText(); // Direct match? if (text.startsWith(prefix)) {