diff options
author | David Schulz <david.schulz@qt.io> | 2019-01-31 08:46:23 +0100 |
---|---|---|
committer | David Schulz <david.schulz@qt.io> | 2019-02-04 09:43:11 +0000 |
commit | eac1b6059c1abd667457b7a3a6d9f78c49139c30 (patch) | |
tree | d6944f3b8b289ad3636ea5afcd04dfb6826e91d4 /src | |
parent | f7e1354ae563ae6c475ee2e18764127e58073d2b (diff) | |
download | qt-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.cpp | 2 | ||||
-rw-r--r-- | src/libs/languageserverprotocol/basemessage.h | 2 | ||||
-rw-r--r-- | src/plugins/languageclient/baseclient.cpp | 164 | ||||
-rw-r--r-- | src/plugins/languageclient/baseclient.h | 46 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclient.pro | 2 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclient.qbs | 2 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientinterface.cpp | 165 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientinterface.h | 96 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientsettings.cpp | 25 | ||||
-rw-r--r-- | src/plugins/languageclient/languageclientsettings.h | 8 |
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; |