From 9ace6d38459600f1923fea3b9c5565facc75e929 Mon Sep 17 00:00:00 2001 From: Ludovic J Date: Sun, 9 May 2021 18:38:42 +0200 Subject: [PATCH] Fix document symbol with a range based method (outline and breadcumbs) (#674) --- src/messages/textDocument_document.cc | 143 ++++++++++---------------- 1 file changed, 56 insertions(+), 87 deletions(-) diff --git a/src/messages/textDocument_document.cc b/src/messages/textDocument_document.cc index a1ce94942..039a45d77 100644 --- a/src/messages/textDocument_document.cc +++ b/src/messages/textDocument_document.cc @@ -6,6 +6,7 @@ #include "query.hh" #include +#include MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind); @@ -112,14 +113,10 @@ struct DocumentSymbol { SymbolKind kind; lsRange range; lsRange selectionRange; - std::vector> children; + std::vector children; }; -void reflect(JsonWriter &vis, std::unique_ptr &v); REFLECT_STRUCT(DocumentSymbol, name, detail, kind, range, selectionRange, children); -void reflect(JsonWriter &vis, std::unique_ptr &v) { - reflect(vis, *v); -} template bool ignore(const Def *def) { return false; } template <> bool ignore(const QueryType::Def *def) { @@ -128,16 +125,6 @@ template <> bool ignore(const QueryType::Def *def) { template <> bool ignore(const QueryVar::Def *def) { return !def || def->is_local(); } - -void uniquify(std::vector> &cs) { - std::sort(cs.begin(), cs.end(), - [](auto &l, auto &r) { return l->range < r->range; }); - cs.erase(std::unique(cs.begin(), cs.end(), - [](auto &l, auto &r) { return l->range == r->range; }), - cs.end()); - for (auto &c : cs) - uniquify(c->children); -} } // namespace void MessageHandler::textDocument_documentSymbol(JsonReader &reader, @@ -165,88 +152,70 @@ void MessageHandler::textDocument_documentSymbol(JsonReader &reader, std::sort(result.begin(), result.end()); reply(result); } else if (g_config->client.hierarchicalDocumentSymbolSupport) { - std::unordered_map> sym2ds; - std::vector, DocumentSymbol *>> funcs, - types; + std::vector syms; + syms.reserve(file->symbol2refcnt.size()); + + /** + * with 2 ranges that start at the same Position, we want the wider one + * first (swap lhs/rhs) + * + * we use range for start to start at symbol name + * issue with: int i, j, k; otherwise + */ + auto sym_cmp = [](ExtentRef const &lhs, ExtentRef const &rhs) { + return lhs.range.start < rhs.range.start || + (lhs.range.start == rhs.range.start && + rhs.extent.end < lhs.extent.end); + }; + for (auto [sym, refcnt] : file->symbol2refcnt) { if (refcnt <= 0 || !sym.extent.valid()) continue; - auto r = sym2ds.try_emplace(SymbolIdx{sym.usr, sym.kind}); - auto &ds = r.first->second; - if (!ds || sym.role & Role::Definition) { - if (!ds) - ds = std::make_unique(); - if (auto range = getLsRange(wf, sym.range)) { - ds->selectionRange = *range; - ds->range = ds->selectionRange; - // For a macro expansion, M(name), we may use `M` for extent and - // `name` for spell, do the check as selectionRange must be a subrange - // of range. - if (sym.extent.valid()) - if (auto range1 = getLsRange(wf, sym.extent); - range1 && range1->includes(*range)) - ds->range = *range1; - } + syms.push_back(sym); + } + std::sort(std::begin(syms), std::end(syms), sym_cmp); + + std::vector result; + std::stack indent; + for (auto sym : syms) { + DocumentSymbol ds; + if (auto range = getLsRange(wf, sym.range)) { + ds.selectionRange = *range; + ds.range = ds.selectionRange; + // For a macro expansion, M(name), we may use `M` for extent and + // `name` for spell, do the check as selectionRange must be a subrange + // of range. + if (sym.extent.valid()) + if (auto range1 = getLsRange(wf, sym.extent); + range1 && range1->includes(*range)) + ds.range = *range1; } - if (!r.second) - continue; - std::vector def_ptrs; - SymbolKind kind = SymbolKind::Unknown; withEntity(db, sym, [&](const auto &entity) { - auto *def = entity.anyDef(); + auto const *def = entity.anyDef(); if (!def) return; - ds->name = def->name(false); - ds->detail = def->detailed_name; - for (auto &def : entity.def) - if (def.file_id == file_id && !ignore(&def)) { - kind = ds->kind = def.kind; - def_ptrs.push_back(&def); + ds.name = def->name(false); + ds.detail = def->detailed_name; + ds.kind = def->kind; + + if (!ignore(def) && (ds.kind == SymbolKind::Namespace || allows(sym))) { + // drop symbols that are behind the current one + while (!indent.empty() && + // use selectionRange for start to start at symbol name + // issue with: int i, j, k; otherwise + indent.top()->range.end < ds.selectionRange.start) { + indent.pop(); } + if (indent.empty()) { + result.push_back(std::move(ds)); + indent.push(&result.back()); + } else { + indent.top()->children.push_back(std::move(ds)); + indent.push(&indent.top()->children.back()); + } + } }); - if (def_ptrs.empty() || !(kind == SymbolKind::Namespace || allows(sym))) { - ds.reset(); - continue; - } - if (sym.kind == Kind::Func) - funcs.emplace_back(std::move(def_ptrs), ds.get()); - else if (sym.kind == Kind::Type) - types.emplace_back(std::move(def_ptrs), ds.get()); } - - for (auto &[def_ptrs, ds] : funcs) - for (const void *def_ptr : def_ptrs) - for (Usr usr1 : ((const QueryFunc::Def *)def_ptr)->vars) { - auto it = sym2ds.find(SymbolIdx{usr1, Kind::Var}); - if (it != sym2ds.end() && it->second) - ds->children.push_back(std::move(it->second)); - } - for (auto &[def_ptrs, ds] : types) - for (const void *def_ptr : def_ptrs) { - auto *def = (const QueryType::Def *)def_ptr; - for (Usr usr1 : def->funcs) { - auto it = sym2ds.find(SymbolIdx{usr1, Kind::Func}); - if (it != sym2ds.end() && it->second) - ds->children.push_back(std::move(it->second)); - } - for (Usr usr1 : def->types) { - auto it = sym2ds.find(SymbolIdx{usr1, Kind::Type}); - if (it != sym2ds.end() && it->second) - ds->children.push_back(std::move(it->second)); - } - for (auto [usr1, _] : def->vars) { - auto it = sym2ds.find(SymbolIdx{usr1, Kind::Var}); - if (it != sym2ds.end() && it->second) - ds->children.push_back(std::move(it->second)); - } - } - std::vector> result; - for (auto &[_, ds] : sym2ds) - if (ds) { - uniquify(ds->children); - result.push_back(std::move(ds)); - } - uniquify(result); reply(result); } else { std::vector result;