Python: remove python specific language client settings

Change-Id: Ic993d525f29c1925f7e64dfc6f5e053234fb4904
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2022-06-09 13:41:41 +02:00
parent ed22ef7854
commit 4c20a880e6
6 changed files with 122 additions and 310 deletions

View File

@@ -69,9 +69,7 @@ using namespace Utils;
namespace Python {
namespace Internal {
static constexpr char startPylsInfoBarId[] = "Python::StartPyls";
static constexpr char installPylsInfoBarId[] = "Python::InstallPyls";
static constexpr char enablePylsInfoBarId[] = "Python::EnablePyls";
class PythonLanguageServerState
{
@@ -79,13 +77,17 @@ public:
enum {
CanNotBeInstalled,
CanBeInstalled,
AlreadyInstalled,
AlreadyConfigured,
ConfiguredButDisabled
AlreadyInstalled
} state;
FilePath pylsModulePath;
};
static QHash<FilePath, PyLSClient*> &pythonClients()
{
static QHash<FilePath, PyLSClient*> clients;
return clients;
}
FilePath getPylsModulePath(CommandLine pylsCommand)
{
static QMutex mutex; // protect the access to the cache
@@ -125,29 +127,11 @@ FilePath getPylsModulePath(CommandLine pylsCommand)
return {};
}
QList<const StdIOSettings *> configuredPythonLanguageServer()
{
using namespace LanguageClient;
QList<const StdIOSettings *> result;
for (const BaseSettings *setting : LanguageClientManager::currentSettings()) {
if (setting->m_languageFilter.isSupported("foo.py", Constants::C_PY_MIMETYPE))
result << dynamic_cast<const StdIOSettings *>(setting);
}
return result;
}
static PythonLanguageServerState checkPythonLanguageServer(const FilePath &python)
{
using namespace LanguageClient;
const CommandLine pythonLShelpCommand(python, {"-m", "pylsp", "-h"});
const FilePath &modulePath = getPylsModulePath(pythonLShelpCommand);
for (const StdIOSettings *serverSetting : configuredPythonLanguageServer()) {
if (modulePath == getPylsModulePath(serverSetting->command())) {
return {serverSetting->m_enabled ? PythonLanguageServerState::AlreadyConfigured
: PythonLanguageServerState::ConfiguredButDisabled,
FilePath()};
}
}
QtcProcess pythonProcess;
pythonProcess.setCommand(pythonLShelpCommand);
@@ -163,137 +147,6 @@ static PythonLanguageServerState checkPythonLanguageServer(const FilePath &pytho
return {PythonLanguageServerState::CanNotBeInstalled, FilePath()};
}
class PyLSSettingsWidget : public QWidget
{
Q_DECLARE_TR_FUNCTIONS(PyLSSettingsWidget)
public:
PyLSSettingsWidget(const PyLSSettings *settings, QWidget *parent)
: QWidget(parent)
, m_name(new QLineEdit(settings->m_name, this))
, m_interpreter(new QComboBox(this))
, m_configure(new QPushButton(tr("Configure..."), this))
{
int row = 0;
auto *mainLayout = new QGridLayout;
mainLayout->addWidget(new QLabel(tr("Name:")), row, 0);
mainLayout->addWidget(m_name, row, 1);
auto chooser = new VariableChooser(this);
chooser->addSupportedWidget(m_name);
mainLayout->addWidget(new QLabel(tr("Python:")), ++row, 0);
QString settingsId = settings->interpreterId();
if (settingsId.isEmpty())
settingsId = PythonSettings::defaultInterpreter().id;
updateInterpreters(PythonSettings::interpreters(), settingsId);
mainLayout->addWidget(m_interpreter, row, 1);
setLayout(mainLayout);
mainLayout->addWidget(m_configure, ++row, 0);
connect(PythonSettings::instance(),
&PythonSettings::interpretersChanged,
this,
&PyLSSettingsWidget::updateInterpreters);
connect(m_configure, &QPushButton::clicked, this, &PyLSSettingsWidget::switchToPylsConfigurePage);
}
void updateInterpreters(const QList<Interpreter> &interpreters, const QString &defaultId)
{
QString currentId = interpreterId();
if (currentId.isEmpty())
currentId = defaultId;
m_interpreter->clear();
for (const Interpreter &interpreter : interpreters) {
if (!interpreter.command.exists())
continue;
const QString name = QString(interpreter.name + " (%1)")
.arg(interpreter.command.toUserOutput());
m_interpreter->addItem(name, interpreter.id);
if (!currentId.isEmpty() && currentId == interpreter.id)
m_interpreter->setCurrentIndex(m_interpreter->count() - 1);
}
}
QString name() const { return m_name->text(); }
QString interpreterId() const { return m_interpreter->currentData().toString(); }
private:
void switchToPylsConfigurePage()
{
Core::ICore::showOptionsDialog(Constants::C_PYLSCONFIGURATION_PAGE_ID);
}
QLineEdit *m_name = nullptr;
QComboBox *m_interpreter = nullptr;
QPushButton *m_configure = nullptr;
};
PyLSSettings::PyLSSettings()
{
m_settingsTypeId = Constants::PYLS_SETTINGS_ID;
m_name = "Python Language Server";
m_startBehavior = RequiresFile;
m_languageFilter.mimeTypes = QStringList()
<< Constants::C_PY_MIMETYPE << Constants::C_PY3_MIMETYPE;
m_arguments = "-m pylsp";
}
bool PyLSSettings::isValid() const
{
return !m_interpreterId.isEmpty() && StdIOSettings::isValid();
}
static const char interpreterKey[] = "interpreter";
QVariantMap PyLSSettings::toMap() const
{
QVariantMap map = StdIOSettings::toMap();
map.insert(interpreterKey, m_interpreterId);
return map;
}
void PyLSSettings::fromMap(const QVariantMap &map)
{
StdIOSettings::fromMap(map);
m_languageFilter.mimeTypes = QStringList()
<< Constants::C_PY_MIMETYPE << Constants::C_PY3_MIMETYPE;
setInterpreter(map[interpreterKey].toString());
}
bool PyLSSettings::applyFromSettingsWidget(QWidget *widget)
{
bool changed = false;
auto pylswidget = static_cast<PyLSSettingsWidget *>(widget);
changed |= m_name != pylswidget->name();
m_name = pylswidget->name();
changed |= m_interpreterId != pylswidget->interpreterId();
setInterpreter(pylswidget->interpreterId());
return changed;
}
QWidget *PyLSSettings::createSettingsWidget(QWidget *parent) const
{
return new PyLSSettingsWidget(this, parent);
}
BaseSettings *PyLSSettings::copy() const
{
return new PyLSSettings(*this);
}
void PyLSSettings::setInterpreter(const QString &interpreterId)
{
m_interpreterId = interpreterId;
if (m_interpreterId.isEmpty())
return;
Interpreter interpreter = Utils::findOrDefault(PythonSettings::interpreters(),
Utils::equal(&Interpreter::id, interpreterId));
m_executable = interpreter.command;
}
class PyLSInterface : public StdIOClientInterface
{
@@ -310,13 +163,22 @@ public:
TemporaryDirectory m_extraPythonPath;
};
BaseClientInterface *PyLSSettings::createInterface(ProjectExplorer::Project *project) const
PyLSClient *clientForPython(const FilePath &python)
{
if (auto client = pythonClients()[python])
return client;
auto interface = new PyLSInterface;
interface->setCommandLine(command());
if (project)
interface->setWorkingDirectory(project->projectDirectory());
return interface;
interface->setCommandLine(CommandLine(python, {"-m", "pylsp"}));
auto client = new PyLSClient(interface);
client->setName(PyLSClient::tr("Python Language Server (%1)").arg(python.toUserOutput()));
client->setActivateDocumentAutomatically(true);
client->updateConfiguration();
LanguageFilter filter;
filter.mimeTypes = QStringList() << Constants::C_PY_MIMETYPE << Constants::C_PY3_MIMETYPE;
client->setSupportedLanguage(filter);
client->start();
pythonClients()[python] = client;
return client;
}
PyLSClient::PyLSClient(BaseClientInterface *interface)
@@ -326,6 +188,17 @@ PyLSClient::PyLSClient(BaseClientInterface *interface)
connect(this, &Client::initialized, this, &PyLSClient::updateConfiguration);
connect(PythonSettings::instance(), &PythonSettings::pylsConfigurationChanged,
this, &PyLSClient::updateConfiguration);
connect(PythonSettings::instance(), &PythonSettings::pylsEnabledChanged,
this, [this](const bool enabled){
if (!enabled)
LanguageClientManager::shutdownClient(this);
});
}
PyLSClient::~PyLSClient()
{
pythonClients().remove(pythonClients().key(this));
}
void PyLSClient::updateConfiguration()
@@ -413,16 +286,7 @@ void PyLSClient::closeExtraCompiler(ProjectExplorer::ExtraCompiler *compiler)
PyLSClient *PyLSClient::clientForPython(const FilePath &python)
{
if (auto setting = PyLSConfigureAssistant::languageServerForPython(python)) {
if (auto client = LanguageClientManager::clientsForSetting(setting).value(0))
return qobject_cast<PyLSClient *>(client);
}
return nullptr;
}
Client *PyLSSettings::createClient(BaseClientInterface *interface) const
{
return new PyLSClient(interface);
return pythonClients()[python];
}
PyLSConfigureAssistant *PyLSConfigureAssistant::instance()
@@ -431,40 +295,6 @@ PyLSConfigureAssistant *PyLSConfigureAssistant::instance()
return instance;
}
const StdIOSettings *PyLSConfigureAssistant::languageServerForPython(const FilePath &python)
{
const FilePath pythonModulePath = getPylsModulePath({python, {"-m", "pylsp"}});
return findOrDefault(configuredPythonLanguageServer(),
[pythonModulePath](const StdIOSettings *setting) {
return getPylsModulePath(setting->command()) == pythonModulePath;
});
}
static Client *registerLanguageServer(const FilePath &python)
{
Interpreter interpreter = Utils::findOrDefault(PythonSettings::interpreters(),
Utils::equal(&Interpreter::command, python));
StdIOSettings *settings = nullptr;
if (!interpreter.id.isEmpty()) {
auto *pylsSettings = new PyLSSettings();
pylsSettings->setInterpreter(interpreter.id);
settings = pylsSettings;
} else {
// cannot find a matching interpreter in settings for the python path add a generic server
auto *settings = new StdIOSettings();
settings->m_executable = python;
settings->m_arguments = "-m pylsp";
settings->m_languageFilter.mimeTypes = QStringList() << Constants::C_PY_MIMETYPE
<< Constants::C_PY3_MIMETYPE;
}
settings->m_name = PyLSConfigureAssistant::tr("Python Language Server (%1)")
.arg(pythonName(python));
LanguageClientManager::registerClientSettings(settings);
Client *client = LanguageClientManager::clientsForSetting(settings).value(0);
PyLSConfigureAssistant::updateEditorInfoBars(python, client);
return client;
}
void PyLSConfigureAssistant::installPythonLanguageServer(const FilePath &python,
QPointer<TextEditor::TextDocument> document)
{
@@ -479,8 +309,8 @@ void PyLSConfigureAssistant::installPythonLanguageServer(const FilePath &python,
connect(install, &PipInstallTask::finished, this, [=](const bool success) {
if (success) {
if (Client *client = registerLanguageServer(python)) {
if (document)
if (document) {
if (PyLSClient *client = clientForPython(python))
LanguageClientManager::openDocumentWithClient(document, client);
}
}
@@ -491,34 +321,18 @@ void PyLSConfigureAssistant::installPythonLanguageServer(const FilePath &python,
install->run();
}
static void setupPythonLanguageServer(const FilePath &python,
QPointer<TextEditor::TextDocument> document)
{
document->infoBar()->removeInfo(startPylsInfoBarId);
if (Client *client = registerLanguageServer(python))
LanguageClientManager::openDocumentWithClient(document, client);
}
static void enablePythonLanguageServer(const FilePath &python,
QPointer<TextEditor::TextDocument> document)
{
document->infoBar()->removeInfo(enablePylsInfoBarId);
if (const StdIOSettings *setting = PyLSConfigureAssistant::languageServerForPython(python)) {
LanguageClientManager::enableClientSettings(setting->m_id);
if (const StdIOSettings *setting = PyLSConfigureAssistant::languageServerForPython(python)) {
if (Client *client = LanguageClientManager::clientsForSetting(setting).value(0)) {
LanguageClientManager::openDocumentWithClient(document, client);
PyLSConfigureAssistant::updateEditorInfoBars(python, client);
}
}
}
}
void PyLSConfigureAssistant::openDocumentWithPython(const FilePath &python,
TextEditor::TextDocument *document)
{
using CheckPylsWatcher = QFutureWatcher<PythonLanguageServerState>;
if (!PythonSettings::pylsEnabled())
return;
if (auto client = pythonClients().value(python)) {
LanguageClientManager::openDocumentWithClient(document, client);
return;
}
using CheckPylsWatcher = QFutureWatcher<PythonLanguageServerState>;
QPointer<CheckPylsWatcher> watcher = new CheckPylsWatcher();
// cancel and delete watcher after a 10 second timeout
@@ -547,19 +361,12 @@ void PyLSConfigureAssistant::handlePyLSState(const FilePath &python,
{
if (state.state == PythonLanguageServerState::CanNotBeInstalled)
return;
if (state.state == PythonLanguageServerState::AlreadyConfigured) {
if (const StdIOSettings *setting = languageServerForPython(python)) {
if (Client *client = LanguageClientManager::clientsForSetting(setting).value(0))
LanguageClientManager::openDocumentWithClient(document, client);
}
return;
}
resetEditorInfoBar(document);
Utils::InfoBar *infoBar = document->infoBar();
if (state.state == PythonLanguageServerState::CanBeInstalled
&& infoBar->canInfoBeAdded(installPylsInfoBarId)) {
auto message = tr("Install and set up Python language server (PyLS) for %1 (%2). "
auto message = tr("Install Python language server (PyLS) for %1 (%2). "
"The language server provides Python specific completion and annotation.")
.arg(pythonName(python), python.toUserOutput());
Utils::InfoBarEntry info(installPylsInfoBarId,
@@ -569,27 +376,9 @@ void PyLSConfigureAssistant::handlePyLSState(const FilePath &python,
[=]() { installPythonLanguageServer(python, document); });
infoBar->addInfo(info);
m_infoBarEntries[python] << document;
} else if (state.state == PythonLanguageServerState::AlreadyInstalled
&& infoBar->canInfoBeAdded(startPylsInfoBarId)) {
auto message = tr("Found a Python language server for %1 (%2). "
"Set it up for this document?")
.arg(pythonName(python), python.toUserOutput());
Utils::InfoBarEntry info(startPylsInfoBarId,
message,
Utils::InfoBarEntry::GlobalSuppression::Enabled);
info.addCustomButton(tr("Set Up"), [=]() { setupPythonLanguageServer(python, document); });
infoBar->addInfo(info);
m_infoBarEntries[python] << document;
} else if (state.state == PythonLanguageServerState::ConfiguredButDisabled
&& infoBar->canInfoBeAdded(enablePylsInfoBarId)) {
auto message = tr("Enable Python language server for %1 (%2)?")
.arg(pythonName(python), python.toUserOutput());
Utils::InfoBarEntry info(enablePylsInfoBarId,
message,
Utils::InfoBarEntry::GlobalSuppression::Enabled);
info.addCustomButton(tr("Enable"), [=]() { enablePythonLanguageServer(python, document); });
infoBar->addInfo(info);
m_infoBarEntries[python] << document;
} else if (state.state == PythonLanguageServerState::AlreadyInstalled) {
if (auto client = clientForPython(python))
LanguageClientManager::openDocumentWithClient(document, client);
}
}
@@ -606,10 +395,7 @@ void PyLSConfigureAssistant::resetEditorInfoBar(TextEditor::TextDocument *docume
{
for (QList<TextEditor::TextDocument *> &documents : m_infoBarEntries)
documents.removeAll(document);
Utils::InfoBar *infoBar = document->infoBar();
infoBar->removeInfo(installPylsInfoBarId);
infoBar->removeInfo(startPylsInfoBarId);
infoBar->removeInfo(enablePylsInfoBarId);
document->infoBar()->removeInfo(installPylsInfoBarId);
}
PyLSConfigureAssistant::PyLSConfigureAssistant(QObject *parent)