Add hierarchicalDocumentSymbolSupport

This commit is contained in:
Fangrui Song 2018-09-16 16:32:06 -07:00
parent 4b2f53aa5d
commit cffc8c8409
5 changed files with 147 additions and 28 deletions

View File

@ -69,6 +69,8 @@ struct Config {
} clang;
struct ClientCapability {
// TextDocumentClientCapabilities.documentSymbol.hierarchicalDocumentSymbolSupport
bool hierarchicalDocumentSymbolSupport = false;
// TextDocumentClientCapabilities.completion.completionItem.snippetSupport
bool snippetSupport = false;
} client;
@ -237,7 +239,8 @@ struct Config {
};
MAKE_REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, pathMappings,
resourceDir);
MAKE_REFLECT_STRUCT(Config::ClientCapability, snippetSupport);
MAKE_REFLECT_STRUCT(Config::ClientCapability, hierarchicalDocumentSymbolSupport,
snippetSupport);
MAKE_REFLECT_STRUCT(Config::CodeLens, localVariables);
MAKE_REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel,
dropOldRequests, duplicateOptional, filterAndSort,

View File

@ -889,22 +889,23 @@ public:
type->def.kind = RD->getTagKind() == TTK_Struct ? lsSymbolKind::Struct
: lsSymbolKind::Class;
if (type->def.detailed_name[0] == '\0' && info->short_name.empty()) {
StringRef Tag;
switch (RD->getTagKind()) {
case TTK_Struct: Tag = "struct"; break;
case TTK_Interface: Tag = "__interface"; break;
case TTK_Union: Tag = "union"; break;
case TTK_Class: Tag = "class"; break;
case TTK_Enum: Tag = "enum"; break;
}
if (TypedefNameDecl *TD = RD->getTypedefNameForAnonDecl()) {
StringRef Name = TD->getName();
StringRef Tag;
switch (RD->getTagKind()) {
case TTK_Struct: Tag = "struct "; break;
case TTK_Interface: Tag = "__interface "; break;
case TTK_Union: Tag = "union "; break;
case TTK_Class: Tag = "class "; break;
case TTK_Enum: Tag = "enum "; break;
}
std::string name = ("anon " + Tag + Name).str();
std::string name = ("anon " + Tag + " " + Name).str();
type->def.detailed_name = Intern(name);
type->def.short_name_size = name.size();
} else {
// e.g. "struct {}"
SetName(OrigD, "", "", type->def);
std::string name = ("anon " + Tag).str();
type->def.detailed_name = Intern(name);
type->def.short_name_size = name.size();
}
}
if (is_def) {

View File

@ -258,6 +258,10 @@ struct lsTextDocumentClientCapabilities {
} completionItem;
} completion;
struct lsDocumentSymbol {
bool hierarchicalDocumentSymbolSupport = false;
} documentSymbol;
struct lsGenericDynamicReg {
// Whether foo supports dynamic registration.
std::optional<bool> dynamicRegistration;
@ -279,6 +283,8 @@ MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsSynchronization,
dynamicRegistration, willSave, willSaveWaitUntil, didSave);
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsCompletion,
dynamicRegistration, completionItem);
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsDocumentSymbol,
hierarchicalDocumentSymbolSupport);
MAKE_REFLECT_STRUCT(
lsTextDocumentClientCapabilities::lsCompletion::lsCompletionItem,
snippetSupport);
@ -287,8 +293,8 @@ MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities::lsGenericDynamicReg,
MAKE_REFLECT_STRUCT(
lsTextDocumentClientCapabilities::CodeLensRegistrationOptions,
dynamicRegistration, resolveProvider);
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities, synchronization,
completion, rename);
MAKE_REFLECT_STRUCT(lsTextDocumentClientCapabilities, completion,
documentSymbol, rename, synchronization);
struct lsClientCapabilities {
// Workspace specific client capabilities.
@ -437,6 +443,8 @@ struct Handler_Initialize : BaseMessageHandler<In_InitializeRequest> {
const auto &capabilities = params.capabilities;
g_config->client.snippetSupport =
capabilities.textDocument.completion.completionItem.snippetSupport;
g_config->client.hierarchicalDocumentSymbolSupport =
capabilities.textDocument.documentSymbol.hierarchicalDocumentSymbolSupport;
// Ensure there is a resource directory.
if (g_config->clang.resourceDir.empty())

View File

@ -7,6 +7,8 @@
using namespace ccls;
using namespace clang;
MAKE_HASHABLE(SymbolIdx, t.usr, t.kind);
namespace {
MethodType kMethodType = "textDocument/documentSymbol";
@ -40,6 +42,28 @@ struct Out_TextDocumentDocumentSymbol
};
MAKE_REFLECT_STRUCT(Out_TextDocumentDocumentSymbol, jsonrpc, id, result);
struct lsDocumentSymbol {
std::string name;
std::string detail;
lsSymbolKind kind;
lsRange range;
lsRange selectionRange;
std::vector<std::unique_ptr<lsDocumentSymbol>> children;
};
void Reflect(Writer &vis, std::unique_ptr<lsDocumentSymbol> &v);
MAKE_REFLECT_STRUCT(lsDocumentSymbol, name, detail, kind, range, selectionRange,
children);
void Reflect(Writer &vis, std::unique_ptr<lsDocumentSymbol> &v) {
Reflect(vis, *v);
}
struct Out_HierarchicalDocumentSymbol
: public lsOutMessage<Out_HierarchicalDocumentSymbol> {
lsRequestId id;
std::vector<std::unique_ptr<lsDocumentSymbol>> result;
};
MAKE_REFLECT_STRUCT(Out_HierarchicalDocumentSymbol, jsonrpc, id, result);
struct Handler_TextDocumentDocumentSymbol
: BaseMessageHandler<In_TextDocumentDocumentSymbol> {
MethodType GetMethodType() const override { return kMethodType; }
@ -51,6 +75,9 @@ struct Handler_TextDocumentDocumentSymbol
if (!FindFileOrFail(db, project, request->id,
params.textDocument.uri.GetPath(), &file, &file_id))
return;
WorkingFile *wfile = working_files->GetFileByFilename(file->def->path);
if (!wfile)
return;
const auto &symbol2refcnt =
params.all ? file->symbol2refcnt : file->outline2refcnt;
@ -66,6 +93,92 @@ struct Handler_TextDocumentDocumentSymbol
out.result.push_back(ls_loc->range);
std::sort(out.result.begin(), out.result.end());
pipeline::WriteStdout(kMethodType, out);
} else if (g_config->client.hierarchicalDocumentSymbolSupport) {
std::unordered_map<
SymbolIdx, std::pair<const void *, std::unique_ptr<lsDocumentSymbol>>>
sym2ds;
for (auto [sym, refcnt] : symbol2refcnt) {
if (refcnt <= 0)
continue;
auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind});
if (!r.second)
continue;
auto &kv = r.first->second;
kv.second = std::make_unique<lsDocumentSymbol>();
lsDocumentSymbol &ds = *kv.second;
WithEntity(db, sym, [&](const auto &entity) {
auto *def = entity.AnyDef();
if (!def)
return;
ds.name = def->Name(false);
ds.detail = def->Name(true);
for (auto &def : entity.def)
if (def.file_id == file_id) {
if (!def.spell || !def.extent)
break;
ds.kind = def.kind;
if (auto ls_range = GetLsRange(wfile, def.extent->range))
ds.range = *ls_range;
else
break;
if (auto ls_range = GetLsRange(wfile, def.spell->range))
ds.selectionRange = *ls_range;
else
break;
kv.first = static_cast<const void *>(&def);
}
});
if (kv.first && sym.kind == SymbolKind::Var)
if (static_cast<const QueryVar::Def *>(kv.first)->is_local())
kv.first = nullptr;
if (!kv.first) {
kv.second.reset();
continue;
}
}
for (auto &[sym, def_ds] : sym2ds) {
if (!def_ds.second)
continue;
lsDocumentSymbol &ds = *def_ds.second;
switch (sym.kind) {
case SymbolKind::Func: {
auto &def = *static_cast<const QueryFunc::Def *>(def_ds.first);
for (Usr usr1 : def.vars) {
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Var});
if (it != sym2ds.end() && it->second.second)
ds.children.push_back(std::move(it->second.second));
}
break;
}
case SymbolKind::Type: {
auto &def = *static_cast<const QueryType::Def *>(def_ds.first);
for (Usr usr1 : def.funcs) {
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Func});
if (it != sym2ds.end() && it->second.second)
ds.children.push_back(std::move(it->second.second));
}
for (Usr usr1 : def.types) {
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Type});
if (it != sym2ds.end() && it->second.second)
ds.children.push_back(std::move(it->second.second));
}
for (auto [usr1, _] : def.vars) {
auto it = sym2ds.find(SymbolIdx{usr1, SymbolKind::Var});
if (it != sym2ds.end() && it->second.second)
ds.children.push_back(std::move(it->second.second));
}
break;
}
default:
break;
}
}
Out_HierarchicalDocumentSymbol out;
out.id = request->id;
for (auto &[sym, def_ds] : sym2ds)
if (def_ds.second)
out.result.push_back(std::move(def_ds.second));
pipeline::WriteStdout(kMethodType, out);
} else {
Out_TextDocumentDocumentSymbol out;
out.id = request->id;

View File

@ -42,28 +42,22 @@ struct Handler_WorkspaceDidChangeWatchedFiles
void Run(In_WorkspaceDidChangeWatchedFiles *request) override {
for (lsFileEvent &event : request->params.changes) {
std::string path = event.uri.GetPath();
Project::Entry entry;
{
std::lock_guard<std::mutex> lock(project->mutex_);
auto it = project->path_to_entry_index.find(path);
if (it == project->path_to_entry_index.end())
continue;
entry = project->entries[it->second];
}
IndexMode mode =
working_files->GetFileByFilename(entry.filename) != nullptr
? IndexMode::Normal
: IndexMode::NonInteractive;
IndexMode mode = working_files->GetFileByFilename(path)
? IndexMode::Normal
: IndexMode::NonInteractive;
switch (event.type) {
case lsFileChangeType::Created:
case lsFileChangeType::Changed: {
pipeline::Index(path, entry.args, mode);
pipeline::Index(path, {}, mode);
if (mode == IndexMode::Normal)
clang_complete->NotifySave(path);
else
clang_complete->FlushSession(path);
break;
}
case lsFileChangeType::Deleted:
pipeline::Index(path, entry.args, mode);
pipeline::Index(path, {}, mode);
clang_complete->FlushSession(path);
break;
}
}