forked from qt-creator/qt-creator
LanguageClient: Move the interface out of the gui thread
Change-Id: Iec34f5a0ca3f7f8e2306d3c8a50c2155b5b96807 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Christian Stenger <christian.stenger@qt.io> Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
@@ -354,7 +354,7 @@ public:
|
|||||||
constexpr static const char methodName[] = "$/cancelRequest";
|
constexpr static const char methodName[] = "$/cancelRequest";
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageServerProtocol
|
||||||
|
|
||||||
template <typename Error>
|
template <typename Error>
|
||||||
inline QDebug operator<<(QDebug stream,
|
inline QDebug operator<<(QDebug stream,
|
||||||
@@ -363,3 +363,5 @@ inline QDebug operator<<(QDebug stream,
|
|||||||
stream.nospace() << error.toString();
|
stream.nospace() << error.toString();
|
||||||
return stream;
|
return stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(LanguageServerProtocol::JsonRpcMessage)
|
||||||
|
|||||||
@@ -62,7 +62,6 @@
|
|||||||
#include <utils/mimeutils.h>
|
#include <utils/mimeutils.h>
|
||||||
#include <utils/qtcprocess.h>
|
#include <utils/qtcprocess.h>
|
||||||
|
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
@@ -71,6 +70,7 @@
|
|||||||
#include <QTextBlock>
|
#include <QTextBlock>
|
||||||
#include <QTextCursor>
|
#include <QTextCursor>
|
||||||
#include <QTextDocument>
|
#include <QTextDocument>
|
||||||
|
#include <QThread>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
using namespace LanguageServerProtocol;
|
using namespace LanguageServerProtocol;
|
||||||
@@ -80,9 +80,56 @@ namespace LanguageClient {
|
|||||||
|
|
||||||
static Q_LOGGING_CATEGORY(LOGLSPCLIENT, "qtc.languageclient.client", QtWarningMsg);
|
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)
|
Client::Client(BaseClientInterface *clientInterface)
|
||||||
: m_id(Utils::Id::fromString(QUuid::createUuid().toString()))
|
: m_id(Utils::Id::fromString(QUuid::createUuid().toString()))
|
||||||
, m_clientInterface(clientInterface)
|
, m_clientInterface(new InterfaceController(clientInterface))
|
||||||
, m_documentSymbolCache(this)
|
, m_documentSymbolCache(this)
|
||||||
, m_hoverHandler(this)
|
, m_hoverHandler(this)
|
||||||
, m_symbolSupport(this)
|
, m_symbolSupport(this)
|
||||||
@@ -105,9 +152,12 @@ Client::Client(BaseClientInterface *clientInterface)
|
|||||||
this, &Client::projectClosed);
|
this, &Client::projectClosed);
|
||||||
|
|
||||||
QTC_ASSERT(clientInterface, return);
|
QTC_ASSERT(clientInterface, return);
|
||||||
connect(clientInterface, &BaseClientInterface::contentReceived, this, &Client::handleContent);
|
connect(m_clientInterface, &InterfaceController::contentReceived, this, &Client::handleContent);
|
||||||
connect(clientInterface, &BaseClientInterface::error, this, &Client::setError);
|
connect(m_clientInterface, &InterfaceController::error, this, &Client::setError);
|
||||||
connect(clientInterface, &BaseClientInterface::finished, this, &Client::finished);
|
connect(m_clientInterface, &InterfaceController::finished, this, &Client::finished);
|
||||||
|
connect(m_clientInterface, &InterfaceController::started, this, [this]() {
|
||||||
|
LanguageClientManager::clientStarted(this);
|
||||||
|
});
|
||||||
connect(Core::EditorManager::instance(),
|
connect(Core::EditorManager::instance(),
|
||||||
&Core::EditorManager::documentClosed,
|
&Core::EditorManager::documentClosed,
|
||||||
this,
|
this,
|
||||||
@@ -159,9 +209,10 @@ Client::~Client()
|
|||||||
m_documentHighlightsTimer.clear();
|
m_documentHighlightsTimer.clear();
|
||||||
updateEditorToolBar(m_openedDocument.keys());
|
updateEditorToolBar(m_openedDocument.keys());
|
||||||
// do not handle messages while shutting down
|
// do not handle messages while shutting down
|
||||||
disconnect(m_clientInterface.data(), &BaseClientInterface::contentReceived,
|
disconnect(m_clientInterface, &InterfaceController::contentReceived,
|
||||||
this, &Client::handleContent);
|
this, &Client::handleContent);
|
||||||
delete m_diagnosticManager;
|
delete m_diagnosticManager;
|
||||||
|
delete m_clientInterface;
|
||||||
}
|
}
|
||||||
|
|
||||||
static ClientCapabilities generateClientCapabilities()
|
static ClientCapabilities generateClientCapabilities()
|
||||||
@@ -1170,10 +1221,7 @@ bool Client::supportsDocumentSymbols(const TextEditor::TextDocument *doc) const
|
|||||||
void Client::start()
|
void Client::start()
|
||||||
{
|
{
|
||||||
LanguageClientManager::addClient(this);
|
LanguageClientManager::addClient(this);
|
||||||
if (m_clientInterface->start())
|
m_clientInterface->start();
|
||||||
LanguageClientManager::clientStarted(this);
|
|
||||||
else
|
|
||||||
LanguageClientManager::clientFinished(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::reset()
|
bool Client::reset()
|
||||||
@@ -1541,8 +1589,8 @@ void Client::sendContentNow(const IContent &content)
|
|||||||
LanguageClientManager::logJsonRpcMessage(LspLogMessage::ClientMessage,
|
LanguageClientManager::logJsonRpcMessage(LspLogMessage::ClientMessage,
|
||||||
name(),
|
name(),
|
||||||
static_cast<const JsonRpcMessage &>(content));
|
static_cast<const JsonRpcMessage &>(content));
|
||||||
|
m_clientInterface->sendContent(static_cast<const JsonRpcMessage &>(content));
|
||||||
}
|
}
|
||||||
m_clientInterface->sendContent(content);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::documentUpdatePostponed(const Utils::FilePath &fileName) const
|
bool Client::documentUpdatePostponed(const Utils::FilePath &fileName) const
|
||||||
@@ -1694,3 +1742,5 @@ QTextCursor Client::adjustedCursorForHighlighting(const QTextCursor &cursor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
|
||||||
|
#include <client.moc>
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ QT_END_NAMESPACE
|
|||||||
namespace LanguageClient {
|
namespace LanguageClient {
|
||||||
|
|
||||||
class BaseClientInterface;
|
class BaseClientInterface;
|
||||||
|
class InterfaceController;
|
||||||
|
|
||||||
class LANGUAGECLIENT_EXPORT Client : public QObject
|
class LANGUAGECLIENT_EXPORT Client : public QObject
|
||||||
{
|
{
|
||||||
@@ -300,7 +301,7 @@ private:
|
|||||||
QMap<TextEditor::TextDocument *, AssistProviders> m_resetAssistProvider;
|
QMap<TextEditor::TextDocument *, AssistProviders> m_resetAssistProvider;
|
||||||
QHash<TextEditor::TextEditorWidget *, LanguageServerProtocol::MessageId> m_highlightRequests;
|
QHash<TextEditor::TextEditorWidget *, LanguageServerProtocol::MessageId> m_highlightRequests;
|
||||||
int m_restartsLeft = 5;
|
int m_restartsLeft = 5;
|
||||||
QScopedPointer<BaseClientInterface> m_clientInterface;
|
InterfaceController *m_clientInterface = nullptr;
|
||||||
DiagnosticManager *m_diagnosticManager = nullptr;
|
DiagnosticManager *m_diagnosticManager = nullptr;
|
||||||
DocumentSymbolCache m_documentSymbolCache;
|
DocumentSymbolCache m_documentSymbolCache;
|
||||||
HoverHandler m_hoverHandler;
|
HoverHandler m_hoverHandler;
|
||||||
|
|||||||
@@ -99,72 +99,72 @@ void BaseClientInterface::parseCurrentMessage()
|
|||||||
m_currentMessage = BaseMessage();
|
m_currentMessage = BaseMessage();
|
||||||
}
|
}
|
||||||
|
|
||||||
StdIOClientInterface::StdIOClientInterface()
|
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();
|
m_process = new Utils::QtcProcess;
|
||||||
if (!m_process.waitForStarted() || m_process.state() != QProcess::Running) {
|
m_process->setProcessMode(ProcessMode::Writer);
|
||||||
emit error(m_process.errorString());
|
connect(m_process, &QtcProcess::readyReadStandardError,
|
||||||
return false;
|
this, &StdIOClientInterface::readError);
|
||||||
}
|
connect(m_process, &QtcProcess::readyReadStandardOutput,
|
||||||
return true;
|
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)
|
void StdIOClientInterface::setCommandLine(const CommandLine &cmd)
|
||||||
{
|
{
|
||||||
m_process.setCommand(cmd);
|
m_cmd = cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdIOClientInterface::setWorkingDirectory(const FilePath &workingDirectory)
|
void StdIOClientInterface::setWorkingDirectory(const FilePath &workingDirectory)
|
||||||
{
|
{
|
||||||
m_process.setWorkingDirectory(workingDirectory);
|
m_workingDirectory = workingDirectory;
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdIOClientInterface::sendData(const QByteArray &data)
|
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")
|
emit error(tr("Cannot send data to unstarted server %1")
|
||||||
.arg(m_process.commandLine().toUserOutput()));
|
.arg(m_cmd.toUserOutput()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
qCDebug(LOGLSPCLIENTV) << "StdIOClient send data:";
|
qCDebug(LOGLSPCLIENTV) << "StdIOClient send data:";
|
||||||
qCDebug(LOGLSPCLIENTV).noquote() << data;
|
qCDebug(LOGLSPCLIENTV).noquote() << data;
|
||||||
m_process.writeRaw(data);
|
m_process->writeRaw(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdIOClientInterface::onProcessFinished()
|
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")
|
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();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdIOClientInterface::readError()
|
void StdIOClientInterface::readError()
|
||||||
{
|
{
|
||||||
|
QTC_ASSERT(m_process, return);
|
||||||
qCDebug(LOGLSPCLIENTV) << "StdIOClient std err:\n";
|
qCDebug(LOGLSPCLIENTV) << "StdIOClient std err:\n";
|
||||||
qCDebug(LOGLSPCLIENTV).noquote() << m_process.readAllStandardError();
|
qCDebug(LOGLSPCLIENTV).noquote() << m_process->readAllStandardError();
|
||||||
}
|
}
|
||||||
|
|
||||||
void StdIOClientInterface::readOutput()
|
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) << "StdIOClient std out:\n";
|
||||||
qCDebug(LOGLSPCLIENTV).noquote() << out;
|
qCDebug(LOGLSPCLIENTV).noquote() << out;
|
||||||
parseData(out);
|
parseData(out);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ public:
|
|||||||
~BaseClientInterface() override;
|
~BaseClientInterface() override;
|
||||||
|
|
||||||
void sendContent(const LanguageServerProtocol::IContent &content);
|
void sendContent(const LanguageServerProtocol::IContent &content);
|
||||||
virtual bool start() { return true; }
|
void start() { startImpl(); }
|
||||||
|
|
||||||
void resetBuffer();
|
void resetBuffer();
|
||||||
|
|
||||||
@@ -54,8 +54,10 @@ signals:
|
|||||||
void contentReceived(const LanguageServerProtocol::JsonRpcMessage message);
|
void contentReceived(const LanguageServerProtocol::JsonRpcMessage message);
|
||||||
void finished();
|
void finished();
|
||||||
void error(const QString &message);
|
void error(const QString &message);
|
||||||
|
void started();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
virtual void startImpl() { emit started(); }
|
||||||
virtual void sendData(const QByteArray &data) = 0;
|
virtual void sendData(const QByteArray &data) = 0;
|
||||||
void parseData(const QByteArray &data);
|
void parseData(const QByteArray &data);
|
||||||
virtual void parseCurrentMessage();
|
virtual void parseCurrentMessage();
|
||||||
@@ -77,7 +79,7 @@ public:
|
|||||||
StdIOClientInterface &operator=(const StdIOClientInterface &) = delete;
|
StdIOClientInterface &operator=(const StdIOClientInterface &) = delete;
|
||||||
StdIOClientInterface &operator=(StdIOClientInterface &&) = delete;
|
StdIOClientInterface &operator=(StdIOClientInterface &&) = delete;
|
||||||
|
|
||||||
bool start() override;
|
void startImpl() override;
|
||||||
|
|
||||||
// These functions only have an effect if they are called before start
|
// These functions only have an effect if they are called before start
|
||||||
void setCommandLine(const Utils::CommandLine &cmd);
|
void setCommandLine(const Utils::CommandLine &cmd);
|
||||||
@@ -85,7 +87,9 @@ public:
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
void sendData(const QByteArray &data) final;
|
void sendData(const QByteArray &data) final;
|
||||||
Utils::QtcProcess m_process;
|
Utils::CommandLine m_cmd;
|
||||||
|
Utils::FilePath m_workingDirectory;
|
||||||
|
Utils::QtcProcess *m_process = nullptr;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void readError();
|
void readError();
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ static LanguageClientPlugin *m_instance = nullptr;
|
|||||||
LanguageClientPlugin::LanguageClientPlugin()
|
LanguageClientPlugin::LanguageClientPlugin()
|
||||||
{
|
{
|
||||||
m_instance = this;
|
m_instance = this;
|
||||||
|
qRegisterMetaType<LanguageServerProtocol::JsonRpcMessage>();
|
||||||
}
|
}
|
||||||
|
|
||||||
LanguageClientPlugin::~LanguageClientPlugin()
|
LanguageClientPlugin::~LanguageClientPlugin()
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ using namespace LanguageServerProtocol;
|
|||||||
|
|
||||||
Q_DECLARE_METATYPE(QTextCodec *)
|
Q_DECLARE_METATYPE(QTextCodec *)
|
||||||
Q_DECLARE_METATYPE(BaseMessage)
|
Q_DECLARE_METATYPE(BaseMessage)
|
||||||
Q_DECLARE_METATYPE(JsonRpcMessage)
|
|
||||||
Q_DECLARE_METATYPE(DocumentUri)
|
Q_DECLARE_METATYPE(DocumentUri)
|
||||||
Q_DECLARE_METATYPE(Range)
|
Q_DECLARE_METATYPE(Range)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user