summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Schulz <david.schulz@qt.io>2019-01-31 08:46:23 +0100
committerDavid Schulz <david.schulz@qt.io>2019-02-04 09:43:11 +0000
commiteac1b6059c1abd667457b7a3a6d9f78c49139c30 (patch)
treed6944f3b8b289ad3636ea5afcd04dfb6826e91d4 /src
parentf7e1354ae563ae6c475ee2e18764127e58073d2b (diff)
downloadqt-creator-eac1b6059c1abd667457b7a3a6d9f78c49139c30.tar.gz
LSP: separate communication interface and client logic
Change-Id: I7d35fa634287b5f858c4a87aa10f99bf18d1ad07 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/libs/languageserverprotocol/basemessage.cpp2
-rw-r--r--src/libs/languageserverprotocol/basemessage.h2
-rw-r--r--src/plugins/languageclient/baseclient.cpp164
-rw-r--r--src/plugins/languageclient/baseclient.h46
-rw-r--r--src/plugins/languageclient/languageclient.pro2
-rw-r--r--src/plugins/languageclient/languageclient.qbs2
-rw-r--r--src/plugins/languageclient/languageclientinterface.cpp165
-rw-r--r--src/plugins/languageclient/languageclientinterface.h96
-rw-r--r--src/plugins/languageclient/languageclientsettings.cpp25
-rw-r--r--src/plugins/languageclient/languageclientsettings.h8
10 files changed, 333 insertions, 179 deletions
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/languageclient/baseclient.cpp b/src/plugins/languageclient/baseclient.cpp
index 2852fa1b43..8c5572f51f 100644
--- a/src/plugins/languageclient/baseclient.cpp
+++ b/src/plugins/languageclient/baseclient.cpp
@@ -25,8 +25,9 @@
#include "baseclient.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()
+BaseClient::BaseClient(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, &BaseClient::handleMessage);
+ connect(clientInterface, &BaseClientInterface::error, this, &BaseClient::setError);
+ connect(clientInterface, &BaseClientInterface::finished, this, &BaseClient::finished);
}
BaseClient::~BaseClient()
{
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)
@@ -91,6 +93,7 @@ BaseClient::~BaseClient()
void BaseClient::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,7 +111,7 @@ 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;
}
@@ -190,12 +193,13 @@ void BaseClient::openDocument(Core::IDocument *document)
void BaseClient::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)
@@ -652,6 +656,11 @@ bool BaseClient::needsRestart(const BaseSettings *settings) const
|| m_languagFilter.filePattern != settings->m_languageFilter.filePattern;
}
+bool BaseClient::start()
+{
+ return m_clientInterface->start();
+}
+
bool BaseClient::reset()
{
if (!m_restartsLeft)
@@ -659,9 +668,7 @@ bool BaseClient::reset()
--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();
@@ -674,6 +681,24 @@ void BaseClient::setError(const QString &message)
m_state = Error;
}
+void BaseClient::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 BaseClient::log(const QString &message, Core::MessageManager::PrintToOutputPaneFlag flag)
{
Core::MessageManager::write(QString("LanguageClient %1: %2").arg(name(), message), flag);
@@ -841,6 +866,7 @@ void BaseClient::intializeCallback(const InitializeRequest::Response &initRespon
void BaseClient::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();
@@ -848,7 +874,7 @@ 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;
}
@@ -872,118 +898,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/baseclient.h
index 318d9ce67f..3487d97e8b 100644
--- a/src/plugins/languageclient/baseclient.h
+++ b/src/plugins/languageclient/baseclient.h
@@ -56,12 +56,14 @@ namespace TextEditor
namespace LanguageClient {
+class BaseClientInterface;
+
class BaseClient : public QObject
{
Q_OBJECT
public:
- BaseClient();
+ explicit BaseClient(BaseClientInterface *clientInterface); // takes ownership
~BaseClient() override;
BaseClient(const BaseClient &) = delete;
@@ -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..6364a78720 100644
--- a/src/plugins/languageclient/languageclient.pro
+++ b/src/plugins/languageclient/languageclient.pro
@@ -7,6 +7,7 @@ HEADERS += \
dynamiccapabilities.h \
languageclient_global.h \
languageclientcodeassist.h \
+ languageclientinterface.h \
languageclientmanager.h \
languageclientoutline.h \
languageclientplugin.h \
@@ -18,6 +19,7 @@ SOURCES += \
baseclient.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..d2db93edbc 100644
--- a/src/plugins/languageclient/languageclient.qbs
+++ b/src/plugins/languageclient/languageclient.qbs
@@ -22,6 +22,8 @@ QtcPlugin {
"languageclient_global.h",
"languageclientcodeassist.cpp",
"languageclientcodeassist.h",
+ "languageclientinterface.cpp",
+ "languageclientinterface.h",
"languageclientmanager.cpp",
"languageclientmanager.h",
"languageclientoutline.cpp",
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/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp
index 36ec211b85..fb30fbe0fc 100644
--- a/src/plugins/languageclient/languageclientsettings.cpp
+++ b/src/plugins/languageclient/languageclientsettings.cpp
@@ -28,6 +28,7 @@
#include "baseclient.h"
#include "languageclientmanager.h"
#include "languageclient_global.h"
+#include "languageclientinterface.h"
#include <coreplugin/icore.h>
#include <utils/algorithm.h>
@@ -399,6 +400,13 @@ bool BaseSettings::isValid() const
BaseClient *BaseSettings::createClient() const
{
+ BaseClientInterface *interface = createInterface();
+ if (QTC_GUARD(interface)) {
+ auto *client = new BaseClient(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..1b229788a6 100644
--- a/src/plugins/languageclient/languageclientsettings.h
+++ b/src/plugins/languageclient/languageclientsettings.h
@@ -44,6 +44,7 @@ namespace LanguageClient {
constexpr char noLanguageFilter[] = "No Filter";
class BaseClient;
+class BaseClientInterface;
struct LanguageFilter
{
@@ -73,11 +74,13 @@ public:
virtual BaseSettings *copy() const { return new BaseSettings(*this); }
virtual bool needsRestart() const;
virtual bool isValid() const ;
- virtual BaseClient *createClient() const;
+ BaseClient *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;