forked from qt-creator/qt-creator
LSP: implement call hierarchy
Fixes: QTCREATORBUG-11660 Change-Id: I006872ba598a807f1f9f16d134fe9ce4fe5dd09d Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
@@ -2,6 +2,7 @@ add_qtc_library(LanguageServerProtocol
|
|||||||
DEPENDS Utils
|
DEPENDS Utils
|
||||||
SOURCES
|
SOURCES
|
||||||
basemessage.cpp basemessage.h
|
basemessage.cpp basemessage.h
|
||||||
|
callhierarchy.cpp callhierarchy.h
|
||||||
client.cpp client.h
|
client.cpp client.h
|
||||||
clientcapabilities.cpp clientcapabilities.h
|
clientcapabilities.cpp clientcapabilities.h
|
||||||
completion.cpp completion.h
|
completion.cpp completion.h
|
||||||
|
28
src/libs/languageserverprotocol/callhierarchy.cpp
Normal file
28
src/libs/languageserverprotocol/callhierarchy.cpp
Normal file
@@ -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 ¶ms)
|
||||||
|
: Request(methodName, params)
|
||||||
|
{}
|
||||||
|
|
||||||
|
CallHierarchyIncomingCallsRequest::CallHierarchyIncomingCallsRequest(
|
||||||
|
const CallHierarchyCallsParams ¶ms)
|
||||||
|
: Request(methodName, params)
|
||||||
|
{}
|
||||||
|
|
||||||
|
CallHierarchyOutgoingCallsRequest::CallHierarchyOutgoingCallsRequest(
|
||||||
|
const CallHierarchyCallsParams ¶ms)
|
||||||
|
: Request(methodName, params)
|
||||||
|
{}
|
||||||
|
|
||||||
|
} // namespace LanguageServerProtocol
|
108
src/libs/languageserverprotocol/callhierarchy.h
Normal file
108
src/libs/languageserverprotocol/callhierarchy.h
Normal file
@@ -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 ¶ms);
|
||||||
|
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 ¶ms);
|
||||||
|
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 ¶ms);
|
||||||
|
using Request::Request;
|
||||||
|
constexpr static const char methodName[] = "callHierarchy/outgoingCalls";
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace LanguageServerProtocol
|
@@ -504,6 +504,12 @@ public:
|
|||||||
std::optional<SemanticTokensClientCapabilities> semanticTokens() const;
|
std::optional<SemanticTokensClientCapabilities> semanticTokens() const;
|
||||||
void setSemanticTokens(const SemanticTokensClientCapabilities &semanticTokens);
|
void setSemanticTokens(const SemanticTokensClientCapabilities &semanticTokens);
|
||||||
void clearSemanticTokens() { remove(semanticTokensKey); }
|
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
|
class LANGUAGESERVERPROTOCOL_EXPORT SemanticTokensWorkspaceClientCapabilities : public JsonObject
|
||||||
|
@@ -16,6 +16,8 @@ constexpr char16_t appliedKey[] = u"applied";
|
|||||||
constexpr char16_t applyEditKey[] = u"applyEdit";
|
constexpr char16_t applyEditKey[] = u"applyEdit";
|
||||||
constexpr char16_t argumentsKey[] = u"arguments";
|
constexpr char16_t argumentsKey[] = u"arguments";
|
||||||
constexpr char16_t blueKey[] = u"blue";
|
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 cancellableKey[] = u"cancellable";
|
||||||
constexpr char16_t capabilitiesKey[] = u"capabilities";
|
constexpr char16_t capabilitiesKey[] = u"capabilities";
|
||||||
constexpr char16_t chKey[] = u"ch";
|
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 firstTriggerCharacterKey[] = u"firstTriggerCharacter";
|
||||||
constexpr char16_t formatsKey[] = u"formats";
|
constexpr char16_t formatsKey[] = u"formats";
|
||||||
constexpr char16_t formattingKey[] = u"formatting";
|
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 fullKey[] = u"full";
|
||||||
constexpr char16_t greenKey[] = u"green";
|
constexpr char16_t greenKey[] = u"green";
|
||||||
constexpr char16_t hierarchicalDocumentSymbolSupportKey[] = u"hierarchicalDocumentSymbolSupport";
|
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 insertTextFormatKey[] = u"insertTextFormat";
|
||||||
constexpr char16_t insertTextKey[] = u"insertText";
|
constexpr char16_t insertTextKey[] = u"insertText";
|
||||||
constexpr char16_t isIncompleteKey[] = u"isIncomplete";
|
constexpr char16_t isIncompleteKey[] = u"isIncomplete";
|
||||||
|
constexpr char16_t itemKey[] = u"item";
|
||||||
constexpr char16_t itemsKey[] = u"items";
|
constexpr char16_t itemsKey[] = u"items";
|
||||||
constexpr char16_t jsonRpcVersionKey[] = u"jsonrpc";
|
constexpr char16_t jsonRpcVersionKey[] = u"jsonrpc";
|
||||||
constexpr char16_t kindKey[] = u"kind";
|
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 textKey[] = u"text";
|
||||||
constexpr char16_t titleKey[] = u"title";
|
constexpr char16_t titleKey[] = u"title";
|
||||||
constexpr char16_t tokenKey[] = u"token";
|
constexpr char16_t tokenKey[] = u"token";
|
||||||
|
constexpr char16_t toKey[] = u"to";
|
||||||
constexpr char16_t tokenModifiersKey[] = u"tokenModifiers";
|
constexpr char16_t tokenModifiersKey[] = u"tokenModifiers";
|
||||||
constexpr char16_t tokenTypesKey[] = u"tokenTypes";
|
constexpr char16_t tokenTypesKey[] = u"tokenTypes";
|
||||||
constexpr char16_t traceKey[] = u"trace";
|
constexpr char16_t traceKey[] = u"trace";
|
||||||
|
@@ -10,6 +10,8 @@ Project {
|
|||||||
files: [
|
files: [
|
||||||
"basemessage.cpp",
|
"basemessage.cpp",
|
||||||
"basemessage.h",
|
"basemessage.h",
|
||||||
|
"callhierarchy.cpp",
|
||||||
|
"callhierarchy.h",
|
||||||
"client.cpp",
|
"client.cpp",
|
||||||
"client.h",
|
"client.h",
|
||||||
"clientcapabilities.cpp",
|
"clientcapabilities.cpp",
|
||||||
|
@@ -156,6 +156,28 @@ void ServerCapabilities::setSemanticTokensProvider(
|
|||||||
insert(semanticTokensProviderKey, semanticTokensProvider);
|
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>>
|
std::optional<std::variant<bool, WorkDoneProgressOptions>>
|
||||||
ServerCapabilities::workspaceSymbolProvider() const
|
ServerCapabilities::workspaceSymbolProvider() const
|
||||||
{
|
{
|
||||||
|
@@ -328,6 +328,10 @@ public:
|
|||||||
void setSemanticTokensProvider(const SemanticTokensOptions &semanticTokensProvider);
|
void setSemanticTokensProvider(const SemanticTokensOptions &semanticTokensProvider);
|
||||||
void clearSemanticTokensProvider() { remove(semanticTokensProviderKey); }
|
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.
|
// The server provides workspace symbol support.
|
||||||
std::optional<std::variant<bool, WorkDoneProgressOptions>> workspaceSymbolProvider() const;
|
std::optional<std::variant<bool, WorkDoneProgressOptions>> workspaceSymbolProvider() const;
|
||||||
void setWorkspaceSymbolProvider(std::variant<bool, WorkDoneProgressOptions> workspaceSymbolProvider);
|
void setWorkspaceSymbolProvider(std::variant<bool, WorkDoneProgressOptions> workspaceSymbolProvider);
|
||||||
|
@@ -2,6 +2,7 @@ add_qtc_plugin(LanguageClient
|
|||||||
PUBLIC_DEPENDS LanguageServerProtocol Qt::Core app_version
|
PUBLIC_DEPENDS LanguageServerProtocol Qt::Core app_version
|
||||||
PLUGIN_DEPENDS ProjectExplorer Core TextEditor
|
PLUGIN_DEPENDS ProjectExplorer Core TextEditor
|
||||||
SOURCES
|
SOURCES
|
||||||
|
callhierarchy.cpp callhierarchy.h
|
||||||
client.cpp client.h
|
client.cpp client.h
|
||||||
diagnosticmanager.cpp diagnosticmanager.h
|
diagnosticmanager.cpp diagnosticmanager.h
|
||||||
documentsymbolcache.cpp documentsymbolcache.h
|
documentsymbolcache.cpp documentsymbolcache.h
|
||||||
|
291
src/plugins/languageclient/callhierarchy.cpp
Normal file
291
src/plugins/languageclient/callhierarchy.cpp
Normal file
@@ -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
|
20
src/plugins/languageclient/callhierarchy.h
Normal file
20
src/plugins/languageclient/callhierarchy.h
Normal file
@@ -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
|
@@ -482,6 +482,7 @@ static ClientCapabilities generateClientCapabilities()
|
|||||||
tokens.setTokenModifiers({"declaration", "definition"});
|
tokens.setTokenModifiers({"declaration", "definition"});
|
||||||
tokens.setFormats({"relative"});
|
tokens.setFormats({"relative"});
|
||||||
documentCapabilities.setSemanticTokens(tokens);
|
documentCapabilities.setSemanticTokens(tokens);
|
||||||
|
documentCapabilities.setCallHierarchy(allowDynamicRegistration);
|
||||||
capabilities.setTextDocument(documentCapabilities);
|
capabilities.setTextDocument(documentCapabilities);
|
||||||
|
|
||||||
WindowClientClientCapabilities window;
|
WindowClientClientCapabilities window;
|
||||||
|
@@ -20,6 +20,8 @@ QtcPlugin {
|
|||||||
Depends { name: "app_version_header" }
|
Depends { name: "app_version_header" }
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
|
"callhierarchy.cpp",
|
||||||
|
"callhierarchy.h",
|
||||||
"client.cpp",
|
"client.cpp",
|
||||||
"client.h",
|
"client.h",
|
||||||
"diagnosticmanager.cpp",
|
"diagnosticmanager.cpp",
|
||||||
|
@@ -3,9 +3,8 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "languageclientmanager.h"
|
|
||||||
#include "languageclientoutline.h"
|
#include "languageclientoutline.h"
|
||||||
#include "languageclientsettings.h"
|
#include "callhierarchy.h"
|
||||||
|
|
||||||
#include <extensionsystem/iplugin.h>
|
#include <extensionsystem/iplugin.h>
|
||||||
|
|
||||||
@@ -29,6 +28,7 @@ private:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
LanguageClientOutlineWidgetFactory m_outlineFactory;
|
LanguageClientOutlineWidgetFactory m_outlineFactory;
|
||||||
|
CallHierarchyFactory m_callHierarchyFactory;
|
||||||
|
|
||||||
#ifdef WITH_TESTS
|
#ifdef WITH_TESTS
|
||||||
private slots:
|
private slots:
|
||||||
|
Reference in New Issue
Block a user