summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--share/qtcreator/templates/wizards/classes/python/file.py46
-rw-r--r--share/qtcreator/templates/wizards/classes/python/wizard.json11
-rw-r--r--share/qtcreator/templates/wizards/files/python/file.py6
-rw-r--r--share/qtcreator/templates/wizards/files/python/icon.pngbin0 -> 606 bytes
-rw-r--r--share/qtcreator/templates/wizards/files/python/icon@2x.pngbin0 -> 989 bytes
-rw-r--r--share/qtcreator/templates/wizards/files/python/wizard.json2
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon.pngbin0 -> 728 bytes
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon@2x.pngbin0 -> 1284 bytes
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json51
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/main.pyproject3
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/main_empty.py9
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/main_mainwindow.py15
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/icon.pngbin0 -> 855 bytes
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/icon@2x.pngbin0 -> 1528 bytes
-rw-r--r--share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json51
-rw-r--r--src/libs/languageserverprotocol/basemessage.cpp2
-rw-r--r--src/libs/languageserverprotocol/basemessage.h2
-rw-r--r--src/plugins/autotest/qtest/qttestoutputreader.cpp85
-rw-r--r--src/plugins/autotest/qtest/qttestoutputreader.h2
-rw-r--r--src/plugins/languageclient/client.cpp (renamed from src/plugins/languageclient/baseclient.cpp)250
-rw-r--r--src/plugins/languageclient/client.h (renamed from src/plugins/languageclient/baseclient.h)58
-rw-r--r--src/plugins/languageclient/languageclient.pro6
-rw-r--r--src/plugins/languageclient/languageclient.qbs6
-rw-r--r--src/plugins/languageclient/languageclientcodeassist.cpp52
-rw-r--r--src/plugins/languageclient/languageclientcodeassist.h6
-rw-r--r--src/plugins/languageclient/languageclientinterface.cpp165
-rw-r--r--src/plugins/languageclient/languageclientinterface.h96
-rw-r--r--src/plugins/languageclient/languageclientmanager.cpp50
-rw-r--r--src/plugins/languageclient/languageclientmanager.h24
-rw-r--r--src/plugins/languageclient/languageclientoutline.cpp14
-rw-r--r--src/plugins/languageclient/languageclientplugin.cpp2
-rw-r--r--src/plugins/languageclient/languageclientsettings.cpp29
-rw-r--r--src/plugins/languageclient/languageclientsettings.h12
-rw-r--r--src/plugins/languageclient/languageclientutils.cpp6
-rw-r--r--src/plugins/languageclient/languageclientutils.h4
-rw-r--r--src/plugins/projectexplorer/buildtargetinfo.h1
-rw-r--r--src/plugins/projectexplorer/runconfiguration.cpp13
-rw-r--r--src/plugins/projectexplorer/runconfiguration.h1
-rw-r--r--src/plugins/pythoneditor/PythonEditor.json.in1
-rw-r--r--src/plugins/pythoneditor/pythoneditorplugin.cpp45
-rw-r--r--src/plugins/qmakeprojectmanager/qmakeproject.cpp6
-rw-r--r--src/tools/icons/qtcreatoricons.svg173
-rw-r--r--tests/manual/debugger/python/README.md2
43 files changed, 906 insertions, 401 deletions
diff --git a/share/qtcreator/templates/wizards/classes/python/file.py b/share/qtcreator/templates/wizards/classes/python/file.py
index 32be237c7a..adcbc1ed2d 100644
--- a/share/qtcreator/templates/wizards/classes/python/file.py
+++ b/share/qtcreator/templates/wizards/classes/python/file.py
@@ -1,30 +1,26 @@
-# -*- coding: utf-8 -*-
-
-@if '%{Imports}'
-try:
-@if '%{ImportQtCore}'
- from PySide import QtCore
-@endif
-@if '%{ImportQtWidgets}'
- from PySide import QtWidgets
-@endif
-@if '%{ImportQtQuick}'
- from PySide import QtQuick
-@endif
-except:
-@if '%{ImportQtCore}'
- from PyQt5.QtCore import pyqtSlot as Slot
- from PyQt5 import QtCore
-@endif
-@if '%{ImportQtWidgets}'
- from PyQt5 import QtWidgets
-@endif
-@if '%{ImportQtQuick}'
- from PyQt5 import QtQuick
+# This Python file uses the following encoding: utf-8
+@if '%{Module}' === 'PySide2'
+ @if '%{ImportQtCore}'
+from PySide2 import QtCore
+ @endif
+ @if '%{ImportQtWidgets}'
+from PySide2 import QtWidgets
+ @endif
+ @if '%{ImportQtQuick}'
+from PySide2 import QtQuick
+ @endif
+@else
+ @if '%{ImportQtCore}'
+from PyQt5 import QtCore
+ @endif
+ @if '%{ImportQtWidgets}'
+from PyQt5 import QtWidgets
+ @endif
+ @if '%{ImportQtQuick}'
+from PyQt5 import QtQuick
+ @endif
@endif
-
-@endif
@if '%{Base}'
class %{Class}(%{Base}):
@else
diff --git a/share/qtcreator/templates/wizards/classes/python/wizard.json b/share/qtcreator/templates/wizards/classes/python/wizard.json
index 64c8d8d435..6d1d72c63b 100644
--- a/share/qtcreator/templates/wizards/classes/python/wizard.json
+++ b/share/qtcreator/templates/wizards/classes/python/wizard.json
@@ -6,7 +6,7 @@
"trDescription": "Creates new Python class file.",
"trDisplayName": "Python Class",
"trDisplayCategory": "Python",
- "iconText": "py",
+ "icon": "../../files/python/icon.png",
"enabled": "%{JS: [ %{Plugins} ].indexOf('PythonEditor') >= 0}",
"options":
@@ -31,6 +31,15 @@
"data": { "validator": "^(?:[^\\d\\W]\\w*|)$" }
},
{
+ "name": "Module",
+ "trDisplayName": "Python module:",
+ "type": "ComboBox",
+ "data":
+ {
+ "items": ["PySide2", "PyQt5"]
+ }
+ },
+ {
"name": "BaseCB",
"trDisplayName": "Base class:",
"type": "ComboBox",
diff --git a/share/qtcreator/templates/wizards/files/python/file.py b/share/qtcreator/templates/wizards/files/python/file.py
index faa18be5bb..003f841497 100644
--- a/share/qtcreator/templates/wizards/files/python/file.py
+++ b/share/qtcreator/templates/wizards/files/python/file.py
@@ -1,2 +1,4 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
+# This Python file uses the following encoding: utf-8
+
+# if__name__ == "__main__":
+# pass
diff --git a/share/qtcreator/templates/wizards/files/python/icon.png b/share/qtcreator/templates/wizards/files/python/icon.png
new file mode 100644
index 0000000000..8ab694fe7a
--- /dev/null
+++ b/share/qtcreator/templates/wizards/files/python/icon.png
Binary files differ
diff --git a/share/qtcreator/templates/wizards/files/python/icon@2x.png b/share/qtcreator/templates/wizards/files/python/icon@2x.png
new file mode 100644
index 0000000000..8d1f275186
--- /dev/null
+++ b/share/qtcreator/templates/wizards/files/python/icon@2x.png
Binary files differ
diff --git a/share/qtcreator/templates/wizards/files/python/wizard.json b/share/qtcreator/templates/wizards/files/python/wizard.json
index 32b720f3d4..d4a6cfd012 100644
--- a/share/qtcreator/templates/wizards/files/python/wizard.json
+++ b/share/qtcreator/templates/wizards/files/python/wizard.json
@@ -6,7 +6,7 @@
"trDescription": "Creates an empty Python script file using UTF-8 charset.",
"trDisplayName": "Python File",
"trDisplayCategory": "Python",
- "iconText": "py",
+ "icon": "icon.png",
"enabled": "%{JS: [ %{Plugins} ].indexOf('PythonEditor') >= 0}",
"pages" :
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon.png b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon.png
new file mode 100644
index 0000000000..483f71196d
--- /dev/null
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon.png
Binary files differ
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon@2x.png b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon@2x.png
new file mode 100644
index 0000000000..81beed5e23
--- /dev/null
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon@2x.png
Binary files differ
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json
new file mode 100644
index 0000000000..0f1ce91cf7
--- /dev/null
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/wizard.json
@@ -0,0 +1,51 @@
+{
+ "version": 1,
+ "supportedProjectTypes": [ "PythonProject" ],
+ "id": "U.QtForPythonApplicationEmpty",
+ "category": "F.Application",
+ "trDescription": "Creates a Qt for Python application that only the main code for a QApplication",
+ "trDisplayName": "Qt for Python - Empty",
+ "trDisplayCategory": "Application",
+ "icon": "icon.png",
+ "enabled": "%{JS: [ %{Plugins} ].indexOf('PythonEditor') >= 0}",
+ "featuresRequired": [ "QtSupport.Wizards.FeatureQt.5.6" ],
+
+ "options":
+ [
+ { "key": "MainPyFileName", "value": "main.py" },
+ { "key": "PyProjectFile", "value": "main.pyproject" }
+ ],
+
+ "pages":
+ [
+ {
+ "trDisplayName": "Project Location",
+ "trShortTitle": "Location",
+ "typeId": "Project"
+ },
+ {
+ "trDisplayName": "Project Management",
+ "trShortTitle": "Summary",
+ "typeId": "Summary"
+ }
+ ],
+ "generators":
+ [
+ {
+ "typeId": "File",
+ "data":
+ [
+ {
+ "source": "../main.pyproject",
+ "target": "%{PyProjectFile}",
+ "openAsProject": true
+ },
+ {
+ "source": "../main_empty.py",
+ "target": "%{MainPyFileName}",
+ "openInEditor": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main.pyproject b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main.pyproject
new file mode 100644
index 0000000000..cc7a74a346
--- /dev/null
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main.pyproject
@@ -0,0 +1,3 @@
+{
+ "files": ["main.py"]
+}
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main_empty.py b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main_empty.py
new file mode 100644
index 0000000000..6cb021403b
--- /dev/null
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main_empty.py
@@ -0,0 +1,9 @@
+# This Python file uses the following encoding: utf-8
+import sys
+from PySide2.QtWidgets import QApplication
+
+
+if __name__ == "__main__":
+ app = QApplication([])
+ # ...
+ sys.exit(app.exec_())
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main_mainwindow.py b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main_mainwindow.py
new file mode 100644
index 0000000000..fa1f92c747
--- /dev/null
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/main_mainwindow.py
@@ -0,0 +1,15 @@
+# This Python file uses the following encoding: utf-8
+import sys
+from PySide2.QtWidgets import QApplication, QMainWindow
+
+
+class MainWindow(QMainWindow):
+ def __init__(self):
+ QMainWindow.__init__(self)
+
+
+if __name__ == "__main__":
+ app = QApplication([])
+ window = MainWindow()
+ window.show()
+ sys.exit(app.exec_())
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/icon.png b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/icon.png
new file mode 100644
index 0000000000..ba97ffd662
--- /dev/null
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/icon.png
Binary files differ
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/icon@2x.png b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/icon@2x.png
new file mode 100644
index 0000000000..4c26be65ba
--- /dev/null
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/icon@2x.png
Binary files differ
diff --git a/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json
new file mode 100644
index 0000000000..c98ebb3ed4
--- /dev/null
+++ b/share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/wizard.json
@@ -0,0 +1,51 @@
+{
+ "version": 1,
+ "supportedProjectTypes": [ "Qt4ProjectManager.Qt4Project" ],
+ "id": "U.QtForPythonApplicationWindow",
+ "category": "F.Application",
+ "trDescription": "Creates a Qt for Python application that contains an empty window.",
+ "trDisplayName": "Qt for Python - Window",
+ "trDisplayCategory": "Application",
+ "icon": "icon.png",
+ "enabled": "%{JS: [ %{Plugins} ].indexOf('PythonEditor') >= 0}",
+ "featuresRequired": [ "QtSupport.Wizards.FeatureQt.5.6" ],
+
+ "options":
+ [
+ { "key": "MainPyFileName", "value": "main.py" },
+ { "key": "PyProjectFile", "value": "main.pyproject" }
+ ],
+
+ "pages":
+ [
+ {
+ "trDisplayName": "Project Location",
+ "trShortTitle": "Location",
+ "typeId": "Project"
+ },
+ {
+ "trDisplayName": "Project Management",
+ "trShortTitle": "Summary",
+ "typeId": "Summary"
+ }
+ ],
+ "generators":
+ [
+ {
+ "typeId": "File",
+ "data":
+ [
+ {
+ "source": "../main.pyproject",
+ "target": "%{PyProjectFile}",
+ "openAsProject": true
+ },
+ {
+ "source": "../main_mainwindow.py",
+ "target": "%{MainPyFileName}",
+ "openInEditor": true
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/libs/languageserverprotocol/basemessage.cpp b/src/libs/languageserverprotocol/basemessage.cpp
index 65fc12e449..1ee17e3eee 100644
--- a/src/libs/languageserverprotocol/basemessage.cpp
+++ b/src/libs/languageserverprotocol/basemessage.cpp
@@ -176,7 +176,7 @@ bool BaseMessage::isValid() const
return contentLength >= 0;
}
-QByteArray BaseMessage::toData()
+QByteArray BaseMessage::toData() const
{
return header() + content;
}
diff --git a/src/libs/languageserverprotocol/basemessage.h b/src/libs/languageserverprotocol/basemessage.h
index 9c35ece1b0..ba8238291a 100644
--- a/src/libs/languageserverprotocol/basemessage.h
+++ b/src/libs/languageserverprotocol/basemessage.h
@@ -55,7 +55,7 @@ public:
bool isComplete() const;
bool isValid() const;
- QByteArray toData();
+ QByteArray toData() const;
QByteArray mimeType;
QByteArray content;
diff --git a/src/plugins/autotest/qtest/qttestoutputreader.cpp b/src/plugins/autotest/qtest/qttestoutputreader.cpp
index 06675dade5..97fb6dc3cf 100644
--- a/src/plugins/autotest/qtest/qttestoutputreader.cpp
+++ b/src/plugins/autotest/qtest/qttestoutputreader.cpp
@@ -32,7 +32,6 @@
#include <QDir>
#include <QFileInfo>
-#include <QRegExp>
#include <QRegularExpression>
namespace Autotest {
@@ -41,17 +40,16 @@ namespace Internal {
static QString decode(const QString& original)
{
QString result(original);
- static QRegExp regex("&#((x[0-9A-F]+)|([0-9]+));", Qt::CaseInsensitive);
- regex.setMinimal(true);
+ static const QRegularExpression regex("&#((x[[:xdigit:]]+)|(\\d+));");
- int pos = 0;
- while ((pos = regex.indexIn(original, pos)) != -1) {
- const QString value = regex.cap(1);
+ QRegularExpressionMatchIterator it = regex.globalMatch(original);
+ while (it.hasNext()) {
+ const QRegularExpressionMatch match = it.next();
+ const QString value = match.captured(1);
if (value.startsWith('x'))
- result.replace(regex.cap(0), QChar(value.midRef(1).toInt(nullptr, 16)));
+ result.replace(match.captured(0), QChar(value.midRef(1).toInt(nullptr, 16)));
else
- result.replace(regex.cap(0), QChar(value.toInt(nullptr, 10)));
- pos += regex.matchedLength();
+ result.replace(match.captured(0), QChar(value.toInt(nullptr, 10)));
}
return result;
@@ -347,39 +345,48 @@ static QStringList extractFunctionInformation(const QString &testClassName,
void QtTestOutputReader::processPlainTextOutput(const QByteArray &outputLineWithNewLine)
{
- static QRegExp start("^[*]{9} Start testing of (.*) [*]{9}$");
- static QRegExp config("^Config: Using QtTest library (.*), (Qt (\\d+(\\.\\d+){2}) \\(.*\\))$");
- static QRegExp summary("^Totals: \\d+ passed, \\d+ failed, \\d+ skipped(, \\d+ blacklisted)?$");
- static QRegExp finish("^[*]{9} Finished testing of (.*) [*]{9}$");
-
- static QRegExp result("^(PASS |FAIL! |XFAIL |XPASS |SKIP |RESULT "
- "|BPASS |BFAIL |BXPASS |BXFAIL "
- "|INFO |QWARN |WARNING|QDEBUG |QSYSTEM): (.*)$");
-
- static QRegExp benchDetails("^\\s+([\\d,.]+ .* per iteration \\(total: [\\d,.]+, iterations: \\d+\\))$");
- static QRegExp locationUnix(QT_TEST_FAIL_UNIX_REGEXP);
- static QRegExp locationWin(QT_TEST_FAIL_WIN_REGEXP);
+ static const QRegularExpression start("^[*]{9} Start testing of (.*) [*]{9}$");
+ static const QRegularExpression config("^Config: Using QtTest library (.*), "
+ "(Qt (\\d+(\\.\\d+){2}) \\(.*\\))$");
+ static const QRegularExpression summary("^Totals: \\d+ passed, \\d+ failed, "
+ "\\d+ skipped(, \\d+ blacklisted)?$");
+ static const QRegularExpression finish("^[*]{9} Finished testing of (.*) [*]{9}$");
+
+ static const QRegularExpression result("^(PASS |FAIL! |XFAIL |XPASS |SKIP |RESULT "
+ "|BPASS |BFAIL |BXPASS |BXFAIL "
+ "|INFO |QWARN |WARNING|QDEBUG |QSYSTEM): (.*)$");
+
+ static const QRegularExpression benchDetails("^\\s+([\\d,.]+ .* per iteration "
+ "\\(total: [\\d,.]+, iterations: \\d+\\))$");
+ static const QRegularExpression locationUnix(QT_TEST_FAIL_UNIX_REGEXP);
+ static const QRegularExpression locationWin(QT_TEST_FAIL_WIN_REGEXP);
if (m_futureInterface.isCanceled())
return;
const QString line = QString::fromUtf8(chopLineBreak(outputLineWithNewLine));
-
- if (result.exactMatch(line)) {
- processResultOutput(result.cap(1).toLower().trimmed(), result.cap(2));
- } else if (locationUnix.exactMatch(line)) {
- processLocationOutput(locationUnix.cap(1));
- } else if (locationWin.exactMatch(line)) {
- processLocationOutput(locationWin.cap(1));
- } else if (benchDetails.exactMatch(line)) {
- m_description = benchDetails.cap(1);
- } else if (config.exactMatch(line)) {
- handleAndSendConfigMessage(config);
- } else if (start.exactMatch(line)) {
- m_className = start.cap(1);
+ QRegularExpressionMatch match;
+
+ auto hasMatch = [&match, line](const QRegularExpression &regex) {
+ match = regex.match(line);
+ return match.hasMatch();
+ };
+
+ if (hasMatch(result)) {
+ processResultOutput(match.captured(1).toLower().trimmed(), match.captured(2));
+ } else if (hasMatch(locationUnix)) {
+ processLocationOutput(match.captured(1));
+ } else if (hasMatch(locationWin)) {
+ processLocationOutput(match.captured(1));
+ } else if (hasMatch(benchDetails)) {
+ m_description = match.captured(1);
+ } else if (hasMatch(config)) {
+ handleAndSendConfigMessage(match);
+ } else if (hasMatch(start)) {
+ m_className = match.captured(1);
QTC_CHECK(!m_className.isEmpty());
sendStartMessage(false);
- } else if (summary.exactMatch(line) || finish.exactMatch(line)) {
+ } else if (summary.match(line).hasMatch() || finish.match(line).hasMatch()) {
processSummaryFinishOutput();
} else { // we have some plain output, but we cannot say where for sure it belongs to..
if (!m_description.isEmpty())
@@ -507,19 +514,19 @@ void QtTestOutputReader::sendFinishMessage(bool isFunction)
reportResult(testResult);
}
-void QtTestOutputReader::handleAndSendConfigMessage(const QRegExp &config)
+void QtTestOutputReader::handleAndSendConfigMessage(const QRegularExpressionMatch &config)
{
TestResultPtr testResult = createDefaultResult();
testResult->setResult(Result::MessageInternal);
- testResult->setDescription(trQtVersion(config.cap(3)));
+ testResult->setDescription(trQtVersion(config.captured(3)));
reportResult(testResult);
testResult = createDefaultResult();
testResult->setResult(Result::MessageInternal);
- testResult->setDescription(trQtBuild(config.cap(2)));
+ testResult->setDescription(trQtBuild(config.captured(2)));
reportResult(testResult);
testResult = createDefaultResult();
testResult->setResult(Result::MessageInternal);
- testResult->setDescription(trQtestVersion(config.cap(1)));
+ testResult->setDescription(trQtestVersion(config.captured(1)));
reportResult(testResult);
}
diff --git a/src/plugins/autotest/qtest/qttestoutputreader.h b/src/plugins/autotest/qtest/qttestoutputreader.h
index 89da00a8b9..a95a51a662 100644
--- a/src/plugins/autotest/qtest/qttestoutputreader.h
+++ b/src/plugins/autotest/qtest/qttestoutputreader.h
@@ -65,7 +65,7 @@ private:
void sendMessageCurrentTest();
void sendStartMessage(bool isFunction);
void sendFinishMessage(bool isFunction);
- void handleAndSendConfigMessage(const QRegExp &config);
+ void handleAndSendConfigMessage(const QRegularExpressionMatch &config);
enum CDATAMode
{
diff --git a/src/plugins/languageclient/baseclient.cpp b/src/plugins/languageclient/client.cpp
index 02f2448521..b208f8ab15 100644
--- a/src/plugins/languageclient/baseclient.cpp
+++ b/src/plugins/languageclient/client.cpp
@@ -23,10 +23,11 @@
**
****************************************************************************/
-#include "baseclient.h"
+#include "client.h"
+#include "languageclientinterface.h"
#include "languageclientmanager.h"
-#include "languageclient/languageclientutils.h"
+#include "languageclientutils.h"
#include <coreplugin/icore.h>
#include <coreplugin/idocument.h>
@@ -60,22 +61,23 @@ using namespace Utils;
namespace LanguageClient {
static Q_LOGGING_CATEGORY(LOGLSPCLIENT, "qtc.languageclient.client", QtWarningMsg);
-static Q_LOGGING_CATEGORY(LOGLSPCLIENTV, "qtc.languageclient.messages", QtWarningMsg);
-static Q_LOGGING_CATEGORY(LOGLSPCLIENTPARSE, "qtc.languageclient.parse", QtWarningMsg);
-BaseClient::BaseClient()
+Client::Client(BaseClientInterface *clientInterface)
: m_id(Core::Id::fromString(QUuid::createUuid().toString()))
, m_completionProvider(this)
+ , m_clientInterface(clientInterface)
{
- m_buffer.open(QIODevice::ReadWrite | QIODevice::Append);
m_contentHandler.insert(JsonRpcMessageHandler::jsonRpcMimeType(),
&JsonRpcMessageHandler::parseContent);
+ QTC_ASSERT(clientInterface, return);
+ connect(clientInterface, &BaseClientInterface::messageReceived, this, &Client::handleMessage);
+ connect(clientInterface, &BaseClientInterface::error, this, &Client::setError);
+ connect(clientInterface, &BaseClientInterface::finished, this, &Client::finished);
}
-BaseClient::~BaseClient()
+Client::~Client()
{
using namespace TextEditor;
- m_buffer.close();
// FIXME: instead of replacing the completion provider in the text document store the
// completion provider as a prioritised list in the text document
for (TextDocument *document : m_resetCompletionProvider)
@@ -88,9 +90,10 @@ BaseClient::~BaseClient()
}
}
-void BaseClient::initialize()
+void Client::initialize()
{
using namespace ProjectExplorer;
+ QTC_ASSERT(m_clientInterface, return);
QTC_ASSERT(m_state == Uninitialized, return);
qCDebug(LOGLSPCLIENT) << "initializing language server " << m_displayName;
auto initRequest = new InitializeRequest();
@@ -108,11 +111,11 @@ void BaseClient::initialize()
});
// directly send data otherwise the state check would fail;
initRequest->registerResponseHandler(&m_responseHandlers);
- sendData(initRequest->toBaseMessage().toData());
+ m_clientInterface->sendMessage(initRequest->toBaseMessage());
m_state = InitializeRequested;
}
-void BaseClient::shutdown()
+void Client::shutdown()
{
QTC_ASSERT(m_state == Initialized, emit finished(); return);
qCDebug(LOGLSPCLIENT) << "shutdown language server " << m_displayName;
@@ -124,12 +127,12 @@ void BaseClient::shutdown()
m_state = ShutdownRequested;
}
-BaseClient::State BaseClient::state() const
+Client::State Client::state() const
{
return m_state;
}
-void BaseClient::openDocument(Core::IDocument *document)
+void Client::openDocument(Core::IDocument *document)
{
using namespace TextEditor;
if (!isSupportedDocument(document))
@@ -188,35 +191,36 @@ void BaseClient::openDocument(Core::IDocument *document)
requestDocumentSymbols(textDocument);
}
-void BaseClient::sendContent(const IContent &content)
+void Client::sendContent(const IContent &content)
{
+ QTC_ASSERT(m_clientInterface, return);
QTC_ASSERT(m_state == Initialized, return);
content.registerResponseHandler(&m_responseHandlers);
QString error;
if (!QTC_GUARD(content.isValid(&error)))
Core::MessageManager::write(error);
- sendData(content.toBaseMessage().toData());
+ m_clientInterface->sendMessage(content.toBaseMessage());
}
-void BaseClient::sendContent(const DocumentUri &uri, const IContent &content)
+void Client::sendContent(const DocumentUri &uri, const IContent &content)
{
if (!m_openedDocument.contains(uri.toFileName()))
return;
sendContent(content);
}
-void BaseClient::cancelRequest(const MessageId &id)
+void Client::cancelRequest(const MessageId &id)
{
m_responseHandlers.remove(id);
sendContent(CancelRequest(CancelParameter(id)));
}
-void BaseClient::closeDocument(const DidCloseTextDocumentParams &params)
+void Client::closeDocument(const DidCloseTextDocumentParams &params)
{
sendContent(params.textDocument().uri(), DidCloseTextDocumentNotification(params));
}
-void BaseClient::documentContentsSaved(Core::IDocument *document)
+void Client::documentContentsSaved(Core::IDocument *document)
{
if (!m_openedDocument.contains(document->filePath()))
return;
@@ -250,7 +254,7 @@ void BaseClient::documentContentsSaved(Core::IDocument *document)
sendContent(DidSaveTextDocumentNotification(params));
}
-void BaseClient::documentWillSave(Core::IDocument *document)
+void Client::documentWillSave(Core::IDocument *document)
{
const FileName &filePath = document->filePath();
if (!m_openedDocument.contains(filePath))
@@ -278,7 +282,7 @@ void BaseClient::documentWillSave(Core::IDocument *document)
sendContent(WillSaveTextDocumentNotification(params));
}
-void BaseClient::documentContentsChanged(Core::IDocument *document)
+void Client::documentContentsChanged(Core::IDocument *document)
{
if (!m_openedDocument.contains(document->filePath()))
return;
@@ -311,18 +315,18 @@ void BaseClient::documentContentsChanged(Core::IDocument *document)
}
}
-void BaseClient::registerCapabilities(const QList<Registration> &registrations)
+void Client::registerCapabilities(const QList<Registration> &registrations)
{
m_dynamicCapabilities.registerCapability(registrations);
}
-void BaseClient::unregisterCapabilities(const QList<Unregistration> &unregistrations)
+void Client::unregisterCapabilities(const QList<Unregistration> &unregistrations)
{
m_dynamicCapabilities.unregisterCapability(unregistrations);
}
template <typename Request>
-static bool sendTextDocumentPositionParamsRequest(BaseClient *client,
+static bool sendTextDocumentPositionParamsRequest(Client *client,
const Request &request,
const DynamicCapabilities &dynamicCapabilities,
const optional<bool> &serverCapability)
@@ -346,13 +350,13 @@ static bool sendTextDocumentPositionParamsRequest(BaseClient *client,
return sendMessage;
}
-bool BaseClient::findLinkAt(GotoDefinitionRequest &request)
+bool Client::findLinkAt(GotoDefinitionRequest &request)
{
return LanguageClient::sendTextDocumentPositionParamsRequest(
this, request, m_dynamicCapabilities, m_serverCapabilities.definitionProvider());
}
-bool BaseClient::findUsages(FindReferencesRequest &request)
+bool Client::findUsages(FindReferencesRequest &request)
{
return LanguageClient::sendTextDocumentPositionParamsRequest(
this, request, m_dynamicCapabilities, m_serverCapabilities.referencesProvider());
@@ -367,7 +371,7 @@ TextEditor::HighlightingResult createHighlightingResult(const SymbolInformation
info.name().length(), info.kind());
}
-void BaseClient::requestDocumentSymbols(TextEditor::TextDocument *document)
+void Client::requestDocumentSymbols(TextEditor::TextDocument *document)
{
// TODO: Do not use this information for highlighting but the overview model
return;
@@ -460,7 +464,7 @@ void BaseClient::requestDocumentSymbols(TextEditor::TextDocument *document)
sendContent(request);
}
-void BaseClient::cursorPositionChanged(TextEditor::TextEditorWidget *widget)
+void Client::cursorPositionChanged(TextEditor::TextEditorWidget *widget)
{
const auto uri = DocumentUri::fromFileName(widget->textDocument()->filePath());
if (m_dynamicCapabilities.isRegistered(DocumentHighlightsRequest::methodName).value_or(false)) {
@@ -511,7 +515,7 @@ void BaseClient::cursorPositionChanged(TextEditor::TextEditorWidget *widget)
sendContent(request);
}
-void BaseClient::requestCodeActions(const DocumentUri &uri, const QList<Diagnostic> &diagnostics)
+void Client::requestCodeActions(const DocumentUri &uri, const QList<Diagnostic> &diagnostics)
{
const Utils::FileName fileName = uri.toFileName();
TextEditor::TextDocument *doc = textDocumentForFileName(fileName);
@@ -544,14 +548,14 @@ void BaseClient::requestCodeActions(const DocumentUri &uri, const QList<Diagnost
codeActionParams.setRange(Range(start, end));
CodeActionRequest request(codeActionParams);
request.setResponseCallback(
- [uri, self = QPointer<BaseClient>(this)](const CodeActionRequest::Response &response) {
+ [uri, self = QPointer<Client>(this)](const CodeActionRequest::Response &response) {
if (self)
self->handleCodeActionResponse(response, uri);
});
sendContent(request);
}
-void BaseClient::handleCodeActionResponse(const CodeActionRequest::Response &response,
+void Client::handleCodeActionResponse(const CodeActionRequest::Response &response,
const DocumentUri &uri)
{
if (const Utils::optional<CodeActionRequest::Response::Error> &error = response.error())
@@ -570,7 +574,7 @@ void BaseClient::handleCodeActionResponse(const CodeActionRequest::Response &res
}
}
-void BaseClient::executeCommand(const Command &command)
+void Client::executeCommand(const Command &command)
{
using CommandOptions = LanguageServerProtocol::ServerCapabilities::ExecuteCommandOptions;
const QString method(ExecuteCommandRequest::methodName);
@@ -591,7 +595,7 @@ void BaseClient::executeCommand(const Command &command)
sendContent(request);
}
-void BaseClient::projectOpened(ProjectExplorer::Project *project)
+void Client::projectOpened(ProjectExplorer::Project *project)
{
if (!sendWorkspceFolderChanges())
return;
@@ -603,7 +607,7 @@ void BaseClient::projectOpened(ProjectExplorer::Project *project)
sendContent(change);
}
-void BaseClient::projectClosed(ProjectExplorer::Project *project)
+void Client::projectClosed(ProjectExplorer::Project *project)
{
if (!sendWorkspceFolderChanges())
return;
@@ -615,18 +619,18 @@ void BaseClient::projectClosed(ProjectExplorer::Project *project)
sendContent(change);
}
-void BaseClient::setSupportedLanguage(const LanguageFilter &filter)
+void Client::setSupportedLanguage(const LanguageFilter &filter)
{
m_languagFilter = filter;
}
-bool BaseClient::isSupportedDocument(const Core::IDocument *document) const
+bool Client::isSupportedDocument(const Core::IDocument *document) const
{
QTC_ASSERT(document, return false);
return isSupportedFile(document->filePath(), document->mimeType());
}
-bool BaseClient::isSupportedFile(const Utils::FileName &filePath, const QString &mimeType) const
+bool Client::isSupportedFile(const Utils::FileName &filePath, const QString &mimeType) const
{
if (m_languagFilter.mimeTypes.isEmpty() && m_languagFilter.filePattern.isEmpty())
return true;
@@ -640,63 +644,84 @@ bool BaseClient::isSupportedFile(const Utils::FileName &filePath, const QString
});
}
-bool BaseClient::isSupportedUri(const DocumentUri &uri) const
+bool Client::isSupportedUri(const DocumentUri &uri) const
{
return isSupportedFile(uri.toFileName(),
Utils::mimeTypeForFile(uri.toFileName().fileName()).name());
}
-bool BaseClient::needsRestart(const BaseSettings *settings) const
+bool Client::needsRestart(const BaseSettings *settings) const
{
QTC_ASSERT(settings, return false);
return m_languagFilter.mimeTypes != settings->m_languageFilter.mimeTypes
|| m_languagFilter.filePattern != settings->m_languageFilter.filePattern;
}
-bool BaseClient::reset()
+bool Client::start()
+{
+ return m_clientInterface->start();
+}
+
+bool Client::reset()
{
if (!m_restartsLeft)
return false;
--m_restartsLeft;
m_state = Uninitialized;
m_responseHandlers.clear();
- m_buffer.close();
- m_buffer.setData(nullptr);
- m_buffer.open(QIODevice::ReadWrite | QIODevice::Append);
+ m_clientInterface->resetBuffer();
m_openedDocument.clear();
m_serverCapabilities = ServerCapabilities();
m_dynamicCapabilities.reset();
return true;
}
-void BaseClient::setError(const QString &message)
+void Client::setError(const QString &message)
{
log(message);
m_state = Error;
}
-void BaseClient::log(const QString &message, Core::MessageManager::PrintToOutputPaneFlag flag)
+void Client::handleMessage(const BaseMessage &message)
+{
+ if (auto handler = m_contentHandler[message.mimeType]) {
+ QString parseError;
+ handler(message.content, message.codec, parseError,
+ [this](MessageId id, const QByteArray &content, QTextCodec *codec){
+ this->handleResponse(id, content, codec);
+ },
+ [this](const QString &method, MessageId id, const IContent *content){
+ this->handleMethod(method, id, content);
+ });
+ if (!parseError.isEmpty())
+ log(parseError);
+ } else {
+ log(tr("Cannot handle content of type: %1").arg(QLatin1String(message.mimeType)));
+ }
+}
+
+void Client::log(const QString &message, Core::MessageManager::PrintToOutputPaneFlag flag)
{
Core::MessageManager::write(QString("LanguageClient %1: %2").arg(name(), message), flag);
}
-const ServerCapabilities &BaseClient::capabilities() const
+const ServerCapabilities &Client::capabilities() const
{
return m_serverCapabilities;
}
-const DynamicCapabilities &BaseClient::dynamicCapabilities() const
+const DynamicCapabilities &Client::dynamicCapabilities() const
{
return m_dynamicCapabilities;
}
-void BaseClient::log(const ShowMessageParams &message,
+void Client::log(const ShowMessageParams &message,
Core::MessageManager::PrintToOutputPaneFlag flag)
{
log(message.toString(), flag);
}
-void BaseClient::showMessageBox(const ShowMessageRequestParams &message, const MessageId &id)
+void Client::showMessageBox(const ShowMessageRequestParams &message, const MessageId &id)
{
auto box = new QMessageBox();
box->setText(message.toString());
@@ -724,13 +749,13 @@ void BaseClient::showMessageBox(const ShowMessageRequestParams &message, const M
box->show();
}
-void BaseClient::handleResponse(const MessageId &id, const QByteArray &content, QTextCodec *codec)
+void Client::handleResponse(const MessageId &id, const QByteArray &content, QTextCodec *codec)
{
if (auto handler = m_responseHandlers[id])
handler(content, codec);
}
-void BaseClient::handleMethod(const QString &method, MessageId id, const IContent *content)
+void Client::handleMethod(const QString &method, MessageId id, const IContent *content)
{
QStringList error;
bool paramsValid = true;
@@ -798,7 +823,7 @@ void BaseClient::handleMethod(const QString &method, MessageId id, const IConten
delete content;
}
-void BaseClient::intializeCallback(const InitializeRequest::Response &initResponse)
+void Client::intializeCallback(const InitializeRequest::Response &initResponse)
{
QTC_ASSERT(m_state == InitializeRequested, return);
if (optional<ResponseError<InitializeError>> error = initResponse.error()) {
@@ -839,9 +864,10 @@ void BaseClient::intializeCallback(const InitializeRequest::Response &initRespon
openDocument(openedDocument);
}
-void BaseClient::shutDownCallback(const ShutdownRequest::Response &shutdownResponse)
+void Client::shutDownCallback(const ShutdownRequest::Response &shutdownResponse)
{
QTC_ASSERT(m_state == ShutdownRequested, return);
+ QTC_ASSERT(m_clientInterface, return);
optional<ShutdownRequest::Response::Error> errorValue = shutdownResponse.error();
if (errorValue.has_value()) {
ShutdownRequest::Response::Error error = errorValue.value();
@@ -849,12 +875,12 @@ void BaseClient::shutDownCallback(const ShutdownRequest::Response &shutdownRespo
return;
}
// directly send data otherwise the state check would fail;
- sendData(ExitNotification().toBaseMessage().toData());
+ m_clientInterface->sendMessage(ExitNotification().toBaseMessage());
qCDebug(LOGLSPCLIENT) << "language server " << m_displayName << " shutdown";
m_state = Shutdown;
}
-bool BaseClient::sendWorkspceFolderChanges() const
+bool Client::sendWorkspceFolderChanges() const
{
if (m_dynamicCapabilities.isRegistered(
DidChangeWorkspaceFoldersNotification::methodName).value_or(false)) {
@@ -873,118 +899,4 @@ bool BaseClient::sendWorkspceFolderChanges() const
return false;
}
-void BaseClient::parseData(const QByteArray &data)
-{
- const qint64 preWritePosition = m_buffer.pos();
- qCDebug(LOGLSPCLIENTPARSE) << "parse buffer pos: " << preWritePosition;
- qCDebug(LOGLSPCLIENTPARSE) << " data: " << data;
- if (!m_buffer.atEnd())
- m_buffer.seek(preWritePosition + m_buffer.bytesAvailable());
- m_buffer.write(data);
- m_buffer.seek(preWritePosition);
- while (!m_buffer.atEnd()) {
- QString parseError;
- BaseMessage::parse(&m_buffer, parseError, m_currentMessage);
- qCDebug(LOGLSPCLIENTPARSE) << " complete: " << m_currentMessage.isComplete();
- qCDebug(LOGLSPCLIENTPARSE) << " length: " << m_currentMessage.contentLength;
- qCDebug(LOGLSPCLIENTPARSE) << " content: " << m_currentMessage.content;
- if (!parseError.isEmpty())
- log(parseError);
- if (!m_currentMessage.isComplete())
- break;
- if (auto handler = m_contentHandler[m_currentMessage.mimeType]){
- QString parseError;
- handler(m_currentMessage.content, m_currentMessage.codec, parseError,
- [this](MessageId id, const QByteArray &content, QTextCodec *codec){
- this->handleResponse(id, content, codec);
- },
- [this](const QString &method, MessageId id, const IContent *content){
- this->handleMethod(method, id, content);
- });
- if (!parseError.isEmpty())
- log(parseError);
- } else {
- log(tr("Cannot handle content of type: %1").arg(QLatin1String(m_currentMessage.mimeType)));
- }
- m_currentMessage = BaseMessage();
- }
- if (m_buffer.atEnd()) {
- m_buffer.close();
- m_buffer.setData(nullptr);
- m_buffer.open(QIODevice::ReadWrite | QIODevice::Append);
- }
-}
-
-StdIOClient::StdIOClient(const QString &executable, const QString &arguments)
- : m_executable(executable)
- , m_arguments(arguments)
-{
- connect(&m_process, &QProcess::readyReadStandardError,
- this, &StdIOClient::readError);
- connect(&m_process, &QProcess::readyReadStandardOutput,
- this, &StdIOClient::readOutput);
- connect(&m_process, static_cast<void(QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished),
- this, &StdIOClient::onProcessFinished);
-
- m_process.setArguments(Utils::QtcProcess::splitArgs(m_arguments));
- m_process.setProgram(m_executable);
-}
-
-StdIOClient::~StdIOClient()
-{
- Utils::SynchronousProcess::stopProcess(m_process);
-}
-
-bool StdIOClient::needsRestart(const StdIOSettings *settings)
-{
- return m_executable != settings->m_executable || m_arguments != settings->m_arguments;
-}
-
-bool StdIOClient::start()
-{
- m_process.start();
- if (!m_process.waitForStarted() || m_process.state() != QProcess::Running) {
- setError(m_process.errorString());
- return false;
- }
- return true;
-}
-
-void StdIOClient::setWorkingDirectory(const QString &workingDirectory)
-{
- m_process.setWorkingDirectory(workingDirectory);
-}
-
-void StdIOClient::sendData(const QByteArray &data)
-{
- if (m_process.state() != QProcess::Running) {
- log(tr("Cannot send data to unstarted server %1").arg(m_process.program()));
- return;
- }
- qCDebug(LOGLSPCLIENTV) << "StdIOClient send data:";
- qCDebug(LOGLSPCLIENTV).noquote() << data;
- m_process.write(data);
-}
-
-void StdIOClient::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
-{
- if (exitStatus == QProcess::CrashExit)
- setError(tr("Crashed with exit code %1: %2").arg(exitCode, m_process.error()));
- emit finished();
-}
-
-void StdIOClient::readError()
-{
- qCDebug(LOGLSPCLIENTV) << "StdIOClient std err:\n";
- qCDebug(LOGLSPCLIENTV).noquote() << m_process.readAllStandardError();
-}
-
-void StdIOClient::readOutput()
-{
- const QByteArray &out = m_process.readAllStandardOutput();
- qDebug(LOGLSPCLIENTV) << "StdIOClient std out:\n";
- qDebug(LOGLSPCLIENTV).noquote() << out;
- parseData(out);
-}
-
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/baseclient.h b/src/plugins/languageclient/client.h
index 318d9ce67f..6ae4166d2d 100644
--- a/src/plugins/languageclient/baseclient.h
+++ b/src/plugins/languageclient/client.h
@@ -56,18 +56,20 @@ namespace TextEditor
namespace LanguageClient {
-class BaseClient : public QObject
+class BaseClientInterface;
+
+class Client : public QObject
{
Q_OBJECT
public:
- BaseClient();
- ~BaseClient() override;
+ explicit Client(BaseClientInterface *clientInterface); // takes ownership
+ ~Client() override;
- BaseClient(const BaseClient &) = delete;
- BaseClient(BaseClient &&) = delete;
- BaseClient &operator=(const BaseClient &) = delete;
- BaseClient &operator=(BaseClient &&) = delete;
+ Client(const Client &) = delete;
+ Client(Client &&) = delete;
+ Client &operator=(const Client &) = delete;
+ Client &operator=(Client &&) = delete;
enum State {
Uninitialized,
@@ -124,8 +126,8 @@ public:
bool needsRestart(const BaseSettings *) const;
- virtual bool start() { return true; }
- virtual bool reset();
+ bool start();
+ bool reset();
void log(const QString &message,
Core::MessageManager::PrintToOutputPaneFlag flag = Core::MessageManager::NoModeSwitch);
@@ -143,8 +145,7 @@ signals:
protected:
void setError(const QString &message);
- virtual void sendData(const QByteArray &data) = 0;
- void parseData(const QByteArray &data);
+ void handleMessage(const LanguageServerProtocol::BaseMessage &message);
private:
void handleResponse(const LanguageServerProtocol::MessageId &id, const QByteArray &content,
@@ -168,7 +169,6 @@ private:
State m_state = Uninitialized;
QHash<LanguageServerProtocol::MessageId, LanguageServerProtocol::ResponseHandler> m_responseHandlers;
QHash<QByteArray, ContentHandler> m_contentHandler;
- QBuffer m_buffer;
QString m_displayName;
LanguageFilter m_languagFilter;
QList<Utils::FileName> m_openedDocument;
@@ -177,41 +177,9 @@ private:
DynamicCapabilities m_dynamicCapabilities;
LanguageClientCompletionAssistProvider m_completionProvider;
QSet<TextEditor::TextDocument *> m_resetCompletionProvider;
- LanguageServerProtocol::BaseMessage m_currentMessage;
QHash<LanguageServerProtocol::DocumentUri, LanguageServerProtocol::MessageId> m_highlightRequests;
int m_restartsLeft = 5;
-};
-
-class StdIOClient : public BaseClient
-{
- Q_OBJECT
-public:
- StdIOClient(const QString &executable, const QString &arguments);
- ~StdIOClient() override;
-
- StdIOClient() = delete;
- StdIOClient(const StdIOClient &) = delete;
- StdIOClient(StdIOClient &&) = delete;
- StdIOClient &operator=(const StdIOClient &) = delete;
- StdIOClient &operator=(StdIOClient &&) = delete;
-
- bool needsRestart(const StdIOSettings *settings);
-
- bool start() override;
-
- void setWorkingDirectory(const QString &workingDirectory);
-
-protected:
- void sendData(const QByteArray &data) final;
- QProcess m_process;
-
-private:
- void readError();
- void readOutput();
- void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
-
- const QString m_executable;
- const QString m_arguments;
+ QScopedPointer<BaseClientInterface> m_clientInterface;
};
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/languageclient.pro b/src/plugins/languageclient/languageclient.pro
index c9c821676f..59c568a75b 100644
--- a/src/plugins/languageclient/languageclient.pro
+++ b/src/plugins/languageclient/languageclient.pro
@@ -3,10 +3,11 @@ include(../../qtcreatorplugin.pri)
DEFINES += LANGUAGECLIENT_LIBRARY
HEADERS += \
- baseclient.h \
+ client.h \
dynamiccapabilities.h \
languageclient_global.h \
languageclientcodeassist.h \
+ languageclientinterface.h \
languageclientmanager.h \
languageclientoutline.h \
languageclientplugin.h \
@@ -15,9 +16,10 @@ HEADERS += \
SOURCES += \
- baseclient.cpp \
+ client.cpp \
dynamiccapabilities.cpp \
languageclientcodeassist.cpp \
+ languageclientinterface.cpp \
languageclientmanager.cpp \
languageclientoutline.cpp \
languageclientplugin.cpp \
diff --git a/src/plugins/languageclient/languageclient.qbs b/src/plugins/languageclient/languageclient.qbs
index 62de6c7309..0da6a58076 100644
--- a/src/plugins/languageclient/languageclient.qbs
+++ b/src/plugins/languageclient/languageclient.qbs
@@ -14,14 +14,16 @@ QtcPlugin {
Depends { name: "TextEditor" }
files: [
- "baseclient.cpp",
- "baseclient.h",
+ "client.cpp",
+ "client.h",
"dynamiccapabilities.cpp",
"dynamiccapabilities.h",
"languageclient.qrc",
"languageclient_global.h",
"languageclientcodeassist.cpp",
"languageclientcodeassist.h",
+ "languageclientinterface.cpp",
+ "languageclientinterface.h",
"languageclientmanager.cpp",
"languageclientmanager.h",
"languageclientoutline.cpp",
diff --git a/src/plugins/languageclient/languageclientcodeassist.cpp b/src/plugins/languageclient/languageclientcodeassist.cpp
index 42a4660f6e..af0e9ce892 100644
--- a/src/plugins/languageclient/languageclientcodeassist.cpp
+++ b/src/plugins/languageclient/languageclientcodeassist.cpp
@@ -25,7 +25,7 @@
#include "languageclientcodeassist.h"
-#include "baseclient.h"
+#include "client.h"
#include "languageclientutils.h"
#include <languageserverprotocol/completion.h>
@@ -48,10 +48,11 @@
static Q_LOGGING_CATEGORY(LOGLSPCOMPLETION, "qtc.languageclient.completion", QtWarningMsg);
using namespace LanguageServerProtocol;
+using namespace TextEditor;
namespace LanguageClient {
-class LanguageClientCompletionItem : public TextEditor::AssistProposalItemInterface
+class LanguageClientCompletionItem : public AssistProposalItemInterface
{
public:
LanguageClientCompletionItem(CompletionItem item);
@@ -60,7 +61,7 @@ public:
QString text() const override;
bool implicitlyApplies() const override;
bool prematurelyApplies(const QChar &typedCharacter) const override;
- void apply(TextEditor::TextDocumentManipulatorInterface &manipulator, int basePosition) const override;
+ void apply(TextDocumentManipulatorInterface &manipulator, int basePosition) const override;
QIcon icon() const override;
QString detail() const override;
bool isSnippet() const override;
@@ -91,7 +92,7 @@ bool LanguageClientCompletionItem::implicitlyApplies() const
bool LanguageClientCompletionItem::prematurelyApplies(const QChar &/*typedCharacter*/) const
{ return false; }
-void LanguageClientCompletionItem::apply(TextEditor::TextDocumentManipulatorInterface &manipulator,
+void LanguageClientCompletionItem::apply(TextDocumentManipulatorInterface &manipulator,
int /*basePosition*/) const
{
const int pos = manipulator.currentPosition();
@@ -207,7 +208,7 @@ bool LanguageClientCompletionItem::isPerfectMatch(int pos, QTextDocument *doc) c
return textToInsert == textAt(QTextCursor(doc), pos - length, length);
}
-class LanguageClientCompletionModel : public TextEditor::GenericProposalModel
+class LanguageClientCompletionModel : public GenericProposalModel
{
public:
// GenericProposalModel interface
@@ -221,7 +222,6 @@ public:
void LanguageClientCompletionModel::sort(const QString &/*prefix*/)
{
- using namespace TextEditor;
std::sort(m_currentItems.begin(), m_currentItems.end(),
[] (AssistProposalItemInterface *a, AssistProposalItemInterface *b){
return *(dynamic_cast<LanguageClientCompletionItem *>(a)) < *(
@@ -229,16 +229,16 @@ void LanguageClientCompletionModel::sort(const QString &/*prefix*/)
});
}
-class LanguageClientCompletionProposal : public TextEditor::GenericProposal
+class LanguageClientCompletionProposal : public GenericProposal
{
public:
LanguageClientCompletionProposal(int cursorPos, LanguageClientCompletionModel *model)
- : TextEditor::GenericProposal(cursorPos, TextEditor::GenericProposalModelPtr(model))
+ : GenericProposal(cursorPos, GenericProposalModelPtr(model))
, m_model(model)
{ }
// IAssistProposal interface
- bool hasItemsToPropose(const QString &/*text*/, TextEditor::AssistReason reason) const override
+ bool hasItemsToPropose(const QString &/*text*/, AssistReason reason) const override
{
if (m_model->size() <= 0 || m_document.isNull())
return false;
@@ -255,11 +255,11 @@ public:
};
-class LanguageClientCompletionAssistProcessor : public TextEditor::IAssistProcessor
+class LanguageClientCompletionAssistProcessor : public IAssistProcessor
{
public:
- LanguageClientCompletionAssistProcessor(BaseClient *client);
- TextEditor::IAssistProposal *perform(const TextEditor::AssistInterface *interface) override;
+ LanguageClientCompletionAssistProcessor(Client *client);
+ IAssistProposal *perform(const AssistInterface *interface) override;
bool running() override;
bool needsRestart() const override { return true; }
@@ -267,31 +267,30 @@ private:
void handleCompletionResponse(const CompletionRequest::Response &response);
QPointer<QTextDocument> m_document;
- QPointer<BaseClient> m_client;
+ QPointer<Client> m_client;
bool m_running = false;
int m_pos = -1;
};
-LanguageClientCompletionAssistProcessor::LanguageClientCompletionAssistProcessor(BaseClient *client)
+LanguageClientCompletionAssistProcessor::LanguageClientCompletionAssistProcessor(Client *client)
: m_client(client)
{ }
-static QString assistReasonString(TextEditor::AssistReason reason)
+static QString assistReasonString(AssistReason reason)
{
switch (reason) {
- case TextEditor::IdleEditor: return QString("idle editor");
- case TextEditor::ActivationCharacter: return QString("activation character");
- case TextEditor::ExplicitlyInvoked: return QString("explicitly invoking");
+ case IdleEditor: return QString("idle editor");
+ case ActivationCharacter: return QString("activation character");
+ case ExplicitlyInvoked: return QString("explicitly invoking");
}
return QString("unknown reason");
}
-TextEditor::IAssistProposal *LanguageClientCompletionAssistProcessor::perform(
- const TextEditor::AssistInterface *interface)
+IAssistProposal *LanguageClientCompletionAssistProcessor::perform(const AssistInterface *interface)
{
QTC_ASSERT(m_client, return nullptr);
m_pos = interface->position();
- if (interface->reason() == TextEditor::IdleEditor) {
+ if (interface->reason() == IdleEditor) {
// Trigger an automatic completion request only when we are on a word with more than 2 "identifier" character
const QRegExp regexp("[_a-zA-Z0-9]*");
int delta = 0;
@@ -302,7 +301,7 @@ TextEditor::IAssistProposal *LanguageClientCompletionAssistProcessor::perform(
}
CompletionRequest completionRequest;
CompletionParams::CompletionContext context;
- context.setTriggerKind(interface->reason() == TextEditor::ActivationCharacter
+ context.setTriggerKind(interface->reason() == ActivationCharacter
? CompletionParams::TriggerCharacter
: CompletionParams::Invoked);
auto params = completionRequest.params().value_or(CompletionParams());
@@ -336,7 +335,6 @@ bool LanguageClientCompletionAssistProcessor::running()
void LanguageClientCompletionAssistProcessor::handleCompletionResponse(
const CompletionRequest::Response &response)
{
- using namespace TextEditor;
qCDebug(LOGLSPCOMPLETION) << QTime::currentTime() << " : got completions";
m_running = false;
QTC_ASSERT(m_client, return);
@@ -369,18 +367,18 @@ void LanguageClientCompletionAssistProcessor::handleCompletionResponse(
<< items.count() << " completions handled";
}
-LanguageClientCompletionAssistProvider::LanguageClientCompletionAssistProvider(BaseClient *client)
+LanguageClientCompletionAssistProvider::LanguageClientCompletionAssistProvider(Client *client)
: m_client(client)
{ }
-TextEditor::IAssistProcessor *LanguageClientCompletionAssistProvider::createProcessor() const
+IAssistProcessor *LanguageClientCompletionAssistProvider::createProcessor() const
{
return new LanguageClientCompletionAssistProcessor(m_client);
}
-TextEditor::IAssistProvider::RunType LanguageClientCompletionAssistProvider::runType() const
+IAssistProvider::RunType LanguageClientCompletionAssistProvider::runType() const
{
- return TextEditor::IAssistProvider::Asynchronous;
+ return IAssistProvider::Asynchronous;
}
int LanguageClientCompletionAssistProvider::activationCharSequenceLength() const
diff --git a/src/plugins/languageclient/languageclientcodeassist.h b/src/plugins/languageclient/languageclientcodeassist.h
index 14af748e72..1410e67f82 100644
--- a/src/plugins/languageclient/languageclientcodeassist.h
+++ b/src/plugins/languageclient/languageclientcodeassist.h
@@ -29,12 +29,12 @@
namespace LanguageClient {
-class BaseClient;
+class Client;
class LanguageClientCompletionAssistProvider : public TextEditor::CompletionAssistProvider
{
public:
- LanguageClientCompletionAssistProvider(BaseClient *client);
+ LanguageClientCompletionAssistProvider(Client *client);
TextEditor::IAssistProcessor *createProcessor() const override;
RunType runType() const override;
@@ -48,7 +48,7 @@ public:
private:
QList<QString> m_triggerChars;
int m_activationCharSequenceLength = 0;
- BaseClient *m_client;
+ Client *m_client;
};
} // namespace LanguageClient
diff --git a/src/plugins/languageclient/languageclientinterface.cpp b/src/plugins/languageclient/languageclientinterface.cpp
new file mode 100644
index 0000000000..b67db95dde
--- /dev/null
+++ b/src/plugins/languageclient/languageclientinterface.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2019 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#include "languageclientinterface.h"
+
+#include "languageclientsettings.h"
+
+#include <utils/qtcprocess.h>
+#include <utils/synchronousprocess.h>
+
+#include <QLoggingCategory>
+
+using namespace LanguageServerProtocol;
+
+static Q_LOGGING_CATEGORY(LOGLSPCLIENTV, "qtc.languageclient.messages", QtWarningMsg);
+static Q_LOGGING_CATEGORY(LOGLSPCLIENTPARSE, "qtc.languageclient.parse", QtWarningMsg);
+
+namespace LanguageClient {
+
+BaseClientInterface::BaseClientInterface()
+{
+ m_buffer.open(QIODevice::ReadWrite | QIODevice::Append);
+}
+
+BaseClientInterface::~BaseClientInterface()
+{
+ m_buffer.close();
+}
+
+void BaseClientInterface::sendMessage(const BaseMessage &message)
+{
+ sendData(message.toData());
+}
+
+void BaseClientInterface::resetBuffer()
+{
+ m_buffer.close();
+ m_buffer.setData(nullptr);
+ m_buffer.open(QIODevice::ReadWrite | QIODevice::Append);
+}
+
+void BaseClientInterface::parseData(const QByteArray &data)
+{
+ const qint64 preWritePosition = m_buffer.pos();
+ qCDebug(LOGLSPCLIENTPARSE) << "parse buffer pos: " << preWritePosition;
+ qCDebug(LOGLSPCLIENTPARSE) << " data: " << data;
+ if (!m_buffer.atEnd())
+ m_buffer.seek(preWritePosition + m_buffer.bytesAvailable());
+ m_buffer.write(data);
+ m_buffer.seek(preWritePosition);
+ while (!m_buffer.atEnd()) {
+ QString parseError;
+ BaseMessage::parse(&m_buffer, parseError, m_currentMessage);
+ qCDebug(LOGLSPCLIENTPARSE) << " complete: " << m_currentMessage.isComplete();
+ qCDebug(LOGLSPCLIENTPARSE) << " length: " << m_currentMessage.contentLength;
+ qCDebug(LOGLSPCLIENTPARSE) << " content: " << m_currentMessage.content;
+ if (!parseError.isEmpty())
+ emit error(parseError);
+ if (!m_currentMessage.isComplete())
+ break;
+ emit messageReceived(m_currentMessage);
+ m_currentMessage = BaseMessage();
+ }
+ if (m_buffer.atEnd()) {
+ m_buffer.close();
+ m_buffer.setData(nullptr);
+ m_buffer.open(QIODevice::ReadWrite | QIODevice::Append);
+ }
+}
+
+StdIOClientInterface::StdIOClientInterface(const QString &executable, const QString &arguments)
+ : m_executable(executable)
+ , m_arguments(arguments)
+{
+ connect(&m_process, &QProcess::readyReadStandardError,
+ this, &StdIOClientInterface::readError);
+ connect(&m_process, &QProcess::readyReadStandardOutput,
+ this, &StdIOClientInterface::readOutput);
+ connect(&m_process, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
+ this, &StdIOClientInterface::onProcessFinished);
+
+ m_process.setArguments(Utils::QtcProcess::splitArgs(m_arguments));
+ m_process.setProgram(m_executable);
+}
+
+StdIOClientInterface::~StdIOClientInterface()
+{
+ Utils::SynchronousProcess::stopProcess(m_process);
+}
+
+bool StdIOClientInterface::needsRestart(const StdIOSettings *settings)
+{
+ return m_executable != settings->m_executable || m_arguments != settings->m_arguments;
+}
+
+bool StdIOClientInterface::start()
+{
+ m_process.start();
+ if (!m_process.waitForStarted() || m_process.state() != QProcess::Running) {
+ emit error(m_process.errorString());
+ return false;
+ }
+ return true;
+}
+
+void StdIOClientInterface::setWorkingDirectory(const QString &workingDirectory)
+{
+ m_process.setWorkingDirectory(workingDirectory);
+}
+
+void StdIOClientInterface::sendData(const QByteArray &data)
+{
+ if (m_process.state() != QProcess::Running) {
+ emit error(tr("Cannot send data to unstarted server %1").arg(m_process.program()));
+ return;
+ }
+ qCDebug(LOGLSPCLIENTV) << "StdIOClient send data:";
+ qCDebug(LOGLSPCLIENTV).noquote() << data;
+ m_process.write(data);
+}
+
+void StdIOClientInterface::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
+{
+ if (exitStatus == QProcess::CrashExit)
+ emit error(tr("Crashed with exit code %1: %2").arg(exitCode, m_process.error()));
+ emit finished();
+}
+
+void StdIOClientInterface::readError()
+{
+ qCDebug(LOGLSPCLIENTV) << "StdIOClient std err:\n";
+ qCDebug(LOGLSPCLIENTV).noquote() << m_process.readAllStandardError();
+}
+
+void StdIOClientInterface::readOutput()
+{
+ const QByteArray &out = m_process.readAllStandardOutput();
+ qCDebug(LOGLSPCLIENTV) << "StdIOClient std out:\n";
+ qCDebug(LOGLSPCLIENTV).noquote() << out;
+ parseData(out);
+}
+
+} // namespace LanguageClient
diff --git a/src/plugins/languageclient/languageclientinterface.h b/src/plugins/languageclient/languageclientinterface.h
new file mode 100644
index 0000000000..3dc2602d52
--- /dev/null
+++ b/src/plugins/languageclient/languageclientinterface.h
@@ -0,0 +1,96 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt Creator.
+**
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 as published by the Free Software
+** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+****************************************************************************/
+
+#pragma once
+
+#include <languageserverprotocol/basemessage.h>
+
+#include <QBuffer>
+#include <QProcess>
+
+namespace LanguageClient {
+
+class StdIOSettings;
+
+class BaseClientInterface : public QObject
+{
+ Q_OBJECT
+public:
+ BaseClientInterface();
+
+ virtual ~BaseClientInterface();
+
+ void sendMessage(const LanguageServerProtocol::BaseMessage &message);
+ virtual bool start() { return true; }
+
+ void resetBuffer();
+
+signals:
+ void messageReceived(LanguageServerProtocol::BaseMessage message);
+ void finished();
+ void error(const QString &message);
+
+protected:
+ virtual void sendData(const QByteArray &data) = 0;
+ void parseData(const QByteArray &data);
+
+private:
+ QBuffer m_buffer;
+ LanguageServerProtocol::BaseMessage m_currentMessage;
+};
+
+class StdIOClientInterface : public BaseClientInterface
+{
+ Q_OBJECT
+public:
+ StdIOClientInterface(const QString &executable, const QString &arguments);
+ ~StdIOClientInterface() override;
+
+ StdIOClientInterface() = delete;
+ StdIOClientInterface(const StdIOClientInterface &) = delete;
+ StdIOClientInterface(StdIOClientInterface &&) = delete;
+ StdIOClientInterface &operator=(const StdIOClientInterface &) = delete;
+ StdIOClientInterface &operator=(StdIOClientInterface &&) = delete;
+
+ bool needsRestart(const StdIOSettings *settings);
+
+ bool start() override;
+
+ void setWorkingDirectory(const QString &workingDirectory);
+
+protected:
+ void sendData(const QByteArray &data) final;
+ QProcess m_process;
+
+private:
+ void readError();
+ void readOutput();
+ void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
+
+ const QString m_executable;
+ const QString m_arguments;
+};
+
+} // namespace LanguageClient
diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp
index b4746b45ef..3e0e25e66c 100644
--- a/src/plugins/languageclient/languageclientmanager.cpp
+++ b/src/plugins/languageclient/languageclientmanager.cpp
@@ -109,7 +109,7 @@ void LanguageClientManager::init()
void LanguageClientManager::publishDiagnostics(const Core::Id &id,
const PublishDiagnosticsParams &params,
- BaseClient *publishingClient)
+ Client *publishingClient)
{
const Utils::FileName fileName = params.uri().toFileName();
TextEditor::TextDocument *doc = textDocumentForFileName(fileName);
@@ -169,7 +169,7 @@ void LanguageClientManager::removeMarks(const Core::Id &id)
removeMarks(fileName, id);
}
-void LanguageClientManager::startClient(BaseClient *client)
+void LanguageClientManager::startClient(Client *client)
{
QTC_ASSERT(client, return);
if (managerInstance->m_shuttingDown) {
@@ -178,7 +178,7 @@ void LanguageClientManager::startClient(BaseClient *client)
}
if (!managerInstance->m_clients.contains(client))
managerInstance->m_clients.append(client);
- connect(client, &BaseClient::finished, managerInstance, [client](){
+ connect(client, &Client::finished, managerInstance, [client](){
managerInstance->clientFinished(client);
});
if (client->start())
@@ -187,26 +187,26 @@ void LanguageClientManager::startClient(BaseClient *client)
managerInstance->clientFinished(client);
}
-QVector<BaseClient *> LanguageClientManager::clients()
+QVector<Client *> LanguageClientManager::clients()
{
return managerInstance->m_clients;
}
-void LanguageClientManager::addExclusiveRequest(const MessageId &id, BaseClient *client)
+void LanguageClientManager::addExclusiveRequest(const MessageId &id, Client *client)
{
managerInstance->m_exclusiveRequests[id] << client;
}
-void LanguageClientManager::reportFinished(const MessageId &id, BaseClient *byClient)
+void LanguageClientManager::reportFinished(const MessageId &id, Client *byClient)
{
- for (BaseClient *client : managerInstance->m_exclusiveRequests[id]) {
+ for (Client *client : managerInstance->m_exclusiveRequests[id]) {
if (client != byClient)
client->cancelRequest(id);
}
managerInstance->m_exclusiveRequests.remove(id);
}
-void LanguageClientManager::deleteClient(BaseClient *client)
+void LanguageClientManager::deleteClient(Client *client)
{
QTC_ASSERT(client, return);
client->disconnect();
@@ -238,23 +238,23 @@ LanguageClientManager *LanguageClientManager::instance()
return managerInstance;
}
-QList<BaseClient *> LanguageClientManager::clientsSupportingDocument(
+QList<Client *> LanguageClientManager::clientsSupportingDocument(
const TextEditor::TextDocument *doc)
{
QTC_ASSERT(doc, return {};);
- return Utils::filtered(managerInstance->reachableClients(), [doc](BaseClient *client) {
+ return Utils::filtered(managerInstance->reachableClients(), [doc](Client *client) {
return client->isSupportedDocument(doc);
}).toList();
}
-QVector<BaseClient *> LanguageClientManager::reachableClients()
+QVector<Client *> LanguageClientManager::reachableClients()
{
- return Utils::filtered(m_clients, &BaseClient::reachable);
+ return Utils::filtered(m_clients, &Client::reachable);
}
-static void sendToInterfaces(const IContent &content, const QVector<BaseClient *> &interfaces)
+static void sendToInterfaces(const IContent &content, const QVector<Client *> &interfaces)
{
- for (BaseClient *interface : interfaces)
+ for (Client *interface : interfaces)
interface->sendContent(content);
}
@@ -263,11 +263,11 @@ void LanguageClientManager::sendToAllReachableServers(const IContent &content)
sendToInterfaces(content, reachableClients());
}
-void LanguageClientManager::clientFinished(BaseClient *client)
+void LanguageClientManager::clientFinished(Client *client)
{
constexpr int restartTimeoutS = 5;
- const bool unexpectedFinish = client->state() != BaseClient::Shutdown
- && client->state() != BaseClient::ShutdownRequested;
+ const bool unexpectedFinish = client->state() != Client::Shutdown
+ && client->state() != Client::ShutdownRequested;
if (unexpectedFinish && !m_shuttingDown && client->reset()) {
removeMarks(client->id());
client->disconnect(this);
@@ -287,7 +287,7 @@ void LanguageClientManager::editorOpened(Core::IEditor *iEditor)
{
using namespace TextEditor;
Core::IDocument *document = iEditor->document();
- for (BaseClient *interface : reachableClients())
+ for (Client *interface : reachableClients())
interface->openDocument(document);
if (auto textDocument = qobject_cast<TextDocument *>(document)) {
@@ -315,7 +315,7 @@ void LanguageClientManager::editorsClosed(const QList<Core::IEditor *> &editors)
removeMarks(editor->document()->filePath());
const DidCloseTextDocumentParams params(TextDocumentIdentifier(
DocumentUri::fromFileName(editor->document()->filePath())));
- for (BaseClient *interface : reachableClients())
+ for (Client *interface : reachableClients())
interface->closeDocument(params);
}
}
@@ -323,13 +323,13 @@ void LanguageClientManager::editorsClosed(const QList<Core::IEditor *> &editors)
void LanguageClientManager::documentContentsSaved(Core::IDocument *document)
{
- for (BaseClient *interface : reachableClients())
+ for (Client *interface : reachableClients())
interface->documentContentsSaved(document);
}
void LanguageClientManager::documentWillSave(Core::IDocument *document)
{
- for (BaseClient *interface : reachableClients())
+ for (Client *interface : reachableClients())
interface->documentContentsSaved(document);
}
@@ -355,7 +355,7 @@ void LanguageClientManager::findLinkAt(const Utils::FileName &filePath,
}
}
});
- for (BaseClient *interface : reachableClients()) {
+ for (Client *interface : reachableClients()) {
if (interface->findLinkAt(request))
m_exclusiveRequests[request.id()] << interface;
}
@@ -421,7 +421,7 @@ void LanguageClientManager::findUsages(const Utils::FileName &filePath, const QT
search->popup();
}
};
- for (BaseClient *client : reachableClients()) {
+ for (Client *client : reachableClients()) {
request.setResponseCallback([callback, clientName = client->name()]
(const FindReferencesRequest::Response &response){
callback(clientName, response);
@@ -433,13 +433,13 @@ void LanguageClientManager::findUsages(const Utils::FileName &filePath, const QT
void LanguageClientManager::projectAdded(ProjectExplorer::Project *project)
{
- for (BaseClient *interface : reachableClients())
+ for (Client *interface : reachableClients())
interface->projectOpened(project);
}
void LanguageClientManager::projectRemoved(ProjectExplorer::Project *project)
{
- for (BaseClient *interface : reachableClients())
+ for (Client *interface : reachableClients())
interface->projectClosed(project);
}
diff --git a/src/plugins/languageclient/languageclientmanager.h b/src/plugins/languageclient/languageclientmanager.h
index 54f267d86d..d062a0bdff 100644
--- a/src/plugins/languageclient/languageclientmanager.h
+++ b/src/plugins/languageclient/languageclientmanager.h
@@ -25,7 +25,7 @@
#pragma once
-#include "baseclient.h"
+#include "client.h"
#include "languageclientsettings.h"
#include <coreplugin/id.h>
@@ -56,26 +56,26 @@ public:
static void init();
static void publishDiagnostics(const Core::Id &id,
- const LanguageServerProtocol::PublishDiagnosticsParams &params, BaseClient *publishingClient);
+ const LanguageServerProtocol::PublishDiagnosticsParams &params, Client *publishingClient);
static void removeMark(LanguageClientMark *mark);
static void removeMarks(const Utils::FileName &fileName);
static void removeMarks(const Utils::FileName &fileName, const Core::Id &id);
static void removeMarks(const Core::Id &id);
- static void startClient(BaseClient *client);
- static QVector<BaseClient *> clients();
+ static void startClient(Client *client);
+ static QVector<Client *> clients();
- static void addExclusiveRequest(const LanguageServerProtocol::MessageId &id, BaseClient *client);
- static void reportFinished(const LanguageServerProtocol::MessageId &id, BaseClient *byClient);
+ static void addExclusiveRequest(const LanguageServerProtocol::MessageId &id, Client *client);
+ static void reportFinished(const LanguageServerProtocol::MessageId &id, Client *byClient);
- static void deleteClient(BaseClient *client);
+ static void deleteClient(Client *client);
static void shutdown();
static LanguageClientManager *instance();
- static QList<BaseClient *> clientsSupportingDocument(const TextEditor::TextDocument *doc);
+ static QList<Client *> clientsSupportingDocument(const TextEditor::TextDocument *doc);
signals:
void shutdownFinished();
@@ -94,15 +94,15 @@ private:
void projectAdded(ProjectExplorer::Project *project);
void projectRemoved(ProjectExplorer::Project *project);
- QVector<BaseClient *> reachableClients();
+ QVector<Client *> reachableClients();
void sendToAllReachableServers(const LanguageServerProtocol::IContent &content);
- void clientFinished(BaseClient *client);
+ void clientFinished(Client *client);
bool m_shuttingDown = false;
- QVector<BaseClient *> m_clients;
+ QVector<Client *> m_clients;
QHash<Utils::FileName, QHash<Core::Id, QVector<LanguageClientMark *>>> m_marks;
- QHash<LanguageServerProtocol::MessageId, QList<BaseClient *>> m_exclusiveRequests;
+ QHash<LanguageServerProtocol::MessageId, QList<Client *>> m_exclusiveRequests;
friend class LanguageClientPlugin;
};
diff --git a/src/plugins/languageclient/languageclientoutline.cpp b/src/plugins/languageclient/languageclientoutline.cpp
index 841e9438ca..3c3a1aa1b1 100644
--- a/src/plugins/languageclient/languageclientoutline.cpp
+++ b/src/plugins/languageclient/languageclientoutline.cpp
@@ -147,7 +147,7 @@ public:
class LanguageClientOutlineWidget : public TextEditor::IOutlineWidget
{
public:
- LanguageClientOutlineWidget(BaseClient *client, TextEditor::BaseTextEditor *editor);
+ LanguageClientOutlineWidget(Client *client, TextEditor::BaseTextEditor *editor);
// IOutlineWidget interface
public:
@@ -160,14 +160,14 @@ private:
void updateSelectionInTree(const QTextCursor &currentCursor);
void onItemActivated(const QModelIndex &index);
- QPointer<BaseClient> m_client;
+ QPointer<Client> m_client;
QPointer<TextEditor::BaseTextEditor> m_editor;
LanguageClientOutlineModel m_model;
Utils::TreeView m_view;
bool m_sync = false;
};
-LanguageClientOutlineWidget::LanguageClientOutlineWidget(BaseClient *client,
+LanguageClientOutlineWidget::LanguageClientOutlineWidget(Client *client,
TextEditor::BaseTextEditor *editor)
: m_client(client)
, m_editor(editor)
@@ -256,7 +256,7 @@ void LanguageClientOutlineWidget::onItemActivated(const QModelIndex &index)
m_editor->widget()->setFocus();
}
-static bool clientSupportsDocumentSymbols(const BaseClient *client, const TextEditor::TextDocument *doc)
+static bool clientSupportsDocumentSymbols(const Client *client, const TextEditor::TextDocument *doc)
{
DynamicCapabilities dc = client->dynamicCapabilities();
if (dc.isRegistered(DocumentSymbolsRequest::methodName).value_or(false)) {
@@ -273,7 +273,7 @@ bool LanguageClientOutlineWidgetFactory::supportsEditor(Core::IEditor *editor) c
if (!doc)
return false;
auto clients = LanguageClientManager::clientsSupportingDocument(doc);
- return Utils::anyOf(clients, [doc](const BaseClient *client){
+ return Utils::anyOf(clients, [doc](const Client *client){
return clientSupportsDocumentSymbols(client, doc);
});
}
@@ -282,9 +282,9 @@ TextEditor::IOutlineWidget *LanguageClientOutlineWidgetFactory::createWidget(Cor
{
auto textEditor = qobject_cast<TextEditor::BaseTextEditor *>(editor);
QTC_ASSERT(textEditor, return nullptr);
- QList<BaseClient *> clients = LanguageClientManager::clientsSupportingDocument(textEditor->textDocument());
+ QList<Client *> clients = LanguageClientManager::clientsSupportingDocument(textEditor->textDocument());
QTC_ASSERT(!clients.isEmpty(), return nullptr);
- clients = Utils::filtered(clients, [doc = textEditor->textDocument()](const BaseClient *client){
+ clients = Utils::filtered(clients, [doc = textEditor->textDocument()](const Client *client){
return clientSupportsDocumentSymbols(client, doc);
});
return new LanguageClientOutlineWidget(clients.first(), textEditor);
diff --git a/src/plugins/languageclient/languageclientplugin.cpp b/src/plugins/languageclient/languageclientplugin.cpp
index 53a0252592..1d64097e62 100644
--- a/src/plugins/languageclient/languageclientplugin.cpp
+++ b/src/plugins/languageclient/languageclientplugin.cpp
@@ -25,7 +25,7 @@
#include "languageclientplugin.h"
-#include "baseclient.h"
+#include "client.h"
namespace LanguageClient {
diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp
index 36ec211b85..e101db412c 100644
--- a/src/plugins/languageclient/languageclientsettings.cpp
+++ b/src/plugins/languageclient/languageclientsettings.cpp
@@ -25,9 +25,10 @@
#include "languageclientsettings.h"
-#include "baseclient.h"
+#include "client.h"
#include "languageclientmanager.h"
#include "languageclient_global.h"
+#include "languageclientinterface.h"
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
@@ -397,8 +398,15 @@ bool BaseSettings::isValid() const
return !m_name.isEmpty();
}
-BaseClient *BaseSettings::createClient() const
+Client *BaseSettings::createClient() const
{
+ BaseClientInterface *interface = createInterface();
+ if (QTC_GUARD(interface)) {
+ auto *client = new Client(interface);
+ client->setName(m_name);
+ client->setSupportedLanguage(m_languageFilter);
+ return client;
+ }
return nullptr;
}
@@ -467,8 +475,8 @@ bool StdIOSettings::needsRestart() const
{
if (BaseSettings::needsRestart())
return true;
- if (auto stdIOClient = qobject_cast<StdIOClient *>(m_client))
- return stdIOClient->needsRestart(this);
+ if (auto stdIOInterface = qobject_cast<StdIOClientInterface *>(m_client))
+ return stdIOInterface->needsRestart(this);
return false;
}
@@ -477,14 +485,6 @@ bool StdIOSettings::isValid() const
return BaseSettings::isValid() && !m_executable.isEmpty();
}
-BaseClient *StdIOSettings::createClient() const
-{
- auto client = new StdIOClient(m_executable, m_arguments);
- client->setName(m_name);
- client->setSupportedLanguage(m_languageFilter);
- return client;
-}
-
QVariantMap StdIOSettings::toMap() const
{
QVariantMap map = BaseSettings::toMap();
@@ -500,6 +500,11 @@ void StdIOSettings::fromMap(const QVariantMap &map)
m_arguments = map[argumentsKey].toString();
}
+BaseClientInterface *StdIOSettings::createInterface() const
+{
+ return new StdIOClientInterface(m_executable, m_arguments);
+}
+
BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *parent)
: QWidget(parent)
, m_name(new QLineEdit(settings->m_name, this))
diff --git a/src/plugins/languageclient/languageclientsettings.h b/src/plugins/languageclient/languageclientsettings.h
index 54877412a7..bdf54e6052 100644
--- a/src/plugins/languageclient/languageclientsettings.h
+++ b/src/plugins/languageclient/languageclientsettings.h
@@ -43,7 +43,8 @@ namespace LanguageClient {
constexpr char noLanguageFilter[] = "No Filter";
-class BaseClient;
+class Client;
+class BaseClientInterface;
struct LanguageFilter
{
@@ -66,18 +67,20 @@ public:
QString m_name = QString("New Language Server");
bool m_enabled = true;
LanguageFilter m_languageFilter;
- QPointer<BaseClient> m_client; // not owned
+ QPointer<Client> m_client; // not owned
virtual void applyFromSettingsWidget(QWidget *widget);
virtual QWidget *createSettingsWidget(QWidget *parent = nullptr) const;
virtual BaseSettings *copy() const { return new BaseSettings(*this); }
virtual bool needsRestart() const;
virtual bool isValid() const ;
- virtual BaseClient *createClient() const;
+ Client *createClient() const;
virtual QVariantMap toMap() const;
virtual void fromMap(const QVariantMap &map);
protected:
+ virtual BaseClientInterface *createInterface() const { return nullptr; }
+
BaseSettings(const BaseSettings &other) = default;
BaseSettings(BaseSettings &&other) = default;
BaseSettings &operator=(const BaseSettings &other) = default;
@@ -105,11 +108,12 @@ public:
BaseSettings *copy() const override { return new StdIOSettings(*this); }
bool needsRestart() const override;
bool isValid() const override;
- BaseClient *createClient() const override;
QVariantMap toMap() const override;
void fromMap(const QVariantMap &map) override;
protected:
+ BaseClientInterface *createInterface() const override;
+
StdIOSettings(const StdIOSettings &other) = default;
StdIOSettings(StdIOSettings &&other) = default;
StdIOSettings &operator=(const StdIOSettings &other) = default;
diff --git a/src/plugins/languageclient/languageclientutils.cpp b/src/plugins/languageclient/languageclientutils.cpp
index 3e0c884c17..5f3f8e0672 100644
--- a/src/plugins/languageclient/languageclientutils.cpp
+++ b/src/plugins/languageclient/languageclientutils.cpp
@@ -25,7 +25,7 @@
#include "languageclientutils.h"
-#include "baseclient.h"
+#include "client.h"
#include <coreplugin/editormanager/documentmodel.h>
@@ -131,7 +131,7 @@ QTextCursor endOfLineCursor(const QTextCursor &cursor)
return ret;
}
-void updateCodeActionRefactoringMarker(BaseClient *client,
+void updateCodeActionRefactoringMarker(Client *client,
const CodeAction &action,
const DocumentUri &uri)
{
@@ -176,7 +176,7 @@ void updateCodeActionRefactoringMarker(BaseClient *client,
}
} else if (action.command().has_value()) {
const Command command = action.command().value();
- marker.callback = [command, client = QPointer<BaseClient>(client)](const TextEditorWidget *) {
+ marker.callback = [command, client = QPointer<Client>(client)](const TextEditorWidget *) {
if (client)
client->executeCommand(command);
};
diff --git a/src/plugins/languageclient/languageclientutils.h b/src/plugins/languageclient/languageclientutils.h
index bf7ac602e4..2891c2d303 100644
--- a/src/plugins/languageclient/languageclientutils.h
+++ b/src/plugins/languageclient/languageclientutils.h
@@ -37,7 +37,7 @@ class TextDocumentManipulatorInterface;
namespace LanguageClient {
-class BaseClient;
+class Client;
bool applyWorkspaceEdit(const LanguageServerProtocol::WorkspaceEdit &edit);
bool applyTextDocumentEdit(const LanguageServerProtocol::TextDocumentEdit &edit);
@@ -46,7 +46,7 @@ bool applyTextEdits(const LanguageServerProtocol::DocumentUri &uri,
void applyTextEdit(TextEditor::TextDocumentManipulatorInterface &manipulator,
const LanguageServerProtocol::TextEdit &edit);
TextEditor::TextDocument *textDocumentForFileName(const Utils::FileName &fileName);
-void updateCodeActionRefactoringMarker(BaseClient *client,
+void updateCodeActionRefactoringMarker(Client *client,
const LanguageServerProtocol::CodeAction &action,
const LanguageServerProtocol::DocumentUri &uri);
diff --git a/src/plugins/projectexplorer/buildtargetinfo.h b/src/plugins/projectexplorer/buildtargetinfo.h
index 0aa5011dda..38cadea991 100644
--- a/src/plugins/projectexplorer/buildtargetinfo.h
+++ b/src/plugins/projectexplorer/buildtargetinfo.h
@@ -41,6 +41,7 @@ class PROJECTEXPLORER_EXPORT BuildTargetInfo
public:
QString buildKey; // Used to identify this BuildTargetInfo object in its list.
QString displayName;
+ QString displayNameUniquifier;
Utils::FileName targetFilePath;
Utils::FileName projectFilePath;
diff --git a/src/plugins/projectexplorer/runconfiguration.cpp b/src/plugins/projectexplorer/runconfiguration.cpp
index 475eeb9dca..2c8e238495 100644
--- a/src/plugins/projectexplorer/runconfiguration.cpp
+++ b/src/plugins/projectexplorer/runconfiguration.cpp
@@ -49,6 +49,7 @@
#include <QDir>
#include <QFormLayout>
+#include <QHash>
#include <QPushButton>
#include <QTimer>
#include <QLoggingCategory>
@@ -466,6 +467,7 @@ RunConfigurationFactory::availableCreators(Target *parent) const
rci.id = m_runConfigBaseId;
rci.buildKey = ti.buildKey;
rci.displayName = displayName;
+ rci.displayNameUniquifier = ti.displayNameUniquifier;
rci.creationMode = ti.isQtcRunnable || !hasAnyQtcRunnable
? RunConfigurationCreationInfo::AlwaysCreate
: RunConfigurationCreationInfo::ManualCreationOnly;
@@ -540,7 +542,7 @@ RunConfiguration *RunConfigurationCreationInfo::create(Target *target) const
rc->m_buildKey = buildKey;
rc->doAdditionalSetup(*this);
- rc->setDefaultDisplayName(displayName);
+ rc->setDisplayName(displayName);
return rc;
}
@@ -575,6 +577,15 @@ const QList<RunConfigurationCreationInfo> RunConfigurationFactory::creatorsForTa
if (factory->canHandle(parent))
items.append(factory->availableCreators(parent));
}
+ QHash<QString, QList<RunConfigurationCreationInfo *>> itemsPerDisplayName;
+ for (RunConfigurationCreationInfo &item : items)
+ itemsPerDisplayName[item.displayName] << &item;
+ for (auto it = itemsPerDisplayName.cbegin(); it != itemsPerDisplayName.cend(); ++it) {
+ if (it.value().size() == 1)
+ continue;
+ for (RunConfigurationCreationInfo * const rci : it.value())
+ rci->displayName += rci->displayNameUniquifier;
+ }
return items;
}
diff --git a/src/plugins/projectexplorer/runconfiguration.h b/src/plugins/projectexplorer/runconfiguration.h
index c454f7e0ad..c0443ae07e 100644
--- a/src/plugins/projectexplorer/runconfiguration.h
+++ b/src/plugins/projectexplorer/runconfiguration.h
@@ -234,6 +234,7 @@ public:
Core::Id id;
QString buildKey;
QString displayName;
+ QString displayNameUniquifier;
CreationMode creationMode = AlwaysCreate;
bool useTerminal = false;
};
diff --git a/src/plugins/pythoneditor/PythonEditor.json.in b/src/plugins/pythoneditor/PythonEditor.json.in
index b4505d5546..89269328b3 100644
--- a/src/plugins/pythoneditor/PythonEditor.json.in
+++ b/src/plugins/pythoneditor/PythonEditor.json.in
@@ -28,6 +28,7 @@
\" <mime-type type=\'text/x-python-project\'>\",
\" <sub-class-of type=\'text/x-python\'/>\",
\" <comment>Qt Creator Python project file</comment>\",
+ \" <glob pattern=\'*.pyproject\'/>\",
\" <glob pattern=\'*.pyqtc\'/>\",
\" </mime-type>\",
\"</mime-info>\"
diff --git a/src/plugins/pythoneditor/pythoneditorplugin.cpp b/src/plugins/pythoneditor/pythoneditorplugin.cpp
index 64722ef749..dfd9c6a50e 100644
--- a/src/plugins/pythoneditor/pythoneditorplugin.cpp
+++ b/src/plugins/pythoneditor/pythoneditorplugin.cpp
@@ -59,6 +59,10 @@
#include <QRegularExpression>
#include <QRegularExpressionMatch>
#include <QTextCursor>
+#include <QJsonDocument>
+#include <QJsonObject>
+#include <QJsonValue>
+#include <QJsonArray>
using namespace Core;
using namespace ProjectExplorer;
@@ -340,6 +344,35 @@ static QStringList readLines(const Utils::FileName &projectFile)
return lines;
}
+static QStringList readLinesJson(const Utils::FileName &projectFile)
+{
+ const QString projectFileName = projectFile.fileName();
+ QStringList lines = { projectFileName };
+
+ QFile file(projectFile.toString());
+ if (!file.open(QFile::ReadOnly))
+ return lines;
+ const QByteArray content = file.readAll();
+
+ // This assumes te project file is formed with only one field called
+ // 'files' that has a list associated of the files to include in the project.
+ if (!content.isEmpty()) {
+ const QJsonDocument doc = QJsonDocument::fromJson(content);
+ const QJsonObject obj = doc.object();
+ if (obj.contains("files")) {
+ QJsonValue files = obj.value("files");
+ QJsonArray files_array = files.toArray();
+ QSet<QString> visited;
+ for (const auto &file : files_array)
+ visited.insert(file.toString());
+
+ lines.append(visited.toList());
+ }
+ }
+
+ return lines;
+}
+
bool PythonProject::saveRawFileList(const QStringList &rawFileList)
{
bool result = saveRawList(rawFileList, projectFilePath().toString());
@@ -418,7 +451,15 @@ bool PythonProject::renameFile(const QString &filePath, const QString &newFilePa
void PythonProject::parseProject()
{
m_rawListEntries.clear();
- m_rawFileList = readLines(projectFilePath());
+ const Utils::FileName filePath = projectFilePath();
+ // The PySide project file is JSON based
+ if (filePath.endsWith(".pyproject"))
+ m_rawFileList = readLinesJson(filePath);
+ // To keep compatibility with PyQt we keep the compatibility with plain
+ // text files as project files.
+ else if (filePath.endsWith(".pyqtc"))
+ m_rawFileList = readLines(filePath);
+
m_files = processEntries(m_rawFileList, &m_rawListEntries);
}
@@ -449,7 +490,7 @@ void PythonProject::refresh(Target *target)
auto newRoot = std::make_unique<PythonProjectNode>(this);
for (const QString &f : m_files) {
const QString displayName = baseDir.relativeFilePath(f);
- FileType fileType = f.endsWith(".pyqtc") ? FileType::Project : FileType::Source;
+ FileType fileType = f.endsWith(".pyproject") || f.endsWith(".pyqtc") ? FileType::Project : FileType::Source;
newRoot->addNestedNode(std::make_unique<PythonFileNode>(FileName::fromString(f),
displayName, fileType));
if (fileType == FileType::Source) {
diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
index 894163ab47..6e92cf45d2 100644
--- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp
+++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp
@@ -1029,6 +1029,12 @@ void QmakeProject::updateBuildSystemData()
bti.projectFilePath = node->filePath();
bti.workingDirectory = FileName::fromString(workingDir);
bti.displayName = bti.projectFilePath.toFileInfo().completeBaseName();
+ const FileName relativePathInProject
+ = bti.projectFilePath.relativeChildPath(projectDirectory());
+ if (!relativePathInProject.isEmpty()) {
+ bti.displayNameUniquifier = QString::fromLatin1(" (%1)")
+ .arg(relativePathInProject.toUserOutput());
+ }
bti.buildKey = bti.projectFilePath.toString();
bti.isQtcRunnable = config.contains("qtc_runnable");
diff --git a/src/tools/icons/qtcreatoricons.svg b/src/tools/icons/qtcreatoricons.svg
index 24f73e097e..86fcbb48d0 100644
--- a/src/tools/icons/qtcreatoricons.svg
+++ b/src/tools/icons/qtcreatoricons.svg
@@ -2645,18 +2645,21 @@
id="path5662-0"
inkscape:connector-curvature="0"
sodipodi:nodetypes="cccccccc" />
- <path
- sodipodi:nodetypes="sssccss"
- inkscape:connector-curvature="0"
- id="rect7867-7-1-4-6-13-8-3"
- d="m 587.94531,306.94336 h 33.10938 c 0.81824,0 1.3335,0.62105 1.44531,1.43164 V 311 h -36 v -2.625 c 0,-0.81826 0.62707,-1.43164 1.44531,-1.43164 z"
- style="fill:#3a4055" />
- <path
- id="rect5668-6"
- style="fill:#dfe0e3"
- d="m 619.5,308 h 2 v 2 h -2 z m -32,0 h 10 v 2 h -10 z"
- inkscape:connector-curvature="0"
- sodipodi:nodetypes="cccccccccc" />
+ <g
+ id="tempateIconGuiWizarfTitleBar">
+ <path
+ style="fill:#3a4055"
+ d="m 587.94531,306.94336 h 33.10938 c 0.81824,0 1.3335,0.62105 1.44531,1.43164 V 311 h -36 v -2.625 c 0,-0.81826 0.62707,-1.43164 1.44531,-1.43164 z"
+ id="rect7867-7-1-4-6-13-8-3"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="sssccss" />
+ <path
+ sodipodi:nodetypes="cccccccccc"
+ inkscape:connector-curvature="0"
+ d="m 619.5,308 h 2 v 2 h -2 z m -32,0 h 10 v 2 h -10 z"
+ style="fill:#dfe0e3"
+ id="rect5668-6" />
+ </g>
</g>
<use
x="0"
@@ -2667,6 +2670,152 @@
width="100%"
height="100%" />
<g
+ id="pythonSnake"
+ transform="matrix(1.03639,0,0,1.03639,42.698825,-127.31617)">
+ <path
+ style="stroke-width:0.62522024"
+ d="m 493.40157,381.58661 c -1.64186,0.28728 -1.95327,1.12936 -1.98311,2.23791 v 1.47075 h 3.8497 l 0.0197,0.96489 h -3.88913 -1.45286 c -1.12761,0 -2.07681,0.6104 -2.5352,1.9502 -0.45839,1.3398 -0.43103,2.68855 0,4.05417 0.43103,1.36562 0.93069,1.7217 2.05828,1.71476 l 0.98462,-5e-5 v -1.46166 c 0,-1.43884 1.23626,-2.39787 2.71574,-2.39787 h 4.24283 c 1.07877,0 1.73984,-0.59015 1.73527,-1.66316 l -0.0197,-4.63208 c -0.004,-1.04142 -0.79695,-2.06408 -1.84987,-2.2379 -0.66651,-0.10988 -1.35625,-0.15393 -2.01952,-0.15085 -0.66326,0.003 -1.2994,0.0532 -1.85674,0.15089 z m -0.53661,1.53569 c 0.40077,0 0.72642,0.32748 0.72642,0.73256 0,0.40364 -0.32565,0.72932 -0.72642,0.72932 -0.40221,0 -0.72644,-0.3258 -0.72644,-0.72944 0,-0.40508 0.32423,-0.73244 0.72644,-0.73244 z"
+ id="path1631"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="ssccccszzscssssscccssssss" />
+ </g>
+ <g
+ id="pythonLogo">
+ <use
+ style="fill:#3a4055;fill-opacity:1"
+ height="100%"
+ width="100%"
+ transform="translate(16)"
+ id="use1658"
+ xlink:href="#pythonSnake"
+ y="0"
+ x="0" />
+ <use
+ style="fill:#848895;fill-opacity:1"
+ height="100%"
+ width="100%"
+ transform="rotate(180,564.00512,277.49996)"
+ id="use1660"
+ xlink:href="#pythonSnake"
+ y="0"
+ x="0" />
+ </g>
+ <g
+ id="share/qtcreator/templates/wizards/projects/qtforpythonapplication/mainwindow/icon">
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#transparentBackgroundRect_60_60"
+ id="use6218"
+ width="100%"
+ height="100%"
+ transform="translate(540,60)" />
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#wizardicons_laptop"
+ id="use6220"
+ width="100%"
+ height="100%"
+ transform="translate(55,-78)" />
+ <use
+ transform="translate(-11.997289,-36)"
+ height="100%"
+ width="100%"
+ id="use6176"
+ xlink:href="#pythonLogo"
+ y="0"
+ x="0"
+ style="display:inline" />
+ <path
+ style="display:inline;fill:#3a4055;stroke-width:1"
+ d="m 571,238 -6e-4,10.32943 2.47755,-1.72702 1.42512,3.25135 2.17205,-0.94811 -1.42511,-3.25136 2.95258,-0.64325 z"
+ id="path6222"
+ inkscape:connector-curvature="0"
+ sodipodi:nodetypes="cccccccc" />
+ <use
+ transform="translate(-38.5,-80)"
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#tempateIconGuiWizarfTitleBar"
+ id="use6247"
+ width="100%"
+ height="100%" />
+ </g>
+ <g
+ transform="translate(115,-78)"
+ id="share/qtcreator/templates/wizards/projects/qtforpythonapplication/empty/icon">
+ <use
+ transform="translate(485,138)"
+ height="100%"
+ width="100%"
+ id="use6264"
+ xlink:href="#transparentBackgroundRect_60_60"
+ y="0"
+ x="0"
+ style="display:inline" />
+ <g
+ transform="translate(0,-1)"
+ id="g6272">
+ <path
+ sodipodi:nodetypes="sssccss"
+ inkscape:connector-curvature="0"
+ id="path6266"
+ d="m 494.5,304.5 h 33 c 1.58695,0 3,1.41305 3,3 v 25 h -39 v -25 c 0,-1.58696 1.41305,-3 3,-3 z"
+ style="fill:#fbfbfb;stroke:#6b7080;stroke-width:3" />
+ <path
+ sodipodi:nodetypes="ccccc"
+ inkscape:connector-curvature="0"
+ id="path6268"
+ d="m 535,333 h -48 c 0,2 1,3 3,4 h 42 c 2,-1 3,-2 3,-4 z"
+ style="fill:#848895" />
+ <path
+ sodipodi:nodetypes="csscc"
+ inkscape:connector-curvature="0"
+ id="path6270"
+ d="m 517,333 c 0,0.77906 -0.69638,1 -1.61124,1 H 506.5 c -0.91487,0 -1.69151,-0.22094 -1.69151,-1 z"
+ style="fill:#53586b" />
+ </g>
+ <path
+ sodipodi:nodetypes="cccccccccccc"
+ inkscape:connector-curvature="0"
+ d="m 502,325 h 6 v 1 h -6 z m -5.75012,-7 h 1.5 l 4,4 -4,4 h -1.5 l 4,-4 z"
+ style="fill:#3a4055;stroke-width:1"
+ id="path6274" />
+ <use
+ style="display:inline"
+ x="0"
+ y="0"
+ xlink:href="#pythonLogo"
+ id="use6278"
+ width="100%"
+ height="100%"
+ transform="translate(-54.999998,40)" />
+ </g>
+ <g
+ id="share/qtcreator/templates/wizards/files/python/icon">
+ <use
+ height="100%"
+ width="100%"
+ transform="translate(480,60)"
+ id="use8679"
+ xlink:href="#src/libs/utils/images/wizardicon-file"
+ y="0"
+ x="0" />
+ <use
+ height="100%"
+ width="100%"
+ id="use8681"
+ xlink:href="#use6278"
+ y="0"
+ x="0"
+ style="display:inline"
+ transform="translate(169,-74)" />
+ </g>
+ <g
id="src/plugins/coreplugin/images/settingscategory_core"
transform="translate(-364,-84)">
<use
diff --git a/tests/manual/debugger/python/README.md b/tests/manual/debugger/python/README.md
index 0ed136ee40..31b9e70a18 100644
--- a/tests/manual/debugger/python/README.md
+++ b/tests/manual/debugger/python/README.md
@@ -1,4 +1,4 @@
-- qtcreator -load PythonEditor ./python.pyqtc
+- qtcreator -load PythonEditor ./python.pyproject (or ./python.pyqtc)
- Switch active runconfiguration to main.py
- <F10>