Skip to content

Commit

Permalink
Add hierarchicalDocumentSymbolSupport
Browse files Browse the repository at this point in the history
  • Loading branch information
MaskRay committed Sep 17, 2018
1 parent a18977b commit 14b73f0
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 28 deletions.
5 changes: 4 additions & 1 deletion src/config.h
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ struct Config {
} clang;

struct ClientCapability {
// TextDocumentClientCapabilities.documentSymbol.hierarchicalDocumentSymbolSupport
bool hierarchicalDocumentSymbolSupport = false;
// TextDocumentClientCapabilities.completion.completionItem.snippetSupport
bool snippetSupport = false;
} client;
Expand Down Expand Up @@ -249,7 +251,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,
Expand Down
23 changes: 12 additions & 11 deletions src/indexer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -904,22 +904,23 @@ class IndexDataConsumer : public index::IndexDataConsumer {
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) {
Expand Down
12 changes: 10 additions & 2 deletions src/messages/initialize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ struct lsTextDocumentClientCapabilities {
} completionItem;
} completion;

struct lsDocumentSymbol {
bool hierarchicalDocumentSymbolSupport = false;
} documentSymbol;

struct lsGenericDynamicReg {
// Whether foo supports dynamic registration.
std::optional<bool> dynamicRegistration;
Expand All @@ -291,6 +295,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);
Expand All @@ -299,8 +305,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.
Expand Down Expand Up @@ -449,6 +455,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())
Expand Down
113 changes: 113 additions & 0 deletions src/messages/textDocument_documentSymbol.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ limitations under the License.
using namespace ccls;
using namespace clang;

MAKE_HASHABLE(SymbolIdx, t.usr, t.kind);

namespace {
MethodType kMethodType = "textDocument/documentSymbol";

Expand Down Expand Up @@ -52,6 +54,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; }
Expand All @@ -63,6 +87,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;
Expand All @@ -78,6 +105,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;
Expand Down
22 changes: 8 additions & 14 deletions src/messages/workspace_didChangeWatchedFiles.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,28 +54,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;
}
}
Expand Down

0 comments on commit 14b73f0

Please sign in to comment.