forked from qt-creator/qt-creator
Python: Add info bar to install python language server
If the detected python for the current document does not have an installed language server, check whether pip is installed, try to install the language server and if that was successful set it up. Change-Id: Ib6cf3bacdcf3489728990cace5417862b7c78be5 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -45,11 +45,13 @@
|
||||
#include <texteditor/texteditoractionhandler.h>
|
||||
#include <texteditor/texteditorconstants.h>
|
||||
|
||||
#include <utils/executeondestruction.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/synchronousprocess.h>
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QRegularExpression>
|
||||
#include <QTimer>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
@@ -58,6 +60,7 @@ namespace Python {
|
||||
namespace Internal {
|
||||
|
||||
static constexpr char startPylsInfoBarId[] = "PythonEditor::StartPyls";
|
||||
static constexpr char installPylsInfoBarId[] = "PythonEditor::InstallPyls";
|
||||
|
||||
struct PythonForProject
|
||||
{
|
||||
@@ -193,6 +196,90 @@ static LanguageClient::Client *registerLanguageServer(const PythonForProject &py
|
||||
return LanguageClient::LanguageClientManager::clientForSetting(settings).value(0);
|
||||
}
|
||||
|
||||
class PythonLSInstallHelper : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
PythonLSInstallHelper(const PythonForProject &python, QPointer<TextEditor::TextDocument> document)
|
||||
: m_python(python)
|
||||
, m_document(document)
|
||||
{}
|
||||
|
||||
void run()
|
||||
{
|
||||
auto killTimer = new QTimer(&m_process);
|
||||
|
||||
connect(&m_process,
|
||||
QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
|
||||
this,
|
||||
&PythonLSInstallHelper::installFinished);
|
||||
connect(&m_process,
|
||||
&QProcess::readyReadStandardError,
|
||||
this,
|
||||
&PythonLSInstallHelper::errorAvailable);
|
||||
connect(&m_process,
|
||||
&QProcess::readyReadStandardOutput,
|
||||
this,
|
||||
&PythonLSInstallHelper::outputAvailable);
|
||||
connect(killTimer, &QTimer::timeout, [this]() {
|
||||
SynchronousProcess::stopProcess(m_process);
|
||||
Core::MessageManager::write(tr("The Python language server installation timed out."));
|
||||
});
|
||||
|
||||
// on windows the pyls 0.28.3 crashes with pylint so just install the pyflakes linter
|
||||
const QString &pylsVersion = HostOsInfo::isWindowsHost()
|
||||
? QString{"python-language-server[pyflakes]"}
|
||||
: QString{"python-language-server[all]"};
|
||||
|
||||
m_process.start(m_python.path.toString(),
|
||||
{"-m", "pip", "install", pylsVersion});
|
||||
|
||||
Core::MessageManager::write(tr("Running '%1 %2' to install python language server")
|
||||
.arg(m_process.program(), m_process.arguments().join(' ')));
|
||||
|
||||
killTimer->start(5 /*minutes*/ * 60 * 1000);
|
||||
}
|
||||
|
||||
private:
|
||||
void installFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
||||
{
|
||||
if (exitStatus == QProcess::NormalExit && exitCode == 0) {
|
||||
if (LanguageClient::Client *client = registerLanguageServer(m_python))
|
||||
LanguageClient::LanguageClientManager::reOpenDocumentWithClient(m_document, client);
|
||||
} else {
|
||||
Core::MessageManager::write(
|
||||
tr("Installing the Python language server failed with exit code %1").arg(exitCode));
|
||||
}
|
||||
deleteLater();
|
||||
}
|
||||
void outputAvailable()
|
||||
{
|
||||
const QString &stdOut = QString::fromLocal8Bit(m_process.readAllStandardOutput().trimmed());
|
||||
if (!stdOut.isEmpty())
|
||||
Core::MessageManager::write(stdOut);
|
||||
}
|
||||
|
||||
void errorAvailable()
|
||||
{
|
||||
const QString &stdErr = QString::fromLocal8Bit(m_process.readAllStandardError().trimmed());
|
||||
if (!stdErr.isEmpty())
|
||||
Core::MessageManager::write(stdErr);
|
||||
}
|
||||
|
||||
QProcess m_process;
|
||||
const PythonForProject m_python;
|
||||
QPointer<TextEditor::TextDocument> m_document;
|
||||
};
|
||||
|
||||
static void installPythonLanguageServer(const PythonForProject &python,
|
||||
QPointer<TextEditor::TextDocument> document)
|
||||
{
|
||||
document->infoBar()->removeInfo(installPylsInfoBarId);
|
||||
|
||||
auto install = new PythonLSInstallHelper(python, document);
|
||||
install->run();
|
||||
}
|
||||
|
||||
static void setupPythonLanguageServer(const PythonForProject &python,
|
||||
QPointer<TextEditor::TextDocument> document)
|
||||
{
|
||||
@@ -206,13 +293,25 @@ static void updateEditorInfoBar(const PythonForProject &python, TextEditor::Text
|
||||
const PythonLanguageServerState &lsState = checkPythonLanguageServer(python.path, document);
|
||||
|
||||
if (lsState.state == PythonLanguageServerState::CanNotBeInstalled
|
||||
|| lsState.state == PythonLanguageServerState::AlreadyConfigured
|
||||
|| lsState.state == PythonLanguageServerState::CanBeInstalled /* TODO */) {
|
||||
|| lsState.state == PythonLanguageServerState::AlreadyConfigured) {
|
||||
return;
|
||||
}
|
||||
|
||||
Core::InfoBar *infoBar = document->infoBar();
|
||||
if (lsState.state == PythonLanguageServerState::AlreadyInstalled
|
||||
if (lsState.state == PythonLanguageServerState::CanBeInstalled
|
||||
&& infoBar->canInfoBeAdded(installPylsInfoBarId)) {
|
||||
auto message
|
||||
= PythonEditorFactory::tr(
|
||||
"Install and set up Python language server for %1 (%2). "
|
||||
"The language server provides Python specific completions and annotations.")
|
||||
.arg(python.name(), python.path.toUserOutput());
|
||||
Core::InfoBarEntry info(installPylsInfoBarId,
|
||||
message,
|
||||
Core::InfoBarEntry::GlobalSuppression::Enabled);
|
||||
info.setCustomButtonInfo(TextEditor::BaseTextEditor::tr("Install"),
|
||||
[=]() { installPythonLanguageServer(python, document); });
|
||||
infoBar->addInfo(info);
|
||||
} else if (lsState.state == PythonLanguageServerState::AlreadyInstalled
|
||||
&& infoBar->canInfoBeAdded(startPylsInfoBarId)) {
|
||||
auto message = PythonEditorFactory::tr("Found a Python language server for %1 (%2). "
|
||||
"Should this one be set up for this document?")
|
||||
@@ -264,3 +363,5 @@ PythonEditorFactory::PythonEditorFactory()
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Python
|
||||
|
||||
#include "pythoneditor.moc"
|
||||
|
Reference in New Issue
Block a user