LSP: separate communication interface and client logic

Change-Id: I7d35fa634287b5f858c4a87aa10f99bf18d1ad07
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
David Schulz
2019-01-31 08:46:23 +01:00
parent f7e1354ae5
commit eac1b6059c
10 changed files with 333 additions and 179 deletions

View File

@@ -176,7 +176,7 @@ bool BaseMessage::isValid() const
return contentLength >= 0; return contentLength >= 0;
} }
QByteArray BaseMessage::toData() QByteArray BaseMessage::toData() const
{ {
return header() + content; return header() + content;
} }

View File

@@ -55,7 +55,7 @@ public:
bool isComplete() const; bool isComplete() const;
bool isValid() const; bool isValid() const;
QByteArray toData(); QByteArray toData() const;
QByteArray mimeType; QByteArray mimeType;
QByteArray content; QByteArray content;

View File

@@ -25,8 +25,9 @@
#include "baseclient.h" #include "baseclient.h"
#include "languageclientinterface.h"
#include "languageclientmanager.h" #include "languageclientmanager.h"
#include "languageclient/languageclientutils.h" #include "languageclientutils.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/idocument.h> #include <coreplugin/idocument.h>
@@ -60,22 +61,23 @@ using namespace Utils;
namespace LanguageClient { namespace LanguageClient {
static Q_LOGGING_CATEGORY(LOGLSPCLIENT, "qtc.languageclient.client", QtWarningMsg); 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_id(Core::Id::fromString(QUuid::createUuid().toString()))
, m_completionProvider(this) , m_completionProvider(this)
, m_clientInterface(clientInterface)
{ {
m_buffer.open(QIODevice::ReadWrite | QIODevice::Append);
m_contentHandler.insert(JsonRpcMessageHandler::jsonRpcMimeType(), m_contentHandler.insert(JsonRpcMessageHandler::jsonRpcMimeType(),
&JsonRpcMessageHandler::parseContent); &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() BaseClient::~BaseClient()
{ {
using namespace TextEditor; using namespace TextEditor;
m_buffer.close();
// FIXME: instead of replacing the completion provider in the text document store the // FIXME: instead of replacing the completion provider in the text document store the
// completion provider as a prioritised list in the text document // completion provider as a prioritised list in the text document
for (TextDocument *document : m_resetCompletionProvider) for (TextDocument *document : m_resetCompletionProvider)
@@ -91,6 +93,7 @@ BaseClient::~BaseClient()
void BaseClient::initialize() void BaseClient::initialize()
{ {
using namespace ProjectExplorer; using namespace ProjectExplorer;
QTC_ASSERT(m_clientInterface, return);
QTC_ASSERT(m_state == Uninitialized, return); QTC_ASSERT(m_state == Uninitialized, return);
qCDebug(LOGLSPCLIENT) << "initializing language server " << m_displayName; qCDebug(LOGLSPCLIENT) << "initializing language server " << m_displayName;
auto initRequest = new InitializeRequest(); auto initRequest = new InitializeRequest();
@@ -108,7 +111,7 @@ void BaseClient::initialize()
}); });
// directly send data otherwise the state check would fail; // directly send data otherwise the state check would fail;
initRequest->registerResponseHandler(&m_responseHandlers); initRequest->registerResponseHandler(&m_responseHandlers);
sendData(initRequest->toBaseMessage().toData()); m_clientInterface->sendMessage(initRequest->toBaseMessage());
m_state = InitializeRequested; m_state = InitializeRequested;
} }
@@ -190,12 +193,13 @@ void BaseClient::openDocument(Core::IDocument *document)
void BaseClient::sendContent(const IContent &content) void BaseClient::sendContent(const IContent &content)
{ {
QTC_ASSERT(m_clientInterface, return);
QTC_ASSERT(m_state == Initialized, return); QTC_ASSERT(m_state == Initialized, return);
content.registerResponseHandler(&m_responseHandlers); content.registerResponseHandler(&m_responseHandlers);
QString error; QString error;
if (!QTC_GUARD(content.isValid(&error))) if (!QTC_GUARD(content.isValid(&error)))
Core::MessageManager::write(error); Core::MessageManager::write(error);
sendData(content.toBaseMessage().toData()); m_clientInterface->sendMessage(content.toBaseMessage());
} }
void BaseClient::sendContent(const DocumentUri &uri, const IContent &content) 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; || m_languagFilter.filePattern != settings->m_languageFilter.filePattern;
} }
bool BaseClient::start()
{
return m_clientInterface->start();
}
bool BaseClient::reset() bool BaseClient::reset()
{ {
if (!m_restartsLeft) if (!m_restartsLeft)
@@ -659,9 +668,7 @@ bool BaseClient::reset()
--m_restartsLeft; --m_restartsLeft;
m_state = Uninitialized; m_state = Uninitialized;
m_responseHandlers.clear(); m_responseHandlers.clear();
m_buffer.close(); m_clientInterface->resetBuffer();
m_buffer.setData(nullptr);
m_buffer.open(QIODevice::ReadWrite | QIODevice::Append);
m_openedDocument.clear(); m_openedDocument.clear();
m_serverCapabilities = ServerCapabilities(); m_serverCapabilities = ServerCapabilities();
m_dynamicCapabilities.reset(); m_dynamicCapabilities.reset();
@@ -674,6 +681,24 @@ void BaseClient::setError(const QString &message)
m_state = Error; 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) void BaseClient::log(const QString &message, Core::MessageManager::PrintToOutputPaneFlag flag)
{ {
Core::MessageManager::write(QString("LanguageClient %1: %2").arg(name(), message), 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) void BaseClient::shutDownCallback(const ShutdownRequest::Response &shutdownResponse)
{ {
QTC_ASSERT(m_state == ShutdownRequested, return); QTC_ASSERT(m_state == ShutdownRequested, return);
QTC_ASSERT(m_clientInterface, return);
optional<ShutdownRequest::Response::Error> errorValue = shutdownResponse.error(); optional<ShutdownRequest::Response::Error> errorValue = shutdownResponse.error();
if (errorValue.has_value()) { if (errorValue.has_value()) {
ShutdownRequest::Response::Error error = errorValue.value(); ShutdownRequest::Response::Error error = errorValue.value();
@@ -848,7 +874,7 @@ void BaseClient::shutDownCallback(const ShutdownRequest::Response &shutdownRespo
return; return;
} }
// directly send data otherwise the state check would fail; // 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"; qCDebug(LOGLSPCLIENT) << "language server " << m_displayName << " shutdown";
m_state = Shutdown; m_state = Shutdown;
} }
@@ -872,118 +898,4 @@ bool BaseClient::sendWorkspceFolderChanges() const
return false; 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 } // namespace LanguageClient

View File

@@ -56,12 +56,14 @@ namespace TextEditor
namespace LanguageClient { namespace LanguageClient {
class BaseClientInterface;
class BaseClient : public QObject class BaseClient : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
BaseClient(); explicit BaseClient(BaseClientInterface *clientInterface); // takes ownership
~BaseClient() override; ~BaseClient() override;
BaseClient(const BaseClient &) = delete; BaseClient(const BaseClient &) = delete;
@@ -124,8 +126,8 @@ public:
bool needsRestart(const BaseSettings *) const; bool needsRestart(const BaseSettings *) const;
virtual bool start() { return true; } bool start();
virtual bool reset(); bool reset();
void log(const QString &message, void log(const QString &message,
Core::MessageManager::PrintToOutputPaneFlag flag = Core::MessageManager::NoModeSwitch); Core::MessageManager::PrintToOutputPaneFlag flag = Core::MessageManager::NoModeSwitch);
@@ -143,8 +145,7 @@ signals:
protected: protected:
void setError(const QString &message); void setError(const QString &message);
virtual void sendData(const QByteArray &data) = 0; void handleMessage(const LanguageServerProtocol::BaseMessage &message);
void parseData(const QByteArray &data);
private: private:
void handleResponse(const LanguageServerProtocol::MessageId &id, const QByteArray &content, void handleResponse(const LanguageServerProtocol::MessageId &id, const QByteArray &content,
@@ -168,7 +169,6 @@ private:
State m_state = Uninitialized; State m_state = Uninitialized;
QHash<LanguageServerProtocol::MessageId, LanguageServerProtocol::ResponseHandler> m_responseHandlers; QHash<LanguageServerProtocol::MessageId, LanguageServerProtocol::ResponseHandler> m_responseHandlers;
QHash<QByteArray, ContentHandler> m_contentHandler; QHash<QByteArray, ContentHandler> m_contentHandler;
QBuffer m_buffer;
QString m_displayName; QString m_displayName;
LanguageFilter m_languagFilter; LanguageFilter m_languagFilter;
QList<Utils::FileName> m_openedDocument; QList<Utils::FileName> m_openedDocument;
@@ -177,41 +177,9 @@ private:
DynamicCapabilities m_dynamicCapabilities; DynamicCapabilities m_dynamicCapabilities;
LanguageClientCompletionAssistProvider m_completionProvider; LanguageClientCompletionAssistProvider m_completionProvider;
QSet<TextEditor::TextDocument *> m_resetCompletionProvider; QSet<TextEditor::TextDocument *> m_resetCompletionProvider;
LanguageServerProtocol::BaseMessage m_currentMessage;
QHash<LanguageServerProtocol::DocumentUri, LanguageServerProtocol::MessageId> m_highlightRequests; QHash<LanguageServerProtocol::DocumentUri, LanguageServerProtocol::MessageId> m_highlightRequests;
int m_restartsLeft = 5; int m_restartsLeft = 5;
}; QScopedPointer<BaseClientInterface> m_clientInterface;
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;
}; };
} // namespace LanguageClient } // namespace LanguageClient

View File

@@ -7,6 +7,7 @@ HEADERS += \
dynamiccapabilities.h \ dynamiccapabilities.h \
languageclient_global.h \ languageclient_global.h \
languageclientcodeassist.h \ languageclientcodeassist.h \
languageclientinterface.h \
languageclientmanager.h \ languageclientmanager.h \
languageclientoutline.h \ languageclientoutline.h \
languageclientplugin.h \ languageclientplugin.h \
@@ -18,6 +19,7 @@ SOURCES += \
baseclient.cpp \ baseclient.cpp \
dynamiccapabilities.cpp \ dynamiccapabilities.cpp \
languageclientcodeassist.cpp \ languageclientcodeassist.cpp \
languageclientinterface.cpp \
languageclientmanager.cpp \ languageclientmanager.cpp \
languageclientoutline.cpp \ languageclientoutline.cpp \
languageclientplugin.cpp \ languageclientplugin.cpp \

View File

@@ -22,6 +22,8 @@ QtcPlugin {
"languageclient_global.h", "languageclient_global.h",
"languageclientcodeassist.cpp", "languageclientcodeassist.cpp",
"languageclientcodeassist.h", "languageclientcodeassist.h",
"languageclientinterface.cpp",
"languageclientinterface.h",
"languageclientmanager.cpp", "languageclientmanager.cpp",
"languageclientmanager.h", "languageclientmanager.h",
"languageclientoutline.cpp", "languageclientoutline.cpp",

View File

@@ -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

View File

@@ -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

View File

@@ -28,6 +28,7 @@
#include "baseclient.h" #include "baseclient.h"
#include "languageclientmanager.h" #include "languageclientmanager.h"
#include "languageclient_global.h" #include "languageclient_global.h"
#include "languageclientinterface.h"
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <utils/algorithm.h> #include <utils/algorithm.h>
@@ -399,6 +400,13 @@ bool BaseSettings::isValid() const
BaseClient *BaseSettings::createClient() 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; return nullptr;
} }
@@ -467,8 +475,8 @@ bool StdIOSettings::needsRestart() const
{ {
if (BaseSettings::needsRestart()) if (BaseSettings::needsRestart())
return true; return true;
if (auto stdIOClient = qobject_cast<StdIOClient *>(m_client)) if (auto stdIOInterface = qobject_cast<StdIOClientInterface *>(m_client))
return stdIOClient->needsRestart(this); return stdIOInterface->needsRestart(this);
return false; return false;
} }
@@ -477,14 +485,6 @@ bool StdIOSettings::isValid() const
return BaseSettings::isValid() && !m_executable.isEmpty(); 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 StdIOSettings::toMap() const
{ {
QVariantMap map = BaseSettings::toMap(); QVariantMap map = BaseSettings::toMap();
@@ -500,6 +500,11 @@ void StdIOSettings::fromMap(const QVariantMap &map)
m_arguments = map[argumentsKey].toString(); m_arguments = map[argumentsKey].toString();
} }
BaseClientInterface *StdIOSettings::createInterface() const
{
return new StdIOClientInterface(m_executable, m_arguments);
}
BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *parent) BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, m_name(new QLineEdit(settings->m_name, this)) , m_name(new QLineEdit(settings->m_name, this))

View File

@@ -44,6 +44,7 @@ namespace LanguageClient {
constexpr char noLanguageFilter[] = "No Filter"; constexpr char noLanguageFilter[] = "No Filter";
class BaseClient; class BaseClient;
class BaseClientInterface;
struct LanguageFilter struct LanguageFilter
{ {
@@ -73,11 +74,13 @@ public:
virtual BaseSettings *copy() const { return new BaseSettings(*this); } virtual BaseSettings *copy() const { return new BaseSettings(*this); }
virtual bool needsRestart() const; virtual bool needsRestart() const;
virtual bool isValid() const ; virtual bool isValid() const ;
virtual BaseClient *createClient() const; BaseClient *createClient() const;
virtual QVariantMap toMap() const; virtual QVariantMap toMap() const;
virtual void fromMap(const QVariantMap &map); virtual void fromMap(const QVariantMap &map);
protected: protected:
virtual BaseClientInterface *createInterface() const { return nullptr; }
BaseSettings(const BaseSettings &other) = default; BaseSettings(const BaseSettings &other) = default;
BaseSettings(BaseSettings &&other) = default; BaseSettings(BaseSettings &&other) = default;
BaseSettings &operator=(const BaseSettings &other) = default; BaseSettings &operator=(const BaseSettings &other) = default;
@@ -105,11 +108,12 @@ public:
BaseSettings *copy() const override { return new StdIOSettings(*this); } BaseSettings *copy() const override { return new StdIOSettings(*this); }
bool needsRestart() const override; bool needsRestart() const override;
bool isValid() const override; bool isValid() const override;
BaseClient *createClient() const override;
QVariantMap toMap() const override; QVariantMap toMap() const override;
void fromMap(const QVariantMap &map) override; void fromMap(const QVariantMap &map) override;
protected: protected:
BaseClientInterface *createInterface() const override;
StdIOSettings(const StdIOSettings &other) = default; StdIOSettings(const StdIOSettings &other) = default;
StdIOSettings(StdIOSettings &&other) = default; StdIOSettings(StdIOSettings &&other) = default;
StdIOSettings &operator=(const StdIOSettings &other) = default; StdIOSettings &operator=(const StdIOSettings &other) = default;