Python: close all info bars after language server setup

Change-Id: I607f7cb5a31f3db0c7d7d77011860a1ea87eb8d2
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2019-10-18 10:31:14 +02:00
parent 088d5c0f77
commit 10c94994db
6 changed files with 134 additions and 82 deletions

View File

@@ -35,19 +35,6 @@
namespace Python { namespace Python {
namespace Internal { namespace Internal {
static void documentOpened(Core::IDocument *document)
{
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
if (!textDocument || textDocument->mimeType() != Constants::C_PY_MIMETYPE)
return;
const Utils::FilePath &python = detectPython(textDocument->filePath());
if (!python.exists())
return;
updateEditorInfoBar(python, textDocument);
}
PythonEditorFactory::PythonEditorFactory() PythonEditorFactory::PythonEditorFactory()
{ {
setId(Constants::C_PYTHONEDITOR_ID); setId(Constants::C_PYTHONEDITOR_ID);
@@ -68,7 +55,7 @@ PythonEditorFactory::PythonEditorFactory()
setCodeFoldingSupported(true); setCodeFoldingSupported(true);
connect(Core::EditorManager::instance(), &Core::EditorManager::documentOpened, connect(Core::EditorManager::instance(), &Core::EditorManager::documentOpened,
this, documentOpened); this, &PyLSConfigureAssistant::documentOpened);
} }
} // namespace Internal } // namespace Internal

View File

@@ -51,6 +51,8 @@ namespace Internal {
// //
//////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////
static PythonPlugin *m_instance = nullptr;
class PythonPluginPrivate class PythonPluginPrivate
{ {
public: public:
@@ -65,11 +67,22 @@ public:
}; };
}; };
PythonPlugin::PythonPlugin()
{
m_instance = this;
}
PythonPlugin::~PythonPlugin() PythonPlugin::~PythonPlugin()
{ {
m_instance = nullptr;
delete d; delete d;
} }
PythonPlugin *PythonPlugin::instance()
{
return m_instance;
}
bool PythonPlugin::initialize(const QStringList &arguments, QString *errorMessage) bool PythonPlugin::initialize(const QStringList &arguments, QString *errorMessage)
{ {
Q_UNUSED(arguments) Q_UNUSED(arguments)

View File

@@ -36,9 +36,11 @@ class PythonPlugin : public ExtensionSystem::IPlugin
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Python.json") Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Python.json")
public: public:
PythonPlugin() = default; PythonPlugin();
~PythonPlugin() final; ~PythonPlugin() final;
static PythonPlugin *instance();
private: private:
bool initialize(const QStringList &arguments, QString *errorMessage) final; bool initialize(const QStringList &arguments, QString *errorMessage) final;
void extensionsInitialized() final; void extensionsInitialized() final;

View File

@@ -297,23 +297,10 @@ void PythonRunConfiguration::updateLanguageServer()
const FilePath python(FilePath::fromUserInput(interpreter())); const FilePath python(FilePath::fromUserInput(interpreter()));
if (const StdIOSettings *lsSetting = languageServerForPython(python)) {
if (Client *client = LanguageClientManager::clientForSetting(lsSetting).value(0)) {
for (FilePath &file : project()->files(Project::AllFiles)) {
if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) {
if (document->mimeType() == Constants::C_PY_MIMETYPE) {
resetEditorInfoBar(document);
LanguageClientManager::reOpenDocumentWithClient(document, client);
}
}
}
}
}
for (FilePath &file : project()->files(Project::AllFiles)) { for (FilePath &file : project()->files(Project::AllFiles)) {
if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) { if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) {
if (document->mimeType() == Constants::C_PY_MIMETYPE) if (document->mimeType() == Constants::C_PY_MIMETYPE)
updateEditorInfoBar(python, document); PyLSConfigureAssistant::instance()->openDocumentWithPython(python, document);
} }
} }
} }

View File

@@ -26,6 +26,7 @@
#include "pythonutils.h" #include "pythonutils.h"
#include "pythonconstants.h" #include "pythonconstants.h"
#include "pythonplugin.h"
#include "pythonproject.h" #include "pythonproject.h"
#include "pythonrunconfiguration.h" #include "pythonrunconfiguration.h"
#include "pythonsettings.h" #include "pythonsettings.h"
@@ -33,8 +34,8 @@
#include <coreplugin/infobar.h> #include <coreplugin/infobar.h>
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/progressmanager.h>
#include <languageclient/languageclientsettings.h>
#include <languageclient/languageclientmanager.h> #include <languageclient/languageclientmanager.h>
#include <languageclient/languageclientsettings.h>
#include <projectexplorer/session.h> #include <projectexplorer/session.h>
#include <projectexplorer/target.h> #include <projectexplorer/target.h>
@@ -49,6 +50,7 @@
#include <QRegularExpression> #include <QRegularExpression>
#include <QTimer> #include <QTimer>
using namespace LanguageClient;
using namespace Utils; using namespace Utils;
namespace Python { namespace Python {
@@ -58,7 +60,6 @@ static constexpr char startPylsInfoBarId[] = "Python::StartPyls";
static constexpr char installPylsInfoBarId[] = "Python::InstallPyls"; static constexpr char installPylsInfoBarId[] = "Python::InstallPyls";
static constexpr char enablePylsInfoBarId[] = "Python::EnablePyls"; static constexpr char enablePylsInfoBarId[] = "Python::EnablePyls";
static constexpr char installPylsTaskId[] = "Python::InstallPylsTask"; static constexpr char installPylsTaskId[] = "Python::InstallPylsTask";
static constexpr char pythonUtilsTrContext[] = "Python::Utils";
struct PythonLanguageServerState struct PythonLanguageServerState
{ {
@@ -116,7 +117,7 @@ FilePath getPylsModulePath(CommandLine pylsCommand)
return {}; return {};
} }
QList<const LanguageClient::StdIOSettings *> configuredPythonLanguageServer() QList<const StdIOSettings *> configuredPythonLanguageServer()
{ {
using namespace LanguageClient; using namespace LanguageClient;
QList<const StdIOSettings *> result; QList<const StdIOSettings *> result;
@@ -156,7 +157,7 @@ static PythonLanguageServerState checkPythonLanguageServer(const FilePath &pytho
return {PythonLanguageServerState::CanNotBeInstalled, FilePath()}; return {PythonLanguageServerState::CanNotBeInstalled, FilePath()};
} }
FilePath detectPython(const FilePath &documentPath) static FilePath detectPython(const FilePath &documentPath)
{ {
FilePath python; FilePath python;
@@ -183,26 +184,33 @@ FilePath detectPython(const FilePath &documentPath)
return python; return python;
} }
const LanguageClient::StdIOSettings *languageServerForPython(const FilePath &python) PyLSConfigureAssistant *PyLSConfigureAssistant::instance()
{
static auto *instance = new PyLSConfigureAssistant(PythonPlugin::instance());
return instance;
}
const StdIOSettings *PyLSConfigureAssistant::languageServerForPython(const FilePath &python)
{ {
return findOrDefault(configuredPythonLanguageServer(), return findOrDefault(configuredPythonLanguageServer(),
[pythonModulePath = getPylsModulePath(CommandLine(python, {"-m", "pyls"}))]( [pythonModulePath = getPylsModulePath(
const LanguageClient::StdIOSettings *setting) { CommandLine(python, {"-m", "pyls"}))](const StdIOSettings *setting) {
return getPylsModulePath(setting->command()) == pythonModulePath; return getPylsModulePath(setting->command()) == pythonModulePath;
}); });
} }
static LanguageClient::Client *registerLanguageServer(const FilePath &python) static Client *registerLanguageServer(const FilePath &python)
{ {
auto *settings = new LanguageClient::StdIOSettings(); auto *settings = new StdIOSettings();
settings->m_executable = python.toString(); settings->m_executable = python.toString();
settings->m_arguments = "-m pyls"; settings->m_arguments = "-m pyls";
settings->m_name = QCoreApplication::translate(pythonUtilsTrContext, settings->m_name = PyLSConfigureAssistant::tr("Python Language Server (%1)")
"Python Language Server (%1)")
.arg(pythonName(python)); .arg(pythonName(python));
settings->m_languageFilter.mimeTypes = QStringList(Constants::C_PY_MIMETYPE); settings->m_languageFilter.mimeTypes = QStringList(Constants::C_PY_MIMETYPE);
LanguageClient::LanguageClientManager::registerClientSettings(settings); LanguageClientManager::registerClientSettings(settings);
return LanguageClient::LanguageClientManager::clientForSetting(settings).value(0); Client *client = LanguageClientManager::clientForSetting(settings).value(0);
PyLSConfigureAssistant::updateEditorInfoBars(python, client);
return client;
} }
class PythonLSInstallHelper : public QObject class PythonLSInstallHelper : public QObject
@@ -253,8 +261,7 @@ private:
void cancel() void cancel()
{ {
SynchronousProcess::stopProcess(m_process); SynchronousProcess::stopProcess(m_process);
Core::MessageManager::write( Core::MessageManager::write(tr("The Python language server installation canceled by %1.")
tr("The Python language server installation canceled by %1.")
.arg(m_killTimer.isActive() ? tr("user") : tr("time out"))); .arg(m_killTimer.isActive() ? tr("user") : tr("time out")));
} }
@@ -262,8 +269,8 @@ private:
{ {
m_future.reportFinished(); m_future.reportFinished();
if (exitStatus == QProcess::NormalExit && exitCode == 0) { if (exitStatus == QProcess::NormalExit && exitCode == 0) {
if (LanguageClient::Client *client = registerLanguageServer(m_python)) if (Client *client = registerLanguageServer(m_python))
LanguageClient::LanguageClientManager::reOpenDocumentWithClient(m_document, client); LanguageClientManager::reOpenDocumentWithClient(m_document, client);
} else { } else {
Core::MessageManager::write( Core::MessageManager::write(
tr("Installing the Python language server failed with exit code %1").arg(exitCode)); tr("Installing the Python language server failed with exit code %1").arg(exitCode));
@@ -293,11 +300,16 @@ private:
QPointer<TextEditor::TextDocument> m_document; QPointer<TextEditor::TextDocument> m_document;
}; };
static void installPythonLanguageServer(const FilePath &python, void PyLSConfigureAssistant::installPythonLanguageServer(const FilePath &python,
QPointer<TextEditor::TextDocument> document) QPointer<TextEditor::TextDocument> document)
{ {
document->infoBar()->removeInfo(installPylsInfoBarId); document->infoBar()->removeInfo(installPylsInfoBarId);
// Hide all install info bar entries for this python, but keep them in the list
// so the language server will be setup properly after the installation is done.
for (TextEditor::TextDocument *additionalDocument : m_infoBarEntries[python])
additionalDocument->infoBar()->removeInfo(installPylsInfoBarId);
auto install = new PythonLSInstallHelper(python, document); auto install = new PythonLSInstallHelper(python, document);
install->run(); install->run();
} }
@@ -306,36 +318,49 @@ static void setupPythonLanguageServer(const FilePath &python,
QPointer<TextEditor::TextDocument> document) QPointer<TextEditor::TextDocument> document)
{ {
document->infoBar()->removeInfo(startPylsInfoBarId); document->infoBar()->removeInfo(startPylsInfoBarId);
if (LanguageClient::Client *client = registerLanguageServer(python)) if (Client *client = registerLanguageServer(python))
LanguageClient::LanguageClientManager::reOpenDocumentWithClient(document, client); LanguageClientManager::reOpenDocumentWithClient(document, client);
} }
static void enablePythonLanguageServer(const FilePath &python, static void enablePythonLanguageServer(const FilePath &python,
QPointer<TextEditor::TextDocument> document) QPointer<TextEditor::TextDocument> document)
{ {
using namespace LanguageClient;
document->infoBar()->removeInfo(enablePylsInfoBarId); document->infoBar()->removeInfo(enablePylsInfoBarId);
if (const StdIOSettings *setting = languageServerForPython(python)) { if (const StdIOSettings *setting = PyLSConfigureAssistant::languageServerForPython(python)) {
LanguageClientManager::enableClientSettings(setting->m_id); LanguageClientManager::enableClientSettings(setting->m_id);
if (const StdIOSettings *setting = languageServerForPython(python)) { if (const StdIOSettings *setting = PyLSConfigureAssistant::languageServerForPython(python)) {
if (Client *client = LanguageClientManager::clientForSetting(setting).value(0)) if (Client *client = LanguageClientManager::clientForSetting(setting).value(0)) {
LanguageClientManager::reOpenDocumentWithClient(document, client); LanguageClientManager::reOpenDocumentWithClient(document, client);
PyLSConfigureAssistant::updateEditorInfoBars(python, client);
}
} }
} }
} }
void updateEditorInfoBar(const FilePath &python, TextEditor::TextDocument *document) void PyLSConfigureAssistant::documentOpened(Core::IDocument *document)
{
auto textDocument = qobject_cast<TextEditor::TextDocument *>(document);
if (!textDocument || textDocument->mimeType() != Constants::C_PY_MIMETYPE)
return;
const FilePath &python = detectPython(textDocument->filePath());
if (!python.exists())
return;
instance()->openDocumentWithPython(python, textDocument);
}
void PyLSConfigureAssistant::openDocumentWithPython(const FilePath &python,
TextEditor::TextDocument *document)
{ {
const PythonLanguageServerState &lsState = checkPythonLanguageServer(python); const PythonLanguageServerState &lsState = checkPythonLanguageServer(python);
if (lsState.state == PythonLanguageServerState::CanNotBeInstalled) if (lsState.state == PythonLanguageServerState::CanNotBeInstalled)
return; return;
if (lsState.state == PythonLanguageServerState::AlreadyConfigured) { if (lsState.state == PythonLanguageServerState::AlreadyConfigured) {
if (const LanguageClient::StdIOSettings *setting = languageServerForPython(python)) { if (const StdIOSettings *setting = languageServerForPython(python)) {
if (LanguageClient::Client *client if (Client *client = LanguageClientManager::clientForSetting(setting).value(0))
= LanguageClient::LanguageClientManager::clientForSetting(setting).value(0)) { LanguageClientManager::reOpenDocumentWithClient(document, client);
LanguageClient::LanguageClientManager::reOpenDocumentWithClient(document, client);
}
} }
return; return;
} }
@@ -345,52 +370,66 @@ void updateEditorInfoBar(const FilePath &python, TextEditor::TextDocument *docum
if (lsState.state == PythonLanguageServerState::CanBeInstalled if (lsState.state == PythonLanguageServerState::CanBeInstalled
&& infoBar->canInfoBeAdded(installPylsInfoBarId)) { && infoBar->canInfoBeAdded(installPylsInfoBarId)) {
auto message auto message
= QCoreApplication::translate(pythonUtilsTrContext, = tr("Install and set up Python language server (PyLS) for %1 (%2). "
"Install and set up Python language server (PyLS) for %1 (%2). "
"The language server provides Python specific completions and annotations.") "The language server provides Python specific completions and annotations.")
.arg(pythonName(python), python.toUserOutput()); .arg(pythonName(python), python.toUserOutput());
Core::InfoBarEntry info(installPylsInfoBarId, Core::InfoBarEntry info(installPylsInfoBarId,
message, message,
Core::InfoBarEntry::GlobalSuppression::Enabled); Core::InfoBarEntry::GlobalSuppression::Enabled);
info.setCustomButtonInfo(QCoreApplication::translate(pythonUtilsTrContext, "Install"), info.setCustomButtonInfo(tr("Install"),
[=]() { installPythonLanguageServer(python, document); }); [=]() { installPythonLanguageServer(python, document); });
infoBar->addInfo(info); infoBar->addInfo(info);
m_infoBarEntries[python] << document;
} else if (lsState.state == PythonLanguageServerState::AlreadyInstalled } else if (lsState.state == PythonLanguageServerState::AlreadyInstalled
&& infoBar->canInfoBeAdded(startPylsInfoBarId)) { && infoBar->canInfoBeAdded(startPylsInfoBarId)) {
auto message = QCoreApplication::translate(pythonUtilsTrContext, auto message = tr("Found a Python language server for %1 (%2). "
"Found a Python language server for %1 (%2). "
"Should this one be set up for this document?") "Should this one be set up for this document?")
.arg(pythonName(python), python.toUserOutput()); .arg(pythonName(python), python.toUserOutput());
Core::InfoBarEntry info(startPylsInfoBarId, Core::InfoBarEntry info(startPylsInfoBarId,
message, message,
Core::InfoBarEntry::GlobalSuppression::Enabled); Core::InfoBarEntry::GlobalSuppression::Enabled);
info.setCustomButtonInfo(QCoreApplication::translate(pythonUtilsTrContext, "Setup"), info.setCustomButtonInfo(tr("Setup"),
[=]() { setupPythonLanguageServer(python, document); }); [=]() { setupPythonLanguageServer(python, document); });
infoBar->addInfo(info); infoBar->addInfo(info);
m_infoBarEntries[python] << document;
} else if (lsState.state == PythonLanguageServerState::ConfiguredButDisabled } else if (lsState.state == PythonLanguageServerState::ConfiguredButDisabled
&& infoBar->canInfoBeAdded(enablePylsInfoBarId)) { && infoBar->canInfoBeAdded(enablePylsInfoBarId)) {
auto message = QCoreApplication::translate(pythonUtilsTrContext, auto message = tr("Enable Python language server for %1 (%2)?")
"Enable Python language server for %1 (%2)?")
.arg(pythonName(python), python.toUserOutput()); .arg(pythonName(python), python.toUserOutput());
Core::InfoBarEntry info(enablePylsInfoBarId, Core::InfoBarEntry info(enablePylsInfoBarId,
message, message,
Core::InfoBarEntry::GlobalSuppression::Enabled); Core::InfoBarEntry::GlobalSuppression::Enabled);
info.setCustomButtonInfo(QCoreApplication::translate(pythonUtilsTrContext, "Enable"), info.setCustomButtonInfo(tr("Enable"),
[=]() { enablePythonLanguageServer(python, document); }); [=]() { enablePythonLanguageServer(python, document); });
infoBar->addInfo(info); infoBar->addInfo(info);
m_infoBarEntries[python] << document;
} }
} }
void resetEditorInfoBar(TextEditor::TextDocument *document) void PyLSConfigureAssistant::updateEditorInfoBars(const FilePath &python, Client *client)
{ {
for (TextEditor::TextDocument *document : instance()->m_infoBarEntries.take(python)) {
instance()->resetEditorInfoBar(document);
if (client)
LanguageClientManager::reOpenDocumentWithClient(document, client);
}
}
void PyLSConfigureAssistant::resetEditorInfoBar(TextEditor::TextDocument *document)
{
for (QList<TextEditor::TextDocument *> &documents : m_infoBarEntries)
documents.removeAll(document);
Core::InfoBar *infoBar = document->infoBar(); Core::InfoBar *infoBar = document->infoBar();
infoBar->removeInfo(installPylsInfoBarId); infoBar->removeInfo(installPylsInfoBarId);
infoBar->removeInfo(startPylsInfoBarId); infoBar->removeInfo(startPylsInfoBarId);
infoBar->removeInfo(enablePylsInfoBarId); infoBar->removeInfo(enablePylsInfoBarId);
} }
PyLSConfigureAssistant::PyLSConfigureAssistant(QObject *parent)
: QObject(parent)
{}
} // namespace Internal } // namespace Internal
} // namespace Python } // namespace Python
#include "pythonutils.moc" #include "pythonutils.moc"

View File

@@ -23,21 +23,45 @@
** **
****************************************************************************/ ****************************************************************************/
#include <utils/fileutils.h>
#pragma once #pragma once
#include <utils/fileutils.h>
#include <QHash>
#include <QObject>
namespace Core { class IDocument; }
namespace LanguageClient {
class Client;
class StdIOSettings;
}
namespace TextEditor { class TextDocument; } namespace TextEditor { class TextDocument; }
namespace LanguageClient { class StdIOSettings; }
namespace Python { namespace Python {
namespace Internal { namespace Internal {
QList<const LanguageClient::StdIOSettings *> configuredPythonLanguageServers(); class PyLSConfigureAssistant : public QObject
const LanguageClient::StdIOSettings *languageServerForPython(const Utils::FilePath &python); {
Utils::FilePath detectPython(const Utils::FilePath &NdocumentPath); Q_OBJECT
void updateEditorInfoBar(const Utils::FilePath &python, TextEditor::TextDocument *document); public:
static PyLSConfigureAssistant *instance();
static const LanguageClient::StdIOSettings *languageServerForPython(
const Utils::FilePath &python);
static void documentOpened(Core::IDocument *document);
static void updateEditorInfoBars(const Utils::FilePath &python, LanguageClient::Client *client);
void openDocumentWithPython(const Utils::FilePath &python, TextEditor::TextDocument *document);
private:
explicit PyLSConfigureAssistant(QObject *parent);
void resetEditorInfoBar(TextEditor::TextDocument *document); void resetEditorInfoBar(TextEditor::TextDocument *document);
void installPythonLanguageServer(const Utils::FilePath &python,
QPointer<TextEditor::TextDocument> document);
QHash<Utils::FilePath, QList<TextEditor::TextDocument *>> m_infoBarEntries;
};
} // namespace Internal } // namespace Internal
} // namespace Python } // namespace Python