Skip to content

Commit

Permalink
[clangd] Add support for hierarchical documentSymbol
Browse files Browse the repository at this point in the history
Reviewers: ioeric, sammccall, simark

Reviewed By: sammccall

Subscribers: MaskRay, jkorous, arphaman, kadircet, cfe-commits

Differential Revision: https://reviews.llvm.org/D52311

llvm-svn: 347498
  • Loading branch information
ilya-biryukov committed Nov 23, 2018
1 parent 0fc5dcd commit 19d7560
Show file tree
Hide file tree
Showing 14 changed files with 457 additions and 161 deletions.
43 changes: 43 additions & 0 deletions clang-tools-extra/clangd/AST.cpp
Expand Up @@ -11,9 +11,12 @@

#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Basic/SourceLocation.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Index/USRGeneration.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ScopedPrinter.h"

using namespace llvm;
namespace clang {
Expand Down Expand Up @@ -61,6 +64,46 @@ std::string printQualifiedName(const NamedDecl &ND) {
return QName;
}

static const TemplateArgumentList *
getTemplateSpecializationArgs(const NamedDecl &ND) {
if (auto *Func = llvm::dyn_cast<FunctionDecl>(&ND))
return Func->getTemplateSpecializationArgs();
if (auto *Cls = llvm::dyn_cast<ClassTemplateSpecializationDecl>(&ND))
return &Cls->getTemplateInstantiationArgs();
if (auto *Var = llvm::dyn_cast<VarTemplateSpecializationDecl>(&ND))
return &Var->getTemplateInstantiationArgs();
return nullptr;
}

std::string printName(const ASTContext &Ctx, const NamedDecl &ND) {
std::string Name;
llvm::raw_string_ostream Out(Name);
PrintingPolicy PP(Ctx.getLangOpts());
// Handle 'using namespace'. They all have the same name - <using-directive>.
if (auto *UD = llvm::dyn_cast<UsingDirectiveDecl>(&ND)) {
Out << "using namespace ";
if (auto *Qual = UD->getQualifier())
Qual->print(Out, PP);
UD->getNominatedNamespaceAsWritten()->printName(Out);
return Out.str();
}
ND.getDeclName().print(Out, PP);
if (!Out.str().empty()) {
// FIXME(ibiryukov): do not show args not explicitly written by the user.
if (auto *ArgList = getTemplateSpecializationArgs(ND))
printTemplateArgumentList(Out, ArgList->asArray(), PP);
return Out.str();
}
// The name was empty, so present an anonymous entity.
if (auto *NS = llvm::dyn_cast<NamespaceDecl>(&ND))
return "(anonymous namespace)";
if (auto *Cls = llvm::dyn_cast<RecordDecl>(&ND))
return ("(anonymous " + Cls->getKindName() + ")").str();
if (auto *En = llvm::dyn_cast<EnumDecl>(&ND))
return "(anonymous enum)";
return "(anonymous)";
}

std::string printNamespaceScope(const DeclContext &DC) {
for (const auto *Ctx = &DC; Ctx != nullptr; Ctx = Ctx->getParent())
if (const auto *NS = dyn_cast<NamespaceDecl>(Ctx))
Expand Down
5 changes: 5 additions & 0 deletions clang-tools-extra/clangd/AST.h
Expand Up @@ -42,6 +42,11 @@ std::string printQualifiedName(const NamedDecl &ND);
/// Returns the first enclosing namespace scope starting from \p DC.
std::string printNamespaceScope(const DeclContext &DC);

/// Prints unqualified name of the decl for the purpose of displaying it to the
/// user. Anonymous decls return names of the form "(anonymous {kind})", e.g.
/// "(anonymous struct)" or "(anonymous namespace)".
std::string printName(const ASTContext &Ctx, const NamedDecl &ND);

/// Gets the symbol ID for a declaration, if possible.
llvm::Optional<SymbolID> getSymbolID(const Decl *D);

Expand Down
55 changes: 47 additions & 8 deletions clang-tools-extra/clangd/ClangdLSPServer.cpp
Expand Up @@ -23,6 +23,14 @@ namespace clang {
namespace clangd {
namespace {

void adjustSymbolKinds(llvm::MutableArrayRef<DocumentSymbol> Syms,
SymbolKindBitset Kinds) {
for (auto &S : Syms) {
S.kind = adjustKindToCapability(S.kind, Kinds);
adjustSymbolKinds(S.children, Kinds);
}
}

SymbolKindBitset defaultSymbolKinds() {
SymbolKindBitset Defaults;
for (size_t I = SymbolKindMin; I <= static_cast<size_t>(SymbolKind::Array);
Expand Down Expand Up @@ -284,6 +292,8 @@ void ClangdLSPServer::onInitialize(const InitializeParams &Params,
if (Params.capabilities.CompletionItemKinds)
SupportedCompletionItemKinds |= *Params.capabilities.CompletionItemKinds;
SupportsCodeAction = Params.capabilities.CodeActionStructure;
SupportsHierarchicalDocumentSymbol =
Params.capabilities.HierarchicalDocumentSymbol;

Reply(json::Object{
{{"capabilities",
Expand Down Expand Up @@ -501,19 +511,48 @@ void ClangdLSPServer::onDocumentFormatting(
Reply(ReplacementsOrError.takeError());
}

void ClangdLSPServer::onDocumentSymbol(
const DocumentSymbolParams &Params,
Callback<std::vector<SymbolInformation>> Reply) {
/// The functions constructs a flattened view of the DocumentSymbol hierarchy.
/// Used by the clients that do not support the hierarchical view.
static std::vector<SymbolInformation>
flattenSymbolHierarchy(llvm::ArrayRef<DocumentSymbol> Symbols,
const URIForFile &FileURI) {

std::vector<SymbolInformation> Results;
std::function<void(const DocumentSymbol &, StringRef)> Process =
[&](const DocumentSymbol &S, Optional<StringRef> ParentName) {
SymbolInformation SI;
SI.containerName = ParentName ? "" : *ParentName;
SI.name = S.name;
SI.kind = S.kind;
SI.location.range = S.range;
SI.location.uri = FileURI;

Results.push_back(std::move(SI));
std::string FullName =
!ParentName ? S.name : (ParentName->str() + "::" + S.name);
for (auto &C : S.children)
Process(C, /*ParentName=*/FullName);
};
for (auto &S : Symbols)
Process(S, /*ParentName=*/"");
return Results;
}

void ClangdLSPServer::onDocumentSymbol(const DocumentSymbolParams &Params,
Callback<json::Value> Reply) {
URIForFile FileURI = Params.textDocument.uri;
Server->documentSymbols(
Params.textDocument.uri.file(),
Bind(
[this](decltype(Reply) Reply,
Expected<std::vector<SymbolInformation>> Items) {
[this, FileURI](decltype(Reply) Reply,
Expected<std::vector<DocumentSymbol>> Items) {
if (!Items)
return Reply(Items.takeError());
for (auto &Sym : *Items)
Sym.kind = adjustKindToCapability(Sym.kind, SupportedSymbolKinds);
Reply(std::move(*Items));
adjustSymbolKinds(*Items, SupportedSymbolKinds);
if (SupportsHierarchicalDocumentSymbol)
return Reply(std::move(*Items));
else
return Reply(flattenSymbolHierarchy(*Items, FileURI));
},
std::move(Reply)));
}
Expand Down
7 changes: 6 additions & 1 deletion clang-tools-extra/clangd/ClangdLSPServer.h
Expand Up @@ -66,8 +66,11 @@ class ClangdLSPServer : private DiagnosticsConsumer {
Callback<std::vector<TextEdit>>);
void onDocumentFormatting(const DocumentFormattingParams &,
Callback<std::vector<TextEdit>>);
// The results are serialized 'vector<DocumentSymbol>' if
// SupportsHierarchicalDocumentSymbol is true and 'vector<SymbolInformation>'
// otherwise.
void onDocumentSymbol(const DocumentSymbolParams &,
Callback<std::vector<SymbolInformation>>);
Callback<llvm::json::Value>);
void onCodeAction(const CodeActionParams &, Callback<llvm::json::Value>);
void onCompletion(const TextDocumentPositionParams &,
Callback<CompletionList>);
Expand Down Expand Up @@ -128,6 +131,8 @@ class ClangdLSPServer : private DiagnosticsConsumer {
CompletionItemKindBitset SupportedCompletionItemKinds;
// Whether the client supports CodeAction response objects.
bool SupportsCodeAction = false;
/// From capabilities of textDocument/documentSymbol.
bool SupportsHierarchicalDocumentSymbol = false;

// Store of the current versions of the open documents.
DraftStore DraftMgr;
Expand Down
8 changes: 4 additions & 4 deletions clang-tools-extra/clangd/ClangdServer.cpp
Expand Up @@ -470,10 +470,10 @@ void ClangdServer::workspaceSymbols(
std::move(CB)));
}

void ClangdServer::documentSymbols(
StringRef File, Callback<std::vector<SymbolInformation>> CB) {
auto Action = [](Callback<std::vector<SymbolInformation>> CB,
Expected<InputsAndAST> InpAST) {
void ClangdServer::documentSymbols(StringRef File,
Callback<std::vector<DocumentSymbol>> CB) {
auto Action = [](Callback<std::vector<DocumentSymbol>> CB,
llvm::Expected<InputsAndAST> InpAST) {
if (!InpAST)
return CB(InpAST.takeError());
CB(clangd::getDocumentSymbols(InpAST->AST));
Expand Down
2 changes: 1 addition & 1 deletion clang-tools-extra/clangd/ClangdServer.h
Expand Up @@ -167,7 +167,7 @@ class ClangdServer {

/// Retrieve the symbols within the specified file.
void documentSymbols(StringRef File,
Callback<std::vector<SymbolInformation>> CB);
Callback<std::vector<DocumentSymbol>> CB);

/// Retrieve locations for symbol references.
void findReferences(PathRef File, Position Pos,
Expand Down

0 comments on commit 19d7560

Please sign in to comment.