forked from qt-creator/qt-creator
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 <marcus.tillmanns@qt.io>
This commit is contained in:
@@ -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
|
||||
|
@@ -16,8 +16,6 @@ QtcPlugin {
|
||||
"copilotclient.cpp",
|
||||
"copilotclient.h",
|
||||
"copilotconstants.h",
|
||||
"copilothoverhandler.cpp",
|
||||
"copilothoverhandler.h",
|
||||
"copilotplugin.cpp",
|
||||
"copilotprojectpanel.cpp",
|
||||
"copilotprojectpanel.h",
|
||||
|
@@ -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<BaseTextEditor *>(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<TextEditor::CyclicSuggestion>(suggestions, editor->document()));
|
||||
editor->addHoverHandler(&m_hoverHandler);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -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<TextEditor::TextEditorWidget *, ScheduleData> m_scheduledRequests;
|
||||
CopilotHoverHandler m_hoverHandler;
|
||||
bool m_isAskingForPassword{false};
|
||||
};
|
||||
|
||||
|
@@ -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 <texteditor/textdocument.h>
|
||||
#include <texteditor/textdocumentlayout.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <utils/tooltip/tooltip.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QScopeGuard>
|
||||
#include <QToolBar>
|
||||
#include <QToolButton>
|
||||
|
||||
using namespace TextEditor;
|
||||
using namespace LanguageServerProtocol;
|
||||
using namespace Utils;
|
||||
|
||||
namespace Copilot::Internal {
|
||||
|
||||
class CopilotCompletionToolTip : public QToolBar
|
||||
{
|
||||
public:
|
||||
CopilotCompletionToolTip(QList<CyclicSuggestion::Data> suggestions,
|
||||
int currentSuggestion,
|
||||
TextEditorWidget *editor)
|
||||
: m_numberLabel(new QLabel)
|
||||
, m_suggestions(suggestions)
|
||||
, m_currentSuggestion(std::max(0, std::min<int>(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<CyclicSuggestion>(
|
||||
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<CyclicSuggestion::Data> 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<CyclicSuggestion *>(TextDocumentLayout::suggestion(m_block));
|
||||
|
||||
if (!suggestion)
|
||||
return;
|
||||
|
||||
const QList<CyclicSuggestion::Data> 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<CyclicSuggestion *>(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
|
@@ -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 <texteditor/basehoverhandler.h>
|
||||
|
||||
#include <QTextBlock>
|
||||
|
||||
#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
|
@@ -10218,6 +10218,7 @@ TextEditorFactory::TextEditorFactory()
|
||||
: d(new TextEditorFactoryPrivate(this))
|
||||
{
|
||||
setEditorCreator([]() { return new BaseTextEditor; });
|
||||
addHoverHandler(new SuggestionHoverHandler);
|
||||
}
|
||||
|
||||
TextEditorFactory::~TextEditorFactory()
|
||||
|
@@ -5,9 +5,13 @@
|
||||
|
||||
#include "textdocumentlayout.h"
|
||||
#include "texteditor.h"
|
||||
#include "texteditortr.h"
|
||||
|
||||
#include <QToolBar>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/stringutils.h>
|
||||
#include <utils/tooltip/tooltip.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
@@ -98,4 +102,142 @@ bool CyclicSuggestion::applyPart(Part part, TextEditorWidget *widget)
|
||||
return false;
|
||||
}
|
||||
|
||||
class SuggestionToolTip : public QToolBar
|
||||
{
|
||||
public:
|
||||
SuggestionToolTip(
|
||||
QList<CyclicSuggestion::Data> suggestions, int currentSuggestion, TextEditorWidget *editor)
|
||||
: m_numberLabel(new QLabel)
|
||||
, m_suggestions(suggestions)
|
||||
, m_currentSuggestion(std::max(0, std::min<int>(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<CyclicSuggestion>(
|
||||
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<CyclicSuggestion::Data> 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<CyclicSuggestion *>(TextDocumentLayout::suggestion(m_block));
|
||||
|
||||
if (!suggestion)
|
||||
return;
|
||||
|
||||
const QList<CyclicSuggestion::Data> 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<CyclicSuggestion *>(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
|
||||
|
@@ -5,8 +5,11 @@
|
||||
|
||||
#include "texteditor_global.h"
|
||||
|
||||
#include "basehoverhandler.h"
|
||||
|
||||
#include <utils/textutils.h>
|
||||
|
||||
#include <QTextBlock>
|
||||
#include <QTextCursor>
|
||||
#include <QTextDocument>
|
||||
|
||||
@@ -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
|
||||
|
Reference in New Issue
Block a user