Copilot: Move closer to latest settings setup and usage pattern

Change-Id: I86983f55cd53a540e2fe1a5b307ebf67f7ea7a98
Reviewed-by: Marcus Tillmanns <marcus.tillmanns@qt.io>
This commit is contained in:
hjk
2023-07-14 16:29:11 +02:00
parent bd24f18dd5
commit 17267bb15c
9 changed files with 130 additions and 175 deletions

View File

@@ -6,7 +6,6 @@ add_qtc_plugin(Copilot
copilotclient.cpp copilotclient.h
copilotconstants.h
copilothoverhandler.cpp copilothoverhandler.h
copilotoptionspage.cpp copilotoptionspage.h
copilotplugin.cpp copilotplugin.h
copilotprojectpanel.cpp copilotprojectpanel.h
copilotsettings.cpp copilotsettings.h

View File

@@ -18,8 +18,6 @@ QtcPlugin {
"copilotconstants.h",
"copilothoverhandler.cpp",
"copilothoverhandler.h",
"copilotoptionspage.cpp",
"copilotoptionspage.h",
"copilotplugin.cpp",
"copilotplugin.h",
"copilotprojectpanel.cpp",

View File

@@ -92,7 +92,7 @@ void CopilotClient::openDocument(TextDocument *document)
this,
[this, document](int position, int charsRemoved, int charsAdded) {
Q_UNUSED(charsRemoved)
if (!CopilotSettings::instance().autoComplete())
if (!settings().autoComplete())
return;
auto project = ProjectManager::projectForFile(document->filePath());
@@ -263,7 +263,7 @@ bool CopilotClient::canOpenProject(Project *project)
bool CopilotClient::isEnabled(Project *project)
{
if (!project)
return CopilotSettings::instance().enableCopilot();
return settings().enableCopilot();
CopilotProjectSettings settings(project);
return settings.isEnabled();

View File

@@ -1,117 +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 "copilotoptionspage.h"
#include "authwidget.h"
#include "copilotconstants.h"
#include "copilotsettings.h"
#include "copilottr.h"
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <QToolTip>
using namespace Utils;
using namespace LanguageClient;
namespace Copilot {
class CopilotOptionsPageWidget : public Core::IOptionsPageWidget
{
public:
CopilotOptionsPageWidget()
{
using namespace Layouting;
auto warningLabel = new QLabel;
warningLabel->setWordWrap(true);
warningLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse
| Qt::LinksAccessibleByKeyboard
| Qt::TextSelectableByMouse);
warningLabel->setText(Tr::tr(
"Enabling %1 is subject to your agreement and abidance with your applicable "
"%1 terms. It is your responsibility to know and accept the requirements and "
"parameters of using tools like %1. This may include, but is not limited to, "
"ensuring you have the rights to allow %1 access to your code, as well as "
"understanding any implications of your use of %1 and suggestions produced "
"(like copyright, accuracy, etc.)." ).arg("Copilot"));
auto authWidget = new AuthWidget();
auto helpLabel = new QLabel();
helpLabel->setTextFormat(Qt::MarkdownText);
helpLabel->setWordWrap(true);
helpLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse
| Qt::LinksAccessibleByKeyboard
| Qt::TextSelectableByMouse);
helpLabel->setOpenExternalLinks(true);
connect(helpLabel, &QLabel::linkHovered, [](const QString &link) {
QToolTip::showText(QCursor::pos(), link);
});
// clang-format off
helpLabel->setText(Tr::tr(
"The Copilot plugin requires node.js and the Copilot neovim plugin. "
"If you install the neovim plugin as described in %1, "
"the plugin will find the agent.js file automatically.\n\n"
"Otherwise you need to specify the path to the %2 "
"file from the Copilot neovim plugin.",
"Markdown text for the copilot instruction label")
.arg("[README.md](https://github.com/github/copilot.vim)")
.arg("[agent.js](https://github.com/github/copilot.vim/tree/release/copilot/dist)"));
Column {
QString("<b>" + Tr::tr("Note:") + "</b>"), br,
warningLabel, br,
CopilotSettings::instance().enableCopilot, br,
authWidget, br,
CopilotSettings::instance().nodeJsPath, br,
CopilotSettings::instance().distPath, br,
CopilotSettings::instance().autoComplete, br,
helpLabel, br,
st
}.attachTo(this);
// clang-format on
auto updateAuthWidget = [authWidget]() {
authWidget->updateClient(
FilePath::fromUserInput(CopilotSettings::instance().nodeJsPath.volatileValue()),
FilePath::fromUserInput(CopilotSettings::instance().distPath.volatileValue()));
};
connect(CopilotSettings::instance().nodeJsPath.pathChooser(),
&PathChooser::textChanged,
authWidget,
updateAuthWidget);
connect(CopilotSettings::instance().distPath.pathChooser(),
&PathChooser::textChanged,
authWidget,
updateAuthWidget);
updateAuthWidget();
setOnApply([] {
CopilotSettings::instance().apply();
CopilotSettings::instance().writeSettings();
});
}
};
CopilotOptionsPage::CopilotOptionsPage()
{
setId(Constants::COPILOT_GENERAL_OPTIONS_ID);
setDisplayName("Copilot");
setCategory(Constants::COPILOT_GENERAL_OPTIONS_CATEGORY);
setDisplayCategory(Constants::COPILOT_GENERAL_OPTIONS_DISPLAY_CATEGORY);
setCategoryIconPath(":/copilot/images/settingscategory_copilot.png");
setWidgetCreator([] { return new CopilotOptionsPageWidget; });
}
CopilotOptionsPage &CopilotOptionsPage::instance()
{
static CopilotOptionsPage settingsPage;
return settingsPage;
}
} // namespace Copilot

View File

@@ -1,18 +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
#pragma once
#include <coreplugin/dialogs/ioptionspage.h>
namespace Copilot {
class CopilotOptionsPage : public Core::IOptionsPage
{
public:
CopilotOptionsPage();
static CopilotOptionsPage &instance();
};
} // Copilot

View File

@@ -6,7 +6,6 @@
#include "copilotclient.h"
#include "copilotconstants.h"
#include "copiloticons.h"
#include "copilotoptionspage.h"
#include "copilotprojectpanel.h"
#include "copilotsettings.h"
#include "copilotsuggestion.h"
@@ -34,6 +33,7 @@ namespace Copilot {
namespace Internal {
enum Direction { Previous, Next };
void cycleSuggestion(TextEditor::TextEditorWidget *editor, Direction direction)
{
QTextBlock block = editor->textCursor().block();
@@ -57,14 +57,9 @@ void cycleSuggestion(TextEditor::TextEditorWidget *editor, Direction direction)
void CopilotPlugin::initialize()
{
CopilotSettings::instance().readSettings();
restartClient();
connect(&CopilotSettings::instance(),
&CopilotSettings::applied,
this,
&CopilotPlugin::restartClient);
connect(&settings(), &AspectContainer::applied, this, &CopilotPlugin::restartClient);
QAction *requestAction = new QAction(this);
requestAction->setText(Tr::tr("Request Copilot Suggestion"));
@@ -108,8 +103,8 @@ void CopilotPlugin::initialize()
disableAction->setText(Tr::tr("Disable Copilot"));
disableAction->setToolTip(Tr::tr("Disable Copilot."));
connect(disableAction, &QAction::triggered, this, [] {
CopilotSettings::instance().enableCopilot.setValue(true);
CopilotSettings::instance().apply();
settings().enableCopilot.setValue(true);
settings().apply();
});
ActionManager::registerAction(disableAction, Constants::COPILOT_DISABLE);
@@ -117,32 +112,31 @@ void CopilotPlugin::initialize()
enableAction->setText(Tr::tr("Enable Copilot"));
enableAction->setToolTip(Tr::tr("Enable Copilot."));
connect(enableAction, &QAction::triggered, this, [] {
CopilotSettings::instance().enableCopilot.setValue(false);
CopilotSettings::instance().apply();
settings().enableCopilot.setValue(false);
settings().apply();
});
ActionManager::registerAction(enableAction, Constants::COPILOT_ENABLE);
QAction *toggleAction = new QAction(this);
toggleAction->setText(Tr::tr("Toggle Copilot"));
toggleAction->setCheckable(true);
toggleAction->setChecked(CopilotSettings::instance().enableCopilot.value());
toggleAction->setChecked(settings().enableCopilot());
toggleAction->setIcon(COPILOT_ICON.icon());
connect(toggleAction, &QAction::toggled, this, [](bool checked) {
CopilotSettings::instance().enableCopilot.setValue(checked);
CopilotSettings::instance().apply();
settings().enableCopilot.setValue(checked);
settings().apply();
});
ActionManager::registerAction(toggleAction, Constants::COPILOT_TOGGLE);
auto updateActions = [toggleAction, requestAction] {
const bool enabled = CopilotSettings::instance().enableCopilot.value();
const bool enabled = settings().enableCopilot();
toggleAction->setToolTip(enabled ? Tr::tr("Disable Copilot.") : Tr::tr("Enable Copilot."));
toggleAction->setChecked(enabled);
requestAction->setEnabled(enabled);
};
connect(&CopilotSettings::instance().enableCopilot, &BaseAspect::changed,
this, updateActions);
connect(&settings().enableCopilot, &BaseAspect::changed, this, updateActions);
updateActions();
@@ -157,19 +151,13 @@ void CopilotPlugin::initialize()
ProjectPanelFactory::registerFactory(panelFactory);
}
void CopilotPlugin::extensionsInitialized()
{
(void)CopilotOptionsPage::instance();
}
void CopilotPlugin::restartClient()
{
LanguageClient::LanguageClientManager::shutdownClient(m_client);
if (!CopilotSettings::instance().nodeJsPath().isExecutableFile())
if (!settings().nodeJsPath().isExecutableFile())
return;
m_client = new CopilotClient(CopilotSettings::instance().nodeJsPath(),
CopilotSettings::instance().distPath());
m_client = new CopilotClient(settings().nodeJsPath(), settings().distPath());
}
ExtensionSystem::IPlugin::ShutdownFlag CopilotPlugin::aboutToShutdown()

View File

@@ -19,7 +19,6 @@ class CopilotPlugin : public ExtensionSystem::IPlugin
public:
void initialize() override;
void extensionsInitialized() override;
void restartClient();
ShutdownFlag aboutToShutdown() override;

View File

@@ -2,13 +2,21 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
#include "copilotsettings.h"
#include "authwidget.h"
#include "copilotconstants.h"
#include "copilottr.h"
#include <coreplugin/dialogs/ioptionspage.h>
#include <projectexplorer/project.h>
#include <utils/algorithm.h>
#include <utils/environment.h>
#include <utils/layoutbuilder.h>
#include <utils/pathchooser.h>
#include <QToolTip>
using namespace Utils;
@@ -23,7 +31,7 @@ static void initEnableAspect(BoolAspect &enableCopilot)
enableCopilot.setDefaultValue(false);
}
CopilotSettings &CopilotSettings::instance()
CopilotSettings &settings()
{
static CopilotSettings settings;
return settings;
@@ -79,6 +87,8 @@ CopilotSettings::CopilotSettings()
"position after changes to the document."));
initEnableAspect(enableCopilot);
readSettings();
}
CopilotProjectSettings::CopilotProjectSettings(ProjectExplorer::Project *project, QObject *parent)
@@ -106,7 +116,7 @@ void CopilotProjectSettings::setUseGlobalSettings(bool useGlobal)
bool CopilotProjectSettings::isEnabled() const
{
if (useGlobalSettings())
return CopilotSettings::instance().enableCopilot();
return settings().enableCopilot();
return enableCopilot();
}
@@ -117,7 +127,105 @@ void CopilotProjectSettings::save(ProjectExplorer::Project *project)
project->setNamedSettings(Constants::COPILOT_PROJECT_SETTINGS_ID, map);
// This triggers a restart of the Copilot language server.
CopilotSettings::instance().apply();
settings().apply();
}
} // namespace Copilot
// CopilotOptionsPageWidget
class CopilotOptionsPageWidget : public Core::IOptionsPageWidget
{
public:
CopilotOptionsPageWidget()
{
using namespace Layouting;
auto warningLabel = new QLabel;
warningLabel->setWordWrap(true);
warningLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse
| Qt::LinksAccessibleByKeyboard
| Qt::TextSelectableByMouse);
warningLabel->setText(Tr::tr(
"Enabling %1 is subject to your agreement and abidance with your applicable "
"%1 terms. It is your responsibility to know and accept the requirements and "
"parameters of using tools like %1. This may include, but is not limited to, "
"ensuring you have the rights to allow %1 access to your code, as well as "
"understanding any implications of your use of %1 and suggestions produced "
"(like copyright, accuracy, etc.)." ).arg("Copilot"));
auto authWidget = new AuthWidget();
auto helpLabel = new QLabel();
helpLabel->setTextFormat(Qt::MarkdownText);
helpLabel->setWordWrap(true);
helpLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse
| Qt::LinksAccessibleByKeyboard
| Qt::TextSelectableByMouse);
helpLabel->setOpenExternalLinks(true);
connect(helpLabel, &QLabel::linkHovered, [](const QString &link) {
QToolTip::showText(QCursor::pos(), link);
});
// clang-format off
helpLabel->setText(Tr::tr(
"The Copilot plugin requires node.js and the Copilot neovim plugin. "
"If you install the neovim plugin as described in %1, "
"the plugin will find the agent.js file automatically.\n\n"
"Otherwise you need to specify the path to the %2 "
"file from the Copilot neovim plugin.",
"Markdown text for the copilot instruction label")
.arg("[README.md](https://github.com/github/copilot.vim)")
.arg("[agent.js](https://github.com/github/copilot.vim/tree/release/copilot/dist)"));
Column {
QString("<b>" + Tr::tr("Note:") + "</b>"), br,
warningLabel, br,
settings().enableCopilot, br,
authWidget, br,
settings().nodeJsPath, br,
settings().distPath, br,
settings().autoComplete, br,
helpLabel, br,
st
}.attachTo(this);
// clang-format on
auto updateAuthWidget = [authWidget]() {
authWidget->updateClient(
FilePath::fromUserInput(settings().nodeJsPath.volatileValue()),
FilePath::fromUserInput(settings().distPath.volatileValue()));
};
connect(settings().nodeJsPath.pathChooser(),
&PathChooser::textChanged,
authWidget,
updateAuthWidget);
connect(settings().distPath.pathChooser(),
&PathChooser::textChanged,
authWidget,
updateAuthWidget);
updateAuthWidget();
setOnApply([] {
settings().apply();
settings().writeSettings();
});
}
};
class CopilotSettingsPage : public Core::IOptionsPage
{
public:
CopilotSettingsPage()
{
setId(Constants::COPILOT_GENERAL_OPTIONS_ID);
setDisplayName("Copilot");
setCategory(Constants::COPILOT_GENERAL_OPTIONS_CATEGORY);
setDisplayCategory(Constants::COPILOT_GENERAL_OPTIONS_DISPLAY_CATEGORY);
setCategoryIconPath(":/copilot/images/settingscategory_copilot.png");
setWidgetCreator([] { return new CopilotOptionsPageWidget; });
}
};
const CopilotSettingsPage settingsPage;
} // Copilot

View File

@@ -5,9 +5,7 @@
#include <utils/aspects.h>
namespace ProjectExplorer {
class Project;
}
namespace ProjectExplorer { class Project; }
namespace Copilot {
@@ -16,14 +14,14 @@ class CopilotSettings : public Utils::AspectContainer
public:
CopilotSettings();
static CopilotSettings &instance();
Utils::FilePathAspect nodeJsPath{this};
Utils::FilePathAspect distPath{this};
Utils::BoolAspect autoComplete{this};
Utils::BoolAspect enableCopilot{this};
};
CopilotSettings &settings();
class CopilotProjectSettings : public Utils::AspectContainer
{
public: