diff --git a/src/libs/languageserverprotocol/jsonrpcmessages.h b/src/libs/languageserverprotocol/jsonrpcmessages.h index 53bcfafba38..97698811ace 100644 --- a/src/libs/languageserverprotocol/jsonrpcmessages.h +++ b/src/libs/languageserverprotocol/jsonrpcmessages.h @@ -354,7 +354,7 @@ public: constexpr static const char methodName[] = "$/cancelRequest"; }; -} // namespace LanguageClient +} // namespace LanguageServerProtocol template inline QDebug operator<<(QDebug stream, @@ -363,3 +363,5 @@ inline QDebug operator<<(QDebug stream, stream.nospace() << error.toString(); return stream; } + +Q_DECLARE_METATYPE(LanguageServerProtocol::JsonRpcMessage) diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 10c42e0a0bb..d4cc2e09761 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -62,7 +62,6 @@ #include #include - #include #include #include @@ -71,6 +70,7 @@ #include #include #include +#include #include using namespace LanguageServerProtocol; @@ -80,9 +80,56 @@ namespace LanguageClient { static Q_LOGGING_CATEGORY(LOGLSPCLIENT, "qtc.languageclient.client", QtWarningMsg); +class InterfaceController : public QObject +{ + Q_OBJECT + +public: + InterfaceController(BaseClientInterface *interface) + : m_interface(interface) + { + using Interface = BaseClientInterface; + interface->moveToThread(&m_thread); + connect(interface, &Interface::contentReceived, this, &InterfaceController::contentReceived); + connect(interface, &Interface::error, this, &InterfaceController::error); + connect(interface, &Interface::finished, this, &InterfaceController::finished); + connect(interface, &Interface::started, this, &InterfaceController::started); + m_thread.start(); + } + ~InterfaceController() + { + m_interface->deleteLater(); + m_thread.quit(); + m_thread.wait(); + } + + void start() + { + QMetaObject::invokeMethod(m_interface, &BaseClientInterface::start); + } + void sendContent(const JsonRpcMessage message) + { + QMetaObject::invokeMethod(m_interface, [=]() { m_interface->sendContent(message); }); + } + void resetBuffer() + { + QMetaObject::invokeMethod(m_interface, &BaseClientInterface::resetBuffer); + } + +signals: + void contentReceived(const JsonRpcMessage &message); + void started(); + void error(const QString &message); + void finished(); + +private: + BaseClientInterface *m_interface; + QThread m_thread; +}; + Client::Client(BaseClientInterface *clientInterface) : m_id(Utils::Id::fromString(QUuid::createUuid().toString())) - , m_clientInterface(clientInterface) + , m_clientInterface(new InterfaceController(clientInterface)) , m_documentSymbolCache(this) , m_hoverHandler(this) , m_symbolSupport(this) @@ -105,9 +152,12 @@ Client::Client(BaseClientInterface *clientInterface) this, &Client::projectClosed); QTC_ASSERT(clientInterface, return); - connect(clientInterface, &BaseClientInterface::contentReceived, this, &Client::handleContent); - connect(clientInterface, &BaseClientInterface::error, this, &Client::setError); - connect(clientInterface, &BaseClientInterface::finished, this, &Client::finished); + connect(m_clientInterface, &InterfaceController::contentReceived, this, &Client::handleContent); + connect(m_clientInterface, &InterfaceController::error, this, &Client::setError); + connect(m_clientInterface, &InterfaceController::finished, this, &Client::finished); + connect(m_clientInterface, &InterfaceController::started, this, [this]() { + LanguageClientManager::clientStarted(this); + }); connect(Core::EditorManager::instance(), &Core::EditorManager::documentClosed, this, @@ -159,9 +209,10 @@ Client::~Client() m_documentHighlightsTimer.clear(); updateEditorToolBar(m_openedDocument.keys()); // do not handle messages while shutting down - disconnect(m_clientInterface.data(), &BaseClientInterface::contentReceived, + disconnect(m_clientInterface, &InterfaceController::contentReceived, this, &Client::handleContent); delete m_diagnosticManager; + delete m_clientInterface; } static ClientCapabilities generateClientCapabilities() @@ -1170,10 +1221,7 @@ bool Client::supportsDocumentSymbols(const TextEditor::TextDocument *doc) const void Client::start() { LanguageClientManager::addClient(this); - if (m_clientInterface->start()) - LanguageClientManager::clientStarted(this); - else - LanguageClientManager::clientFinished(this); + m_clientInterface->start(); } bool Client::reset() @@ -1541,8 +1589,8 @@ void Client::sendContentNow(const IContent &content) LanguageClientManager::logJsonRpcMessage(LspLogMessage::ClientMessage, name(), static_cast(content)); + m_clientInterface->sendContent(static_cast(content)); } - m_clientInterface->sendContent(content); } bool Client::documentUpdatePostponed(const Utils::FilePath &fileName) const @@ -1694,3 +1742,5 @@ QTextCursor Client::adjustedCursorForHighlighting(const QTextCursor &cursor, } } // namespace LanguageClient + +#include diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 98de0835554..977835aa0a1 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -80,6 +80,7 @@ QT_END_NAMESPACE namespace LanguageClient { class BaseClientInterface; +class InterfaceController; class LANGUAGECLIENT_EXPORT Client : public QObject { @@ -300,7 +301,7 @@ private: QMap m_resetAssistProvider; QHash m_highlightRequests; int m_restartsLeft = 5; - QScopedPointer m_clientInterface; + InterfaceController *m_clientInterface = nullptr; DiagnosticManager *m_diagnosticManager = nullptr; DocumentSymbolCache m_documentSymbolCache; HoverHandler m_hoverHandler; diff --git a/src/plugins/languageclient/languageclientinterface.cpp b/src/plugins/languageclient/languageclientinterface.cpp index 5425933ef73..b9d806a3501 100644 --- a/src/plugins/languageclient/languageclientinterface.cpp +++ b/src/plugins/languageclient/languageclientinterface.cpp @@ -99,72 +99,72 @@ void BaseClientInterface::parseCurrentMessage() m_currentMessage = BaseMessage(); } -StdIOClientInterface::StdIOClientInterface() -{ - m_process.setProcessMode(ProcessMode::Writer); - - connect(&m_process, &QtcProcess::readyReadStandardError, - this, &StdIOClientInterface::readError); - connect(&m_process, &QtcProcess::readyReadStandardOutput, - this, &StdIOClientInterface::readOutput); - connect(&m_process, &QtcProcess::finished, - this, &StdIOClientInterface::onProcessFinished); -} +StdIOClientInterface::StdIOClientInterface() {} StdIOClientInterface::~StdIOClientInterface() { - m_process.stopProcess(); + if (m_process) + m_process->stopProcess(); + delete m_process; } -bool StdIOClientInterface::start() +void StdIOClientInterface::startImpl() { - m_process.start(); - if (!m_process.waitForStarted() || m_process.state() != QProcess::Running) { - emit error(m_process.errorString()); - return false; - } - return true; + m_process = new Utils::QtcProcess; + m_process->setProcessMode(ProcessMode::Writer); + connect(m_process, &QtcProcess::readyReadStandardError, + this, &StdIOClientInterface::readError); + connect(m_process, &QtcProcess::readyReadStandardOutput, + this, &StdIOClientInterface::readOutput); + connect(m_process, &QtcProcess::finished, this, &StdIOClientInterface::onProcessFinished); + connect(m_process, &QtcProcess::started, this, &StdIOClientInterface::started); + m_process->setCommand(m_cmd); + m_process->setWorkingDirectory(m_workingDirectory); + m_process->start(); } void StdIOClientInterface::setCommandLine(const CommandLine &cmd) { - m_process.setCommand(cmd); + m_cmd = cmd; } void StdIOClientInterface::setWorkingDirectory(const FilePath &workingDirectory) { - m_process.setWorkingDirectory(workingDirectory); + m_workingDirectory = workingDirectory; } void StdIOClientInterface::sendData(const QByteArray &data) { - if (m_process.state() != QProcess::Running) { + if (!m_process || m_process->state() != QProcess::Running) { emit error(tr("Cannot send data to unstarted server %1") - .arg(m_process.commandLine().toUserOutput())); + .arg(m_cmd.toUserOutput())); return; } qCDebug(LOGLSPCLIENTV) << "StdIOClient send data:"; qCDebug(LOGLSPCLIENTV).noquote() << data; - m_process.writeRaw(data); + m_process->writeRaw(data); } void StdIOClientInterface::onProcessFinished() { - if (m_process.exitStatus() == QProcess::CrashExit) + QTC_ASSERT(m_process, return); + if (m_process->exitStatus() == QProcess::CrashExit) emit error(tr("Crashed with exit code %1: %2") - .arg(m_process.exitCode()).arg(m_process.errorString())); + .arg(m_process->exitCode()).arg(m_process->errorString())); emit finished(); } void StdIOClientInterface::readError() { + QTC_ASSERT(m_process, return); qCDebug(LOGLSPCLIENTV) << "StdIOClient std err:\n"; - qCDebug(LOGLSPCLIENTV).noquote() << m_process.readAllStandardError(); + qCDebug(LOGLSPCLIENTV).noquote() << m_process->readAllStandardError(); } void StdIOClientInterface::readOutput() { - const QByteArray &out = m_process.readAllStandardOutput(); + QTC_ASSERT(m_process, return); + const QByteArray &out = m_process->readAllStandardOutput(); qCDebug(LOGLSPCLIENTV) << "StdIOClient std out:\n"; qCDebug(LOGLSPCLIENTV).noquote() << out; parseData(out); diff --git a/src/plugins/languageclient/languageclientinterface.h b/src/plugins/languageclient/languageclientinterface.h index db5f33ff65b..4cb21722801 100644 --- a/src/plugins/languageclient/languageclientinterface.h +++ b/src/plugins/languageclient/languageclientinterface.h @@ -46,7 +46,7 @@ public: ~BaseClientInterface() override; void sendContent(const LanguageServerProtocol::IContent &content); - virtual bool start() { return true; } + void start() { startImpl(); } void resetBuffer(); @@ -54,8 +54,10 @@ signals: void contentReceived(const LanguageServerProtocol::JsonRpcMessage message); void finished(); void error(const QString &message); + void started(); protected: + virtual void startImpl() { emit started(); } virtual void sendData(const QByteArray &data) = 0; void parseData(const QByteArray &data); virtual void parseCurrentMessage(); @@ -77,7 +79,7 @@ public: StdIOClientInterface &operator=(const StdIOClientInterface &) = delete; StdIOClientInterface &operator=(StdIOClientInterface &&) = delete; - bool start() override; + void startImpl() override; // These functions only have an effect if they are called before start void setCommandLine(const Utils::CommandLine &cmd); @@ -85,7 +87,9 @@ public: protected: void sendData(const QByteArray &data) final; - Utils::QtcProcess m_process; + Utils::CommandLine m_cmd; + Utils::FilePath m_workingDirectory; + Utils::QtcProcess *m_process = nullptr; private: void readError(); diff --git a/src/plugins/languageclient/languageclientplugin.cpp b/src/plugins/languageclient/languageclientplugin.cpp index 9271540b1c1..526e255e314 100644 --- a/src/plugins/languageclient/languageclientplugin.cpp +++ b/src/plugins/languageclient/languageclientplugin.cpp @@ -41,6 +41,7 @@ static LanguageClientPlugin *m_instance = nullptr; LanguageClientPlugin::LanguageClientPlugin() { m_instance = this; + qRegisterMetaType(); } LanguageClientPlugin::~LanguageClientPlugin() diff --git a/tests/auto/languageserverprotocol/tst_languageserverprotocol.cpp b/tests/auto/languageserverprotocol/tst_languageserverprotocol.cpp index a3c79033653..073e9ac05c2 100644 --- a/tests/auto/languageserverprotocol/tst_languageserverprotocol.cpp +++ b/tests/auto/languageserverprotocol/tst_languageserverprotocol.cpp @@ -34,7 +34,6 @@ using namespace LanguageServerProtocol; Q_DECLARE_METATYPE(QTextCodec *) Q_DECLARE_METATYPE(BaseMessage) -Q_DECLARE_METATYPE(JsonRpcMessage) Q_DECLARE_METATYPE(DocumentUri) Q_DECLARE_METATYPE(Range)