forked from qt-creator/qt-creator
Copilot: Add support for proxy settings
For easier testing a docker file is added. You can start "buildandrun.sh" in copilot/tests/proxy to get a simple proxy server up and running. The argument "PWDMODE" in buildandrun.sh can be set to "with" and "without" to get a proxy server that needs a password or not. The username and password are user/1234. Fixes: QTCREATORBUG-29485 Change-Id: I3859c9ad04ebd4f9349e25665ba710e23fb64dea Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
@@ -44,18 +44,21 @@ AuthWidget::AuthWidget(QWidget *parent)
|
|||||||
}.attachTo(this);
|
}.attachTo(this);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
connect(m_button, &QPushButton::clicked, this, [this]() {
|
|
||||||
if (m_status == Status::SignedIn)
|
|
||||||
signOut();
|
|
||||||
else if (m_status == Status::SignedOut)
|
|
||||||
signIn();
|
|
||||||
});
|
|
||||||
|
|
||||||
auto update = [this] {
|
auto update = [this] {
|
||||||
updateClient(FilePath::fromUserInput(settings().nodeJsPath.volatileValue()),
|
updateClient(FilePath::fromUserInput(settings().nodeJsPath.volatileValue()),
|
||||||
FilePath::fromUserInput(settings().distPath.volatileValue()));
|
FilePath::fromUserInput(settings().distPath.volatileValue()));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
connect(m_button, &QPushButton::clicked, this, [this, update]() {
|
||||||
|
if (m_status == Status::SignedIn)
|
||||||
|
signOut();
|
||||||
|
else if (m_status == Status::SignedOut)
|
||||||
|
signIn();
|
||||||
|
else
|
||||||
|
update();
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(&settings(), &CopilotSettings::applied, this, update);
|
||||||
connect(settings().nodeJsPath.pathChooser(), &PathChooser::textChanged, this, update);
|
connect(settings().nodeJsPath.pathChooser(), &PathChooser::textChanged, this, update);
|
||||||
connect(settings().distPath.pathChooser(), &PathChooser::textChanged, this, update);
|
connect(settings().distPath.pathChooser(), &PathChooser::textChanged, this, update);
|
||||||
|
|
||||||
@@ -68,35 +71,39 @@ AuthWidget::~AuthWidget()
|
|||||||
LanguageClientManager::shutdownClient(m_client);
|
LanguageClientManager::shutdownClient(m_client);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthWidget::setState(const QString &buttonText, bool working)
|
void AuthWidget::setState(const QString &buttonText, const QString &errorText, bool working)
|
||||||
{
|
{
|
||||||
m_button->setText(buttonText);
|
m_button->setText(buttonText);
|
||||||
m_button->setVisible(true);
|
m_button->setVisible(true);
|
||||||
m_progressIndicator->setVisible(working);
|
m_progressIndicator->setVisible(working);
|
||||||
|
m_statusLabel->setText(errorText);
|
||||||
m_statusLabel->setVisible(!m_statusLabel->text().isEmpty());
|
m_statusLabel->setVisible(!m_statusLabel->text().isEmpty());
|
||||||
m_button->setEnabled(!working);
|
m_button->setEnabled(!working);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AuthWidget::checkStatus()
|
void AuthWidget::checkStatus()
|
||||||
{
|
{
|
||||||
|
if (!isEnabled())
|
||||||
|
return;
|
||||||
|
|
||||||
QTC_ASSERT(m_client && m_client->reachable(), return);
|
QTC_ASSERT(m_client && m_client->reachable(), return);
|
||||||
|
|
||||||
setState("Checking status ...", true);
|
setState("Checking status ...", {}, true);
|
||||||
|
|
||||||
m_client->requestCheckStatus(false, [this](const CheckStatusRequest::Response &response) {
|
m_client->requestCheckStatus(false, [this](const CheckStatusRequest::Response &response) {
|
||||||
if (response.error()) {
|
if (response.error()) {
|
||||||
setState("failed: " + response.error()->message(), false);
|
setState("Failed to authenticate", response.error()->message(), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const CheckStatusResponse result = *response.result();
|
const CheckStatusResponse result = *response.result();
|
||||||
|
|
||||||
if (result.user().isEmpty()) {
|
if (result.user().isEmpty()) {
|
||||||
setState("Sign in", false);
|
setState("Sign in", {}, false);
|
||||||
m_status = Status::SignedOut;
|
m_status = Status::SignedOut;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState("Sign out " + result.user(), false);
|
setState("Sign out " + result.user(), {}, false);
|
||||||
m_status = Status::SignedIn;
|
m_status = Status::SignedIn;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -105,12 +112,12 @@ void AuthWidget::updateClient(const FilePath &nodeJs, const FilePath &agent)
|
|||||||
{
|
{
|
||||||
LanguageClientManager::shutdownClient(m_client);
|
LanguageClientManager::shutdownClient(m_client);
|
||||||
m_client = nullptr;
|
m_client = nullptr;
|
||||||
setState(Tr::tr("Sign In"), false);
|
setState(Tr::tr("Sign In"), {}, false);
|
||||||
m_button->setEnabled(false);
|
m_button->setEnabled(false);
|
||||||
if (!nodeJs.isExecutableFile() || !agent.exists())
|
if (!nodeJs.isExecutableFile() || !agent.exists())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
setState(Tr::tr("Sign In"), true);
|
setState(Tr::tr("Sign In"), {}, true);
|
||||||
|
|
||||||
m_client = new CopilotClient(nodeJs, agent);
|
m_client = new CopilotClient(nodeJs, agent);
|
||||||
connect(m_client, &Client::initialized, this, &AuthWidget::checkStatus);
|
connect(m_client, &Client::initialized, this, &AuthWidget::checkStatus);
|
||||||
@@ -127,7 +134,7 @@ void AuthWidget::signIn()
|
|||||||
qCritical() << "Not implemented";
|
qCritical() << "Not implemented";
|
||||||
QTC_ASSERT(m_client && m_client->reachable(), return);
|
QTC_ASSERT(m_client && m_client->reachable(), return);
|
||||||
|
|
||||||
setState("Signing in ...", true);
|
setState("Signing in ...", {}, true);
|
||||||
|
|
||||||
m_client->requestSignInInitiate([this](const SignInInitiateRequest::Response &response) {
|
m_client->requestSignInInitiate([this](const SignInInitiateRequest::Response &response) {
|
||||||
QTC_ASSERT(!response.error(), return);
|
QTC_ASSERT(!response.error(), return);
|
||||||
@@ -144,19 +151,17 @@ void AuthWidget::signIn()
|
|||||||
m_client
|
m_client
|
||||||
->requestSignInConfirm(response.result()->userCode(),
|
->requestSignInConfirm(response.result()->userCode(),
|
||||||
[this](const SignInConfirmRequest::Response &response) {
|
[this](const SignInConfirmRequest::Response &response) {
|
||||||
m_statusLabel->setText("");
|
|
||||||
|
|
||||||
if (response.error()) {
|
if (response.error()) {
|
||||||
QMessageBox::critical(this,
|
QMessageBox::critical(this,
|
||||||
Tr::tr("Login Failed"),
|
Tr::tr("Login Failed"),
|
||||||
Tr::tr(
|
Tr::tr(
|
||||||
"The login request failed: ")
|
"The login request failed: ")
|
||||||
+ response.error()->message());
|
+ response.error()->message());
|
||||||
setState("Sign in", false);
|
setState("Sign in", response.error()->message(), false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
setState("Sign Out " + response.result()->user(), false);
|
setState("Sign Out " + response.result()->user(), {}, false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -165,7 +170,7 @@ void AuthWidget::signOut()
|
|||||||
{
|
{
|
||||||
QTC_ASSERT(m_client && m_client->reachable(), return);
|
QTC_ASSERT(m_client && m_client->reachable(), return);
|
||||||
|
|
||||||
setState("Signing out ...", true);
|
setState("Signing out ...", {}, true);
|
||||||
|
|
||||||
m_client->requestSignOut([this](const SignOutRequest::Response &response) {
|
m_client->requestSignOut([this](const SignOutRequest::Response &response) {
|
||||||
QTC_ASSERT(!response.error(), return);
|
QTC_ASSERT(!response.error(), return);
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ public:
|
|||||||
void updateClient(const Utils::FilePath &nodeJs, const Utils::FilePath &agent);
|
void updateClient(const Utils::FilePath &nodeJs, const Utils::FilePath &agent);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setState(const QString &buttonText, bool working);
|
void setState(const QString &buttonText, const QString &errorText, bool working);
|
||||||
void checkStatus();
|
void checkStatus();
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -2,26 +2,32 @@
|
|||||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
#include "copilotclient.h"
|
#include "copilotclient.h"
|
||||||
#include "copilotconstants.h"
|
|
||||||
#include "copilotsettings.h"
|
#include "copilotsettings.h"
|
||||||
#include "copilotsuggestion.h"
|
#include "copilotsuggestion.h"
|
||||||
|
#include "copilottr.h"
|
||||||
|
|
||||||
|
#include <app/app_version.h>
|
||||||
|
|
||||||
#include <languageclient/languageclientinterface.h>
|
#include <languageclient/languageclientinterface.h>
|
||||||
#include <languageclient/languageclientmanager.h>
|
#include <languageclient/languageclientmanager.h>
|
||||||
#include <languageclient/languageclientsettings.h>
|
#include <languageclient/languageclientsettings.h>
|
||||||
|
#include <languageserverprotocol/lsptypes.h>
|
||||||
|
|
||||||
#include <coreplugin/actionmanager/actionmanager.h>
|
#include <coreplugin/actionmanager/actionmanager.h>
|
||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
|
||||||
#include <projectexplorer/projectmanager.h>
|
#include <projectexplorer/projectmanager.h>
|
||||||
|
|
||||||
#include <utils/filepath.h>
|
|
||||||
|
|
||||||
#include <texteditor/textdocumentlayout.h>
|
#include <texteditor/textdocumentlayout.h>
|
||||||
#include <texteditor/texteditor.h>
|
#include <texteditor/texteditor.h>
|
||||||
|
|
||||||
#include <languageserverprotocol/lsptypes.h>
|
#include <utils/checkablemessagebox.h>
|
||||||
|
#include <utils/filepath.h>
|
||||||
|
#include <utils/passworddialog.h>
|
||||||
|
|
||||||
|
#include <QInputDialog>
|
||||||
|
#include <QLoggingCategory>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
#include <QToolButton>
|
#include <QToolButton>
|
||||||
|
|
||||||
@@ -31,6 +37,8 @@ using namespace Utils;
|
|||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
using namespace Core;
|
using namespace Core;
|
||||||
|
|
||||||
|
Q_LOGGING_CATEGORY(copilotClientLog, "qtc.copilot.client", QtWarningMsg)
|
||||||
|
|
||||||
namespace Copilot::Internal {
|
namespace Copilot::Internal {
|
||||||
|
|
||||||
static LanguageClient::BaseClientInterface *clientInterface(const FilePath &nodePath,
|
static LanguageClient::BaseClientInterface *clientInterface(const FilePath &nodePath,
|
||||||
@@ -52,6 +60,23 @@ CopilotClient::CopilotClient(const FilePath &nodePath, const FilePath &distPath)
|
|||||||
langFilter.filePattern = {"*"};
|
langFilter.filePattern = {"*"};
|
||||||
|
|
||||||
setSupportedLanguage(langFilter);
|
setSupportedLanguage(langFilter);
|
||||||
|
|
||||||
|
registerCustomMethod("LogMessage", [this](const LanguageServerProtocol::JsonRpcMessage &message) {
|
||||||
|
QString msg = message.toJsonObject().value("params").toObject().value("message").toString();
|
||||||
|
qCDebug(copilotClientLog) << message.toJsonObject()
|
||||||
|
.value("params")
|
||||||
|
.toObject()
|
||||||
|
.value("message")
|
||||||
|
.toString();
|
||||||
|
|
||||||
|
if (msg.contains("Socket Connect returned status code,407")) {
|
||||||
|
qCWarning(copilotClientLog) << "Proxy authentication required";
|
||||||
|
QMetaObject::invokeMethod(this,
|
||||||
|
&CopilotClient::proxyAuthenticationFailed,
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
start();
|
start();
|
||||||
|
|
||||||
auto openDoc = [this](IDocument *document) {
|
auto openDoc = [this](IDocument *document) {
|
||||||
@@ -68,6 +93,8 @@ CopilotClient::CopilotClient(const FilePath &nodePath, const FilePath &distPath)
|
|||||||
closeDocument(textDocument);
|
closeDocument(textDocument);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
connect(this, &LanguageClient::Client::initialized, this, &CopilotClient::requestSetEditorInfo);
|
||||||
|
|
||||||
for (IDocument *doc : DocumentModel::openedDocuments())
|
for (IDocument *doc : DocumentModel::openedDocuments())
|
||||||
openDoc(doc);
|
openDoc(doc);
|
||||||
}
|
}
|
||||||
@@ -218,6 +245,32 @@ void CopilotClient::cancelRunningRequest(TextEditor::TextEditorWidget *editor)
|
|||||||
m_runningRequests.erase(it);
|
m_runningRequests.erase(it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static QString currentProxyPassword;
|
||||||
|
|
||||||
|
void CopilotClient::requestSetEditorInfo()
|
||||||
|
{
|
||||||
|
if (settings().saveProxyPassword())
|
||||||
|
currentProxyPassword = settings().proxyPassword();
|
||||||
|
|
||||||
|
const EditorInfo editorInfo{Core::Constants::IDE_VERSION_DISPLAY, "Qt Creator"};
|
||||||
|
const EditorPluginInfo editorPluginInfo{Core::Constants::IDE_VERSION_DISPLAY,
|
||||||
|
"Qt Creator Copilot plugin"};
|
||||||
|
|
||||||
|
SetEditorInfoParams params(editorInfo, editorPluginInfo);
|
||||||
|
|
||||||
|
if (settings().useProxy()) {
|
||||||
|
params.setNetworkProxy(
|
||||||
|
Copilot::NetworkProxy{settings().proxyHost(),
|
||||||
|
static_cast<int>(settings().proxyPort()),
|
||||||
|
settings().proxyUser(),
|
||||||
|
currentProxyPassword,
|
||||||
|
settings().proxyRejectUnauthorized()});
|
||||||
|
}
|
||||||
|
|
||||||
|
SetEditorInfoRequest request(params);
|
||||||
|
sendMessage(request);
|
||||||
|
}
|
||||||
|
|
||||||
void CopilotClient::requestCheckStatus(
|
void CopilotClient::requestCheckStatus(
|
||||||
bool localChecksOnly, std::function<void(const CheckStatusRequest::Response &response)> callback)
|
bool localChecksOnly, std::function<void(const CheckStatusRequest::Response &response)> callback)
|
||||||
{
|
{
|
||||||
@@ -269,4 +322,36 @@ bool CopilotClient::isEnabled(Project *project)
|
|||||||
return settings.isEnabled();
|
return settings.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CopilotClient::proxyAuthenticationFailed()
|
||||||
|
{
|
||||||
|
static bool doNotAskAgain = false;
|
||||||
|
|
||||||
|
if (m_isAskingForPassword || !settings().enableCopilot())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_isAskingForPassword = true;
|
||||||
|
|
||||||
|
auto answer = Utils::PasswordDialog::getUserAndPassword(
|
||||||
|
Tr::tr("Copilot"),
|
||||||
|
Tr::tr("Proxy username and password required:"),
|
||||||
|
Tr::tr("Do not ask again. This will disable Copilot for now."),
|
||||||
|
settings().proxyUser(),
|
||||||
|
&doNotAskAgain,
|
||||||
|
Core::ICore::dialogParent());
|
||||||
|
|
||||||
|
if (answer) {
|
||||||
|
settings().proxyUser.setValue(answer->first);
|
||||||
|
currentProxyPassword = answer->second;
|
||||||
|
} else {
|
||||||
|
settings().enableCopilot.setValue(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings().saveProxyPassword())
|
||||||
|
settings().proxyPassword.setValue(currentProxyPassword);
|
||||||
|
|
||||||
|
settings().apply();
|
||||||
|
|
||||||
|
m_isAskingForPassword = false;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Copilot::Internal
|
} // namespace Copilot::Internal
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "copilothoverhandler.h"
|
#include "copilothoverhandler.h"
|
||||||
#include "requests/checkstatus.h"
|
#include "requests/checkstatus.h"
|
||||||
#include "requests/getcompletions.h"
|
#include "requests/getcompletions.h"
|
||||||
|
#include "requests/seteditorinfo.h"
|
||||||
#include "requests/signinconfirm.h"
|
#include "requests/signinconfirm.h"
|
||||||
#include "requests/signininitiate.h"
|
#include "requests/signininitiate.h"
|
||||||
#include "requests/signout.h"
|
#include "requests/signout.h"
|
||||||
@@ -50,7 +51,11 @@ public:
|
|||||||
|
|
||||||
bool isEnabled(ProjectExplorer::Project *project);
|
bool isEnabled(ProjectExplorer::Project *project);
|
||||||
|
|
||||||
|
void proxyAuthenticationFailed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void requestSetEditorInfo();
|
||||||
|
|
||||||
QMap<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests;
|
QMap<TextEditor::TextEditorWidget *, GetCompletionRequest> m_runningRequests;
|
||||||
struct ScheduleData
|
struct ScheduleData
|
||||||
{
|
{
|
||||||
@@ -59,6 +64,7 @@ private:
|
|||||||
};
|
};
|
||||||
QMap<TextEditor::TextEditorWidget *, ScheduleData> m_scheduledRequests;
|
QMap<TextEditor::TextEditorWidget *, ScheduleData> m_scheduledRequests;
|
||||||
CopilotHoverHandler m_hoverHandler;
|
CopilotHoverHandler m_hoverHandler;
|
||||||
|
bool m_isAskingForPassword{false};
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Copilot::Internal
|
} // namespace Copilot::Internal
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ CopilotSettings::CopilotSettings()
|
|||||||
const FilePath nodeFromPath = FilePath("node").searchInPath();
|
const FilePath nodeFromPath = FilePath("node").searchInPath();
|
||||||
|
|
||||||
const FilePaths searchDirs
|
const FilePaths searchDirs
|
||||||
|
|
||||||
= {FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/dist/agent.js"),
|
= {FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/dist/agent.js"),
|
||||||
FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/copilot/dist/agent.js"),
|
FilePath::fromUserInput("~/.vim/pack/github/start/copilot.vim/copilot/dist/agent.js"),
|
||||||
FilePath::fromUserInput(
|
FilePath::fromUserInput(
|
||||||
@@ -80,13 +79,75 @@ CopilotSettings::CopilotSettings()
|
|||||||
|
|
||||||
autoComplete.setDisplayName(Tr::tr("Auto Complete"));
|
autoComplete.setDisplayName(Tr::tr("Auto Complete"));
|
||||||
autoComplete.setSettingsKey("Copilot.Autocomplete");
|
autoComplete.setSettingsKey("Copilot.Autocomplete");
|
||||||
autoComplete.setLabelText(Tr::tr("Request completions automatically"));
|
autoComplete.setLabelText(Tr::tr("Auto request"));
|
||||||
autoComplete.setDefaultValue(true);
|
autoComplete.setDefaultValue(true);
|
||||||
autoComplete.setEnabler(&enableCopilot);
|
autoComplete.setEnabler(&enableCopilot);
|
||||||
autoComplete.setToolTip(Tr::tr("Automatically request suggestions for the current text cursor "
|
autoComplete.setToolTip(Tr::tr("Automatically request suggestions for the current text cursor "
|
||||||
"position after changes to the document."));
|
"position after changes to the document."));
|
||||||
|
autoComplete.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||||
|
|
||||||
|
useProxy.setDisplayName(Tr::tr("Use Proxy"));
|
||||||
|
useProxy.setSettingsKey("Copilot.UseProxy");
|
||||||
|
useProxy.setLabelText(Tr::tr("Use Proxy"));
|
||||||
|
useProxy.setDefaultValue(false);
|
||||||
|
useProxy.setEnabler(&enableCopilot);
|
||||||
|
useProxy.setToolTip(Tr::tr("Use a proxy to connect to the Copilot servers."));
|
||||||
|
useProxy.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||||
|
|
||||||
|
proxyHost.setDisplayName(Tr::tr("Proxy Host"));
|
||||||
|
proxyHost.setDisplayStyle(StringAspect::LineEditDisplay);
|
||||||
|
proxyHost.setSettingsKey("Copilot.ProxyHost");
|
||||||
|
proxyHost.setLabelText(Tr::tr("Proxy Host"));
|
||||||
|
proxyHost.setDefaultValue("");
|
||||||
|
proxyHost.setEnabler(&useProxy);
|
||||||
|
proxyHost.setToolTip(Tr::tr("The host name of the proxy server."));
|
||||||
|
proxyHost.setHistoryCompleter("Copilot.ProxyHost.History");
|
||||||
|
|
||||||
|
proxyPort.setDisplayName(Tr::tr("Proxy Port"));
|
||||||
|
proxyPort.setSettingsKey("Copilot.ProxyPort");
|
||||||
|
proxyPort.setLabelText(Tr::tr("Proxy Port"));
|
||||||
|
proxyPort.setDefaultValue(3128);
|
||||||
|
proxyPort.setEnabler(&useProxy);
|
||||||
|
proxyPort.setToolTip(Tr::tr("The port of the proxy server."));
|
||||||
|
proxyPort.setRange(1, 65535);
|
||||||
|
|
||||||
|
proxyUser.setDisplayName(Tr::tr("Proxy User"));
|
||||||
|
proxyUser.setDisplayStyle(StringAspect::LineEditDisplay);
|
||||||
|
proxyUser.setSettingsKey("Copilot.ProxyUser");
|
||||||
|
proxyUser.setLabelText(Tr::tr("Proxy User"));
|
||||||
|
proxyUser.setDefaultValue("");
|
||||||
|
proxyUser.setEnabler(&useProxy);
|
||||||
|
proxyUser.setToolTip(Tr::tr("The user name for the proxy server."));
|
||||||
|
proxyUser.setHistoryCompleter("Copilot.ProxyUser.History");
|
||||||
|
|
||||||
|
saveProxyPassword.setDisplayName(Tr::tr("Save Proxy Password"));
|
||||||
|
saveProxyPassword.setSettingsKey("Copilot.SaveProxyPassword");
|
||||||
|
saveProxyPassword.setLabelText(Tr::tr("Save Proxy Password"));
|
||||||
|
saveProxyPassword.setDefaultValue(false);
|
||||||
|
saveProxyPassword.setEnabler(&useProxy);
|
||||||
|
saveProxyPassword.setToolTip(
|
||||||
|
Tr::tr("Save the password for the proxy server (Password is stored insecurely!)."));
|
||||||
|
saveProxyPassword.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||||
|
|
||||||
|
proxyPassword.setDisplayName(Tr::tr("Proxy Password"));
|
||||||
|
proxyPassword.setDisplayStyle(StringAspect::PasswordLineEditDisplay);
|
||||||
|
proxyPassword.setSettingsKey("Copilot.ProxyPassword");
|
||||||
|
proxyPassword.setLabelText(Tr::tr("Proxy Password"));
|
||||||
|
proxyPassword.setDefaultValue("");
|
||||||
|
proxyPassword.setEnabler(&saveProxyPassword);
|
||||||
|
proxyPassword.setToolTip(Tr::tr("The password for the proxy server."));
|
||||||
|
|
||||||
|
proxyRejectUnauthorized.setDisplayName(Tr::tr("Reject Unauthorized"));
|
||||||
|
proxyRejectUnauthorized.setSettingsKey("Copilot.ProxyRejectUnauthorized");
|
||||||
|
proxyRejectUnauthorized.setLabelText(Tr::tr("Reject Unauthorized"));
|
||||||
|
proxyRejectUnauthorized.setDefaultValue(true);
|
||||||
|
proxyRejectUnauthorized.setEnabler(&useProxy);
|
||||||
|
proxyRejectUnauthorized.setToolTip(Tr::tr("Reject unauthorized certificates from the proxy "
|
||||||
|
"server. This is a security risk."));
|
||||||
|
proxyRejectUnauthorized.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||||
|
|
||||||
initEnableAspect(enableCopilot);
|
initEnableAspect(enableCopilot);
|
||||||
|
enableCopilot.setLabelPlacement(BoolAspect::LabelPlacement::InExtraLabel);
|
||||||
|
|
||||||
readSettings();
|
readSettings();
|
||||||
}
|
}
|
||||||
@@ -140,25 +201,24 @@ public:
|
|||||||
|
|
||||||
auto warningLabel = new QLabel;
|
auto warningLabel = new QLabel;
|
||||||
warningLabel->setWordWrap(true);
|
warningLabel->setWordWrap(true);
|
||||||
warningLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse
|
warningLabel->setTextInteractionFlags(
|
||||||
| Qt::LinksAccessibleByKeyboard
|
Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard | Qt::TextSelectableByMouse);
|
||||||
| Qt::TextSelectableByMouse);
|
warningLabel->setText(
|
||||||
warningLabel->setText(Tr::tr(
|
Tr::tr("Enabling %1 is subject to your agreement and abidance with your applicable "
|
||||||
"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 "
|
||||||
"%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, "
|
||||||
"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 "
|
||||||
"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 "
|
||||||
"understanding any implications of your use of %1 and suggestions produced "
|
"(like copyright, accuracy, etc.).")
|
||||||
"(like copyright, accuracy, etc.)." ).arg("Copilot"));
|
.arg("Copilot"));
|
||||||
|
|
||||||
auto authWidget = new AuthWidget();
|
auto authWidget = new AuthWidget();
|
||||||
|
|
||||||
auto helpLabel = new QLabel();
|
auto helpLabel = new QLabel();
|
||||||
helpLabel->setTextFormat(Qt::MarkdownText);
|
helpLabel->setTextFormat(Qt::MarkdownText);
|
||||||
helpLabel->setWordWrap(true);
|
helpLabel->setWordWrap(true);
|
||||||
helpLabel->setTextInteractionFlags(Qt::LinksAccessibleByMouse
|
helpLabel->setTextInteractionFlags(
|
||||||
| Qt::LinksAccessibleByKeyboard
|
Qt::LinksAccessibleByMouse | Qt::LinksAccessibleByKeyboard | Qt::TextSelectableByMouse);
|
||||||
| Qt::TextSelectableByMouse);
|
|
||||||
helpLabel->setOpenExternalLinks(true);
|
helpLabel->setOpenExternalLinks(true);
|
||||||
connect(helpLabel, &QLabel::linkHovered, [](const QString &link) {
|
connect(helpLabel, &QLabel::linkHovered, [](const QString &link) {
|
||||||
QToolTip::showText(QCursor::pos(), link);
|
QToolTip::showText(QCursor::pos(), link);
|
||||||
@@ -176,14 +236,28 @@ public:
|
|||||||
.arg("[agent.js](https://github.com/github/copilot.vim/tree/release/dist)"));
|
.arg("[agent.js](https://github.com/github/copilot.vim/tree/release/dist)"));
|
||||||
|
|
||||||
Column {
|
Column {
|
||||||
QString("<b>" + Tr::tr("Note:") + "</b>"), br,
|
Group {
|
||||||
warningLabel, br,
|
title(Tr::tr("Note")),
|
||||||
settings().enableCopilot, br,
|
Column {
|
||||||
authWidget, br,
|
warningLabel, br,
|
||||||
settings().nodeJsPath, br,
|
helpLabel, br,
|
||||||
settings().distPath, br,
|
}
|
||||||
settings().autoComplete, br,
|
},
|
||||||
helpLabel, br,
|
Form {
|
||||||
|
authWidget, br,
|
||||||
|
settings().enableCopilot, br,
|
||||||
|
settings().nodeJsPath, br,
|
||||||
|
settings().distPath, br,
|
||||||
|
settings().autoComplete, br,
|
||||||
|
hr, br,
|
||||||
|
settings().useProxy, br,
|
||||||
|
settings().proxyHost, br,
|
||||||
|
settings().proxyPort, br,
|
||||||
|
settings().proxyRejectUnauthorized, br,
|
||||||
|
settings().proxyUser, br,
|
||||||
|
settings().saveProxyPassword, br,
|
||||||
|
settings().proxyPassword, br,
|
||||||
|
},
|
||||||
st
|
st
|
||||||
}.attachTo(this);
|
}.attachTo(this);
|
||||||
// clang-format on
|
// clang-format on
|
||||||
@@ -211,4 +285,4 @@ public:
|
|||||||
|
|
||||||
const CopilotSettingsPage settingsPage;
|
const CopilotSettingsPage settingsPage;
|
||||||
|
|
||||||
} // Copilot
|
} // namespace Copilot
|
||||||
|
|||||||
@@ -18,6 +18,15 @@ public:
|
|||||||
Utils::FilePathAspect distPath{this};
|
Utils::FilePathAspect distPath{this};
|
||||||
Utils::BoolAspect autoComplete{this};
|
Utils::BoolAspect autoComplete{this};
|
||||||
Utils::BoolAspect enableCopilot{this};
|
Utils::BoolAspect enableCopilot{this};
|
||||||
|
|
||||||
|
Utils::BoolAspect useProxy{this};
|
||||||
|
Utils::StringAspect proxyHost{this};
|
||||||
|
Utils::IntegerAspect proxyPort{this};
|
||||||
|
Utils::StringAspect proxyUser{this};
|
||||||
|
|
||||||
|
Utils::BoolAspect saveProxyPassword{this};
|
||||||
|
Utils::StringAspect proxyPassword{this};
|
||||||
|
Utils::BoolAspect proxyRejectUnauthorized{this};
|
||||||
};
|
};
|
||||||
|
|
||||||
CopilotSettings &settings();
|
CopilotSettings &settings();
|
||||||
|
|||||||
126
src/plugins/copilot/requests/seteditorinfo.h
Normal file
126
src/plugins/copilot/requests/seteditorinfo.h
Normal file
@@ -0,0 +1,126 @@
|
|||||||
|
// 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 "checkstatus.h"
|
||||||
|
|
||||||
|
#include <languageserverprotocol/jsonrpcmessages.h>
|
||||||
|
#include <languageserverprotocol/lsptypes.h>
|
||||||
|
|
||||||
|
namespace Copilot {
|
||||||
|
|
||||||
|
class EditorPluginInfo : public LanguageServerProtocol::JsonObject
|
||||||
|
{
|
||||||
|
static constexpr char version[] = "version";
|
||||||
|
static constexpr char name[] = "name";
|
||||||
|
|
||||||
|
public:
|
||||||
|
using JsonObject::JsonObject;
|
||||||
|
|
||||||
|
EditorPluginInfo(const QString &version, const QString &name)
|
||||||
|
{
|
||||||
|
setEditorVersion(version);
|
||||||
|
setEditorName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEditorVersion(const QString &v) { insert(version, v); }
|
||||||
|
void setEditorName(const QString &n) { insert(name, n); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class EditorInfo : public LanguageServerProtocol::JsonObject
|
||||||
|
{
|
||||||
|
static constexpr char version[] = "version";
|
||||||
|
static constexpr char name[] = "name";
|
||||||
|
|
||||||
|
public:
|
||||||
|
using JsonObject::JsonObject;
|
||||||
|
|
||||||
|
EditorInfo(const QString &version, const QString &name)
|
||||||
|
{
|
||||||
|
setEditorVersion(version);
|
||||||
|
setEditorName(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEditorVersion(const QString &v) { insert(version, v); }
|
||||||
|
void setEditorName(const QString &n) { insert(name, n); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class NetworkProxy : public LanguageServerProtocol::JsonObject
|
||||||
|
{
|
||||||
|
static constexpr char host[] = "host";
|
||||||
|
static constexpr char port[] = "port";
|
||||||
|
static constexpr char user[] = "username";
|
||||||
|
static constexpr char password[] = "password";
|
||||||
|
static constexpr char rejectUnauthorized[] = "rejectUnauthorized";
|
||||||
|
|
||||||
|
public:
|
||||||
|
using JsonObject::JsonObject;
|
||||||
|
|
||||||
|
NetworkProxy(const QString &host,
|
||||||
|
int port,
|
||||||
|
const QString &user,
|
||||||
|
const QString &password,
|
||||||
|
bool rejectUnauthorized)
|
||||||
|
{
|
||||||
|
setHost(host);
|
||||||
|
setPort(port);
|
||||||
|
setUser(user);
|
||||||
|
setPassword(password);
|
||||||
|
setRejectUnauthorized(rejectUnauthorized);
|
||||||
|
}
|
||||||
|
|
||||||
|
void insertIfNotEmpty(const std::string_view key, const QString &value)
|
||||||
|
{
|
||||||
|
if (!value.isEmpty())
|
||||||
|
insert(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setHost(const QString &h) { insert(host, h); }
|
||||||
|
void setPort(int p) { insert(port, p); }
|
||||||
|
void setUser(const QString &u) { insertIfNotEmpty(user, u); }
|
||||||
|
void setPassword(const QString &p) { insertIfNotEmpty(password, p); }
|
||||||
|
void setRejectUnauthorized(bool r) { insert(rejectUnauthorized, r); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class SetEditorInfoParams : public LanguageServerProtocol::JsonObject
|
||||||
|
{
|
||||||
|
static constexpr char editorInfo[] = "editorInfo";
|
||||||
|
static constexpr char editorPluginInfo[] = "editorPluginInfo";
|
||||||
|
static constexpr char networkProxy[] = "networkProxy";
|
||||||
|
|
||||||
|
public:
|
||||||
|
using JsonObject::JsonObject;
|
||||||
|
|
||||||
|
SetEditorInfoParams(const EditorInfo &editorInfo, const EditorPluginInfo &editorPluginInfo)
|
||||||
|
{
|
||||||
|
setEditorInfo(editorInfo);
|
||||||
|
setEditorPluginInfo(editorPluginInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
SetEditorInfoParams(const EditorInfo &editorInfo,
|
||||||
|
const EditorPluginInfo &editorPluginInfo,
|
||||||
|
const NetworkProxy &networkProxy)
|
||||||
|
{
|
||||||
|
setEditorInfo(editorInfo);
|
||||||
|
setEditorPluginInfo(editorPluginInfo);
|
||||||
|
setNetworkProxy(networkProxy);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEditorInfo(const EditorInfo &info) { insert(editorInfo, info); }
|
||||||
|
void setEditorPluginInfo(const EditorPluginInfo &info) { insert(editorPluginInfo, info); }
|
||||||
|
void setNetworkProxy(const NetworkProxy &proxy) { insert(networkProxy, proxy); }
|
||||||
|
};
|
||||||
|
|
||||||
|
class SetEditorInfoRequest
|
||||||
|
: public LanguageServerProtocol::Request<CheckStatusResponse, std::nullptr_t, SetEditorInfoParams>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SetEditorInfoRequest(const SetEditorInfoParams ¶ms)
|
||||||
|
: Request(methodName, params)
|
||||||
|
{}
|
||||||
|
using Request::Request;
|
||||||
|
constexpr static const char methodName[] = "setEditorInfo";
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Copilot
|
||||||
20
src/plugins/copilot/tests/proxy/Dockerfile
Normal file
20
src/plugins/copilot/tests/proxy/Dockerfile
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
ARG PWDMODE=with
|
||||||
|
|
||||||
|
|
||||||
|
FROM ubuntu:20.04 AS base
|
||||||
|
ARG DEBIAN_FRONTEND=noninteractive
|
||||||
|
ENV TZ=Etc/UTC
|
||||||
|
RUN apt-get update && apt-get install -y squid apache2-utils && rm -rf /var/lib/apt/lists/*
|
||||||
|
COPY run.sh /
|
||||||
|
RUN chmod +x /run.sh
|
||||||
|
|
||||||
|
|
||||||
|
FROM base as image-with-pwd
|
||||||
|
RUN echo 1234 | htpasswd -i -c /etc/squid/pswds user
|
||||||
|
COPY userauth.conf /etc/squid/conf.d/
|
||||||
|
|
||||||
|
FROM base as image-without-pwd
|
||||||
|
COPY noauth.conf /etc/squid/conf.d/
|
||||||
|
|
||||||
|
FROM image-${PWDMODE}-pwd AS final
|
||||||
|
CMD [ "/run.sh" ]
|
||||||
4
src/plugins/copilot/tests/proxy/buildandrun.sh
Executable file
4
src/plugins/copilot/tests/proxy/buildandrun.sh
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
docker build --build-arg PWDMODE=with -t copilot-proxy-test . && \
|
||||||
|
docker run --rm -it -p 3128:3128 copilot-proxy-test
|
||||||
1
src/plugins/copilot/tests/proxy/noauth.conf
Normal file
1
src/plugins/copilot/tests/proxy/noauth.conf
Normal file
@@ -0,0 +1 @@
|
|||||||
|
http_access allow all
|
||||||
7
src/plugins/copilot/tests/proxy/run.sh
Executable file
7
src/plugins/copilot/tests/proxy/run.sh
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
touch /var/log/squid/access.log
|
||||||
|
chmod 640 /var/log/squid/access.log
|
||||||
|
chown proxy:proxy /var/log/squid/access.log
|
||||||
|
tail -f /var/log/squid/access.log &
|
||||||
|
exec squid --foreground
|
||||||
4
src/plugins/copilot/tests/proxy/userauth.conf
Normal file
4
src/plugins/copilot/tests/proxy/userauth.conf
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
auth_param basic program /usr/lib/squid3/basic_ncsa_auth /etc/squid/pswds
|
||||||
|
auth_param basic realm proxy
|
||||||
|
acl authenticated proxy_auth REQUIRED
|
||||||
|
http_access allow authenticated
|
||||||
Reference in New Issue
Block a user