From 6547d1e776a901418796ec5cf5e89fdaf5b2c813 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 5 Sep 2024 10:54:50 +0200 Subject: [PATCH] Copilot: move CopilotHoverHandler to TextEditor And rename it to SuggestionHoverHandler in order to reuse it in the lua text editor interface. Change-Id: I8be9a0c2b22c20fc35adf020f8483aee573ab826 Reviewed-by: Marcus Tillmanns --- src/plugins/copilot/CMakeLists.txt | 1 - src/plugins/copilot/copilot.qbs | 2 - src/plugins/copilot/copilotclient.cpp | 9 +- src/plugins/copilot/copilotclient.h | 2 - src/plugins/copilot/copilothoverhandler.cpp | 169 -------------------- src/plugins/copilot/copilothoverhandler.h | 32 ---- src/plugins/texteditor/texteditor.cpp | 1 + src/plugins/texteditor/textsuggestion.cpp | 142 ++++++++++++++++ src/plugins/texteditor/textsuggestion.h | 18 +++ 9 files changed, 162 insertions(+), 214 deletions(-) delete mode 100644 src/plugins/copilot/copilothoverhandler.cpp delete mode 100644 src/plugins/copilot/copilothoverhandler.h diff --git a/src/plugins/copilot/CMakeLists.txt b/src/plugins/copilot/CMakeLists.txt index 8feed9d17d5..7c82008a065 100644 --- a/src/plugins/copilot/CMakeLists.txt +++ b/src/plugins/copilot/CMakeLists.txt @@ -5,7 +5,6 @@ add_qtc_plugin(Copilot copilot.qrc copilotclient.cpp copilotclient.h copilotconstants.h - copilothoverhandler.cpp copilothoverhandler.h copilotplugin.cpp copilotprojectpanel.cpp copilotprojectpanel.h copilotsettings.cpp copilotsettings.h diff --git a/src/plugins/copilot/copilot.qbs b/src/plugins/copilot/copilot.qbs index b02829f79cf..d2dcb3d5ec0 100644 --- a/src/plugins/copilot/copilot.qbs +++ b/src/plugins/copilot/copilot.qbs @@ -16,8 +16,6 @@ QtcPlugin { "copilotclient.cpp", "copilotclient.h", "copilotconstants.h", - "copilothoverhandler.cpp", - "copilothoverhandler.h", "copilotplugin.cpp", "copilotprojectpanel.cpp", "copilotprojectpanel.h", diff --git a/src/plugins/copilot/copilotclient.cpp b/src/plugins/copilot/copilotclient.cpp index 66463093574..e667a1431ec 100644 --- a/src/plugins/copilot/copilotclient.cpp +++ b/src/plugins/copilot/copilotclient.cpp @@ -97,13 +97,7 @@ CopilotClient::CopilotClient(const FilePath &nodePath, const FilePath &distPath) openDoc(doc); } -CopilotClient::~CopilotClient() -{ - for (IEditor *editor : DocumentModel::editorsForOpenedDocuments()) { - if (auto textEditor = qobject_cast(editor)) - textEditor->editorWidget()->removeHoverHandler(&m_hoverHandler); - } -} +CopilotClient::~CopilotClient() = default; void CopilotClient::openDocument(TextDocument *document) { @@ -240,7 +234,6 @@ void CopilotClient::handleCompletions(const GetCompletionRequest::Response &resp return; editor->insertSuggestion( std::make_unique(suggestions, editor->document())); - editor->addHoverHandler(&m_hoverHandler); } } diff --git a/src/plugins/copilot/copilotclient.h b/src/plugins/copilot/copilotclient.h index e9fcf043432..07d1b17b893 100644 --- a/src/plugins/copilot/copilotclient.h +++ b/src/plugins/copilot/copilotclient.h @@ -3,7 +3,6 @@ #pragma once -#include "copilothoverhandler.h" #include "requests/checkstatus.h" #include "requests/getcompletions.h" #include "requests/seteditorinfo.h" @@ -63,7 +62,6 @@ private: QTimer *timer = nullptr; }; QHash m_scheduledRequests; - CopilotHoverHandler m_hoverHandler; bool m_isAskingForPassword{false}; }; diff --git a/src/plugins/copilot/copilothoverhandler.cpp b/src/plugins/copilot/copilothoverhandler.cpp deleted file mode 100644 index c1fd7b22367..00000000000 --- a/src/plugins/copilot/copilothoverhandler.cpp +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 - -#include "copilothoverhandler.h" - -#include "copilotclient.h" -#include "copilottr.h" - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include - -using namespace TextEditor; -using namespace LanguageServerProtocol; -using namespace Utils; - -namespace Copilot::Internal { - -class CopilotCompletionToolTip : public QToolBar -{ -public: - CopilotCompletionToolTip(QList suggestions, - int currentSuggestion, - TextEditorWidget *editor) - : m_numberLabel(new QLabel) - , m_suggestions(suggestions) - , 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 Copilot Suggestion")); - prev->setEnabled(m_suggestions.size() > 1); - addWidget(m_numberLabel); - auto next = addAction(Utils::Icons::NEXT_TOOLBAR.icon(), - Tr::tr("Select Next Copilot Suggestion")); - next->setEnabled(m_suggestions.size() > 1); - - 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, &CopilotCompletionToolTip::selectPrevious); - connect(next, &QAction::triggered, this, &CopilotCompletionToolTip::selectNext); - connect(apply, &QAction::triggered, this, &CopilotCompletionToolTip::apply); - connect(applyWord, &QAction::triggered, this, &CopilotCompletionToolTip::applyWord); - connect(applyLine, &QAction::triggered, this, &CopilotCompletionToolTip::applyLine); - - updateLabels(); - } - -private: - void updateLabels() - { - m_numberLabel->setText(Tr::tr("%1 of %2") - .arg(m_currentSuggestion + 1) - .arg(m_suggestions.count())); - } - - void selectPrevious() - { - --m_currentSuggestion; - if (m_currentSuggestion < 0) - m_currentSuggestion = m_suggestions.size() - 1; - setCurrentSuggestion(); - } - - void selectNext() - { - ++m_currentSuggestion; - if (m_currentSuggestion >= m_suggestions.size()) - m_currentSuggestion = 0; - setCurrentSuggestion(); - } - - void setCurrentSuggestion() - { - updateLabels(); - if (TextSuggestion *suggestion = m_editor->currentSuggestion()) - suggestion->reset(); - m_editor->insertSuggestion(std::make_unique( - m_suggestions, m_editor->document(), m_currentSuggestion)); - } - - void apply() - { - if (TextSuggestion *suggestion = m_editor->currentSuggestion()) { - if (!suggestion->apply()) - return; - } - ToolTip::hide(); - } - - void applyWord() - { - if (TextSuggestion *suggestion = m_editor->currentSuggestion()) { - if (!suggestion->applyWord(m_editor)) - return; - } - ToolTip::hide(); - } - - void applyLine() - { - if (TextSuggestion *suggestion = m_editor->currentSuggestion()) { - if (!suggestion->applyLine(m_editor)) - return; - } - ToolTip::hide(); - } - - QLabel *m_numberLabel; - QList m_suggestions; - int m_currentSuggestion = 0; - TextEditorWidget *m_editor; -}; - -void CopilotHoverHandler::identifyMatch(TextEditorWidget *editorWidget, - int pos, - ReportPriority report) -{ - QScopeGuard cleanup([&] { report(Priority_None); }); - if (!editorWidget->suggestionVisible()) - return; - - QTextCursor cursor(editorWidget->document()); - cursor.setPosition(pos); - m_block = cursor.block(); - auto *suggestion = dynamic_cast(TextDocumentLayout::suggestion(m_block)); - - if (!suggestion) - return; - - const QList suggestions = suggestion->suggestions(); - if (suggestions.isEmpty()) - return; - - cleanup.dismiss(); - report(Priority_Suggestion); -} - -void CopilotHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point) -{ - Q_UNUSED(point) - auto *suggestion = dynamic_cast(TextDocumentLayout::suggestion(m_block)); - - if (!suggestion) - return; - - auto tooltipWidget = new CopilotCompletionToolTip(suggestion->suggestions(), - suggestion->currentSuggestion(), - editorWidget); - - const QRect cursorRect = editorWidget->cursorRect(editorWidget->textCursor()); - QPoint pos = editorWidget->viewport()->mapToGlobal(cursorRect.topLeft()) - - Utils::ToolTip::offsetFromPosition(); - pos.ry() -= tooltipWidget->sizeHint().height(); - ToolTip::show(pos, tooltipWidget, editorWidget); -} - -} // namespace Copilot::Internal diff --git a/src/plugins/copilot/copilothoverhandler.h b/src/plugins/copilot/copilothoverhandler.h deleted file mode 100644 index 1c48e75d5b7..00000000000 --- a/src/plugins/copilot/copilothoverhandler.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (C) 2023 The Qt Company Ltd. -// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0 - -#include "requests/getcompletions.h" - -#include - -#include - -#pragma once - -namespace TextEditor { class TextSuggestion; } -namespace Copilot::Internal { - -class CopilotClient; - -class CopilotHoverHandler final : public TextEditor::BaseHoverHandler -{ -public: - CopilotHoverHandler() = default; - -protected: - void identifyMatch(TextEditor::TextEditorWidget *editorWidget, - int pos, - ReportPriority report) final; - void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) final; - -private: - QTextBlock m_block; -}; - -} // namespace Copilot::Internal diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp index c7145f60ef0..afcf558ab9f 100644 --- a/src/plugins/texteditor/texteditor.cpp +++ b/src/plugins/texteditor/texteditor.cpp @@ -10218,6 +10218,7 @@ TextEditorFactory::TextEditorFactory() : d(new TextEditorFactoryPrivate(this)) { setEditorCreator([]() { return new BaseTextEditor; }); + addHoverHandler(new SuggestionHoverHandler); } TextEditorFactory::~TextEditorFactory() diff --git a/src/plugins/texteditor/textsuggestion.cpp b/src/plugins/texteditor/textsuggestion.cpp index 7b6c93a559f..21a583dd798 100644 --- a/src/plugins/texteditor/textsuggestion.cpp +++ b/src/plugins/texteditor/textsuggestion.cpp @@ -5,9 +5,13 @@ #include "textdocumentlayout.h" #include "texteditor.h" +#include "texteditortr.h" +#include #include #include +#include +#include using namespace Utils; @@ -98,4 +102,142 @@ bool CyclicSuggestion::applyPart(Part part, TextEditorWidget *widget) return false; } +class SuggestionToolTip : public QToolBar +{ +public: + SuggestionToolTip( + QList suggestions, int currentSuggestion, TextEditorWidget *editor) + : m_numberLabel(new QLabel) + , m_suggestions(suggestions) + , 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 Copilot Suggestion")); + prev->setEnabled(m_suggestions.size() > 1); + addWidget(m_numberLabel); + auto next + = addAction(Utils::Icons::NEXT_TOOLBAR.icon(), Tr::tr("Select Next Copilot Suggestion")); + next->setEnabled(m_suggestions.size() > 1); + + 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(apply, &QAction::triggered, this, &SuggestionToolTip::apply); + connect(applyWord, &QAction::triggered, this, &SuggestionToolTip::applyWord); + connect(applyLine, &QAction::triggered, this, &SuggestionToolTip::applyLine); + + updateLabels(); + } + +private: + void updateLabels() + { + m_numberLabel->setText( + Tr::tr("%1 of %2").arg(m_currentSuggestion + 1).arg(m_suggestions.count())); + } + + void selectPrevious() + { + --m_currentSuggestion; + if (m_currentSuggestion < 0) + m_currentSuggestion = m_suggestions.size() - 1; + setCurrentSuggestion(); + } + + void selectNext() + { + ++m_currentSuggestion; + if (m_currentSuggestion >= m_suggestions.size()) + m_currentSuggestion = 0; + setCurrentSuggestion(); + } + + void setCurrentSuggestion() + { + updateLabels(); + if (TextSuggestion *suggestion = m_editor->currentSuggestion()) + suggestion->reset(); + m_editor->insertSuggestion(std::make_unique( + m_suggestions, m_editor->document(), m_currentSuggestion)); + } + + void apply() + { + if (TextSuggestion *suggestion = m_editor->currentSuggestion()) { + if (!suggestion->apply()) + return; + } + ToolTip::hide(); + } + + void applyWord() + { + if (TextSuggestion *suggestion = m_editor->currentSuggestion()) { + if (!suggestion->applyWord(m_editor)) + return; + } + ToolTip::hide(); + } + + void applyLine() + { + if (TextSuggestion *suggestion = m_editor->currentSuggestion()) { + if (!suggestion->applyLine(m_editor)) + return; + } + ToolTip::hide(); + } + + QLabel *m_numberLabel; + QList m_suggestions; + int m_currentSuggestion = 0; + TextEditorWidget *m_editor; +}; + +void SuggestionHoverHandler::identifyMatch( + TextEditorWidget *editorWidget, int pos, ReportPriority report) +{ + QScopeGuard cleanup([&] { report(Priority_None); }); + if (!editorWidget->suggestionVisible()) + return; + + QTextCursor cursor(editorWidget->document()); + cursor.setPosition(pos); + m_block = cursor.block(); + auto *suggestion = dynamic_cast(TextDocumentLayout::suggestion(m_block)); + + if (!suggestion) + return; + + const QList suggestions = suggestion->suggestions(); + if (suggestions.isEmpty()) + return; + + cleanup.dismiss(); + report(Priority_Suggestion); +} + +void SuggestionHoverHandler::operateTooltip(TextEditorWidget *editorWidget, const QPoint &point) +{ + Q_UNUSED(point) + auto *suggestion = dynamic_cast(TextDocumentLayout::suggestion(m_block)); + + if (!suggestion) + return; + + auto tooltipWidget = new SuggestionToolTip( + suggestion->suggestions(), suggestion->currentSuggestion(), editorWidget); + + const QRect cursorRect = editorWidget->cursorRect(editorWidget->textCursor()); + QPoint pos = editorWidget->viewport()->mapToGlobal(cursorRect.topLeft()) + - Utils::ToolTip::offsetFromPosition(); + pos.ry() -= tooltipWidget->sizeHint().height(); + ToolTip::show(pos, tooltipWidget, editorWidget); +} + } // namespace TextEditor diff --git a/src/plugins/texteditor/textsuggestion.h b/src/plugins/texteditor/textsuggestion.h index acff9eac1fe..7745de49b33 100644 --- a/src/plugins/texteditor/textsuggestion.h +++ b/src/plugins/texteditor/textsuggestion.h @@ -5,8 +5,11 @@ #include "texteditor_global.h" +#include "basehoverhandler.h" + #include +#include #include #include @@ -67,4 +70,19 @@ private: QTextDocument *m_sourceDocument = nullptr; }; +class SuggestionHoverHandler final : public BaseHoverHandler +{ +public: + SuggestionHoverHandler() = default; + +protected: + void identifyMatch(TextEditor::TextEditorWidget *editorWidget, + int pos, + ReportPriority report) final; + void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) final; + +private: + QTextBlock m_block; +}; + } // namespace TextEditor