Skip to content

Commit 8665802

Browse files
committedMar 19, 2019
[clangd] Add support for type hierarchy (super types only for now)
Summary: Patch by Nathan Ridge(@nridge)! This is an LSP extension proposed here: microsoft/vscode-languageserver-node#426 An example client implementation can be found here: eclipse-theia/theia#3802 Reviewers: kadircet, sammccall Reviewed By: kadircet Subscribers: jdoerfert, sammccall, cfe-commits, mgorny, dschaefer, simark, ilya-biryukov, ioeric, MaskRay, jkorous, arphaman, kadircet Tags: #clang Differential Revision: https://reviews.llvm.org/D56370 llvm-svn: 356445
1 parent ad78768 commit 8665802

17 files changed

+991
-71
lines changed
 

‎clang-tools-extra/clangd/ClangdLSPServer.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -368,6 +368,7 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
368368
{ExecuteCommandParams::CLANGD_APPLY_FIX_COMMAND,
369369
ExecuteCommandParams::CLANGD_APPLY_TWEAK}},
370370
}},
371+
{"typeHierarchyProvider", true},
371372
}}}});
372373
}
373374

@@ -806,6 +807,13 @@ void ClangdLSPServer::onHover(const TextDocumentPositionParams &Params,
806807
std::move(Reply));
807808
}
808809

810+
void ClangdLSPServer::onTypeHierarchy(
811+
const TypeHierarchyParams &Params,
812+
Callback<Optional<TypeHierarchyItem>> Reply) {
813+
Server->typeHierarchy(Params.textDocument.uri.file(), Params.position,
814+
Params.resolve, Params.direction, std::move(Reply));
815+
}
816+
809817
void ClangdLSPServer::applyConfiguration(
810818
const ConfigurationSettings &Settings) {
811819
// Per-file update to the compilation database.
@@ -885,6 +893,7 @@ ClangdLSPServer::ClangdLSPServer(class Transport &Transp,
885893
MsgHandler->bind("workspace/didChangeWatchedFiles", &ClangdLSPServer::onFileEvent);
886894
MsgHandler->bind("workspace/didChangeConfiguration", &ClangdLSPServer::onChangeConfiguration);
887895
MsgHandler->bind("textDocument/symbolInfo", &ClangdLSPServer::onSymbolInfo);
896+
MsgHandler->bind("textDocument/typeHierarchy", &ClangdLSPServer::onTypeHierarchy);
888897
// clang-format on
889898
}
890899

‎clang-tools-extra/clangd/ClangdLSPServer.h

+2
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,8 @@ class ClangdLSPServer : private DiagnosticsConsumer {
9393
void onRename(const RenameParams &, Callback<WorkspaceEdit>);
9494
void onHover(const TextDocumentPositionParams &,
9595
Callback<llvm::Optional<Hover>>);
96+
void onTypeHierarchy(const TypeHierarchyParams &,
97+
Callback<llvm::Optional<TypeHierarchyItem>>);
9698
void onChangeConfiguration(const DidChangeConfigurationParams &);
9799
void onSymbolInfo(const TextDocumentPositionParams &,
98100
Callback<std::vector<SymbolDetails>>);

‎clang-tools-extra/clangd/ClangdServer.cpp

+15-3
Original file line numberDiff line numberDiff line change
@@ -362,9 +362,8 @@ void ClangdServer::enumerateTweaks(PathRef File, Range Sel,
362362

363363
void ClangdServer::applyTweak(PathRef File, Range Sel, StringRef TweakID,
364364
Callback<tooling::Replacements> CB) {
365-
auto Action = [Sel](decltype(CB) CB, std::string File,
366-
std::string TweakID,
367-
Expected<InputsAndAST> InpAST) {
365+
auto Action = [Sel](decltype(CB) CB, std::string File, std::string TweakID,
366+
Expected<InputsAndAST> InpAST) {
368367
if (!InpAST)
369368
return CB(InpAST.takeError());
370369
auto Selection = tweakSelection(Sel, *InpAST);
@@ -523,6 +522,19 @@ void ClangdServer::findHover(PathRef File, Position Pos,
523522
WorkScheduler.runWithAST("Hover", File, Bind(Action, std::move(CB)));
524523
}
525524

525+
void ClangdServer::typeHierarchy(PathRef File, Position Pos, int Resolve,
526+
TypeHierarchyDirection Direction,
527+
Callback<Optional<TypeHierarchyItem>> CB) {
528+
auto Action = [Pos, Resolve, Direction](decltype(CB) CB,
529+
Expected<InputsAndAST> InpAST) {
530+
if (!InpAST)
531+
return CB(InpAST.takeError());
532+
CB(clangd::getTypeHierarchy(InpAST->AST, Pos, Resolve, Direction));
533+
};
534+
535+
WorkScheduler.runWithAST("Type Hierarchy", File, Bind(Action, std::move(CB)));
536+
}
537+
526538
tooling::CompileCommand ClangdServer::getCompileCommand(PathRef File) {
527539
trace::Span Span("GetCompileCommand");
528540
llvm::Optional<tooling::CompileCommand> C = CDB.getCompileCommand(File);

‎clang-tools-extra/clangd/ClangdServer.h

+5
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,11 @@ class ClangdServer {
184184
void findHover(PathRef File, Position Pos,
185185
Callback<llvm::Optional<Hover>> CB);
186186

187+
/// Get information about type hierarchy for a given position.
188+
void typeHierarchy(PathRef File, Position Pos, int Resolve,
189+
TypeHierarchyDirection Direction,
190+
Callback<llvm::Optional<TypeHierarchyItem>> CB);
191+
187192
/// Retrieve the top symbols from the workspace matching a query.
188193
void workspaceSymbols(StringRef Query, int Limit,
189194
Callback<std::vector<SymbolInformation>> CB);

‎clang-tools-extra/clangd/FindSymbols.cpp

+1-60
Original file line numberDiff line numberDiff line change
@@ -26,67 +26,8 @@
2626

2727
namespace clang {
2828
namespace clangd {
29-
namespace {
30-
31-
// Convert a index::SymbolKind to clangd::SymbolKind (LSP)
32-
// Note, some are not perfect matches and should be improved when this LSP
33-
// issue is addressed:
34-
// https://github.com/Microsoft/language-server-protocol/issues/344
35-
SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) {
36-
switch (Kind) {
37-
case index::SymbolKind::Unknown:
38-
return SymbolKind::Variable;
39-
case index::SymbolKind::Module:
40-
return SymbolKind::Module;
41-
case index::SymbolKind::Namespace:
42-
return SymbolKind::Namespace;
43-
case index::SymbolKind::NamespaceAlias:
44-
return SymbolKind::Namespace;
45-
case index::SymbolKind::Macro:
46-
return SymbolKind::String;
47-
case index::SymbolKind::Enum:
48-
return SymbolKind::Enum;
49-
case index::SymbolKind::Struct:
50-
return SymbolKind::Struct;
51-
case index::SymbolKind::Class:
52-
return SymbolKind::Class;
53-
case index::SymbolKind::Protocol:
54-
return SymbolKind::Interface;
55-
case index::SymbolKind::Extension:
56-
return SymbolKind::Interface;
57-
case index::SymbolKind::Union:
58-
return SymbolKind::Class;
59-
case index::SymbolKind::TypeAlias:
60-
return SymbolKind::Class;
61-
case index::SymbolKind::Function:
62-
return SymbolKind::Function;
63-
case index::SymbolKind::Variable:
64-
return SymbolKind::Variable;
65-
case index::SymbolKind::Field:
66-
return SymbolKind::Field;
67-
case index::SymbolKind::EnumConstant:
68-
return SymbolKind::EnumMember;
69-
case index::SymbolKind::InstanceMethod:
70-
case index::SymbolKind::ClassMethod:
71-
case index::SymbolKind::StaticMethod:
72-
return SymbolKind::Method;
73-
case index::SymbolKind::InstanceProperty:
74-
case index::SymbolKind::ClassProperty:
75-
case index::SymbolKind::StaticProperty:
76-
return SymbolKind::Property;
77-
case index::SymbolKind::Constructor:
78-
case index::SymbolKind::Destructor:
79-
return SymbolKind::Method;
80-
case index::SymbolKind::ConversionFunction:
81-
return SymbolKind::Function;
82-
case index::SymbolKind::Parameter:
83-
return SymbolKind::Variable;
84-
case index::SymbolKind::Using:
85-
return SymbolKind::Namespace;
86-
}
87-
llvm_unreachable("invalid symbol kind");
88-
}
8929

30+
namespace {
9031
using ScoredSymbolInfo = std::pair<float, SymbolInformation>;
9132
struct ScoredSymbolGreater {
9233
bool operator()(const ScoredSymbolInfo &L, const ScoredSymbolInfo &R) {

‎clang-tools-extra/clangd/FindSymbols.h

+4-3
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,10 @@ class ParsedAST;
2121
class SymbolIndex;
2222

2323
/// Searches for the symbols matching \p Query. The syntax of \p Query can be
24-
/// the non-qualified name or fully qualified of a symbol. For example, "vector"
25-
/// will match the symbol std::vector and "std::vector" would also match it.
26-
/// Direct children of scopes (namepaces, etc) can be listed with a trailing
24+
/// the non-qualified name or fully qualified of a symbol. For example,
25+
/// "vector" will match the symbol std::vector and "std::vector" would also
26+
/// match it. Direct children of scopes (namepaces, etc) can be listed with a
27+
/// trailing
2728
/// "::". For example, "std::" will list all children of the std namespace and
2829
/// "::" alone will list all children of the global namespace.
2930
/// \p Limit limits the number of results returned (0 means no limit).

‎clang-tools-extra/clangd/Protocol.cpp

+115
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,61 @@ SymbolKind adjustKindToCapability(SymbolKind Kind,
211211
}
212212
}
213213

214+
SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind) {
215+
switch (Kind) {
216+
case index::SymbolKind::Unknown:
217+
return SymbolKind::Variable;
218+
case index::SymbolKind::Module:
219+
return SymbolKind::Module;
220+
case index::SymbolKind::Namespace:
221+
return SymbolKind::Namespace;
222+
case index::SymbolKind::NamespaceAlias:
223+
return SymbolKind::Namespace;
224+
case index::SymbolKind::Macro:
225+
return SymbolKind::String;
226+
case index::SymbolKind::Enum:
227+
return SymbolKind::Enum;
228+
case index::SymbolKind::Struct:
229+
return SymbolKind::Struct;
230+
case index::SymbolKind::Class:
231+
return SymbolKind::Class;
232+
case index::SymbolKind::Protocol:
233+
return SymbolKind::Interface;
234+
case index::SymbolKind::Extension:
235+
return SymbolKind::Interface;
236+
case index::SymbolKind::Union:
237+
return SymbolKind::Class;
238+
case index::SymbolKind::TypeAlias:
239+
return SymbolKind::Class;
240+
case index::SymbolKind::Function:
241+
return SymbolKind::Function;
242+
case index::SymbolKind::Variable:
243+
return SymbolKind::Variable;
244+
case index::SymbolKind::Field:
245+
return SymbolKind::Field;
246+
case index::SymbolKind::EnumConstant:
247+
return SymbolKind::EnumMember;
248+
case index::SymbolKind::InstanceMethod:
249+
case index::SymbolKind::ClassMethod:
250+
case index::SymbolKind::StaticMethod:
251+
return SymbolKind::Method;
252+
case index::SymbolKind::InstanceProperty:
253+
case index::SymbolKind::ClassProperty:
254+
case index::SymbolKind::StaticProperty:
255+
return SymbolKind::Property;
256+
case index::SymbolKind::Constructor:
257+
case index::SymbolKind::Destructor:
258+
return SymbolKind::Method;
259+
case index::SymbolKind::ConversionFunction:
260+
return SymbolKind::Function;
261+
case index::SymbolKind::Parameter:
262+
return SymbolKind::Variable;
263+
case index::SymbolKind::Using:
264+
return SymbolKind::Namespace;
265+
}
266+
llvm_unreachable("invalid symbol kind");
267+
}
268+
214269
bool fromJSON(const llvm::json::Value &Params, ClientCapabilities &R) {
215270
const llvm::json::Object *O = Params.getAsObject();
216271
if (!O)
@@ -812,6 +867,66 @@ bool fromJSON(const llvm::json::Value &Params, InitializationOptions &Opts) {
812867
return true;
813868
}
814869

870+
bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out) {
871+
auto T = E.getAsInteger();
872+
if (!T)
873+
return false;
874+
if (*T < static_cast<int>(TypeHierarchyDirection::Children) ||
875+
*T > static_cast<int>(TypeHierarchyDirection::Both))
876+
return false;
877+
Out = static_cast<TypeHierarchyDirection>(*T);
878+
return true;
879+
}
880+
881+
bool fromJSON(const llvm::json::Value &Params, TypeHierarchyParams &R) {
882+
llvm::json::ObjectMapper O(Params);
883+
return O && O.map("textDocument", R.textDocument) &&
884+
O.map("position", R.position) && O.map("resolve", R.resolve) &&
885+
O.map("direction", R.direction);
886+
}
887+
888+
llvm::raw_ostream &operator<<(llvm::raw_ostream &O,
889+
const TypeHierarchyItem &I) {
890+
return O << I.name << " - " << toJSON(I);
891+
}
892+
893+
llvm::json::Value toJSON(const TypeHierarchyItem &I) {
894+
llvm::json::Object Result{{"name", I.name},
895+
{"kind", static_cast<int>(I.kind)},
896+
{"range", I.range},
897+
{"selectionRange", I.selectionRange},
898+
{"uri", I.uri}};
899+
900+
if (I.detail)
901+
Result["detail"] = I.detail;
902+
if (I.deprecated)
903+
Result["deprecated"] = I.deprecated;
904+
if (I.parents)
905+
Result["parents"] = I.parents;
906+
if (I.children)
907+
Result["children"] = I.children;
908+
return std::move(Result);
909+
}
910+
911+
bool fromJSON(const llvm::json::Value &Params, TypeHierarchyItem &I) {
912+
llvm::json::ObjectMapper O(Params);
913+
914+
// Required fields.
915+
if (!(O && O.map("name", I.name) && O.map("kind", I.kind) &&
916+
O.map("uri", I.uri) && O.map("range", I.range) &&
917+
O.map("selectionRange", I.selectionRange))) {
918+
return false;
919+
}
920+
921+
// Optional fields.
922+
O.map("detail", I.detail);
923+
O.map("deprecated", I.deprecated);
924+
O.map("parents", I.parents);
925+
O.map("children", I.children);
926+
927+
return true;
928+
}
929+
815930
bool fromJSON(const llvm::json::Value &Params, ReferenceParams &R) {
816931
TextDocumentPositionParams &Base = R;
817932
return fromJSON(Params, Base);

‎clang-tools-extra/clangd/Protocol.h

+68
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
#include "URI.h"
2727
#include "index/SymbolID.h"
28+
#include "clang/Index/IndexSymbol.h"
2829
#include "llvm/ADT/Optional.h"
2930
#include "llvm/Support/JSON.h"
3031
#include <bitset>
@@ -331,6 +332,12 @@ bool fromJSON(const llvm::json::Value &, SymbolKindBitset &);
331332
SymbolKind adjustKindToCapability(SymbolKind Kind,
332333
SymbolKindBitset &supportedSymbolKinds);
333334

335+
// Convert a index::SymbolKind to clangd::SymbolKind (LSP)
336+
// Note, some are not perfect matches and should be improved when this LSP
337+
// issue is addressed:
338+
// https://github.com/Microsoft/language-server-protocol/issues/344
339+
SymbolKind indexSymbolKindToSymbolKind(index::SymbolKind Kind);
340+
334341
// This struct doesn't mirror LSP!
335342
// The protocol defines deeply nested structures for client capabilities.
336343
// Instead of mapping them all, this just parses out the bits we care about.
@@ -1014,6 +1021,67 @@ struct DocumentHighlight {
10141021
llvm::json::Value toJSON(const DocumentHighlight &DH);
10151022
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const DocumentHighlight &);
10161023

1024+
enum class TypeHierarchyDirection { Children = 0, Parents = 1, Both = 2 };
1025+
bool fromJSON(const llvm::json::Value &E, TypeHierarchyDirection &Out);
1026+
1027+
/// The type hierarchy params is an extension of the
1028+
/// `TextDocumentPositionsParams` with optional properties which can be used to
1029+
/// eagerly resolve the item when requesting from the server.
1030+
struct TypeHierarchyParams : public TextDocumentPositionParams {
1031+
/// The hierarchy levels to resolve. `0` indicates no level.
1032+
int resolve = 0;
1033+
1034+
/// The direction of the hierarchy levels to resolve.
1035+
TypeHierarchyDirection direction = TypeHierarchyDirection::Parents;
1036+
};
1037+
bool fromJSON(const llvm::json::Value &, TypeHierarchyParams &);
1038+
1039+
struct TypeHierarchyItem {
1040+
/// The human readable name of the hierarchy item.
1041+
std::string name;
1042+
1043+
/// Optional detail for the hierarchy item. It can be, for instance, the
1044+
/// signature of a function or method.
1045+
llvm::Optional<std::string> detail;
1046+
1047+
/// The kind of the hierarchy item. For instance, class or interface.
1048+
SymbolKind kind;
1049+
1050+
/// `true` if the hierarchy item is deprecated. Otherwise, `false`.
1051+
bool deprecated;
1052+
1053+
/// The URI of the text document where this type hierarchy item belongs to.
1054+
URIForFile uri;
1055+
1056+
/// The range enclosing this type hierarchy item not including
1057+
/// leading/trailing whitespace but everything else like comments. This
1058+
/// information is typically used to determine if the client's cursor is
1059+
/// inside the type hierarch item to reveal in the symbol in the UI.
1060+
Range range;
1061+
1062+
/// The range that should be selected and revealed when this type hierarchy
1063+
/// item is being picked, e.g. the name of a function. Must be contained by
1064+
/// the `range`.
1065+
Range selectionRange;
1066+
1067+
/// If this type hierarchy item is resolved, it contains the direct parents.
1068+
/// Could be empty if the item does not have direct parents. If not defined,
1069+
/// the parents have not been resolved yet.
1070+
llvm::Optional<std::vector<TypeHierarchyItem>> parents;
1071+
1072+
/// If this type hierarchy item is resolved, it contains the direct children
1073+
/// of the current item. Could be empty if the item does not have any
1074+
/// descendants. If not defined, the children have not been resolved.
1075+
llvm::Optional<std::vector<TypeHierarchyItem>> children;
1076+
1077+
/// The protocol has a slot here for an optional 'data' filed, which can
1078+
/// be used to identify a type hierarchy item in a resolve request. We don't
1079+
/// need this (the item itself is sufficient to identify what to resolve)
1080+
/// so don't declare it.
1081+
};
1082+
llvm::json::Value toJSON(const TypeHierarchyItem &);
1083+
llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TypeHierarchyItem &);
1084+
10171085
struct ReferenceParams : public TextDocumentPositionParams {
10181086
// For now, no options like context.includeDeclaration are supported.
10191087
};

0 commit comments

Comments
 (0)
Please sign in to comment.