forked from qt-creator/qt-creator
LSP: separate communication interface and client logic
Change-Id: I7d35fa634287b5f858c4a87aa10f99bf18d1ad07 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -176,7 +176,7 @@ bool BaseMessage::isValid() const
|
||||
return contentLength >= 0;
|
||||
}
|
||||
|
||||
QByteArray BaseMessage::toData()
|
||||
QByteArray BaseMessage::toData() const
|
||||
{
|
||||
return header() + content;
|
||||
}
|
||||
|
@@ -55,7 +55,7 @@ public:
|
||||
|
||||
bool isComplete() const;
|
||||
bool isValid() const;
|
||||
QByteArray toData();
|
||||
QByteArray toData() const;
|
||||
|
||||
QByteArray mimeType;
|
||||
QByteArray content;
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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 \
|
||||
|
@@ -22,6 +22,8 @@ QtcPlugin {
|
||||
"languageclient_global.h",
|
||||
"languageclientcodeassist.cpp",
|
||||
"languageclientcodeassist.h",
|
||||
"languageclientinterface.cpp",
|
||||
"languageclientinterface.h",
|
||||
"languageclientmanager.cpp",
|
||||
"languageclientmanager.h",
|
||||
"languageclientoutline.cpp",
|
||||
|
165
src/plugins/languageclient/languageclientinterface.cpp
Normal file
165
src/plugins/languageclient/languageclientinterface.cpp
Normal 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
|
96
src/plugins/languageclient/languageclientinterface.h
Normal file
96
src/plugins/languageclient/languageclientinterface.h
Normal 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
|
@@ -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))
|
||||
|
@@ -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;
|
||||
|
Reference in New Issue
Block a user