mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-25 17:11:59 +00:00
Add hierarchicalDocumentSymbolSupport
This commit is contained in:
parent
4b2f53aa5d
commit
cffc8c8409
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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())
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user