Skip to content

Commit

Permalink
Fix document symbol with a range based method (outline and breadcumbs) (
Browse files Browse the repository at this point in the history
#674)

* refactor all hierarchical document symbol

now based on ranges of symbols

* remove pointer in DocumentSymbol.children and useless things

* use sort instead of heap

* fix outline, multiple symbols defined on the same line:

eg:
int i, j, k;

outline before:
k
 j
  i
after:
i
j
k
  • Loading branch information
FederAndInk authored May 9, 2021
1 parent c6686be commit a27148c
Showing 1 changed file with 56 additions and 87 deletions.
143 changes: 56 additions & 87 deletions src/messages/textDocument_document.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "query.hh"

#include <algorithm>
#include <stack>

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

Expand Down Expand Up @@ -112,14 +113,10 @@ struct DocumentSymbol {
SymbolKind kind;
lsRange range;
lsRange selectionRange;
std::vector<std::unique_ptr<DocumentSymbol>> children;
std::vector<DocumentSymbol> children;
};
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v);
REFLECT_STRUCT(DocumentSymbol, name, detail, kind, range, selectionRange,
children);
void reflect(JsonWriter &vis, std::unique_ptr<DocumentSymbol> &v) {
reflect(vis, *v);
}

template <typename Def> bool ignore(const Def *def) { return false; }
template <> bool ignore(const QueryType::Def *def) {
Expand All @@ -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<std::unique_ptr<DocumentSymbol>> &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,
Expand Down Expand Up @@ -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<SymbolIdx, std::unique_ptr<DocumentSymbol>> sym2ds;
std::vector<std::pair<std::vector<const void *>, DocumentSymbol *>> funcs,
types;
std::vector<ExtentRef> 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<DocumentSymbol>();
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<DocumentSymbol> result;
std::stack<DocumentSymbol *> 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<const void *> 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<std::unique_ptr<DocumentSymbol>> result;
for (auto &[_, ds] : sym2ds)
if (ds) {
uniquify(ds->children);
result.push_back(std::move(ds));
}
uniquify(result);
reply(result);
} else {
std::vector<SymbolInformation> result;
Expand Down

0 comments on commit a27148c

Please sign in to comment.