forked from qt-creator/qt-creator
LanguageClient: Move the BaseMessage to JsonRpcMessage conversion
... to the client interface. JsonRpcMessages are the only messages used so far and no other types of messages are currently used by any of the supported Language Servers. If a client is going to need special message parsing it can still implement a specialized client interface and overwrite parseCurrentMessage. This is the preparation to move receiving and parsing data passed to and from the language server out of the GUI thread. Change-Id: Ibd4cd95daab7efff947273ca9e7d457de0286f47 Reviewed-by: <github-actions-qt-creator@cristianadam.eu> Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
@@ -38,12 +38,12 @@ namespace LanguageServerProtocol {
|
|||||||
Q_LOGGING_CATEGORY(parseLog, "qtc.languageserverprotocol.parse", QtWarningMsg)
|
Q_LOGGING_CATEGORY(parseLog, "qtc.languageserverprotocol.parse", QtWarningMsg)
|
||||||
|
|
||||||
BaseMessage::BaseMessage()
|
BaseMessage::BaseMessage()
|
||||||
: mimeType(JsonRpcMessageHandler::jsonRpcMimeType())
|
: mimeType(JsonRpcMessage::jsonRpcMimeType())
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
BaseMessage::BaseMessage(const QByteArray &mimeType, const QByteArray &content,
|
BaseMessage::BaseMessage(const QByteArray &mimeType, const QByteArray &content,
|
||||||
int expectedLength, QTextCodec *codec)
|
int expectedLength, QTextCodec *codec)
|
||||||
: mimeType(mimeType.isEmpty() ? JsonRpcMessageHandler::jsonRpcMimeType() : mimeType)
|
: mimeType(mimeType.isEmpty() ? JsonRpcMessage::jsonRpcMimeType() : mimeType)
|
||||||
, content(content)
|
, content(content)
|
||||||
, contentLength(expectedLength)
|
, contentLength(expectedLength)
|
||||||
, codec(codec)
|
, codec(codec)
|
||||||
@@ -177,7 +177,7 @@ QByteArray BaseMessage::header() const
|
|||||||
QByteArray header;
|
QByteArray header;
|
||||||
header.append(lengthHeader());
|
header.append(lengthHeader());
|
||||||
if (codec != defaultCodec()
|
if (codec != defaultCodec()
|
||||||
|| (!mimeType.isEmpty() && mimeType != JsonRpcMessageHandler::jsonRpcMimeType())) {
|
|| (!mimeType.isEmpty() && mimeType != JsonRpcMessage::jsonRpcMimeType())) {
|
||||||
header.append(typeHeader());
|
header.append(typeHeader());
|
||||||
}
|
}
|
||||||
header.append(headerSeparator);
|
header.append(headerSeparator);
|
||||||
|
@@ -47,11 +47,16 @@ QByteArray JsonRpcMessage::toRawData() const
|
|||||||
|
|
||||||
QByteArray JsonRpcMessage::mimeType() const
|
QByteArray JsonRpcMessage::mimeType() const
|
||||||
{
|
{
|
||||||
return JsonRpcMessageHandler::jsonRpcMimeType();
|
return jsonRpcMimeType();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool JsonRpcMessage::isValid(QString * /*errorMessage*/) const
|
bool JsonRpcMessage::isValid(QString *errorMessage) const
|
||||||
{
|
{
|
||||||
|
if (!m_parseError.isEmpty()) {
|
||||||
|
if (errorMessage)
|
||||||
|
*errorMessage = m_parseError;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
return m_jsonObject[jsonRpcVersionKey] == "2.0";
|
return m_jsonObject[jsonRpcVersionKey] == "2.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,38 +71,6 @@ JsonRpcMessage::JsonRpcMessage()
|
|||||||
m_jsonObject[jsonRpcVersionKey] = "2.0";
|
m_jsonObject[jsonRpcVersionKey] = "2.0";
|
||||||
}
|
}
|
||||||
|
|
||||||
JsonRpcMessage::JsonRpcMessage(const QJsonObject &jsonObject)
|
|
||||||
: m_jsonObject(jsonObject)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
JsonRpcMessage::JsonRpcMessage(QJsonObject &&jsonObject)
|
|
||||||
: m_jsonObject(std::move(jsonObject))
|
|
||||||
{ }
|
|
||||||
|
|
||||||
QByteArray JsonRpcMessageHandler::jsonRpcMimeType()
|
|
||||||
{
|
|
||||||
return "application/vscode-jsonrpc";
|
|
||||||
}
|
|
||||||
|
|
||||||
void JsonRpcMessageHandler::parseContent(const QByteArray &content,
|
|
||||||
QTextCodec *codec,
|
|
||||||
QString &parseError,
|
|
||||||
const ResponseHandlers &responseHandlers,
|
|
||||||
const MethodHandler &methodHandler)
|
|
||||||
{
|
|
||||||
const QJsonObject &jsonObject = toJsonObject(content, codec, parseError);
|
|
||||||
if (jsonObject.isEmpty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
const MessageId id(jsonObject.value(idKey));
|
|
||||||
const QString &method = jsonObject.value(methodKey).toString();
|
|
||||||
const JsonRpcMessage jsonContent(jsonObject);
|
|
||||||
if (method.isEmpty())
|
|
||||||
responseHandlers(id, jsonContent);
|
|
||||||
else
|
|
||||||
methodHandler(method, id, jsonContent);
|
|
||||||
}
|
|
||||||
|
|
||||||
constexpr int utf8mib = 106;
|
constexpr int utf8mib = 106;
|
||||||
|
|
||||||
static QString docTypeName(const QJsonDocument &doc)
|
static QString docTypeName(const QJsonDocument &doc)
|
||||||
@@ -113,29 +86,43 @@ static QString docTypeName(const QJsonDocument &doc)
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject JsonRpcMessageHandler::toJsonObject(const QByteArray &_content,
|
JsonRpcMessage::JsonRpcMessage(const BaseMessage &message)
|
||||||
QTextCodec *codec,
|
|
||||||
QString &parseError)
|
|
||||||
{
|
{
|
||||||
if (_content.isEmpty())
|
if (message.content.isEmpty())
|
||||||
return QJsonObject();
|
return;
|
||||||
QByteArray content;
|
QByteArray content;
|
||||||
if (codec && codec->mibEnum() != utf8mib) {
|
if (message.codec && message.codec->mibEnum() != utf8mib) {
|
||||||
QTextCodec *utf8 = QTextCodec::codecForMib(utf8mib);
|
QTextCodec *utf8 = QTextCodec::codecForMib(utf8mib);
|
||||||
if (utf8)
|
if (utf8)
|
||||||
content = utf8->fromUnicode(codec->toUnicode(_content));
|
content = utf8->fromUnicode(message.codec->toUnicode(message.content));
|
||||||
}
|
}
|
||||||
if (content.isEmpty())
|
if (content.isEmpty())
|
||||||
content = _content;
|
content = message.content;
|
||||||
QJsonParseError error = {0, QJsonParseError::NoError};
|
QJsonParseError error = {0, QJsonParseError::NoError};
|
||||||
const QJsonDocument doc = QJsonDocument::fromJson(content, &error);
|
const QJsonDocument doc = QJsonDocument::fromJson(content, &error);
|
||||||
if (doc.isObject())
|
if (doc.isObject()) {
|
||||||
return doc.object();
|
m_jsonObject = doc.object();
|
||||||
if (doc.isNull())
|
} else if (doc.isNull()) {
|
||||||
parseError = tr("Could not parse JSON message \"%1\".").arg(error.errorString());
|
m_parseError = tr("LanguageServerProtocol::JsonRpcMessage",
|
||||||
else
|
"Could not parse JSON message \"%1\".")
|
||||||
parseError = tr("Expected a JSON object, but got a JSON \"%1\" value.").arg(docTypeName(doc));
|
.arg(error.errorString());
|
||||||
return QJsonObject();
|
} else {
|
||||||
|
m_parseError = tr("Expected a JSON object, but got a JSON \"%1\" value.")
|
||||||
|
.arg(docTypeName(doc));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonRpcMessage::JsonRpcMessage(const QJsonObject &jsonObject)
|
||||||
|
: m_jsonObject(jsonObject)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
JsonRpcMessage::JsonRpcMessage(QJsonObject &&jsonObject)
|
||||||
|
: m_jsonObject(std::move(jsonObject))
|
||||||
|
{ }
|
||||||
|
|
||||||
|
QByteArray JsonRpcMessage::jsonRpcMimeType()
|
||||||
|
{
|
||||||
|
return "application/vscode-jsonrpc";
|
||||||
}
|
}
|
||||||
|
|
||||||
CancelRequest::CancelRequest(const CancelParameter ¶ms)
|
CancelRequest::CancelRequest(const CancelParameter ¶ms)
|
||||||
|
@@ -45,16 +45,23 @@ namespace LanguageServerProtocol {
|
|||||||
|
|
||||||
class LANGUAGESERVERPROTOCOL_EXPORT JsonRpcMessage : public IContent
|
class LANGUAGESERVERPROTOCOL_EXPORT JsonRpcMessage : public IContent
|
||||||
{
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(JsonRpcMessage)
|
||||||
public:
|
public:
|
||||||
JsonRpcMessage();
|
JsonRpcMessage();
|
||||||
|
explicit JsonRpcMessage(const BaseMessage &message);
|
||||||
explicit JsonRpcMessage(const QJsonObject &jsonObject);
|
explicit JsonRpcMessage(const QJsonObject &jsonObject);
|
||||||
explicit JsonRpcMessage(QJsonObject &&jsonObject);
|
explicit JsonRpcMessage(QJsonObject &&jsonObject);
|
||||||
|
|
||||||
|
static QByteArray jsonRpcMimeType();
|
||||||
|
|
||||||
QByteArray toRawData() const final;
|
QByteArray toRawData() const final;
|
||||||
QByteArray mimeType() const final;
|
QByteArray mimeType() const final;
|
||||||
bool isValid(QString *errorMessage) const override;
|
bool isValid(QString *errorMessage) const override;
|
||||||
|
|
||||||
const QJsonObject &toJsonObject() const;
|
const QJsonObject &toJsonObject() const;
|
||||||
|
|
||||||
|
const QString parseError() { return m_parseError; }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
QJsonObject m_jsonObject;
|
QJsonObject m_jsonObject;
|
||||||
|
|
||||||
@@ -62,18 +69,6 @@ private:
|
|||||||
QString m_parseError;
|
QString m_parseError;
|
||||||
};
|
};
|
||||||
|
|
||||||
class LANGUAGESERVERPROTOCOL_EXPORT JsonRpcMessageHandler
|
|
||||||
{
|
|
||||||
Q_DECLARE_TR_FUNCTIONS(JsonRpcMessageHandler)
|
|
||||||
|
|
||||||
public:
|
|
||||||
static QByteArray jsonRpcMimeType();
|
|
||||||
static void parseContent(const QByteArray &content, QTextCodec *codec, QString &errorMessage,
|
|
||||||
const ResponseHandlers &responseHandlers,
|
|
||||||
const MethodHandler &methodHandler);
|
|
||||||
static QJsonObject toJsonObject(const QByteArray &content, QTextCodec *codec, QString &parseError);
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename Params>
|
template <typename Params>
|
||||||
class Notification : public JsonRpcMessage
|
class Notification : public JsonRpcMessage
|
||||||
{
|
{
|
||||||
|
@@ -104,10 +104,8 @@ Client::Client(BaseClientInterface *clientInterface)
|
|||||||
connect(SessionManager::instance(), &SessionManager::projectRemoved,
|
connect(SessionManager::instance(), &SessionManager::projectRemoved,
|
||||||
this, &Client::projectClosed);
|
this, &Client::projectClosed);
|
||||||
|
|
||||||
m_contentHandler.insert(JsonRpcMessageHandler::jsonRpcMimeType(),
|
|
||||||
&JsonRpcMessageHandler::parseContent);
|
|
||||||
QTC_ASSERT(clientInterface, return);
|
QTC_ASSERT(clientInterface, return);
|
||||||
connect(clientInterface, &BaseClientInterface::messageReceived, this, &Client::handleMessage);
|
connect(clientInterface, &BaseClientInterface::contentReceived, this, &Client::handleContent);
|
||||||
connect(clientInterface, &BaseClientInterface::error, this, &Client::setError);
|
connect(clientInterface, &BaseClientInterface::error, this, &Client::setError);
|
||||||
connect(clientInterface, &BaseClientInterface::finished, this, &Client::finished);
|
connect(clientInterface, &BaseClientInterface::finished, this, &Client::finished);
|
||||||
connect(Core::EditorManager::instance(),
|
connect(Core::EditorManager::instance(),
|
||||||
@@ -161,8 +159,8 @@ 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::messageReceived,
|
disconnect(m_clientInterface.data(), &BaseClientInterface::contentReceived,
|
||||||
this, &Client::handleMessage);
|
this, &Client::handleContent);
|
||||||
delete m_diagnosticManager;
|
delete m_diagnosticManager;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -327,8 +325,8 @@ void Client::initialize()
|
|||||||
if (Utils::optional<ResponseHandler> responseHandler = initRequest.responseHandler())
|
if (Utils::optional<ResponseHandler> responseHandler = initRequest.responseHandler())
|
||||||
m_responseHandlers[responseHandler->id] = responseHandler->callback;
|
m_responseHandlers[responseHandler->id] = responseHandler->callback;
|
||||||
|
|
||||||
// directly send message otherwise the state check of sendContent would fail
|
// directly send content now otherwise the state check of sendContent would fail
|
||||||
sendMessage(initRequest.toBaseMessage());
|
sendContentNow(initRequest);
|
||||||
m_state = InitializeRequested;
|
m_state = InitializeRequested;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -444,7 +442,7 @@ void Client::sendContent(const IContent &content, SendDocUpdates sendUpdates)
|
|||||||
QString error;
|
QString error;
|
||||||
if (!QTC_GUARD(content.isValid(&error)))
|
if (!QTC_GUARD(content.isValid(&error)))
|
||||||
Core::MessageManager::writeFlashing(error);
|
Core::MessageManager::writeFlashing(error);
|
||||||
sendMessage(content.toBaseMessage());
|
sendContentNow(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::cancelRequest(const MessageId &id)
|
void Client::cancelRequest(const MessageId &id)
|
||||||
@@ -1218,23 +1216,15 @@ void Client::setProgressTitleForToken(const LanguageServerProtocol::ProgressToke
|
|||||||
m_progressManager.setTitleForToken(token, message);
|
m_progressManager.setTitleForToken(token, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::handleMessage(const BaseMessage &message)
|
void Client::handleContent(const LanguageServerProtocol::JsonRpcMessage &message)
|
||||||
{
|
{
|
||||||
LanguageClientManager::logBaseMessage(LspLogMessage::ServerMessage, name(), message);
|
LanguageClientManager::logJsonRpcMessage(LspLogMessage::ServerMessage, name(), message);
|
||||||
if (auto handler = m_contentHandler[message.mimeType]) {
|
const MessageId id(message.toJsonObject().value(idKey));
|
||||||
QString parseError;
|
const QString method = message.toJsonObject().value(methodKey).toString();
|
||||||
handler(message.content, message.codec, parseError,
|
if (method.isEmpty())
|
||||||
[this](const MessageId &id, const IContent &content){
|
handleResponse(id, message);
|
||||||
this->handleResponse(id, content);
|
else
|
||||||
},
|
handleMethod(method, id, message);
|
||||||
[this](const QString &method, const 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 Client::log(const QString &message) const
|
void Client::log(const QString &message) const
|
||||||
@@ -1544,10 +1534,14 @@ void Client::handleDiagnostics(const PublishDiagnosticsParams ¶ms)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::sendMessage(const BaseMessage &message)
|
void Client::sendContentNow(const IContent &content)
|
||||||
{
|
{
|
||||||
LanguageClientManager::logBaseMessage(LspLogMessage::ClientMessage, name(), message);
|
if (content.mimeType() == JsonRpcMessage::jsonRpcMimeType()) {
|
||||||
m_clientInterface->sendMessage(message);
|
LanguageClientManager::logJsonRpcMessage(LspLogMessage::ClientMessage,
|
||||||
|
name(),
|
||||||
|
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
|
||||||
@@ -1663,8 +1657,8 @@ void Client::shutDownCallback(const ShutdownRequest::Response &shutdownResponse)
|
|||||||
QTC_ASSERT(m_clientInterface, return);
|
QTC_ASSERT(m_clientInterface, return);
|
||||||
if (optional<ShutdownRequest::Response::Error> error = shutdownResponse.error())
|
if (optional<ShutdownRequest::Response::Error> error = shutdownResponse.error())
|
||||||
log(*error);
|
log(*error);
|
||||||
// directly send message otherwise the state check of sendContent would fail
|
// directly send content now otherwise the state check of sendContent would fail
|
||||||
sendMessage(ExitNotification().toBaseMessage());
|
sendContentNow(ExitNotification());
|
||||||
qCDebug(LOGLSPCLIENT) << "language server " << m_displayName << " shutdown";
|
qCDebug(LOGLSPCLIENT) << "language server " << m_displayName << " shutdown";
|
||||||
m_state = Shutdown;
|
m_state = Shutdown;
|
||||||
m_shutdownTimer.start();
|
m_shutdownTimer.start();
|
||||||
|
@@ -225,12 +225,12 @@ protected:
|
|||||||
void setError(const QString &message);
|
void setError(const QString &message);
|
||||||
void setProgressTitleForToken(const LanguageServerProtocol::ProgressToken &token,
|
void setProgressTitleForToken(const LanguageServerProtocol::ProgressToken &token,
|
||||||
const QString &message);
|
const QString &message);
|
||||||
void handleMessage(const LanguageServerProtocol::BaseMessage &message);
|
void handleContent(const LanguageServerProtocol::JsonRpcMessage &message);
|
||||||
virtual void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms);
|
virtual void handleDiagnostics(const LanguageServerProtocol::PublishDiagnosticsParams ¶ms);
|
||||||
virtual DiagnosticManager *createDiagnosticManager();
|
virtual DiagnosticManager *createDiagnosticManager();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void sendMessage(const LanguageServerProtocol::BaseMessage &message);
|
void sendContentNow(const LanguageServerProtocol::IContent &content);
|
||||||
void handleResponse(const LanguageServerProtocol::MessageId &id,
|
void handleResponse(const LanguageServerProtocol::MessageId &id,
|
||||||
const LanguageServerProtocol::IContent &content);
|
const LanguageServerProtocol::IContent &content);
|
||||||
void handleMethod(const QString &method,
|
void handleMethod(const QString &method,
|
||||||
@@ -274,7 +274,6 @@ private:
|
|||||||
State m_state = Uninitialized;
|
State m_state = Uninitialized;
|
||||||
QHash<LanguageServerProtocol::MessageId,
|
QHash<LanguageServerProtocol::MessageId,
|
||||||
LanguageServerProtocol::ResponseHandler::Callback> m_responseHandlers;
|
LanguageServerProtocol::ResponseHandler::Callback> m_responseHandlers;
|
||||||
QHash<QByteArray, ContentHandler> m_contentHandler;
|
|
||||||
QString m_displayName;
|
QString m_displayName;
|
||||||
LanguageFilter m_languagFilter;
|
LanguageFilter m_languagFilter;
|
||||||
QJsonObject m_initializationOptions;
|
QJsonObject m_initializationOptions;
|
||||||
|
@@ -46,8 +46,9 @@ BaseClientInterface::~BaseClientInterface()
|
|||||||
m_buffer.close();
|
m_buffer.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BaseClientInterface::sendMessage(const BaseMessage &message)
|
void BaseClientInterface::sendContent(const IContent &content)
|
||||||
{
|
{
|
||||||
|
const BaseMessage message = content.toBaseMessage();
|
||||||
sendData(message.header());
|
sendData(message.header());
|
||||||
sendData(message.content);
|
sendData(message.content);
|
||||||
}
|
}
|
||||||
@@ -78,8 +79,7 @@ void BaseClientInterface::parseData(const QByteArray &data)
|
|||||||
emit error(parseError);
|
emit error(parseError);
|
||||||
if (!m_currentMessage.isComplete())
|
if (!m_currentMessage.isComplete())
|
||||||
break;
|
break;
|
||||||
emit messageReceived(m_currentMessage);
|
parseCurrentMessage();
|
||||||
m_currentMessage = BaseMessage();
|
|
||||||
}
|
}
|
||||||
if (m_buffer.atEnd()) {
|
if (m_buffer.atEnd()) {
|
||||||
m_buffer.close();
|
m_buffer.close();
|
||||||
@@ -88,6 +88,17 @@ void BaseClientInterface::parseData(const QByteArray &data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BaseClientInterface::parseCurrentMessage()
|
||||||
|
{
|
||||||
|
if (m_currentMessage.mimeType == JsonRpcMessage::jsonRpcMimeType()) {
|
||||||
|
emit contentReceived(JsonRpcMessage(m_currentMessage));
|
||||||
|
} else {
|
||||||
|
emit error(tr("Cannot handle mimetype of message %1")
|
||||||
|
.arg(QString::fromUtf8(m_currentMessage.mimeType)));
|
||||||
|
}
|
||||||
|
m_currentMessage = BaseMessage();
|
||||||
|
}
|
||||||
|
|
||||||
StdIOClientInterface::StdIOClientInterface()
|
StdIOClientInterface::StdIOClientInterface()
|
||||||
{
|
{
|
||||||
m_process.setProcessMode(ProcessMode::Writer);
|
m_process.setProcessMode(ProcessMode::Writer);
|
||||||
|
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
#include "languageclient_global.h"
|
#include "languageclient_global.h"
|
||||||
|
|
||||||
#include <languageserverprotocol/basemessage.h>
|
#include <languageserverprotocol/jsonrpcmessages.h>
|
||||||
|
|
||||||
#include <utils/qtcprocess.h>
|
#include <utils/qtcprocess.h>
|
||||||
|
|
||||||
@@ -45,19 +45,20 @@ public:
|
|||||||
|
|
||||||
~BaseClientInterface() override;
|
~BaseClientInterface() override;
|
||||||
|
|
||||||
void sendMessage(const LanguageServerProtocol::BaseMessage &message);
|
void sendContent(const LanguageServerProtocol::IContent &content);
|
||||||
virtual bool start() { return true; }
|
virtual bool start() { return true; }
|
||||||
|
|
||||||
void resetBuffer();
|
void resetBuffer();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void messageReceived(LanguageServerProtocol::BaseMessage message);
|
void contentReceived(const LanguageServerProtocol::JsonRpcMessage message);
|
||||||
void finished();
|
void finished();
|
||||||
void error(const QString &message);
|
void error(const QString &message);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
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();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QBuffer m_buffer;
|
QBuffer m_buffer;
|
||||||
|
@@ -424,9 +424,9 @@ void LanguageClientManager::openDocumentWithClient(TextEditor::TextDocument *doc
|
|||||||
TextEditor::IOutlineWidgetFactory::updateOutline();
|
TextEditor::IOutlineWidgetFactory::updateOutline();
|
||||||
}
|
}
|
||||||
|
|
||||||
void LanguageClientManager::logBaseMessage(const LspLogMessage::MessageSender sender,
|
void LanguageClientManager::logJsonRpcMessage(const LspLogMessage::MessageSender sender,
|
||||||
const QString &clientName,
|
const QString &clientName,
|
||||||
const BaseMessage &message)
|
const LanguageServerProtocol::JsonRpcMessage &message)
|
||||||
{
|
{
|
||||||
instance()->m_inspector.log(sender, clientName, message);
|
instance()->m_inspector.log(sender, clientName, message);
|
||||||
}
|
}
|
||||||
|
@@ -96,9 +96,10 @@ public:
|
|||||||
///
|
///
|
||||||
static void openDocumentWithClient(TextEditor::TextDocument *document, Client *client);
|
static void openDocumentWithClient(TextEditor::TextDocument *document, Client *client);
|
||||||
|
|
||||||
static void logBaseMessage(const LspLogMessage::MessageSender sender,
|
static void logJsonRpcMessage(const LspLogMessage::MessageSender sender,
|
||||||
const QString &clientName,
|
const QString &clientName,
|
||||||
const LanguageServerProtocol::BaseMessage &message);
|
const LanguageServerProtocol::JsonRpcMessage &message);
|
||||||
|
|
||||||
static void showInspector();
|
static void showInspector();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
@@ -120,8 +120,7 @@ public:
|
|||||||
void clear();
|
void clear();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QLabel *m_contentLength = nullptr;
|
QTreeView *m_jsonTree = nullptr;
|
||||||
QLabel *m_mimeType = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class LspCapabilitiesWidget : public QWidget
|
class LspCapabilitiesWidget : public QWidget
|
||||||
@@ -258,8 +257,11 @@ LspLogWidget::LspLogWidget()
|
|||||||
void LspLogWidget::currentMessageChanged(const QModelIndex &index)
|
void LspLogWidget::currentMessageChanged(const QModelIndex &index)
|
||||||
{
|
{
|
||||||
m_messages->clearSelection();
|
m_messages->clearSelection();
|
||||||
if (!index.isValid())
|
if (!index.isValid()) {
|
||||||
|
m_clientDetails->clear();
|
||||||
|
m_serverDetails->clear();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
LspLogMessage message = m_model.itemAt(index.row())->itemData;
|
LspLogMessage message = m_model.itemAt(index.row())->itemData;
|
||||||
if (message.sender == LspLogMessage::ClientMessage)
|
if (message.sender == LspLogMessage::ClientMessage)
|
||||||
m_clientDetails->setMessage(message);
|
m_clientDetails->setMessage(message);
|
||||||
@@ -274,8 +276,6 @@ static bool matches(LspLogMessage::MessageSender sender,
|
|||||||
{
|
{
|
||||||
if (message.sender != sender)
|
if (message.sender != sender)
|
||||||
return false;
|
return false;
|
||||||
if (message.message.mimeType != JsonRpcMessageHandler::jsonRpcMimeType())
|
|
||||||
return false;
|
|
||||||
return message.id() == id;
|
return message.id() == id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -322,7 +322,7 @@ void LspLogWidget::saveLog()
|
|||||||
stream << (message.sender == LspLogMessage::ClientMessage ? QString{"Client"}
|
stream << (message.sender == LspLogMessage::ClientMessage ? QString{"Client"}
|
||||||
: QString{"Server"});
|
: QString{"Server"});
|
||||||
stream << '\n';
|
stream << '\n';
|
||||||
stream << message.message.codec->toUnicode(message.message.content);
|
stream << QJsonDocument(message.message.toJsonObject()).toJson();
|
||||||
stream << "\n\n";
|
stream << "\n\n";
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -365,7 +365,7 @@ QWidget *LspInspector::createWidget(const QString &defaultClient)
|
|||||||
|
|
||||||
void LspInspector::log(const LspLogMessage::MessageSender sender,
|
void LspInspector::log(const LspLogMessage::MessageSender sender,
|
||||||
const QString &clientName,
|
const QString &clientName,
|
||||||
const BaseMessage &message)
|
const JsonRpcMessage &message)
|
||||||
{
|
{
|
||||||
std::list<LspLogMessage> &clientLog = m_logs[clientName];
|
std::list<LspLogMessage> &clientLog = m_logs[clientName];
|
||||||
while (clientLog.size() >= static_cast<std::size_t>(m_logSize))
|
while (clientLog.size() >= static_cast<std::size_t>(m_logSize))
|
||||||
@@ -510,50 +510,27 @@ LspCapabilitiesWidget *LspInspectorWidget::capabilities() const
|
|||||||
|
|
||||||
MessageDetailWidget::MessageDetailWidget()
|
MessageDetailWidget::MessageDetailWidget()
|
||||||
{
|
{
|
||||||
auto layout = new QFormLayout;
|
auto layout = new QVBoxLayout;
|
||||||
setLayout(layout);
|
setLayout(layout);
|
||||||
|
|
||||||
m_contentLength = new QLabel;
|
m_jsonTree = new QTreeView;
|
||||||
m_mimeType = new QLabel;
|
|
||||||
|
|
||||||
layout->addRow("Content Length:", m_contentLength);
|
layout->addWidget(m_jsonTree);
|
||||||
layout->addRow("MIME Type:", m_mimeType);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageDetailWidget::setMessage(const LspLogMessage &message)
|
void MessageDetailWidget::setMessage(const LspLogMessage &message)
|
||||||
{
|
{
|
||||||
m_contentLength->setText(QString::number(message.message.contentLength));
|
m_jsonTree->setModel(createJsonModel("content", message.message.toJsonObject()));
|
||||||
m_mimeType->setText(QString::fromLatin1(message.message.mimeType));
|
|
||||||
|
|
||||||
QWidget *newContentWidget = nullptr;
|
|
||||||
if (message.message.mimeType == JsonRpcMessageHandler::jsonRpcMimeType()) {
|
|
||||||
newContentWidget = createJsonTreeView("content", message.json());
|
|
||||||
} else {
|
|
||||||
auto edit = new QPlainTextEdit();
|
|
||||||
edit->setReadOnly(true);
|
|
||||||
edit->setPlainText(message.message.codec->toUnicode(message.message.content));
|
|
||||||
newContentWidget = edit;
|
|
||||||
}
|
|
||||||
auto formLayout = static_cast<QFormLayout *>(layout());
|
|
||||||
if (formLayout->rowCount() > 2)
|
|
||||||
formLayout->removeRow(2);
|
|
||||||
formLayout->setWidget(2, QFormLayout::SpanningRole, newContentWidget);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MessageDetailWidget::clear()
|
void MessageDetailWidget::clear()
|
||||||
{
|
{
|
||||||
m_contentLength->setText({});
|
m_jsonTree->setModel(createJsonModel("", QJsonObject()));
|
||||||
m_mimeType->setText({});
|
|
||||||
auto formLayout = static_cast<QFormLayout *>(layout());
|
|
||||||
if (formLayout->rowCount() > 2)
|
|
||||||
formLayout->removeRow(2);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
LspLogMessage::LspLogMessage() = default;
|
LspLogMessage::LspLogMessage() = default;
|
||||||
|
|
||||||
LspLogMessage::LspLogMessage(MessageSender sender,
|
LspLogMessage::LspLogMessage(MessageSender sender, const QTime &time, const JsonRpcMessage &message)
|
||||||
const QTime &time,
|
|
||||||
const LanguageServerProtocol::BaseMessage &message)
|
|
||||||
: sender(sender)
|
: sender(sender)
|
||||||
, time(time)
|
, time(time)
|
||||||
, message(message)
|
, message(message)
|
||||||
@@ -562,7 +539,7 @@ LspLogMessage::LspLogMessage(MessageSender sender,
|
|||||||
MessageId LspLogMessage::id() const
|
MessageId LspLogMessage::id() const
|
||||||
{
|
{
|
||||||
if (!m_id.has_value())
|
if (!m_id.has_value())
|
||||||
m_id = MessageId(json().value(idKey));
|
m_id = MessageId(message.toJsonObject().value(idKey));
|
||||||
return *m_id;
|
return *m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -570,25 +547,10 @@ QString LspLogMessage::displayText() const
|
|||||||
{
|
{
|
||||||
if (!m_displayText.has_value()) {
|
if (!m_displayText.has_value()) {
|
||||||
m_displayText = QString(time.toString("hh:mm:ss.zzz") + '\n');
|
m_displayText = QString(time.toString("hh:mm:ss.zzz") + '\n');
|
||||||
if (message.mimeType == JsonRpcMessageHandler::jsonRpcMimeType())
|
m_displayText->append(
|
||||||
m_displayText->append(json().value(QString{methodKey}).toString(id().toString()));
|
message.toJsonObject().value(QString{methodKey}).toString(id().toString()));
|
||||||
else
|
|
||||||
m_displayText->append(message.codec->toUnicode(message.content));
|
|
||||||
}
|
}
|
||||||
return *m_displayText;
|
return *m_displayText;
|
||||||
}
|
}
|
||||||
|
|
||||||
QJsonObject &LspLogMessage::json() const
|
|
||||||
{
|
|
||||||
if (!m_json.has_value()) {
|
|
||||||
if (message.mimeType == JsonRpcMessageHandler::jsonRpcMimeType()) {
|
|
||||||
QString error;
|
|
||||||
m_json = JsonRpcMessageHandler::toJsonObject(message.content, message.codec, error);
|
|
||||||
} else {
|
|
||||||
m_json = QJsonObject();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return *m_json;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
@@ -45,18 +45,16 @@ public:
|
|||||||
LspLogMessage();
|
LspLogMessage();
|
||||||
LspLogMessage(MessageSender sender,
|
LspLogMessage(MessageSender sender,
|
||||||
const QTime &time,
|
const QTime &time,
|
||||||
const LanguageServerProtocol::BaseMessage &message);
|
const LanguageServerProtocol::JsonRpcMessage &message);
|
||||||
QTime time;
|
QTime time;
|
||||||
LanguageServerProtocol::BaseMessage message;
|
LanguageServerProtocol::JsonRpcMessage message;
|
||||||
|
|
||||||
LanguageServerProtocol::MessageId id() const;
|
LanguageServerProtocol::MessageId id() const;
|
||||||
QString displayText() const;
|
QString displayText() const;
|
||||||
QJsonObject &json() const;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
mutable Utils::optional<LanguageServerProtocol::MessageId> m_id;
|
mutable Utils::optional<LanguageServerProtocol::MessageId> m_id;
|
||||||
mutable Utils::optional<QString> m_displayText;
|
mutable Utils::optional<QString> m_displayText;
|
||||||
mutable Utils::optional<QJsonObject> m_json;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Capabilities
|
struct Capabilities
|
||||||
@@ -76,7 +74,7 @@ public:
|
|||||||
|
|
||||||
void log(const LspLogMessage::MessageSender sender,
|
void log(const LspLogMessage::MessageSender sender,
|
||||||
const QString &clientName,
|
const QString &clientName,
|
||||||
const LanguageServerProtocol::BaseMessage &message);
|
const LanguageServerProtocol::JsonRpcMessage &message);
|
||||||
void clientInitialized(const QString &clientName,
|
void clientInitialized(const QString &clientName,
|
||||||
const LanguageServerProtocol::ServerCapabilities &capabilities);
|
const LanguageServerProtocol::ServerCapabilities &capabilities);
|
||||||
void updateCapabilities(const QString &clientName,
|
void updateCapabilities(const QString &clientName,
|
||||||
|
@@ -72,7 +72,7 @@ private:
|
|||||||
|
|
||||||
void tst_LanguageServerProtocol::initTestCase()
|
void tst_LanguageServerProtocol::initTestCase()
|
||||||
{
|
{
|
||||||
defaultMimeType = JsonRpcMessageHandler::jsonRpcMimeType();
|
defaultMimeType = JsonRpcMessage::jsonRpcMimeType();
|
||||||
defaultCodec = QTextCodec::codecForName("utf-8");
|
defaultCodec = QTextCodec::codecForName("utf-8");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -447,13 +447,13 @@ void tst_LanguageServerProtocol::toJsonObject()
|
|||||||
QFETCH(bool, error);
|
QFETCH(bool, error);
|
||||||
QFETCH(QJsonObject, expected);
|
QFETCH(QJsonObject, expected);
|
||||||
|
|
||||||
QString parseError;
|
BaseMessage baseMessage(JsonRpcMessage::jsonRpcMimeType(), content, content.length(), codec);
|
||||||
const QJsonObject object = JsonRpcMessageHandler::toJsonObject(content, codec, parseError);
|
JsonRpcMessage jsonRpcMessage(baseMessage);
|
||||||
|
|
||||||
if (!error && !parseError.isEmpty())
|
if (!error && !jsonRpcMessage.parseError().isEmpty())
|
||||||
QFAIL(parseError.toLocal8Bit().data());
|
QFAIL(jsonRpcMessage.parseError().toLocal8Bit().data());
|
||||||
QCOMPARE(object, expected);
|
QCOMPARE(jsonRpcMessage.toJsonObject(), expected);
|
||||||
QCOMPARE(!parseError.isEmpty(), error);
|
QCOMPARE(!jsonRpcMessage.parseError().isEmpty(), error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void tst_LanguageServerProtocol::jsonMessageToBaseMessage_data()
|
void tst_LanguageServerProtocol::jsonMessageToBaseMessage_data()
|
||||||
@@ -462,11 +462,11 @@ void tst_LanguageServerProtocol::jsonMessageToBaseMessage_data()
|
|||||||
QTest::addColumn<BaseMessage>("baseMessage");
|
QTest::addColumn<BaseMessage>("baseMessage");
|
||||||
|
|
||||||
QTest::newRow("empty object") << JsonRpcMessage(QJsonObject())
|
QTest::newRow("empty object") << JsonRpcMessage(QJsonObject())
|
||||||
<< BaseMessage(JsonRpcMessageHandler::jsonRpcMimeType(),
|
<< BaseMessage(JsonRpcMessage::jsonRpcMimeType(),
|
||||||
"{}");
|
"{}");
|
||||||
|
|
||||||
QTest::newRow("key value pair") << JsonRpcMessage({{"key", "value"}})
|
QTest::newRow("key value pair") << JsonRpcMessage(QJsonObject{{"key", "value"}})
|
||||||
<< BaseMessage(JsonRpcMessageHandler::jsonRpcMimeType(),
|
<< BaseMessage(JsonRpcMessage::jsonRpcMimeType(),
|
||||||
R"({"key":"value"})");
|
R"({"key":"value"})");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user