forked from qt-creator/qt-creator
LanguageClient: Add logging window
Change-Id: Ib86b70381b69df55d62a97abdf52b747f7f1c75e Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -19,5 +19,6 @@ add_qtc_plugin(LanguageClient
|
|||||||
languageclientutils.cpp languageclientutils.h
|
languageclientutils.cpp languageclientutils.h
|
||||||
languageclient_global.h
|
languageclient_global.h
|
||||||
locatorfilter.cpp locatorfilter.h
|
locatorfilter.cpp locatorfilter.h
|
||||||
|
lsplogger.cpp lsplogger.h
|
||||||
semantichighlightsupport.cpp semantichighlightsupport.h
|
semantichighlightsupport.cpp semantichighlightsupport.h
|
||||||
)
|
)
|
||||||
|
@@ -262,6 +262,9 @@ void Client::initialize()
|
|||||||
});
|
});
|
||||||
// directly send data otherwise the state check would fail;
|
// directly send data otherwise the state check would fail;
|
||||||
initRequest.registerResponseHandler(&m_responseHandlers);
|
initRequest.registerResponseHandler(&m_responseHandlers);
|
||||||
|
LanguageClientManager::logBaseMessage(LspLogMessage::ClientMessage,
|
||||||
|
name(),
|
||||||
|
initRequest.toBaseMessage());
|
||||||
m_clientInterface->sendMessage(initRequest.toBaseMessage());
|
m_clientInterface->sendMessage(initRequest.toBaseMessage());
|
||||||
m_state = InitializeRequested;
|
m_state = InitializeRequested;
|
||||||
}
|
}
|
||||||
@@ -334,6 +337,9 @@ void Client::sendContent(const IContent &content)
|
|||||||
QString error;
|
QString error;
|
||||||
if (!QTC_GUARD(content.isValid(&error)))
|
if (!QTC_GUARD(content.isValid(&error)))
|
||||||
Core::MessageManager::write(error);
|
Core::MessageManager::write(error);
|
||||||
|
LanguageClientManager::logBaseMessage(LspLogMessage::ClientMessage,
|
||||||
|
name(),
|
||||||
|
content.toBaseMessage());
|
||||||
m_clientInterface->sendMessage(content.toBaseMessage());
|
m_clientInterface->sendMessage(content.toBaseMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -931,6 +937,7 @@ void Client::setError(const QString &message)
|
|||||||
|
|
||||||
void Client::handleMessage(const BaseMessage &message)
|
void Client::handleMessage(const BaseMessage &message)
|
||||||
{
|
{
|
||||||
|
LanguageClientManager::logBaseMessage(LspLogMessage::ServerMessage, name(), message);
|
||||||
if (auto handler = m_contentHandler[message.mimeType]) {
|
if (auto handler = m_contentHandler[message.mimeType]) {
|
||||||
QString parseError;
|
QString parseError;
|
||||||
handler(message.content, message.codec, parseError,
|
handler(message.content, message.codec, parseError,
|
||||||
|
@@ -19,6 +19,7 @@ HEADERS += \
|
|||||||
languageclientsettings.h \
|
languageclientsettings.h \
|
||||||
languageclientutils.h \
|
languageclientutils.h \
|
||||||
locatorfilter.h \
|
locatorfilter.h \
|
||||||
|
lsplogger.h \
|
||||||
semantichighlightsupport.h
|
semantichighlightsupport.h
|
||||||
|
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ SOURCES += \
|
|||||||
languageclientsettings.cpp \
|
languageclientsettings.cpp \
|
||||||
languageclientutils.cpp \
|
languageclientutils.cpp \
|
||||||
locatorfilter.cpp \
|
locatorfilter.cpp \
|
||||||
|
lsplogger.cpp \
|
||||||
semantichighlightsupport.cpp
|
semantichighlightsupport.cpp
|
||||||
|
|
||||||
RESOURCES += \
|
RESOURCES += \
|
||||||
|
@@ -46,6 +46,8 @@ QtcPlugin {
|
|||||||
"languageclientutils.h",
|
"languageclientutils.h",
|
||||||
"locatorfilter.cpp",
|
"locatorfilter.cpp",
|
||||||
"locatorfilter.h",
|
"locatorfilter.h",
|
||||||
|
"lsplogger.cpp",
|
||||||
|
"lsplogger.h",
|
||||||
"semantichighlightsupport.cpp",
|
"semantichighlightsupport.cpp",
|
||||||
"semantichighlightsupport.h",
|
"semantichighlightsupport.h",
|
||||||
]
|
]
|
||||||
|
@@ -340,6 +340,20 @@ void LanguageClientManager::reOpenDocumentWithClient(TextEditor::TextDocument *d
|
|||||||
client->activateDocument(document);
|
client->activateDocument(document);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LanguageClientManager::logBaseMessage(const LspLogMessage::MessageSender sender,
|
||||||
|
const QString &clientName,
|
||||||
|
const BaseMessage &message)
|
||||||
|
{
|
||||||
|
instance()->m_logger.log(sender, clientName, message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LanguageClientManager::showLogger()
|
||||||
|
{
|
||||||
|
QWidget *loggerWidget = instance()->m_logger.createWidget();
|
||||||
|
loggerWidget->setAttribute(Qt::WA_DeleteOnClose);
|
||||||
|
loggerWidget->show();
|
||||||
|
}
|
||||||
|
|
||||||
QVector<Client *> LanguageClientManager::reachableClients()
|
QVector<Client *> LanguageClientManager::reachableClients()
|
||||||
{
|
{
|
||||||
return Utils::filtered(m_clients, &Client::reachable);
|
return Utils::filtered(m_clients, &Client::reachable);
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
#include "languageclient_global.h"
|
#include "languageclient_global.h"
|
||||||
#include "languageclientsettings.h"
|
#include "languageclientsettings.h"
|
||||||
#include "locatorfilter.h"
|
#include "locatorfilter.h"
|
||||||
|
#include "lsplogger.h"
|
||||||
|
|
||||||
#include <coreplugin/id.h>
|
#include <coreplugin/id.h>
|
||||||
|
|
||||||
@@ -84,6 +85,11 @@ public:
|
|||||||
static Client *clientForUri(const LanguageServerProtocol::DocumentUri &uri);
|
static Client *clientForUri(const LanguageServerProtocol::DocumentUri &uri);
|
||||||
static void reOpenDocumentWithClient(TextEditor::TextDocument *document, Client *client);
|
static void reOpenDocumentWithClient(TextEditor::TextDocument *document, Client *client);
|
||||||
|
|
||||||
|
static void logBaseMessage(const LspLogMessage::MessageSender sender,
|
||||||
|
const QString &clientName,
|
||||||
|
const LanguageServerProtocol::BaseMessage &message);
|
||||||
|
static void showLogger();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void shutdownFinished();
|
void shutdownFinished();
|
||||||
|
|
||||||
@@ -118,5 +124,6 @@ private:
|
|||||||
WorkspaceLocatorFilter m_workspaceLocatorFilter;
|
WorkspaceLocatorFilter m_workspaceLocatorFilter;
|
||||||
WorkspaceClassLocatorFilter m_workspaceClassLocatorFilter;
|
WorkspaceClassLocatorFilter m_workspaceClassLocatorFilter;
|
||||||
WorkspaceMethodLocatorFilter m_workspaceMethodLocatorFilter;
|
WorkspaceMethodLocatorFilter m_workspaceMethodLocatorFilter;
|
||||||
|
LspLogger m_logger;
|
||||||
};
|
};
|
||||||
} // namespace LanguageClient
|
} // namespace LanguageClient
|
||||||
|
@@ -239,6 +239,9 @@ void updateEditorToolBar(Core::IEditor *editor)
|
|||||||
QObject::connect(action, &QAction::triggered, reopen);
|
QObject::connect(action, &QAction::triggered, reopen);
|
||||||
}
|
}
|
||||||
menu->addActions(clientsGroup->actions());
|
menu->addActions(clientsGroup->actions());
|
||||||
|
menu->addAction("Language Client Logs", []() {
|
||||||
|
LanguageClientManager::showLogger();
|
||||||
|
});
|
||||||
menu->addAction("Manage...", []() {
|
menu->addAction("Manage...", []() {
|
||||||
Core::ICore::showOptionsDialog(Constants::LANGUAGECLIENT_SETTINGS_PAGE);
|
Core::ICore::showOptionsDialog(Constants::LANGUAGECLIENT_SETTINGS_PAGE);
|
||||||
});
|
});
|
||||||
|
364
src/plugins/languageclient/lsplogger.cpp
Normal file
364
src/plugins/languageclient/lsplogger.cpp
Normal file
@@ -0,0 +1,364 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 "lsplogger.h"
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/minisplitter.h>
|
||||||
|
#include <languageserverprotocol/jsonkeys.h>
|
||||||
|
#include <languageserverprotocol/jsonrpcmessages.h>
|
||||||
|
#include <utils/jsontreeitem.h>
|
||||||
|
#include <utils/listmodel.h>
|
||||||
|
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
#include <QFileDialog>
|
||||||
|
#include <QFormLayout>
|
||||||
|
#include <QGroupBox>
|
||||||
|
#include <QHeaderView>
|
||||||
|
#include <QLabel>
|
||||||
|
#include <QListWidget>
|
||||||
|
#include <QPlainTextEdit>
|
||||||
|
#include <QSplitter>
|
||||||
|
#include <QStyledItemDelegate>
|
||||||
|
#include <QTextCodec>
|
||||||
|
#include <QTreeView>
|
||||||
|
|
||||||
|
using namespace LanguageServerProtocol;
|
||||||
|
|
||||||
|
namespace LanguageClient {
|
||||||
|
|
||||||
|
class MessageDetailWidget : public QGroupBox
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MessageDetailWidget();
|
||||||
|
|
||||||
|
void setMessage(const BaseMessage &message);
|
||||||
|
void clear();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QLabel *m_contentLength = nullptr;
|
||||||
|
QLabel *m_mimeType = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LspLoggerWidget : public QDialog
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(LspLoggerWidget)
|
||||||
|
public:
|
||||||
|
explicit LspLoggerWidget(LspLogger *logger);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void addMessage(const QString &clientName, const LspLogMessage &message);
|
||||||
|
void setCurrentClient(const QString &clientName);
|
||||||
|
void currentMessageChanged(const QModelIndex &index);
|
||||||
|
void selectMatchingMessage(LspLogMessage::MessageSender sender, const QJsonValue &id);
|
||||||
|
void saveLog();
|
||||||
|
|
||||||
|
LspLogger *m_logger = nullptr;
|
||||||
|
QListWidget *m_clients = nullptr;
|
||||||
|
MessageDetailWidget *m_clientDetails = nullptr;
|
||||||
|
QListView *m_messages = nullptr;
|
||||||
|
MessageDetailWidget *m_serverDetails = nullptr;
|
||||||
|
Utils::ListModel<LspLogMessage> m_model;
|
||||||
|
};
|
||||||
|
|
||||||
|
QWidget *LspLogger::createWidget()
|
||||||
|
{
|
||||||
|
return new LspLoggerWidget(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LspLogger::log(const LspLogMessage::MessageSender sender,
|
||||||
|
const QString &clientName,
|
||||||
|
const BaseMessage &message)
|
||||||
|
{
|
||||||
|
QLinkedList<LspLogMessage> &clientLog = m_logs[clientName];
|
||||||
|
auto delta = clientLog.size() - m_logSize + 1;
|
||||||
|
if (delta > 0)
|
||||||
|
clientLog.erase(clientLog.begin(), clientLog.begin() + delta);
|
||||||
|
m_logs[clientName].append({sender, QTime::currentTime(), message});
|
||||||
|
emit newMessage(clientName, m_logs[clientName].last());
|
||||||
|
}
|
||||||
|
|
||||||
|
QLinkedList<LspLogMessage> LspLogger::messages(const QString &clientName) const
|
||||||
|
{
|
||||||
|
return m_logs[clientName];
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<QString> LspLogger::clients() const
|
||||||
|
{
|
||||||
|
return m_logs.keys();
|
||||||
|
}
|
||||||
|
|
||||||
|
static QVariant messageData(const LspLogMessage &message, int, int role)
|
||||||
|
{
|
||||||
|
if (role == Qt::DisplayRole) {
|
||||||
|
QString result = message.time.toString("hh:mm:ss.zzz") + '\n';
|
||||||
|
if (message.message.mimeType == JsonRpcMessageHandler::jsonRpcMimeType()) {
|
||||||
|
QString error;
|
||||||
|
auto json = JsonRpcMessageHandler::toJsonObject(message.message.content,
|
||||||
|
message.message.codec,
|
||||||
|
error);
|
||||||
|
result += json.value(QString{methodKey}).toString(json.value(QString{idKey}).toString());
|
||||||
|
} else {
|
||||||
|
result += message.message.codec->toUnicode(message.message.content);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
if (role == Qt::TextAlignmentRole)
|
||||||
|
return message.sender == LspLogMessage::ClientMessage ? Qt::AlignLeft : Qt::AlignRight;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
LspLoggerWidget::LspLoggerWidget(LspLogger *logger)
|
||||||
|
: m_logger(logger)
|
||||||
|
{
|
||||||
|
setWindowTitle(tr("Language Client Log"));
|
||||||
|
|
||||||
|
connect(logger, &LspLogger::newMessage, this, &LspLoggerWidget::addMessage);
|
||||||
|
connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose, this, &QWidget::close);
|
||||||
|
|
||||||
|
m_clients = new QListWidget;
|
||||||
|
m_clients->addItems(logger->clients());
|
||||||
|
connect(m_clients, &QListWidget::currentTextChanged, this, &LspLoggerWidget::setCurrentClient);
|
||||||
|
m_clients->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding);
|
||||||
|
|
||||||
|
m_clientDetails = new MessageDetailWidget;
|
||||||
|
m_clientDetails->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||||
|
m_clientDetails->setTitle(tr("Client Message"));
|
||||||
|
m_serverDetails = new MessageDetailWidget;
|
||||||
|
m_serverDetails->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
|
||||||
|
m_serverDetails->setTitle(tr("Server Message"));
|
||||||
|
|
||||||
|
m_model.setDataAccessor(&messageData);
|
||||||
|
m_messages = new QListView;
|
||||||
|
m_messages->setModel(&m_model);
|
||||||
|
m_messages->setAlternatingRowColors(true);
|
||||||
|
m_model.setHeader({tr("Messages")});
|
||||||
|
connect(m_messages->selectionModel(),
|
||||||
|
&QItemSelectionModel::currentChanged,
|
||||||
|
this,
|
||||||
|
&LspLoggerWidget::currentMessageChanged);
|
||||||
|
m_messages->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding);
|
||||||
|
m_messages->setSelectionMode(QAbstractItemView::MultiSelection);
|
||||||
|
|
||||||
|
auto layout = new QVBoxLayout;
|
||||||
|
setLayout(layout);
|
||||||
|
auto splitter = new Core::MiniSplitter;
|
||||||
|
splitter->setOrientation(Qt::Horizontal);
|
||||||
|
splitter->addWidget(m_clients);
|
||||||
|
splitter->addWidget(m_clientDetails);
|
||||||
|
splitter->addWidget(m_messages);
|
||||||
|
splitter->addWidget(m_serverDetails);
|
||||||
|
splitter->setStretchFactor(0, 0);
|
||||||
|
splitter->setStretchFactor(1, 1);
|
||||||
|
splitter->setStretchFactor(2, 1);
|
||||||
|
splitter->setStretchFactor(3, 1);
|
||||||
|
layout->addWidget(splitter);
|
||||||
|
|
||||||
|
auto buttonBox = new QDialogButtonBox(this);
|
||||||
|
buttonBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Close);
|
||||||
|
layout->addWidget(buttonBox);
|
||||||
|
|
||||||
|
// save
|
||||||
|
connect(buttonBox, &QDialogButtonBox::accepted, this, &LspLoggerWidget::saveLog);
|
||||||
|
|
||||||
|
// close
|
||||||
|
connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
resize(1024, 768);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LspLoggerWidget::addMessage(const QString &clientName, const LspLogMessage &message)
|
||||||
|
{
|
||||||
|
if (m_clients->findItems(clientName, Qt::MatchExactly).isEmpty())
|
||||||
|
m_clients->addItem(clientName);
|
||||||
|
if (clientName != m_clients->currentItem()->text())
|
||||||
|
return;
|
||||||
|
m_model.appendItem(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LspLoggerWidget::setCurrentClient(const QString &clientName)
|
||||||
|
{
|
||||||
|
m_model.clear();
|
||||||
|
for (const LspLogMessage &message : m_logger->messages(clientName))
|
||||||
|
m_model.appendItem(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LspLoggerWidget::currentMessageChanged(const QModelIndex &index)
|
||||||
|
{
|
||||||
|
m_messages->clearSelection();
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
LspLogMessage selectedMessage = m_model.itemAt(index.row())->itemData;
|
||||||
|
BaseMessage message = selectedMessage.message;
|
||||||
|
if (selectedMessage.sender == LspLogMessage::ClientMessage)
|
||||||
|
m_clientDetails->setMessage(message);
|
||||||
|
else
|
||||||
|
m_serverDetails->setMessage(message);
|
||||||
|
if (message.mimeType == JsonRpcMessageHandler::jsonRpcMimeType()) {
|
||||||
|
QString error;
|
||||||
|
QJsonValue id = JsonRpcMessageHandler::toJsonObject(message.content, message.codec, error)
|
||||||
|
.value(idKey);
|
||||||
|
if (!id.isUndefined()) {
|
||||||
|
selectMatchingMessage(selectedMessage.sender == LspLogMessage::ClientMessage
|
||||||
|
? LspLogMessage::ServerMessage
|
||||||
|
: LspLogMessage::ClientMessage,
|
||||||
|
id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool matches(LspLogMessage::MessageSender sender,
|
||||||
|
const QJsonValue &id,
|
||||||
|
const LspLogMessage &message)
|
||||||
|
{
|
||||||
|
if (message.sender != sender)
|
||||||
|
return false;
|
||||||
|
if (message.message.mimeType != JsonRpcMessageHandler::jsonRpcMimeType())
|
||||||
|
return false;
|
||||||
|
QString error;
|
||||||
|
auto json = JsonRpcMessageHandler::toJsonObject(message.message.content,
|
||||||
|
message.message.codec,
|
||||||
|
error);
|
||||||
|
return json.value(QString{idKey}) == id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LspLoggerWidget::selectMatchingMessage(LspLogMessage::MessageSender sender,
|
||||||
|
const QJsonValue &id)
|
||||||
|
{
|
||||||
|
LspLogMessage *matchingMessage = m_model.findData(
|
||||||
|
[&](const LspLogMessage &message) { return matches(sender, id, message); });
|
||||||
|
if (!matchingMessage)
|
||||||
|
return;
|
||||||
|
auto item = m_model.findItemByData(
|
||||||
|
[&](const LspLogMessage &message) { return &message == matchingMessage; });
|
||||||
|
|
||||||
|
m_messages->selectionModel()->select(m_model.indexForItem(item), QItemSelectionModel::Select);
|
||||||
|
if (matchingMessage->sender == LspLogMessage::ServerMessage)
|
||||||
|
m_serverDetails->setMessage(matchingMessage->message);
|
||||||
|
else
|
||||||
|
m_clientDetails->setMessage(matchingMessage->message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void LspLoggerWidget::saveLog()
|
||||||
|
{
|
||||||
|
QString contents;
|
||||||
|
QTextStream stream(&contents);
|
||||||
|
m_model.forItems([&](const LspLogMessage &message) {
|
||||||
|
stream << message.time.toString("hh:mm:ss.zzz") << ' ';
|
||||||
|
stream << (message.sender == LspLogMessage::ClientMessage ? QString{"Client"}
|
||||||
|
: QString{"Server"});
|
||||||
|
stream << '\n';
|
||||||
|
stream << message.message.codec->toUnicode(message.message.content);
|
||||||
|
stream << "\n\n";
|
||||||
|
});
|
||||||
|
|
||||||
|
const QString fileName = QFileDialog::getSaveFileName(this, tr("Log File"));
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
return;
|
||||||
|
Utils::FileSaver saver(fileName, QIODevice::Text);
|
||||||
|
saver.write(contents.toUtf8());
|
||||||
|
if (!saver.finalize(this))
|
||||||
|
saveLog();
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageDetailWidget::MessageDetailWidget()
|
||||||
|
{
|
||||||
|
auto layout = new QFormLayout;
|
||||||
|
setLayout(layout);
|
||||||
|
|
||||||
|
m_contentLength = new QLabel;
|
||||||
|
m_mimeType = new QLabel;
|
||||||
|
|
||||||
|
layout->addRow("Content Length:", m_contentLength);
|
||||||
|
layout->addRow("MIME Type:", m_mimeType);
|
||||||
|
}
|
||||||
|
|
||||||
|
class JsonTreeItemDelegate : public QStyledItemDelegate
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
QString displayText(const QVariant &value, const QLocale &) const override
|
||||||
|
{
|
||||||
|
QString result = value.toString();
|
||||||
|
if (result.size() == 1) {
|
||||||
|
switch (result.at(0).toLatin1()) {
|
||||||
|
case '\n':
|
||||||
|
return QString("\\n");
|
||||||
|
case '\t':
|
||||||
|
return QString("\\t");
|
||||||
|
case '\r':
|
||||||
|
return QString("\\r");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void MessageDetailWidget::setMessage(const BaseMessage &message)
|
||||||
|
{
|
||||||
|
m_contentLength->setText(QString::number(message.contentLength));
|
||||||
|
m_mimeType->setText(QString::fromLatin1(message.mimeType));
|
||||||
|
|
||||||
|
QWidget *newContentWidget = nullptr;
|
||||||
|
if (message.mimeType == JsonRpcMessageHandler::jsonRpcMimeType()) {
|
||||||
|
QString error;
|
||||||
|
auto json = JsonRpcMessageHandler::toJsonObject(message.content, message.codec, error);
|
||||||
|
if (json.isEmpty()) {
|
||||||
|
newContentWidget = new QLabel(error);
|
||||||
|
} else {
|
||||||
|
auto root = new Utils::JsonTreeItem("content", json);
|
||||||
|
if (root->canFetchMore())
|
||||||
|
root->fetchMore();
|
||||||
|
|
||||||
|
auto model = new Utils::TreeModel<Utils::JsonTreeItem>(root);
|
||||||
|
model->setHeader({{"Name"}, {"Value"}, {"Type"}});
|
||||||
|
auto view = new QTreeView;
|
||||||
|
view->setModel(model);
|
||||||
|
view->setAlternatingRowColors(true);
|
||||||
|
view->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||||
|
view->setItemDelegate(new JsonTreeItemDelegate);
|
||||||
|
newContentWidget = view;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
auto edit = new QPlainTextEdit();
|
||||||
|
edit->setReadOnly(true);
|
||||||
|
edit->setPlainText(message.codec->toUnicode(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()
|
||||||
|
{
|
||||||
|
m_contentLength->setText({});
|
||||||
|
m_mimeType->setText({});
|
||||||
|
auto formLayout = static_cast<QFormLayout *>(layout());
|
||||||
|
if (formLayout->rowCount() > 2)
|
||||||
|
formLayout->removeRow(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace LanguageClient
|
69
src/plugins/languageclient/lsplogger.h
Normal file
69
src/plugins/languageclient/lsplogger.h
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QLinkedList>
|
||||||
|
#include <QTime>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
#include <languageserverprotocol/basemessage.h>
|
||||||
|
|
||||||
|
class QWidget;
|
||||||
|
|
||||||
|
namespace LanguageClient {
|
||||||
|
|
||||||
|
struct LspLogMessage
|
||||||
|
{
|
||||||
|
enum MessageSender { ClientMessage, ServerMessage } sender;
|
||||||
|
QTime time;
|
||||||
|
LanguageServerProtocol::BaseMessage message;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LspLogger : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
LspLogger() {}
|
||||||
|
|
||||||
|
QWidget *createWidget();
|
||||||
|
|
||||||
|
|
||||||
|
void log(const LspLogMessage::MessageSender sender,
|
||||||
|
const QString &clientName,
|
||||||
|
const LanguageServerProtocol::BaseMessage &message);
|
||||||
|
|
||||||
|
QLinkedList<LspLogMessage> messages(const QString &clientName) const;
|
||||||
|
QList<QString> clients() const;
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void newMessage(const QString &clientName, const LspLogMessage &message);
|
||||||
|
|
||||||
|
private:
|
||||||
|
QMap<QString, QLinkedList<LspLogMessage>> m_logs;
|
||||||
|
int m_logSize = 100; // default log size if no widget is currently visible
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace LanguageClient
|
Reference in New Issue
Block a user