summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDavid Schulz <david.schulz@qt.io>2022-12-06 15:16:41 +0100
committerDavid Schulz <david.schulz@qt.io>2023-01-11 09:49:01 +0000
commit887db6b419b443bfd7f1fd7d95632d6ad18c603c (patch)
treef86eb1e246f600515428f6cb4379fd3f0681691a /src
parentac5db861293a656f8ad6e0b97d4213272f1e81e7 (diff)
downloadqt-creator-887db6b419b443bfd7f1fd7d95632d6ad18c603c.tar.gz
LSP: implement call hierarchy
Fixes: QTCREATORBUG-11660 Change-Id: I006872ba598a807f1f9f16d134fe9ce4fe5dd09d Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/libs/languageserverprotocol/CMakeLists.txt1
-rw-r--r--src/libs/languageserverprotocol/callhierarchy.cpp28
-rw-r--r--src/libs/languageserverprotocol/callhierarchy.h108
-rw-r--r--src/libs/languageserverprotocol/clientcapabilities.h6
-rw-r--r--src/libs/languageserverprotocol/jsonkeys.h6
-rw-r--r--src/libs/languageserverprotocol/languageserverprotocol.qbs2
-rw-r--r--src/libs/languageserverprotocol/servercapabilities.cpp22
-rw-r--r--src/libs/languageserverprotocol/servercapabilities.h4
-rw-r--r--src/plugins/languageclient/CMakeLists.txt1
-rw-r--r--src/plugins/languageclient/callhierarchy.cpp291
-rw-r--r--src/plugins/languageclient/callhierarchy.h20
-rw-r--r--src/plugins/languageclient/client.cpp1
-rw-r--r--src/plugins/languageclient/languageclient.qbs2
-rw-r--r--src/plugins/languageclient/languageclientplugin.h4
14 files changed, 494 insertions, 2 deletions
diff --git a/src/libs/languageserverprotocol/CMakeLists.txt b/src/libs/languageserverprotocol/CMakeLists.txt
index d4c2d635d2..a3031413a4 100644
--- a/src/libs/languageserverprotocol/CMakeLists.txt
+++ b/src/libs/languageserverprotocol/CMakeLists.txt
@@ -2,6 +2,7 @@ add_qtc_library(LanguageServerProtocol
DEPENDS Utils
SOURCES
basemessage.cpp basemessage.h
+ callhierarchy.cpp callhierarchy.h
client.cpp client.h
clientcapabilities.cpp clientcapabilities.h
completion.cpp completion.h
diff --git a/src/libs/languageserverprotocol/callhierarchy.cpp b/src/libs/languageserverprotocol/callhierarchy.cpp
new file mode 100644
index 0000000000..fc61faf664
--- /dev/null
+++ b/src/libs/languageserverprotocol/callhierarchy.cpp
@@ -0,0 +1,28 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "callhierarchy.h"
+
+namespace LanguageServerProtocol {
+
+bool CallHierarchyItem::isValid() const
+{
+ return contains(nameKey) && contains(symbolKindKey) && contains(rangeKey) && contains(uriKey)
+ && contains(selectionRangeKey);
+}
+
+PrepareCallHierarchyRequest::PrepareCallHierarchyRequest(const TextDocumentPositionParams &params)
+ : Request(methodName, params)
+{}
+
+CallHierarchyIncomingCallsRequest::CallHierarchyIncomingCallsRequest(
+ const CallHierarchyCallsParams &params)
+ : Request(methodName, params)
+{}
+
+CallHierarchyOutgoingCallsRequest::CallHierarchyOutgoingCallsRequest(
+ const CallHierarchyCallsParams &params)
+ : Request(methodName, params)
+{}
+
+} // namespace LanguageServerProtocol
diff --git a/src/libs/languageserverprotocol/callhierarchy.h b/src/libs/languageserverprotocol/callhierarchy.h
new file mode 100644
index 0000000000..a284611a12
--- /dev/null
+++ b/src/libs/languageserverprotocol/callhierarchy.h
@@ -0,0 +1,108 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#pragma once
+
+#include "jsonrpcmessages.h"
+
+namespace LanguageServerProtocol {
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyItem : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ QString name() const { return typedValue<QString>(nameKey); }
+ void setName(const QString &name) { insert(nameKey, name); }
+
+ SymbolKind symbolKind() const { return SymbolKind(typedValue<int>(symbolKindKey)); }
+ void setSymbolKind(const SymbolKind &symbolKind) { insert(symbolKindKey, int(symbolKind)); }
+
+ Range range() const { return typedValue<Range>(rangeKey); }
+ void setRange(const Range &range) { insert(rangeKey, range); }
+
+ DocumentUri uri() const { return DocumentUri::fromProtocol(typedValue<QString>(uriKey)); }
+ void setUri(const DocumentUri &uri) { insert(uriKey, uri); }
+
+ Range selectionRange() const { return typedValue<Range>(selectionRangeKey); }
+ void setSelectionRange(Range selectionRange) { insert(selectionRangeKey, selectionRange); }
+
+ std::optional<QString> detail() const { return optionalValue<QString>(detailKey); }
+ void setDetail(const QString &detail) { insert(detailKey, detail); }
+ void clearDetail() { remove(detailKey); }
+
+ std::optional<QList<DocumentSymbol>> children() const
+ { return optionalArray<DocumentSymbol>(childrenKey); }
+ void setChildren(const QList<DocumentSymbol> &children) { insertArray(childrenKey, children); }
+ void clearChildren() { remove(childrenKey); }
+
+ bool isValid() const override;
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT PrepareCallHierarchyRequest : public Request<
+ LanguageClientArray<CallHierarchyItem>, std::nullptr_t, TextDocumentPositionParams>
+{
+public:
+ explicit PrepareCallHierarchyRequest(const TextDocumentPositionParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "textDocument/prepareCallHierarchy";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyCallsParams : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ CallHierarchyItem item() const { return typedValue<CallHierarchyItem>(itemKey); }
+ void setItem(const CallHierarchyItem &item) { insert(itemKey, item); }
+
+ bool isValid() const override { return contains(itemKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyIncomingCall : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ CallHierarchyItem from() const { return typedValue<CallHierarchyItem>(fromKey); }
+ void setFrom(const CallHierarchyItem &from) { insert(fromKey, from); }
+
+ QList<Range> fromRanges() const { return array<Range>(fromRangesKey); }
+ void setFromRanges(const QList<Range> &fromRanges) { insertArray(fromRangesKey, fromRanges); }
+
+ bool isValid() const override { return contains(fromRangesKey) && contains(fromRangesKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyIncomingCallsRequest : public Request<
+ LanguageClientArray<CallHierarchyIncomingCall>, std::nullptr_t, CallHierarchyCallsParams>
+{
+public:
+ explicit CallHierarchyIncomingCallsRequest(const CallHierarchyCallsParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "callHierarchy/incomingCalls";
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyOutgoingCall : public JsonObject
+{
+public:
+ using JsonObject::JsonObject;
+
+ CallHierarchyItem to() const { return typedValue<CallHierarchyItem>(toKey); }
+ void setTo(const CallHierarchyItem &to) { insert(toKey, to); }
+
+ QList<Range> fromRanges() const { return array<Range>(fromRangesKey); }
+ void setFromRanges(const QList<Range> &fromRanges) { insertArray(fromRangesKey, fromRanges); }
+
+ bool isValid() const override { return contains(fromRangesKey) && contains(fromRangesKey); }
+};
+
+class LANGUAGESERVERPROTOCOL_EXPORT CallHierarchyOutgoingCallsRequest : public Request<
+ LanguageClientArray<CallHierarchyOutgoingCall>, std::nullptr_t, CallHierarchyCallsParams>
+{
+public:
+ explicit CallHierarchyOutgoingCallsRequest(const CallHierarchyCallsParams &params);
+ using Request::Request;
+ constexpr static const char methodName[] = "callHierarchy/outgoingCalls";
+};
+
+} // namespace LanguageServerProtocol
diff --git a/src/libs/languageserverprotocol/clientcapabilities.h b/src/libs/languageserverprotocol/clientcapabilities.h
index a9df171b49..92f78ae056 100644
--- a/src/libs/languageserverprotocol/clientcapabilities.h
+++ b/src/libs/languageserverprotocol/clientcapabilities.h
@@ -504,6 +504,12 @@ public:
std::optional<SemanticTokensClientCapabilities> semanticTokens() const;
void setSemanticTokens(const SemanticTokensClientCapabilities &semanticTokens);
void clearSemanticTokens() { remove(semanticTokensKey); }
+
+ std::optional<DynamicRegistrationCapabilities> callHierarchy() const
+ { return optionalValue<DynamicRegistrationCapabilities>(callHierarchyKey); }
+ void setCallHierarchy(const DynamicRegistrationCapabilities &callHierarchy)
+ { insert(callHierarchyKey, callHierarchy); }
+ void clearCallHierarchy() { remove(callHierarchyKey); }
};
class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensWorkspaceClientCapabilities : public JsonObject
diff --git a/src/libs/languageserverprotocol/jsonkeys.h b/src/libs/languageserverprotocol/jsonkeys.h
index 2dbbecb1ef..302c57bc20 100644
--- a/src/libs/languageserverprotocol/jsonkeys.h
+++ b/src/libs/languageserverprotocol/jsonkeys.h
@@ -16,6 +16,8 @@ constexpr char16_t appliedKey[] = u"applied";
constexpr char16_t applyEditKey[] = u"applyEdit";
constexpr char16_t argumentsKey[] = u"arguments";
constexpr char16_t blueKey[] = u"blue";
+constexpr char16_t callHierarchyKey[] = u"callHierarchy";
+constexpr char16_t callHierarchyProviderKey[] = u"callHierarchyProvider";
constexpr char16_t cancellableKey[] = u"cancellable";
constexpr char16_t capabilitiesKey[] = u"capabilities";
constexpr char16_t chKey[] = u"ch";
@@ -88,6 +90,8 @@ constexpr char16_t filterTextKey[] = u"filterText";
constexpr char16_t firstTriggerCharacterKey[] = u"firstTriggerCharacter";
constexpr char16_t formatsKey[] = u"formats";
constexpr char16_t formattingKey[] = u"formatting";
+constexpr char16_t fromKey[] = u"from";
+constexpr char16_t fromRangesKey[] = u"fromRanges";
constexpr char16_t fullKey[] = u"full";
constexpr char16_t greenKey[] = u"green";
constexpr char16_t hierarchicalDocumentSymbolSupportKey[] = u"hierarchicalDocumentSymbolSupport";
@@ -104,6 +108,7 @@ constexpr char16_t insertSpaceKey[] = u"insertSpace";
constexpr char16_t insertTextFormatKey[] = u"insertTextFormat";
constexpr char16_t insertTextKey[] = u"insertText";
constexpr char16_t isIncompleteKey[] = u"isIncomplete";
+constexpr char16_t itemKey[] = u"item";
constexpr char16_t itemsKey[] = u"items";
constexpr char16_t jsonRpcVersionKey[] = u"jsonrpc";
constexpr char16_t kindKey[] = u"kind";
@@ -190,6 +195,7 @@ constexpr char16_t textEditKey[] = u"textEdit";
constexpr char16_t textKey[] = u"text";
constexpr char16_t titleKey[] = u"title";
constexpr char16_t tokenKey[] = u"token";
+constexpr char16_t toKey[] = u"to";
constexpr char16_t tokenModifiersKey[] = u"tokenModifiers";
constexpr char16_t tokenTypesKey[] = u"tokenTypes";
constexpr char16_t traceKey[] = u"trace";
diff --git a/src/libs/languageserverprotocol/languageserverprotocol.qbs b/src/libs/languageserverprotocol/languageserverprotocol.qbs
index cf243929ce..163b2d6d4e 100644
--- a/src/libs/languageserverprotocol/languageserverprotocol.qbs
+++ b/src/libs/languageserverprotocol/languageserverprotocol.qbs
@@ -10,6 +10,8 @@ Project {
files: [
"basemessage.cpp",
"basemessage.h",
+ "callhierarchy.cpp",
+ "callhierarchy.h",
"client.cpp",
"client.h",
"clientcapabilities.cpp",
diff --git a/src/libs/languageserverprotocol/servercapabilities.cpp b/src/libs/languageserverprotocol/servercapabilities.cpp
index d87403781d..1c0d322e6e 100644
--- a/src/libs/languageserverprotocol/servercapabilities.cpp
+++ b/src/libs/languageserverprotocol/servercapabilities.cpp
@@ -156,6 +156,28 @@ void ServerCapabilities::setSemanticTokensProvider(
insert(semanticTokensProviderKey, semanticTokensProvider);
}
+std::optional<std::variant<bool, WorkDoneProgressOptions> >
+ServerCapabilities::callHierarchyProvider() const
+{
+ const QJsonValue &provider = value(callHierarchyProviderKey);
+ if (provider.isBool())
+ return provider.toBool();
+ else if (provider.isObject())
+ return WorkDoneProgressOptions(provider.toObject());
+ return std::nullopt;
+}
+
+void ServerCapabilities::setCallHierarchyProvider(
+ const std::variant<bool, WorkDoneProgressOptions> &callHierarchyProvider)
+{
+ QJsonValue val;
+ if (std::holds_alternative<bool>(callHierarchyProvider))
+ val = std::get<bool>(callHierarchyProvider);
+ else if (std::holds_alternative<WorkDoneProgressOptions>(callHierarchyProvider))
+ val = QJsonObject(std::get<WorkDoneProgressOptions>(callHierarchyProvider));
+ insert(callHierarchyProviderKey, val);
+}
+
std::optional<std::variant<bool, WorkDoneProgressOptions>>
ServerCapabilities::workspaceSymbolProvider() const
{
diff --git a/src/libs/languageserverprotocol/servercapabilities.h b/src/libs/languageserverprotocol/servercapabilities.h
index a25c542578..bacddda19e 100644
--- a/src/libs/languageserverprotocol/servercapabilities.h
+++ b/src/libs/languageserverprotocol/servercapabilities.h
@@ -328,6 +328,10 @@ public:
void setSemanticTokensProvider(const SemanticTokensOptions &semanticTokensProvider);
void clearSemanticTokensProvider() { remove(semanticTokensProviderKey); }
+ std::optional<std::variant<bool, WorkDoneProgressOptions>> callHierarchyProvider() const;
+ void setCallHierarchyProvider(const std::variant<bool, WorkDoneProgressOptions> &callHierarchyProvider);
+ void clearCallHierarchyProvider() { remove(callHierarchyProviderKey); }
+
// The server provides workspace symbol support.
std::optional<std::variant<bool, WorkDoneProgressOptions>> workspaceSymbolProvider() const;
void setWorkspaceSymbolProvider(std::variant<bool, WorkDoneProgressOptions> workspaceSymbolProvider);
diff --git a/src/plugins/languageclient/CMakeLists.txt b/src/plugins/languageclient/CMakeLists.txt
index 42dde24dba..8e1748a04a 100644
--- a/src/plugins/languageclient/CMakeLists.txt
+++ b/src/plugins/languageclient/CMakeLists.txt
@@ -2,6 +2,7 @@ add_qtc_plugin(LanguageClient
PUBLIC_DEPENDS LanguageServerProtocol Qt::Core app_version
PLUGIN_DEPENDS ProjectExplorer Core TextEditor
SOURCES
+ callhierarchy.cpp callhierarchy.h
client.cpp client.h
diagnosticmanager.cpp diagnosticmanager.h
documentsymbolcache.cpp documentsymbolcache.h
diff --git a/src/plugins/languageclient/callhierarchy.cpp b/src/plugins/languageclient/callhierarchy.cpp
new file mode 100644
index 0000000000..098a1a4b64
--- /dev/null
+++ b/src/plugins/languageclient/callhierarchy.cpp
@@ -0,0 +1,291 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include "callhierarchy.h"
+
+#include "languageclientmanager.h"
+#include "languageclienttr.h"
+
+#include <QToolButton>
+#include <coreplugin/editormanager/editormanager.h>
+#include <languageserverprotocol/callhierarchy.h>
+#include <texteditor/texteditor.h>
+#include <utils/delegates.h>
+#include <utils/navigationtreeview.h>
+#include <utils/treemodel.h>
+#include <utils/utilsicons.h>
+
+using namespace Utils;
+using namespace TextEditor;
+using namespace LanguageServerProtocol;
+
+namespace LanguageClient {
+
+const char CALL_HIERARCHY_FACTORY_ID[] = "LanguageClient.CallHierarchy";
+
+namespace {
+enum Direction { Incoming, Outgoing };
+
+enum {
+ AnnotationRole = Qt::UserRole + 1,
+ LinkRole
+};
+}
+
+class CallHierarchyItem : public TreeItem
+{
+public:
+ CallHierarchyItem(const LanguageServerProtocol::CallHierarchyItem &item,
+ const Direction direction,
+ Client *client)
+ : m_item(item)
+ , m_direction(direction)
+ , m_client(client)
+ {
+ }
+
+ QVariant data(int column, int role) const override
+ {
+ switch (role) {
+ case Qt::DecorationRole:
+ return symbolIcon(int(m_item.symbolKind()));
+ case Qt::DisplayRole:
+ return m_item.name();
+ case LinkRole: {
+ if (!m_client)
+ return QVariant();
+ const Position start = m_item.selectionRange().start();
+ return QVariant::fromValue(
+ Link(m_client->serverUriToHostPath(m_item.uri()), start.line(), start.character()));
+ }
+ case AnnotationRole:
+ if (const std::optional<QString> detail = m_item.detail())
+ return *detail;
+ return {};
+ default:
+ return TreeItem::data(column, role);
+ }
+ }
+ bool canFetchMore() const override
+ {
+ return m_client && !m_fetchedChildren;
+ }
+
+ void fetchMore() override
+ {
+ m_fetchedChildren = true;
+ if (!m_client)
+ return;
+
+ CallHierarchyCallsParams params;
+ params.setItem(m_item);
+
+ if (m_direction == Incoming) {
+ CallHierarchyIncomingCallsRequest request(params);
+ request.setResponseCallback(
+ [this](const CallHierarchyIncomingCallsRequest::Response &response) {
+ const std::optional<LanguageClientArray<CallHierarchyIncomingCall>> result
+ = response.result();
+ if (result && !result->isNull()) {
+ for (const CallHierarchyIncomingCall &item : result->toList()) {
+ if (item.isValid())
+ appendChild(new CallHierarchyItem(item.from(), m_direction, m_client));
+ }
+ }
+ if (!hasChildren())
+ update();
+ });
+ m_client->sendMessage(request);
+ } else {
+ CallHierarchyOutgoingCallsRequest request(params);
+ request.setResponseCallback(
+ [this](const CallHierarchyOutgoingCallsRequest::Response &response) {
+ const std::optional<LanguageClientArray<CallHierarchyOutgoingCall>> result
+ = response.result();
+ if (result && !result->isNull()) {
+ for (const CallHierarchyOutgoingCall &item : result->toList()) {
+ if (item.isValid())
+ appendChild(new CallHierarchyItem(item.to(), m_direction, m_client));
+ }
+ }
+ if (!hasChildren())
+ update();
+ });
+ m_client->sendMessage(request);
+ }
+ }
+
+protected:
+ const LanguageServerProtocol::CallHierarchyItem m_item;
+ const Direction m_direction;
+ bool m_fetchedChildren = false;
+ QPointer<Client> m_client;
+};
+
+class CallHierarchyDirectionItem : public CallHierarchyItem
+{
+public:
+ CallHierarchyDirectionItem(const LanguageServerProtocol::CallHierarchyItem &item,
+ const Direction direction,
+ Client *client)
+ : CallHierarchyItem(item, direction, client)
+ {}
+
+ QVariant data(int column, int role) const override
+ {
+ if (role == Qt::DisplayRole)
+ return m_direction == Incoming ? Tr::tr("Incoming") : Tr::tr("Outgoing");
+ return CallHierarchyItem::data(column, role);
+ }
+};
+
+
+class CallHierarchyRootItem : public TreeItem
+{
+public:
+ CallHierarchyRootItem(const LanguageServerProtocol::CallHierarchyItem &item, Client *client)
+ : m_item(item)
+ {
+ appendChild(new CallHierarchyDirectionItem(m_item, Incoming, client));
+ appendChild(new CallHierarchyDirectionItem(m_item, Outgoing, client));
+ }
+
+ QVariant data(int column, int role) const override
+ {
+ switch (role) {
+ case Qt::DecorationRole:
+ return symbolIcon(int(m_item.symbolKind()));
+ case Qt::DisplayRole:
+ return m_item.name();
+ default:
+ return TreeItem::data(column, role);
+ }
+ }
+
+private:
+ const LanguageServerProtocol::CallHierarchyItem m_item;
+};
+
+class CallHierarchy : public QWidget
+{
+public:
+ CallHierarchy() : m_view(new NavigationTreeView(this))
+ {
+ m_delegate.setDelimiter(" ");
+ m_delegate.setAnnotationRole(AnnotationRole);
+
+ m_view->setModel(&m_model);
+ m_view->setActivationMode(SingleClickActivation);
+ m_view->setItemDelegate(&m_delegate);
+
+ setLayout(new QVBoxLayout);
+ layout()->addWidget(m_view);
+ layout()->setContentsMargins(0, 0, 0, 0);
+ layout()->setSpacing(0);
+
+ connect(m_view, &NavigationTreeView::activated, this, &CallHierarchy::onItemActivated);
+ }
+
+ void onItemActivated(const QModelIndex &index)
+ {
+ const auto link = index.data(LinkRole).value<Utils::Link>();
+ if (link.hasValidTarget())
+ Core::EditorManager::openEditorAt(link);
+ }
+
+ void updateHierarchyAtCursorPosition();
+ void handlePrepareResponse(Client *client,
+ const PrepareCallHierarchyRequest::Response &response);
+
+ AnnotatedItemDelegate m_delegate;
+ NavigationTreeView *m_view;
+ TreeModel<TreeItem, CallHierarchyRootItem, CallHierarchyDirectionItem, CallHierarchyItem> m_model;
+};
+
+void CallHierarchy::updateHierarchyAtCursorPosition()
+{
+ m_model.clear();
+
+ BaseTextEditor *editor = BaseTextEditor::currentTextEditor();
+ if (!editor)
+ return;
+ Client *client = LanguageClientManager::clientForFilePath(editor->document()->filePath());
+ if (!client)
+ return;
+
+ const QString methodName = PrepareCallHierarchyRequest::methodName;
+ std::optional<bool> registered = client->dynamicCapabilities().isRegistered(methodName);
+ bool supported = registered.value_or(false);
+ const Core::IDocument *document = editor->document();
+ if (registered) {
+ if (supported) {
+ const QJsonValue &options = client->dynamicCapabilities().option(methodName);
+ const TextDocumentRegistrationOptions docOptions(options);
+ supported = docOptions.filterApplies(document->filePath(),
+ Utils::mimeTypeForName(document->mimeType()));
+ }
+ } else {
+ supported = client->capabilities().callHierarchyProvider().has_value();
+ }
+
+ if (!supported)
+ return;
+
+ TextDocumentPositionParams params;
+ params.setTextDocument(TextDocumentIdentifier(client->hostPathToServerUri(document->filePath())));
+ params.setPosition(Position(editor->editorWidget()->textCursor()));
+
+ PrepareCallHierarchyRequest request(params);
+ request.setResponseCallback([this, client = QPointer<Client>(client)](
+ const PrepareCallHierarchyRequest::Response &response) {
+ handlePrepareResponse(client, response);
+ });
+
+ client->sendMessage(request);
+}
+
+void CallHierarchy::handlePrepareResponse(Client *client,
+ const PrepareCallHierarchyRequest::Response &response)
+{
+ if (!client)
+ return;
+ const std::optional<PrepareCallHierarchyRequest::Response::Error> error = response.error();
+ if (error)
+ client->log(*error);
+
+ const std::optional<LanguageClientArray<LanguageServerProtocol::CallHierarchyItem>>
+ result = response.result();
+ if (result && !result->isNull()) {
+ for (const LanguageServerProtocol::CallHierarchyItem &item : result->toList()) {
+ auto newItem = new CallHierarchyRootItem(item, client);
+ m_model.rootItem()->appendChild(newItem);
+ m_view->expand(newItem->index());
+ newItem->forChildrenAtLevel(1, [&](const TreeItem *child) {
+ m_view->expand(child->index());
+ });
+ }
+ }
+}
+
+CallHierarchyFactory::CallHierarchyFactory()
+{
+ setDisplayName(tr("Call Hierarchy"));
+ setPriority(650);
+ setId(CALL_HIERARCHY_FACTORY_ID);
+}
+
+Core::NavigationView CallHierarchyFactory::createWidget()
+{
+ auto h = new CallHierarchy;
+ h->updateHierarchyAtCursorPosition();
+
+ Icons::RELOAD_TOOLBAR.icon();
+ auto button = new QToolButton;
+ button->setIcon(Icons::RELOAD_TOOLBAR.icon());
+ connect(button, &QToolButton::clicked, [h](){
+ h->updateHierarchyAtCursorPosition();
+ });
+ return {h,{button}};
+}
+
+} // namespace LanguageClient
diff --git a/src/plugins/languageclient/callhierarchy.h b/src/plugins/languageclient/callhierarchy.h
new file mode 100644
index 0000000000..f707c4fbcb
--- /dev/null
+++ b/src/plugins/languageclient/callhierarchy.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2022 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0 WITH Qt-GPL-exception-1.0
+
+#include <coreplugin/inavigationwidgetfactory.h>
+
+#pragma once
+
+namespace LanguageClient {
+
+class CallHierarchyFactory : public Core::INavigationWidgetFactory
+{
+ Q_OBJECT
+
+public:
+ CallHierarchyFactory();
+
+ Core::NavigationView createWidget() override;
+};
+
+} // namespace LanguageClient
diff --git a/src/plugins/languageclient/client.cpp b/src/plugins/languageclient/client.cpp
index 466fbd1d43..dc391575e8 100644
--- a/src/plugins/languageclient/client.cpp
+++ b/src/plugins/languageclient/client.cpp
@@ -482,6 +482,7 @@ static ClientCapabilities generateClientCapabilities()
tokens.setTokenModifiers({"declaration", "definition"});
tokens.setFormats({"relative"});
documentCapabilities.setSemanticTokens(tokens);
+ documentCapabilities.setCallHierarchy(allowDynamicRegistration);
capabilities.setTextDocument(documentCapabilities);
WindowClientClientCapabilities window;
diff --git a/src/plugins/languageclient/languageclient.qbs b/src/plugins/languageclient/languageclient.qbs
index 3e28480e6d..9a48caf9e3 100644
--- a/src/plugins/languageclient/languageclient.qbs
+++ b/src/plugins/languageclient/languageclient.qbs
@@ -20,6 +20,8 @@ QtcPlugin {
Depends { name: "app_version_header" }
files: [
+ "callhierarchy.cpp",
+ "callhierarchy.h",
"client.cpp",
"client.h",
"diagnosticmanager.cpp",
diff --git a/src/plugins/languageclient/languageclientplugin.h b/src/plugins/languageclient/languageclientplugin.h
index 59179e88ee..5bdbfc0a0a 100644
--- a/src/plugins/languageclient/languageclientplugin.h
+++ b/src/plugins/languageclient/languageclientplugin.h
@@ -3,9 +3,8 @@
#pragma once
-#include "languageclientmanager.h"
#include "languageclientoutline.h"
-#include "languageclientsettings.h"
+#include "callhierarchy.h"
#include <extensionsystem/iplugin.h>
@@ -29,6 +28,7 @@ private:
private:
LanguageClientOutlineWidgetFactory m_outlineFactory;
+ CallHierarchyFactory m_callHierarchyFactory;
#ifdef WITH_TESTS
private slots: