From 85862d3ca6892411d61731da70663022772e9e9d Mon Sep 17 00:00:00 2001 From: David Schulz Date: Tue, 1 Oct 2024 13:27:12 +0200 Subject: [PATCH] TextEditor: filter suggestions while typing This will remove suggestions that are no longer applyable with the current editor content. Change-Id: Ibfc47495d5dda99a9ef2d7fe3f69c83a2d7e3997 Reviewed-by: Reviewed-by: Marcus Tillmanns --- src/plugins/copilot/copilotplugin.cpp | 1 - src/plugins/texteditor/textdocumentlayout.cpp | 20 ------ src/plugins/texteditor/textdocumentlayout.h | 3 - src/plugins/texteditor/texteditor.cpp | 21 ++++-- src/plugins/texteditor/textsuggestion.cpp | 72 ++++++++++++++----- src/plugins/texteditor/textsuggestion.h | 4 +- 6 files changed, 73 insertions(+), 48 deletions(-) diff --git a/src/plugins/copilot/copilotplugin.cpp b/src/plugins/copilot/copilotplugin.cpp index 8a9ac9a9adf..670fa9d435d 100644 --- a/src/plugins/copilot/copilotplugin.cpp +++ b/src/plugins/copilot/copilotplugin.cpp @@ -45,7 +45,6 @@ static void cycleSuggestion(TextEditor::TextEditorWidget *editor, Direction dire index = suggestion->suggestions().count() - 1; else if (index >= suggestion->suggestions().count()) index = 0; - suggestion->reset(); editor->insertSuggestion(std::make_unique( suggestion->suggestions(), editor->document(), index)); } diff --git a/src/plugins/texteditor/textdocumentlayout.cpp b/src/plugins/texteditor/textdocumentlayout.cpp index 551c389cdf0..26c7242cbd1 100644 --- a/src/plugins/texteditor/textdocumentlayout.cpp +++ b/src/plugins/texteditor/textdocumentlayout.cpp @@ -608,26 +608,6 @@ void TextDocumentLayout::updateSuggestionFormats(const QTextBlock &block, } } -bool TextDocumentLayout::updateSuggestion(const QTextBlock &block, - int position, - const FontSettings &fontSettings) -{ - if (TextSuggestion *suggestion = TextDocumentLayout::suggestion(block)) { - if (position < suggestion->currentPosition()) - return false; - const int positionInBlock = position - block.position(); - const QString start = block.text().left(positionInBlock); - const QString end = block.text().mid(positionInBlock); - const QString replacement = suggestion->replacementDocument()->firstBlock().text(); - if (replacement.startsWith(start) && replacement.indexOf(end, start.size()) >= 0) { - suggestion->setCurrentPosition(position); - TextDocumentLayout::updateSuggestionFormats(block, fontSettings); - return true; - } - } - return false; -} - void TextDocumentLayout::requestExtraAreaUpdate() { emit updateExtraArea(); diff --git a/src/plugins/texteditor/textdocumentlayout.h b/src/plugins/texteditor/textdocumentlayout.h index 05e815a3ff3..1d400c8a459 100644 --- a/src/plugins/texteditor/textdocumentlayout.h +++ b/src/plugins/texteditor/textdocumentlayout.h @@ -188,9 +188,6 @@ public: static quint8 attributeState(const QTextBlock &block); static void updateSuggestionFormats(const QTextBlock &block, const FontSettings &fontSettings); - static bool updateSuggestion(const QTextBlock &block, - int position, - const FontSettings &fontSettings); class TEXTEDITOR_EXPORT FoldValidator { diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index 52aefa4305d..4ba6d070378 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -1836,15 +1836,22 @@ void TextEditorWidgetPrivate::updateSuggestion() { if (!m_suggestionBlock.isValid()) return; - if (m_cursors.mainCursor().block() != m_suggestionBlock) { - clearCurrentSuggestion(); - } else { - if (!TextDocumentLayout::updateSuggestion(m_suggestionBlock, - m_cursors.mainCursor().position(), - m_document->fontSettings())) { - clearCurrentSuggestion(); + const QTextCursor cursor = m_cursors.mainCursor(); + if (cursor.block() == m_suggestionBlock) { + TextSuggestion *suggestion = TextDocumentLayout::suggestion(m_suggestionBlock); + if (QTC_GUARD(suggestion)) { + const int pos = cursor.position(); + if (pos >= suggestion->currentPosition()) { + suggestion->setCurrentPosition(pos); + if (suggestion->filterSuggestions(q)) { + TextDocumentLayout::updateSuggestionFormats( + m_suggestionBlock, m_document->fontSettings()); + return; + } + } } } + clearCurrentSuggestion(); } void TextEditorWidgetPrivate::clearCurrentSuggestion() diff --git a/src/plugins/texteditor/textsuggestion.cpp b/src/plugins/texteditor/textsuggestion.cpp index bc85a81a3bc..f1163e532f2 100644 --- a/src/plugins/texteditor/textsuggestion.cpp +++ b/src/plugins/texteditor/textsuggestion.cpp @@ -47,11 +47,11 @@ bool TextSuggestion::applyLine(TextEditorWidget *widget) return applyPart(Line, widget); } -void TextSuggestion::reset() +bool TextSuggestion::filterSuggestions(TextEditorWidget *widget) { - QTextCursor c = m_suggestion.position.toTextCursor(sourceDocument()); + QTextCursor c = m_suggestion.range.begin.toTextCursor(sourceDocument()); c.setPosition(currentPosition(), QTextCursor::KeepAnchor); - c.removeSelectedText(); + return m_suggestion.text.startsWith(c.selectedText(), Qt::CaseInsensitive); } bool TextSuggestion::applyPart(Part part, TextEditorWidget *widget) @@ -98,6 +98,38 @@ CyclicSuggestion::CyclicSuggestion( , m_currentSuggestion(currentSuggestion) {} +bool operator==(const TextSuggestion::Data &lhs, const TextSuggestion::Data &rhs) +{ + return lhs.text == rhs.text && lhs.range == rhs.range && lhs.position == rhs.position; +} + +bool CyclicSuggestion::filterSuggestions(TextEditorWidget *widget) +{ + QList newSuggestions; + int newIndex = -1; + int currentIndex = 0; + for (auto suggestion : m_suggestions) { + QTextCursor c = suggestion.range.begin.toTextCursor(sourceDocument()); + c.setPosition(currentPosition(), QTextCursor::KeepAnchor); + if (suggestion.text.startsWith(c.selectedText())) { + newSuggestions.append(suggestion); + if (currentIndex == m_currentSuggestion) + newIndex = newSuggestions.size() - 1; + } else if (currentIndex == m_currentSuggestion) { + newIndex = 0; + } + ++currentIndex; + } + if (newSuggestions.isEmpty()) + return false; + + if (newSuggestions != m_suggestions) { + widget->insertSuggestion( + std::make_unique(newSuggestions, sourceDocument(), newIndex)); + } + return true; +} + class SuggestionToolTip : public QToolBar { public: @@ -108,33 +140,41 @@ public: , m_currentSuggestion(std::max(0, std::min(currentSuggestion, suggestions.size() - 1))) , m_editor(editor) { - auto prev = addAction( - Utils::Icons::PREV_TOOLBAR.icon(), Tr::tr("Select Previous Suggestion")); - prev->setEnabled(m_suggestions.size() > 1); + m_prev = addAction(Utils::Icons::PREV_TOOLBAR.icon(), Tr::tr("Select Previous Suggestion")); addWidget(m_numberLabel); - auto next - = addAction(Utils::Icons::NEXT_TOOLBAR.icon(), Tr::tr("Select Next Suggestion")); - next->setEnabled(m_suggestions.size() > 1); + m_next = addAction(Utils::Icons::NEXT_TOOLBAR.icon(), Tr::tr("Select Next Suggestion")); auto apply = addAction(Tr::tr("Apply (%1)").arg(QKeySequence(Qt::Key_Tab).toString())); auto applyWord = addAction( Tr::tr("Apply Word (%1)").arg(QKeySequence(QKeySequence::MoveToNextWord).toString())); auto applyLine = addAction(Tr::tr("Apply Line")); - connect(prev, &QAction::triggered, this, &SuggestionToolTip::selectPrevious); - connect(next, &QAction::triggered, this, &SuggestionToolTip::selectNext); + connect(m_prev, &QAction::triggered, this, &SuggestionToolTip::selectPrevious); + connect(m_next, &QAction::triggered, this, &SuggestionToolTip::selectNext); connect(apply, &QAction::triggered, this, &SuggestionToolTip::apply); connect(applyWord, &QAction::triggered, this, &SuggestionToolTip::applyWord); connect(applyLine, &QAction::triggered, this, &SuggestionToolTip::applyLine); + connect(editor->document(), &QTextDocument::contentsChange, this, &SuggestionToolTip::contentsChanged); - updateLabels(); + updateSuggestionSelector(); } private: - void updateLabels() + void contentsChanged() + { + if (auto *suggestion = dynamic_cast(m_editor->currentSuggestion())) { + m_suggestions = suggestion->suggestions(); + m_currentSuggestion = suggestion->currentSuggestion(); + updateSuggestionSelector(); + } + } + + void updateSuggestionSelector() { m_numberLabel->setText( Tr::tr("%1 of %2").arg(m_currentSuggestion + 1).arg(m_suggestions.count())); + m_prev->setEnabled(m_suggestions.size() > 1); + m_next->setEnabled(m_suggestions.size() > 1); } void selectPrevious() @@ -155,9 +195,7 @@ private: void setCurrentSuggestion() { - updateLabels(); - if (TextSuggestion *suggestion = m_editor->currentSuggestion()) - suggestion->reset(); + updateSuggestionSelector(); m_editor->insertSuggestion(std::make_unique( m_suggestions, m_editor->document(), m_currentSuggestion)); } @@ -190,6 +228,8 @@ private: } QLabel *m_numberLabel; + QAction *m_prev; + QAction *m_next; QList m_suggestions; int m_currentSuggestion = 0; TextEditorWidget *m_editor; diff --git a/src/plugins/texteditor/textsuggestion.h b/src/plugins/texteditor/textsuggestion.h index 8e40fdd1b49..19fc7a15c53 100644 --- a/src/plugins/texteditor/textsuggestion.h +++ b/src/plugins/texteditor/textsuggestion.h @@ -35,7 +35,7 @@ public: // Returns true if the suggestion was applied completely, false if it was only partially applied. virtual bool applyWord(TextEditorWidget *widget); virtual bool applyLine(TextEditorWidget *widget); - virtual void reset(); + virtual bool filterSuggestions(TextEditorWidget *widget); int currentPosition() const { return m_currentPosition; } void setCurrentPosition(int position) { m_currentPosition = position; } @@ -63,6 +63,8 @@ public: int currentSuggestion() const { return m_currentSuggestion; } private: + bool filterSuggestions(TextEditorWidget *widget) override; + QList m_suggestions; int m_currentSuggestion = 0; };