diff options
author | Eike Ziller <eike.ziller@qt.io> | 2019-10-28 11:48:31 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@qt.io> | 2019-10-28 11:48:31 +0100 |
commit | bea3a8fa6fd53cf1e85aa30cc45d1e62464c985d (patch) | |
tree | c9755caff5786475c51c65cfec939ec90ab6ccd2 /src/plugins/python | |
parent | 3c556096caf17cf6b09a91ebf0d262d0be3f65a1 (diff) | |
parent | 3f74b04c3072b2beb23ecdd108894da4057830fc (diff) | |
download | qt-creator-bea3a8fa6fd53cf1e85aa30cc45d1e62464c985d.tar.gz |
Merge remote-tracking branch 'origin/4.11'
Change-Id: I66389d88d5a60c6c86547b93cca945af42aa807b
Diffstat (limited to 'src/plugins/python')
-rw-r--r-- | src/plugins/python/pythoneditor.cpp | 15 | ||||
-rw-r--r-- | src/plugins/python/pythonplugin.cpp | 13 | ||||
-rw-r--r-- | src/plugins/python/pythonplugin.h | 4 | ||||
-rw-r--r-- | src/plugins/python/pythonrunconfiguration.cpp | 15 | ||||
-rw-r--r-- | src/plugins/python/pythonsettings.cpp | 24 | ||||
-rw-r--r-- | src/plugins/python/pythonsettings.h | 10 | ||||
-rw-r--r-- | src/plugins/python/pythonutils.cpp | 131 | ||||
-rw-r--r-- | src/plugins/python/pythonutils.h | 38 |
8 files changed, 157 insertions, 93 deletions
diff --git a/src/plugins/python/pythoneditor.cpp b/src/plugins/python/pythoneditor.cpp index 6481897e69..eea74c1786 100644 --- a/src/plugins/python/pythoneditor.cpp +++ b/src/plugins/python/pythoneditor.cpp @@ -35,19 +35,6 @@ namespace Python { 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() { setId(Constants::C_PYTHONEDITOR_ID); @@ -68,7 +55,7 @@ PythonEditorFactory::PythonEditorFactory() setCodeFoldingSupported(true); connect(Core::EditorManager::instance(), &Core::EditorManager::documentOpened, - this, documentOpened); + this, &PyLSConfigureAssistant::documentOpened); } } // namespace Internal diff --git a/src/plugins/python/pythonplugin.cpp b/src/plugins/python/pythonplugin.cpp index bdb0d6a32e..63d7c663c2 100644 --- a/src/plugins/python/pythonplugin.cpp +++ b/src/plugins/python/pythonplugin.cpp @@ -51,6 +51,8 @@ namespace Internal { // //////////////////////////////////////////////////////////////////////////////////// +static PythonPlugin *m_instance = nullptr; + class PythonPluginPrivate { public: @@ -65,11 +67,22 @@ public: }; }; +PythonPlugin::PythonPlugin() +{ + m_instance = this; +} + PythonPlugin::~PythonPlugin() { + m_instance = nullptr; delete d; } +PythonPlugin *PythonPlugin::instance() +{ + return m_instance; +} + bool PythonPlugin::initialize(const QStringList &arguments, QString *errorMessage) { Q_UNUSED(arguments) diff --git a/src/plugins/python/pythonplugin.h b/src/plugins/python/pythonplugin.h index 5b2e395d91..c0d79d7a87 100644 --- a/src/plugins/python/pythonplugin.h +++ b/src/plugins/python/pythonplugin.h @@ -36,9 +36,11 @@ class PythonPlugin : public ExtensionSystem::IPlugin Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "Python.json") public: - PythonPlugin() = default; + PythonPlugin(); ~PythonPlugin() final; + static PythonPlugin *instance(); + private: bool initialize(const QStringList &arguments, QString *errorMessage) final; void extensionsInitialized() final; diff --git a/src/plugins/python/pythonrunconfiguration.cpp b/src/plugins/python/pythonrunconfiguration.cpp index 37cbc53a9a..4477f11cb1 100644 --- a/src/plugins/python/pythonrunconfiguration.cpp +++ b/src/plugins/python/pythonrunconfiguration.cpp @@ -294,23 +294,10 @@ void PythonRunConfiguration::updateLanguageServer() 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)) { if (auto document = TextEditor::TextDocument::textDocumentForFilePath(file)) { if (document->mimeType() == Constants::C_PY_MIMETYPE) - updateEditorInfoBar(python, document); + PyLSConfigureAssistant::instance()->openDocumentWithPython(python, document); } } } diff --git a/src/plugins/python/pythonsettings.cpp b/src/plugins/python/pythonsettings.cpp index be1ad7b4ee..d9ab47093c 100644 --- a/src/plugins/python/pythonsettings.cpp +++ b/src/plugins/python/pythonsettings.cpp @@ -272,15 +272,14 @@ static bool alreadyRegistered(const QList<Interpreter> &pythons, const FilePath }); } -Interpreter interpreterForPythonExecutable(const FilePath &python, - const QString &defaultName, - bool windowedSuffix = false) +Interpreter::Interpreter(const FilePath &python, const QString &defaultName, bool windowedSuffix) + : id(QUuid::createUuid().toString()) + , command(python) { SynchronousProcess pythonProcess; pythonProcess.setProcessChannelMode(QProcess::MergedChannels); SynchronousProcessResponse response = pythonProcess.runBlocking( CommandLine(python, {"--version"})); - QString name; if (response.result == SynchronousProcessResponse::Finished) name = response.stdOut().trimmed(); if (name.isEmpty()) @@ -290,9 +289,14 @@ Interpreter interpreterForPythonExecutable(const FilePath &python, QDir pythonDir(python.parentDir().toString()); if (pythonDir.exists() && pythonDir.exists("activate") && pythonDir.cdUp()) name += QString(" (%1 Virtual Environment)").arg(pythonDir.dirName()); - return Interpreter{QUuid::createUuid().toString(), name, python}; } +Interpreter::Interpreter(const QString &_id, const QString &_name, const FilePath &_command) + : id(_id) + , name(_name) + , command(_command) +{} + static InterpreterOptionsPage &interpreterOptionsPage() { static InterpreterOptionsPage page; @@ -388,11 +392,11 @@ static void addPythonsFromRegistry(QList<Interpreter> &pythons) const FilePath &path = FilePath::fromUserInput(regVal.toString()); const FilePath &python = path.pathAppended(HostOsInfo::withExecutableSuffix("python")); if (python.exists() && !alreadyRegistered(pythons, python)) - pythons << interpreterForPythonExecutable(python, "Python " + versionGroup); + pythons << Interpreter(python, "Python " + versionGroup); const FilePath &pythonw = path.pathAppended( HostOsInfo::withExecutableSuffix("pythonw")); if (pythonw.exists() && !alreadyRegistered(pythons, pythonw)) - pythons << interpreterForPythonExecutable(pythonw, "Python " + versionGroup, true); + pythons << Interpreter(pythonw, "Python " + versionGroup, true); } pythonRegistry.endGroup(); } @@ -405,11 +409,11 @@ static void addPythonsFromPath(QList<Interpreter> &pythons) if (HostOsInfo::isWindowsHost()) { for (const FilePath &executable : env.findAllInPath("python")) { if (executable.exists() && !alreadyRegistered(pythons, executable)) - pythons << interpreterForPythonExecutable(executable, "Python from Path"); + pythons << Interpreter(executable, "Python from Path"); } for (const FilePath &executable : env.findAllInPath("pythonw")) { if (executable.exists() && !alreadyRegistered(pythons, executable)) - pythons << interpreterForPythonExecutable(executable, "Python from Path", true); + pythons << Interpreter(executable, "Python from Path", true); } } else { const QStringList filters = {"python", @@ -421,7 +425,7 @@ static void addPythonsFromPath(QList<Interpreter> &pythons) for (const QFileInfo &fi : dir.entryInfoList(filters)) { const FilePath executable = Utils::FilePath::fromFileInfo(fi); if (executable.exists() && !alreadyRegistered(pythons, executable)) - pythons << interpreterForPythonExecutable(executable, "Python from Path"); + pythons << Interpreter(executable, "Python from Path"); } } } diff --git a/src/plugins/python/pythonsettings.h b/src/plugins/python/pythonsettings.h index 583d4fb212..7ca5218ae3 100644 --- a/src/plugins/python/pythonsettings.h +++ b/src/plugins/python/pythonsettings.h @@ -35,7 +35,15 @@ namespace Internal { class Interpreter { public: - QString id; + Interpreter() = default; + Interpreter(const Utils::FilePath &python, + const QString &defaultName, + bool windowedSuffix = false); + Interpreter(const QString &id, + const QString &name, + const Utils::FilePath &command); + + QString id = QUuid::createUuid().toString(); QString name; Utils::FilePath command; }; diff --git a/src/plugins/python/pythonutils.cpp b/src/plugins/python/pythonutils.cpp index 0af6d5fd5a..03096ee96e 100644 --- a/src/plugins/python/pythonutils.cpp +++ b/src/plugins/python/pythonutils.cpp @@ -26,6 +26,7 @@ #include "pythonutils.h" #include "pythonconstants.h" +#include "pythonplugin.h" #include "pythonproject.h" #include "pythonrunconfiguration.h" #include "pythonsettings.h" @@ -33,8 +34,8 @@ #include <coreplugin/infobar.h> #include <coreplugin/progressmanager/progressmanager.h> -#include <languageclient/languageclientsettings.h> #include <languageclient/languageclientmanager.h> +#include <languageclient/languageclientsettings.h> #include <projectexplorer/session.h> #include <projectexplorer/target.h> @@ -49,6 +50,7 @@ #include <QRegularExpression> #include <QTimer> +using namespace LanguageClient; using namespace Utils; namespace Python { @@ -58,7 +60,6 @@ static constexpr char startPylsInfoBarId[] = "Python::StartPyls"; static constexpr char installPylsInfoBarId[] = "Python::InstallPyls"; static constexpr char enablePylsInfoBarId[] = "Python::EnablePyls"; static constexpr char installPylsTaskId[] = "Python::InstallPylsTask"; -static constexpr char pythonUtilsTrContext[] = "Python::Utils"; struct PythonLanguageServerState { @@ -116,7 +117,7 @@ FilePath getPylsModulePath(CommandLine pylsCommand) return {}; } -QList<const LanguageClient::StdIOSettings *> configuredPythonLanguageServer() +QList<const StdIOSettings *> configuredPythonLanguageServer() { using namespace LanguageClient; QList<const StdIOSettings *> result; @@ -156,7 +157,7 @@ static PythonLanguageServerState checkPythonLanguageServer(const FilePath &pytho return {PythonLanguageServerState::CanNotBeInstalled, FilePath()}; } -FilePath detectPython(const FilePath &documentPath) +static FilePath detectPython(const FilePath &documentPath) { FilePath python; @@ -183,26 +184,33 @@ FilePath detectPython(const FilePath &documentPath) 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(), - [pythonModulePath = getPylsModulePath(CommandLine(python, {"-m", "pyls"}))]( - const LanguageClient::StdIOSettings *setting) { + [pythonModulePath = getPylsModulePath( + CommandLine(python, {"-m", "pyls"}))](const StdIOSettings *setting) { 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_arguments = "-m pyls"; - settings->m_name = QCoreApplication::translate(pythonUtilsTrContext, - "Python Language Server (%1)") + settings->m_name = PyLSConfigureAssistant::tr("Python Language Server (%1)") .arg(pythonName(python)); settings->m_languageFilter.mimeTypes = QStringList(Constants::C_PY_MIMETYPE); - LanguageClient::LanguageClientManager::registerClientSettings(settings); - return LanguageClient::LanguageClientManager::clientForSetting(settings).value(0); + LanguageClientManager::registerClientSettings(settings); + Client *client = LanguageClientManager::clientForSetting(settings).value(0); + PyLSConfigureAssistant::updateEditorInfoBars(python, client); + return client; } class PythonLSInstallHelper : public QObject @@ -240,7 +248,7 @@ public: ? QString{"python-language-server[pyflakes]"} : QString{"python-language-server[all]"}; - m_process.start(m_python.toString(), {"-m", "pip", "install", pylsVersion}); + m_process.start(m_python.toString(), {"-m", "pip", "install", "--user", pylsVersion}); Core::MessageManager::write(tr("Running '%1 %2' to install python language server") .arg(m_process.program(), m_process.arguments().join(' '))); @@ -253,17 +261,16 @@ private: void cancel() { SynchronousProcess::stopProcess(m_process); - Core::MessageManager::write( - tr("The Python language server installation canceled by %1.") - .arg(m_killTimer.isActive() ? tr("user") : tr("time out"))); + Core::MessageManager::write(tr("The Python language server installation canceled by %1.") + .arg(m_killTimer.isActive() ? tr("user") : tr("time out"))); } void installFinished(int exitCode, QProcess::ExitStatus exitStatus) { m_future.reportFinished(); if (exitStatus == QProcess::NormalExit && exitCode == 0) { - if (LanguageClient::Client *client = registerLanguageServer(m_python)) - LanguageClient::LanguageClientManager::reOpenDocumentWithClient(m_document, client); + if (Client *client = registerLanguageServer(m_python)) + LanguageClientManager::reOpenDocumentWithClient(m_document, client); } else { Core::MessageManager::write( tr("Installing the Python language server failed with exit code %1").arg(exitCode)); @@ -293,11 +300,16 @@ private: QPointer<TextEditor::TextDocument> m_document; }; -static void installPythonLanguageServer(const FilePath &python, - QPointer<TextEditor::TextDocument> document) +void PyLSConfigureAssistant::installPythonLanguageServer(const FilePath &python, + QPointer<TextEditor::TextDocument> document) { 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); install->run(); } @@ -306,36 +318,49 @@ static void setupPythonLanguageServer(const FilePath &python, QPointer<TextEditor::TextDocument> document) { document->infoBar()->removeInfo(startPylsInfoBarId); - if (LanguageClient::Client *client = registerLanguageServer(python)) - LanguageClient::LanguageClientManager::reOpenDocumentWithClient(document, client); + if (Client *client = registerLanguageServer(python)) + LanguageClientManager::reOpenDocumentWithClient(document, client); } static void enablePythonLanguageServer(const FilePath &python, QPointer<TextEditor::TextDocument> document) { - using namespace LanguageClient; document->infoBar()->removeInfo(enablePylsInfoBarId); - if (const StdIOSettings *setting = languageServerForPython(python)) { + if (const StdIOSettings *setting = PyLSConfigureAssistant::languageServerForPython(python)) { LanguageClientManager::enableClientSettings(setting->m_id); - if (const StdIOSettings *setting = languageServerForPython(python)) { - if (Client *client = LanguageClientManager::clientForSetting(setting).value(0)) + if (const StdIOSettings *setting = PyLSConfigureAssistant::languageServerForPython(python)) { + if (Client *client = LanguageClientManager::clientForSetting(setting).value(0)) { 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); if (lsState.state == PythonLanguageServerState::CanNotBeInstalled) return; if (lsState.state == PythonLanguageServerState::AlreadyConfigured) { - if (const LanguageClient::StdIOSettings *setting = languageServerForPython(python)) { - if (LanguageClient::Client *client - = LanguageClient::LanguageClientManager::clientForSetting(setting).value(0)) { - LanguageClient::LanguageClientManager::reOpenDocumentWithClient(document, client); - } + if (const StdIOSettings *setting = languageServerForPython(python)) { + if (Client *client = LanguageClientManager::clientForSetting(setting).value(0)) + LanguageClientManager::reOpenDocumentWithClient(document, client); } return; } @@ -345,52 +370,66 @@ void updateEditorInfoBar(const FilePath &python, TextEditor::TextDocument *docum if (lsState.state == PythonLanguageServerState::CanBeInstalled && infoBar->canInfoBeAdded(installPylsInfoBarId)) { auto message - = QCoreApplication::translate(pythonUtilsTrContext, - "Install and set up Python language server (PyLS) for %1 (%2). " - "The language server provides Python specific completions and annotations.") + = tr("Install and set up Python language server (PyLS) for %1 (%2). " + "The language server provides Python specific completions and annotations.") .arg(pythonName(python), python.toUserOutput()); Core::InfoBarEntry info(installPylsInfoBarId, message, Core::InfoBarEntry::GlobalSuppression::Enabled); - info.setCustomButtonInfo(QCoreApplication::translate(pythonUtilsTrContext, "Install"), + info.setCustomButtonInfo(tr("Install"), [=]() { installPythonLanguageServer(python, document); }); infoBar->addInfo(info); + m_infoBarEntries[python] << document; } else if (lsState.state == PythonLanguageServerState::AlreadyInstalled && infoBar->canInfoBeAdded(startPylsInfoBarId)) { - auto message = QCoreApplication::translate(pythonUtilsTrContext, - "Found a Python language server for %1 (%2). " - "Should this one be set up for this document?") + auto message = tr("Found a Python language server for %1 (%2). " + "Should this one be set up for this document?") .arg(pythonName(python), python.toUserOutput()); Core::InfoBarEntry info(startPylsInfoBarId, message, Core::InfoBarEntry::GlobalSuppression::Enabled); - info.setCustomButtonInfo(QCoreApplication::translate(pythonUtilsTrContext, "Setup"), + info.setCustomButtonInfo(tr("Setup"), [=]() { setupPythonLanguageServer(python, document); }); infoBar->addInfo(info); + m_infoBarEntries[python] << document; } else if (lsState.state == PythonLanguageServerState::ConfiguredButDisabled && infoBar->canInfoBeAdded(enablePylsInfoBarId)) { - auto message = QCoreApplication::translate(pythonUtilsTrContext, - "Enable Python language server for %1 (%2)?") + auto message = tr("Enable Python language server for %1 (%2)?") .arg(pythonName(python), python.toUserOutput()); Core::InfoBarEntry info(enablePylsInfoBarId, message, Core::InfoBarEntry::GlobalSuppression::Enabled); - info.setCustomButtonInfo(QCoreApplication::translate(pythonUtilsTrContext, "Enable"), + info.setCustomButtonInfo(tr("Enable"), [=]() { enablePythonLanguageServer(python, document); }); 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(); infoBar->removeInfo(installPylsInfoBarId); infoBar->removeInfo(startPylsInfoBarId); infoBar->removeInfo(enablePylsInfoBarId); } +PyLSConfigureAssistant::PyLSConfigureAssistant(QObject *parent) + : QObject(parent) +{} + } // namespace Internal } // namespace Python #include "pythonutils.moc" - diff --git a/src/plugins/python/pythonutils.h b/src/plugins/python/pythonutils.h index f376a4d749..20cb674ddb 100644 --- a/src/plugins/python/pythonutils.h +++ b/src/plugins/python/pythonutils.h @@ -23,21 +23,45 @@ ** ****************************************************************************/ +#pragma once + #include <utils/fileutils.h> -#pragma once +#include <QHash> +#include <QObject> +namespace Core { class IDocument; } +namespace LanguageClient { +class Client; +class StdIOSettings; +} namespace TextEditor { class TextDocument; } -namespace LanguageClient { class StdIOSettings; } namespace Python { namespace Internal { -QList<const LanguageClient::StdIOSettings *> configuredPythonLanguageServers(); -const LanguageClient::StdIOSettings *languageServerForPython(const Utils::FilePath &python); -Utils::FilePath detectPython(const Utils::FilePath &NdocumentPath); -void updateEditorInfoBar(const Utils::FilePath &python, TextEditor::TextDocument *document); -void resetEditorInfoBar(TextEditor::TextDocument *document); +class PyLSConfigureAssistant : public QObject +{ + Q_OBJECT +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 installPythonLanguageServer(const Utils::FilePath &python, + QPointer<TextEditor::TextDocument> document); + + QHash<Utils::FilePath, QList<TextEditor::TextDocument *>> m_infoBarEntries; +}; } // namespace Internal } // namespace Python |