From f9502a3ba6f5925dfe2be8c5284b11d4aa2c0b0a Mon Sep 17 00:00:00 2001 From: David Schulz Date: Thu, 11 Feb 2021 09:32:11 +0100 Subject: [PATCH] LanguageClient: move capabilities widget to inspector The capabilities are not changeable for the user, but only there to check whether a server is capable of a specific task. This will also allow us to have more specialized settings widgets for specific servers like for the java language server without the need to add the capabilities to each of those special widgets. Also add the dynamic capabilities to the widget so users have a complete overview of the capabilities. Change-Id: I9f2ed6ed11b458f0d4c67be3df632fd810023286 Reviewed-by: Christian Stenger --- src/plugins/languageclient/client.cpp | 4 +- src/plugins/languageclient/client.h | 3 +- .../languageclient/dynamiccapabilities.cpp | 5 + .../languageclient/dynamiccapabilities.h | 3 +- .../languageclient/languageclientmanager.cpp | 17 +- .../languageclient/languageclientsettings.cpp | 44 -- src/plugins/languageclient/lspinspector.cpp | 437 ++++++++++++------ src/plugins/languageclient/lspinspector.h | 16 + 8 files changed, 338 insertions(+), 191 deletions(-) diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp index 66c7bfdd814..ddcd59bd568 100644 --- a/src/plugins/languageclient/client.cpp +++ b/src/plugins/languageclient/client.cpp @@ -597,11 +597,13 @@ void Client::registerCapabilities(const QList ®istrations) for (auto document : m_openedDocument.keys()) updateFunctionHintProvider(document); } + emit capabilitiesChanged(m_dynamicCapabilities); } void Client::unregisterCapabilities(const QList &unregistrations) { m_dynamicCapabilities.unregisterCapability(unregistrations); + emit capabilitiesChanged(m_dynamicCapabilities); } TextEditor::HighlightingResult createHighlightingResult(const SymbolInformation &info) @@ -1079,7 +1081,7 @@ void Client::handleMethod(const QString &method, const MessageId &id, const ICon } else if (method == UnregisterCapabilityRequest::methodName) { auto params = dynamic_cast(content)->params().value_or(UnregistrationParams()); if (params.isValid(&error)) - m_dynamicCapabilities.unregisterCapability(params.unregistrations()); + unregisterCapabilities(params.unregistrations()); else logError(params); } else if (method == ApplyWorkspaceEditRequest::methodName) { diff --git a/src/plugins/languageclient/client.h b/src/plugins/languageclient/client.h index 72894536790..5e8b2b32089 100644 --- a/src/plugins/languageclient/client.h +++ b/src/plugins/languageclient/client.h @@ -170,7 +170,8 @@ public: bool documentUpdatePostponed(const Utils::FilePath &fileName) const; signals: - void initialized(LanguageServerProtocol::ServerCapabilities capabilities); + void initialized(const LanguageServerProtocol::ServerCapabilities &capabilities); + void capabilitiesChanged(const DynamicCapabilities &capabilities); void documentUpdated(TextEditor::TextDocument *document); void finished(); diff --git a/src/plugins/languageclient/dynamiccapabilities.cpp b/src/plugins/languageclient/dynamiccapabilities.cpp index 1b0aef037f1..53c6c64e51f 100644 --- a/src/plugins/languageclient/dynamiccapabilities.cpp +++ b/src/plugins/languageclient/dynamiccapabilities.cpp @@ -56,6 +56,11 @@ Utils::optional DynamicCapabilities::isRegistered(const QString &method) c return m_capability[method].enabled(); } +QStringList DynamicCapabilities::registeredMethods() const +{ + return m_capability.keys(); +} + void DynamicCapabilities::reset() { m_capability.clear(); diff --git a/src/plugins/languageclient/dynamiccapabilities.h b/src/plugins/languageclient/dynamiccapabilities.h index 9abee3eac9e..18240f8b982 100644 --- a/src/plugins/languageclient/dynamiccapabilities.h +++ b/src/plugins/languageclient/dynamiccapabilities.h @@ -68,7 +68,8 @@ public: void unregisterCapability(const QList &unregistrations); Utils::optional isRegistered(const QString &method) const; - QJsonValue option(const QString &method) const { return m_capability[method].options(); } + QJsonValue option(const QString &method) const { return m_capability.value(method).options(); } + QStringList registeredMethods() const; void reset(); diff --git a/src/plugins/languageclient/languageclientmanager.cpp b/src/plugins/languageclient/languageclientmanager.cpp index fef29a2402e..0892fbf6455 100644 --- a/src/plugins/languageclient/languageclientmanager.cpp +++ b/src/plugins/languageclient/languageclientmanager.cpp @@ -108,13 +108,20 @@ void LanguageClientManager::clientStarted(Client *client) } if (!managerInstance->m_clients.contains(client)) { managerInstance->m_clients << client; - connect(client, &Client::finished, managerInstance, [client](){ - clientFinished(client); - }); + connect(client, &Client::finished, managerInstance, [client]() { clientFinished(client); }); connect(client, &Client::initialized, - &managerInstance->m_currentDocumentLocatorFilter, - &DocumentLocatorFilter::updateCurrentClient); + managerInstance, + [client](const LanguageServerProtocol::ServerCapabilities &capabilities) { + managerInstance->m_currentDocumentLocatorFilter.updateCurrentClient(); + managerInstance->m_inspector.clientInitialized(client->name(), capabilities); + }); + connect(client, + &Client::capabilitiesChanged, + managerInstance, + [client](const DynamicCapabilities &capabilities) { + managerInstance->m_inspector.updateCapabilities(client->name(), capabilities); + }); } client->initialize(); diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index 4dd37cfb2e8..c5b6fd557ae 100644 --- a/src/plugins/languageclient/languageclientsettings.cpp +++ b/src/plugins/languageclient/languageclientsettings.cpp @@ -740,24 +740,6 @@ public: } }; -static QWidget *createCapabilitiesView(const QJsonValue &capabilities) -{ - auto root = new Utils::JsonTreeItem("Capabilities", capabilities); - if (root->canFetchMore()) - root->fetchMore(); - - auto capabilitiesModel = new Utils::TreeModel(root); - capabilitiesModel->setHeader({BaseSettingsWidget::tr("Name"), - BaseSettingsWidget::tr("Value"), - BaseSettingsWidget::tr("Type")}); - auto capabilitiesView = new QTreeView(); - capabilitiesView->setModel(capabilitiesModel); - capabilitiesView->setAlternatingRowColors(true); - capabilitiesView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - capabilitiesView->setItemDelegate(new JsonTreeItemDelegate); - return capabilitiesView; -} - static QString startupBehaviorString(BaseSettings::StartBehavior behavior) { switch (behavior) { @@ -810,32 +792,6 @@ BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *pa connect(addMimeTypeButton, &QPushButton::pressed, this, &BaseSettingsWidget::showAddMimeTypeDialog); - auto createInfoLabel = []() { - return new QLabel(tr("Available after server was initialized")); - }; - - mainLayout->addWidget(new QLabel(tr("Capabilities:")), ++row, 0, Qt::AlignTop); - QVector clients = LanguageClientManager::clientForSetting(settings); - if (clients.isEmpty()) { - mainLayout->addWidget(createInfoLabel()); - } else { // TODO move the capabilities view into a new widget outside of the settings - Client *client = clients.first(); - if (client->state() == Client::Initialized) - mainLayout->addWidget(createCapabilitiesView(QJsonValue(client->capabilities()))); - else - mainLayout->addWidget(createInfoLabel(), row, 1); - connect(client, &Client::finished, mainLayout, [mainLayout, row, createInfoLabel]() { - delete mainLayout->itemAtPosition(row, 1)->widget(); - mainLayout->addWidget(createInfoLabel(), row, 1); - }); - connect(client, &Client::initialized, mainLayout, - [mainLayout, row]( - const LanguageServerProtocol::ServerCapabilities &capabilities) { - delete mainLayout->itemAtPosition(row, 1)->widget(); - mainLayout->addWidget(createCapabilitiesView(QJsonValue(capabilities)), row, 1); - }); - } - mainLayout->addWidget(new QLabel(tr("Initialization options:")), ++row, 0); mainLayout->addWidget(m_initializationOptions, row, 1); chooser->addSupportedWidget(m_initializationOptions); diff --git a/src/plugins/languageclient/lspinspector.cpp b/src/plugins/languageclient/lspinspector.cpp index 624d0572168..b8ae2938a16 100644 --- a/src/plugins/languageclient/lspinspector.cpp +++ b/src/plugins/languageclient/lspinspector.cpp @@ -51,6 +51,61 @@ using namespace LanguageServerProtocol; namespace LanguageClient { +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; + } +}; + +using JsonModel = Utils::TreeModel; + +JsonModel *createJsonModel(const QString &displayName, const QJsonValue &value) +{ + if (value.isNull()) + return nullptr; + auto root = new Utils::JsonTreeItem(displayName, value); + if (root->canFetchMore()) + root->fetchMore(); + + auto model = new JsonModel(root); + model->setHeader({{"Name"}, {"Value"}, {"Type"}}); + return model; +} + +QTreeView *createJsonTreeView() +{ + auto view = new QTreeView; + view->setContextMenuPolicy(Qt::ActionsContextMenu); + auto action = new QAction(LspInspector::tr("Expand All"), view); + QObject::connect(action, &QAction::triggered, view, &QTreeView::expandAll); + view->addAction(action); + view->setAlternatingRowColors(true); + view->header()->setSectionResizeMode(QHeaderView::ResizeToContents); + view->setItemDelegate(new JsonTreeItemDelegate); + return view; +} + +QTreeView *createJsonTreeView(const QString &displayName, const QJsonValue &value) +{ + auto view = createJsonTreeView(); + view->setModel(createJsonModel(displayName, value)); + return view; +} + class MessageDetailWidget : public QGroupBox { public: @@ -64,53 +119,96 @@ private: QLabel *m_mimeType = nullptr; }; -class LspInspectorWidget : public QDialog +class LspCapabilitiesWidget : public QWidget { - Q_DECLARE_TR_FUNCTIONS(LspInspectorWidget) + Q_DECLARE_TR_FUNCTIONS(LspCapabilitiesWidget) public: - explicit LspInspectorWidget(LspInspector *inspector); + LspCapabilitiesWidget(); + void setCapabilities(const Capabilities &serverCapabilities); 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 updateOptionsView(const QString &method); + + DynamicCapabilities m_dynamicCapabilities; + QTreeView *m_capabilitiesView = nullptr; + QListWidget *m_dynamicCapabilitiesView = nullptr; + QTreeView *m_dynamicOptionsView = nullptr; + QGroupBox *m_dynamicCapabilitiesGroup = nullptr; +}; + +LspCapabilitiesWidget::LspCapabilitiesWidget() +{ + auto mainLayout = new QHBoxLayout; + + auto group = new QGroupBox(tr("Capabilities:")); + QLayout *layout = new QHBoxLayout; + m_capabilitiesView = createJsonTreeView(); + layout->addWidget(m_capabilitiesView); + group->setLayout(layout); + mainLayout->addWidget(group); + + m_dynamicCapabilitiesGroup = new QGroupBox(tr("Dynamic Capabilities:")); + layout = new QVBoxLayout; + auto label = new QLabel(tr("Method:")); + layout->addWidget(label); + m_dynamicCapabilitiesView = new QListWidget(); + layout->addWidget(m_dynamicCapabilitiesView); + label = new QLabel(tr("Options:")); + layout->addWidget(label); + m_dynamicOptionsView = createJsonTreeView(); + layout->addWidget(m_dynamicOptionsView); + m_dynamicCapabilitiesGroup->setLayout(layout); + mainLayout->addWidget(m_dynamicCapabilitiesGroup); + + setLayout(mainLayout); + + connect(m_dynamicCapabilitiesView, + &QListWidget::currentTextChanged, + this, + &LspCapabilitiesWidget::updateOptionsView); +} + +void LspCapabilitiesWidget::setCapabilities(const Capabilities &serverCapabilities) +{ + m_capabilitiesView->setModel( + createJsonModel(tr("Server Capabilities"), QJsonObject(serverCapabilities.capabilities))); + m_dynamicCapabilities = serverCapabilities.dynamicCapabilities; + const QStringList &methods = m_dynamicCapabilities.registeredMethods(); + if (methods.isEmpty()) { + m_dynamicCapabilitiesGroup->hide(); + return; + } + m_dynamicCapabilitiesGroup->show(); + m_dynamicCapabilitiesView->clear(); + m_dynamicCapabilitiesView->addItems(methods); +} + +void LspCapabilitiesWidget::updateOptionsView(const QString &method) +{ + QAbstractItemModel *oldModel = m_dynamicOptionsView->model(); + m_dynamicOptionsView->setModel(createJsonModel(method, m_dynamicCapabilities.option(method))); + delete oldModel; +} + +class LspLogWidget : public Core::MiniSplitter +{ +public: + LspLogWidget(); + + void addMessage(const LspLogMessage &message); + void setMessages(const std::list &messages); void saveLog(); - LspInspector *m_inspector = nullptr; - QListWidget *m_clients = nullptr; MessageDetailWidget *m_clientDetails = nullptr; QListView *m_messages = nullptr; MessageDetailWidget *m_serverDetails = nullptr; Utils::ListModel m_model; + +private: + void currentMessageChanged(const QModelIndex &index); + void selectMatchingMessage(LspLogMessage::MessageSender sender, const QJsonValue &id); }; -QWidget *LspInspector::createWidget() -{ - return new LspInspectorWidget(this); -} - -void LspInspector::log(const LspLogMessage::MessageSender sender, - const QString &clientName, - const BaseMessage &message) -{ - std::list &clientLog = m_logs[clientName]; - while (clientLog.size() >= static_cast(m_logSize)) - clientLog.pop_front(); - clientLog.push_back({sender, QTime::currentTime(), message}); - emit newMessage(clientName, clientLog.back()); -} - -std::list LspInspector::messages(const QString &clientName) const -{ - return m_logs[clientName]; -} - -QList LspInspector::clients() const -{ - return m_logs.keys(); -} - static QVariant messageData(const LspLogMessage &message, int, int role) { if (role == Qt::DisplayRole) { @@ -131,84 +229,39 @@ static QVariant messageData(const LspLogMessage &message, int, int role) return {}; } -LspInspectorWidget::LspInspectorWidget(LspInspector *inspector) - : m_inspector(inspector) +LspLogWidget::LspLogWidget() { - setWindowTitle(tr("Language Client Inspector")); - - connect(inspector, &LspInspector::newMessage, this, &LspInspectorWidget::addMessage); - connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose, this, &QWidget::close); - - m_clients = new QListWidget; - m_clients->addItems(inspector->clients()); - connect(m_clients, - &QListWidget::currentTextChanged, - this, - &LspInspectorWidget::setCurrentClient); - m_clients->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding); + setOrientation(Qt::Horizontal); 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")); + addWidget(m_clientDetails); + setStretchFactor(0, 1); m_model.setDataAccessor(&messageData); m_messages = new QListView; m_messages->setModel(&m_model); m_messages->setAlternatingRowColors(true); m_model.setHeader({tr("Messages")}); + m_messages->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Expanding); + m_messages->setSelectionMode(QAbstractItemView::MultiSelection); + addWidget(m_messages); + setStretchFactor(1, 0); + + m_serverDetails = new MessageDetailWidget; + m_serverDetails->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + m_serverDetails->setTitle(tr("Server Message")); + addWidget(m_serverDetails); + setStretchFactor(2, 1); + connect(m_messages->selectionModel(), &QItemSelectionModel::currentChanged, this, - &LspInspectorWidget::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, &LspInspectorWidget::saveLog); - - // close - connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); - resize(1024, 768); + &LspLogWidget::currentMessageChanged); } -void LspInspectorWidget::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 LspInspectorWidget::setCurrentClient(const QString &clientName) -{ - m_model.clear(); - for (const LspLogMessage &message : m_inspector->messages(clientName)) - m_model.appendItem(message); -} - -void LspInspectorWidget::currentMessageChanged(const QModelIndex &index) +void LspLogWidget::currentMessageChanged(const QModelIndex &index) { m_messages->clearSelection(); if (!index.isValid()) @@ -247,8 +300,7 @@ static bool matches(LspLogMessage::MessageSender sender, return json.value(QString{idKey}) == id; } -void LspInspectorWidget::selectMatchingMessage(LspLogMessage::MessageSender sender, - const QJsonValue &id) +void LspLogWidget::selectMatchingMessage(LspLogMessage::MessageSender sender, const QJsonValue &id) { LspLogMessage *matchingMessage = m_model.findData( [&](const LspLogMessage &message) { return matches(sender, id, message); }); @@ -264,7 +316,19 @@ void LspInspectorWidget::selectMatchingMessage(LspLogMessage::MessageSender send m_clientDetails->setMessage(matchingMessage->message); } -void LspInspectorWidget::saveLog() +void LspLogWidget::addMessage(const LspLogMessage &message) +{ + m_model.appendItem(message); +} + +void LspLogWidget::setMessages(const std::list &messages) +{ + m_model.clear(); + for (const LspLogMessage &message : messages) + m_model.appendItem(message); +} + +void LspLogWidget::saveLog() { QString contents; QTextStream stream(&contents); @@ -286,6 +350,137 @@ void LspInspectorWidget::saveLog() saveLog(); } +class LspInspectorWidget : public QDialog +{ + Q_DECLARE_TR_FUNCTIONS(LspInspectorWidget) +public: + explicit LspInspectorWidget(LspInspector *inspector); + +private: + void addMessage(const QString &clientName, const LspLogMessage &message); + void updateCapabilities(const QString &clientName); + void setCurrentClient(const QString &clientName); + + LspInspector *m_inspector = nullptr; + LspLogWidget *m_log = nullptr; + LspCapabilitiesWidget *m_capabilities = nullptr; + QListWidget *m_clients = nullptr; +}; + +QWidget *LspInspector::createWidget() +{ + return new LspInspectorWidget(this); +} + +void LspInspector::log(const LspLogMessage::MessageSender sender, + const QString &clientName, + const BaseMessage &message) +{ + std::list &clientLog = m_logs[clientName]; + while (clientLog.size() >= static_cast(m_logSize)) + clientLog.pop_front(); + clientLog.push_back({sender, QTime::currentTime(), message}); + emit newMessage(clientName, clientLog.back()); +} + +void LspInspector::clientInitialized(const QString &clientName, const ServerCapabilities &capabilities) +{ + m_capabilities[clientName].capabilities = capabilities; + m_capabilities[clientName].dynamicCapabilities.reset(); + emit capabilitiesUpdated(clientName); +} + +void LspInspector::updateCapabilities(const QString &clientName, + const DynamicCapabilities &dynamicCapabilities) +{ + m_capabilities[clientName].dynamicCapabilities = dynamicCapabilities; + emit capabilitiesUpdated(clientName); +} + +std::list LspInspector::messages(const QString &clientName) const +{ + return m_logs[clientName]; +} + +Capabilities LspInspector::capabilities(const QString &clientName) const +{ + return m_capabilities.value(clientName); +} + +QList LspInspector::clients() const +{ + return m_logs.keys(); +} + +LspInspectorWidget::LspInspectorWidget(LspInspector *inspector) + : m_inspector(inspector) +{ + setWindowTitle(tr("Language Client Inspector")); + + connect(inspector, &LspInspector::newMessage, this, &LspInspectorWidget::addMessage); + connect(inspector, &LspInspector::capabilitiesUpdated, + this, &LspInspectorWidget::updateCapabilities); + connect(Core::ICore::instance(), &Core::ICore::coreAboutToClose, this, &QWidget::close); + + m_clients = new QListWidget; + m_clients->addItems(inspector->clients()); + m_clients->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::MinimumExpanding); + + auto tabWidget = new QTabWidget; + + auto mainLayout = new QVBoxLayout; + auto mainSplitter = new Core::MiniSplitter; + mainSplitter->setOrientation(Qt::Horizontal); + mainSplitter->addWidget(m_clients); + mainSplitter->addWidget(tabWidget); + mainSplitter->setStretchFactor(0, 0); + mainSplitter->setStretchFactor(1, 1); + m_log = new LspLogWidget; + m_capabilities = new LspCapabilitiesWidget; + tabWidget->addTab(m_log, tr("Log")); + tabWidget->addTab(m_capabilities, tr("Capabilities")); + mainLayout->addWidget(mainSplitter); + + auto buttonBox = new QDialogButtonBox(this); + buttonBox->setStandardButtons(QDialogButtonBox::Save | QDialogButtonBox::Close); + mainLayout->addWidget(buttonBox); + setLayout(mainLayout); + + connect(m_clients, + &QListWidget::currentTextChanged, + this, + &LspInspectorWidget::setCurrentClient); + + // save + connect(buttonBox, &QDialogButtonBox::accepted, m_log, &LspLogWidget::saveLog); + + // close + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + resize(1024, 768); +} + +void LspInspectorWidget::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()) + m_log->addMessage(message); +} + +void LspInspectorWidget::updateCapabilities(const QString &clientName) +{ + if (m_clients->findItems(clientName, Qt::MatchExactly).isEmpty()) + m_clients->addItem(clientName); + if (clientName != m_clients->currentItem()->text()) + m_capabilities->setCapabilities(m_inspector->capabilities(clientName)); +} + +void LspInspectorWidget::setCurrentClient(const QString &clientName) +{ + m_log->setMessages(m_inspector->messages(clientName)); + m_capabilities->setCapabilities(m_inspector->capabilities(clientName)); +} + MessageDetailWidget::MessageDetailWidget() { auto layout = new QFormLayout; @@ -298,26 +493,6 @@ MessageDetailWidget::MessageDetailWidget() 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)); @@ -327,26 +502,10 @@ void MessageDetailWidget::setMessage(const BaseMessage &message) if (message.mimeType == JsonRpcMessageHandler::jsonRpcMimeType()) { QString error; auto json = JsonRpcMessageHandler::toJsonObject(message.content, message.codec, error); - if (json.isEmpty()) { + 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(root); - model->setHeader({{"Name"}, {"Value"}, {"Type"}}); - auto view = new QTreeView; - view->setContextMenuPolicy(Qt::ActionsContextMenu); - auto action = new QAction(tr("Expand All"), view); - connect(action, &QAction::triggered, view, &QTreeView::expandAll); - view->addAction(action); - view->setModel(model); - view->setAlternatingRowColors(true); - view->header()->setSectionResizeMode(QHeaderView::ResizeToContents); - view->setItemDelegate(new JsonTreeItemDelegate); - newContentWidget = view; - } + else + newContentWidget = createJsonTreeView("content", json); } else { auto edit = new QPlainTextEdit(); edit->setReadOnly(true); diff --git a/src/plugins/languageclient/lspinspector.h b/src/plugins/languageclient/lspinspector.h index 1796adc9643..d69661eccdf 100644 --- a/src/plugins/languageclient/lspinspector.h +++ b/src/plugins/languageclient/lspinspector.h @@ -25,10 +25,13 @@ #pragma once +#include "dynamiccapabilities.h" + #include #include #include +#include #include @@ -41,6 +44,12 @@ struct LspLogMessage LanguageServerProtocol::BaseMessage message; }; +struct Capabilities +{ + LanguageServerProtocol::ServerCapabilities capabilities; + DynamicCapabilities dynamicCapabilities; +}; + class LspInspector : public QObject { Q_OBJECT @@ -53,15 +62,22 @@ public: void log(const LspLogMessage::MessageSender sender, const QString &clientName, const LanguageServerProtocol::BaseMessage &message); + void clientInitialized(const QString &clientName, + const LanguageServerProtocol::ServerCapabilities &capabilities); + void updateCapabilities(const QString &clientName, + const DynamicCapabilities &dynamicCapabilities); std::list messages(const QString &clientName) const; + Capabilities capabilities(const QString &clientName) const; QList clients() const; signals: void newMessage(const QString &clientName, const LspLogMessage &message); + void capabilitiesUpdated(const QString &clientName); private: QMap> m_logs; + QMap m_capabilities; int m_logSize = 100; // default log size if no widget is currently visible };