diff --git a/CMakeLists.txt b/CMakeLists.txt index b794d398..fa116816 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -196,7 +196,6 @@ target_sources(ccls PRIVATE src/platform_win.cc src/position.cc src/project.cc - src/query_utils.cc src/query.cc src/serializer.cc src/test.cc diff --git a/src/message_handler.cc b/src/message_handler.cc index 4c093f4a..19db4abe 100644 --- a/src/message_handler.cc +++ b/src/message_handler.cc @@ -6,7 +6,7 @@ #include "log.hh" #include "pipeline.hh" #include "project.hh" -#include "query_utils.hh" +#include "query.hh" #include "serializers/json.hh" #include diff --git a/src/messages/ccls_call.cc b/src/messages/ccls_call.cc index 84341457..32078759 100644 --- a/src/messages/ccls_call.cc +++ b/src/messages/ccls_call.cc @@ -4,7 +4,7 @@ #include "hierarchy.hh" #include "message_handler.hh" #include "pipeline.hh" -#include "query_utils.hh" +#include "query.hh" #include diff --git a/src/messages/ccls_info.cc b/src/messages/ccls_info.cc index 119acd57..e1a5f1f6 100644 --- a/src/messages/ccls_info.cc +++ b/src/messages/ccls_info.cc @@ -4,7 +4,7 @@ #include "message_handler.hh" #include "pipeline.hh" #include "project.hh" -#include "query_utils.hh" +#include "query.hh" namespace ccls { MAKE_REFLECT_STRUCT(QueryFile::Def, path, args, language, skipped_ranges, diff --git a/src/messages/ccls_inheritance.cc b/src/messages/ccls_inheritance.cc index 35f9a640..b806fede 100644 --- a/src/messages/ccls_inheritance.cc +++ b/src/messages/ccls_inheritance.cc @@ -4,7 +4,7 @@ #include "hierarchy.hh" #include "message_handler.hh" #include "pipeline.hh" -#include "query_utils.hh" +#include "query.hh" #include diff --git a/src/messages/ccls_member.cc b/src/messages/ccls_member.cc index f14e4b89..6fd80529 100644 --- a/src/messages/ccls_member.cc +++ b/src/messages/ccls_member.cc @@ -5,7 +5,7 @@ #include "hierarchy.hh" #include "message_handler.hh" #include "pipeline.hh" -#include "query_utils.hh" +#include "query.hh" #include #include diff --git a/src/messages/ccls_navigate.cc b/src/messages/ccls_navigate.cc index 3c541328..dad671a5 100644 --- a/src/messages/ccls_navigate.cc +++ b/src/messages/ccls_navigate.cc @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "message_handler.hh" -#include "query_utils.hh" +#include "query.hh" namespace ccls { namespace { diff --git a/src/messages/ccls_vars.cc b/src/messages/ccls_vars.cc index 52c41ebe..51537768 100644 --- a/src/messages/ccls_vars.cc +++ b/src/messages/ccls_vars.cc @@ -3,7 +3,7 @@ #include "message_handler.hh" #include "pipeline.hh" -#include "query_utils.hh" +#include "query.hh" namespace ccls { namespace { diff --git a/src/messages/textDocument_code.cc b/src/messages/textDocument_code.cc index 3d455abf..17395b75 100644 --- a/src/messages/textDocument_code.cc +++ b/src/messages/textDocument_code.cc @@ -3,7 +3,7 @@ #include "message_handler.hh" #include "pipeline.hh" -#include "query_utils.hh" +#include "query.hh" #include "serializers/json.hh" #include diff --git a/src/messages/textDocument_definition.cc b/src/messages/textDocument_definition.cc index b2913fdc..c1e3f806 100644 --- a/src/messages/textDocument_definition.cc +++ b/src/messages/textDocument_definition.cc @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "message_handler.hh" -#include "query_utils.hh" +#include "query.hh" #include #include diff --git a/src/messages/textDocument_document.cc b/src/messages/textDocument_document.cc index 39cc69dd..eaddd7ca 100644 --- a/src/messages/textDocument_document.cc +++ b/src/messages/textDocument_document.cc @@ -3,7 +3,7 @@ #include "message_handler.hh" #include "pipeline.hh" -#include "query_utils.hh" +#include "query.hh" #include diff --git a/src/messages/textDocument_foldingRange.cc b/src/messages/textDocument_foldingRange.cc index a8a19596..da3f5247 100644 --- a/src/messages/textDocument_foldingRange.cc +++ b/src/messages/textDocument_foldingRange.cc @@ -4,7 +4,7 @@ #include "message_handler.hh" #include "pipeline.hh" #include "project.hh" -#include "query_utils.hh" +#include "query.hh" #include "working_files.hh" namespace ccls { diff --git a/src/messages/textDocument_hover.cc b/src/messages/textDocument_hover.cc index d21ec458..f0a71f1c 100644 --- a/src/messages/textDocument_hover.cc +++ b/src/messages/textDocument_hover.cc @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "message_handler.hh" -#include "query_utils.hh" +#include "query.hh" namespace ccls { namespace { diff --git a/src/messages/textDocument_references.cc b/src/messages/textDocument_references.cc index ed66379c..410554dd 100644 --- a/src/messages/textDocument_references.cc +++ b/src/messages/textDocument_references.cc @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "message_handler.hh" -#include "query_utils.hh" +#include "query.hh" #include diff --git a/src/messages/textDocument_rename.cc b/src/messages/textDocument_rename.cc index 02f326c5..18abde67 100644 --- a/src/messages/textDocument_rename.cc +++ b/src/messages/textDocument_rename.cc @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 #include "message_handler.hh" -#include "query_utils.hh" +#include "query.hh" namespace ccls { namespace { diff --git a/src/messages/workspace.cc b/src/messages/workspace.cc index 4ac5b8bd..3f14ed89 100644 --- a/src/messages/workspace.cc +++ b/src/messages/workspace.cc @@ -7,7 +7,7 @@ #include "message_handler.hh" #include "pipeline.hh" #include "project.hh" -#include "query_utils.hh" +#include "query.hh" #include #include diff --git a/src/pipeline.cc b/src/pipeline.cc index 0aeaa8d6..805e8c2e 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -12,7 +12,7 @@ #include "pipeline.hh" #include "platform.hh" #include "project.hh" -#include "query_utils.hh" +#include "query.hh" #include "serializers/json.hh" #include diff --git a/src/query.cc b/src/query.cc index 248a61cc..d015229a 100644 --- a/src/query.cc +++ b/src/query.cc @@ -4,11 +4,13 @@ #include "query.hh" #include "indexer.hh" +#include "pipeline.hh" #include "serializer.hh" #include "serializers/json.hh" -#include -#include +#include +#include +#include #include #include #include @@ -40,7 +42,7 @@ void RemoveRange(std::vector &from, const std::vector &to_remove) { } } -QueryFile::DefUpdate BuildFileDefUpdate(const IndexFile &indexed) { +QueryFile::DefUpdate BuildFileDefUpdate(IndexFile &&indexed) { QueryFile::Def def; def.path = std::move(indexed.path); def.args = std::move(indexed.args); @@ -75,7 +77,6 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) { else previous = ∅ r.lid2path = std::move(current->lid2path); - r.files_def_update = BuildFileDefUpdate(std::move(*current)); r.funcs_hint = int(current->usr2func.size() - previous->usr2func.size()); for (auto &it : previous->usr2func) { @@ -131,6 +132,7 @@ IndexUpdate IndexUpdate::CreateDelta(IndexFile *previous, IndexFile *current) { r.vars_uses[var.usr].second = std::move(var.uses); } + r.files_def_update = BuildFileDefUpdate(std::move(*current)); return r; } @@ -474,4 +476,316 @@ std::vector DB::GetFileSet(const std::vector &folders) { } return file_set; } + +namespace { +// Computes roughly how long |range| is. +int ComputeRangeSize(const Range &range) { + if (range.start.line != range.end.line) + return INT_MAX; + return range.end.column - range.start.column; +} + +template +std::vector +GetDeclarations(llvm::DenseMap &entity_usr, + std::vector &entities, const std::vector &usrs) { + std::vector ret; + ret.reserve(usrs.size()); + for (Usr usr : usrs) { + Q &entity = entities[entity_usr[{usr}]]; + bool has_def = false; + for (auto &def : entity.def) + if (def.spell) { + ret.push_back(*def.spell); + has_def = true; + break; + } + if (!has_def && entity.declarations.size()) + ret.push_back(entity.declarations[0]); + } + return ret; +} +} + +Maybe GetDefinitionSpell(DB *db, SymbolIdx sym) { + Maybe ret; + EachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); }); + return ret; +} + +std::vector GetFuncDeclarations(DB *db, const std::vector &usrs) { + return GetDeclarations(db->func_usr, db->funcs, usrs); +} +std::vector GetTypeDeclarations(DB *db, const std::vector &usrs) { + return GetDeclarations(db->type_usr, db->types, usrs); +} +std::vector GetVarDeclarations(DB *db, const std::vector &usrs, + unsigned kind) { + std::vector ret; + ret.reserve(usrs.size()); + for (Usr usr : usrs) { + QueryVar &var = db->Var(usr); + bool has_def = false; + for (auto &def : var.def) + if (def.spell) { + has_def = true; + // See messages/ccls_vars.cc + if (def.kind == SymbolKind::Field) { + if (!(kind & 1)) + break; + } else if (def.kind == SymbolKind::Variable) { + if (!(kind & 2)) + break; + } else if (def.kind == SymbolKind::Parameter) { + if (!(kind & 4)) + break; + } + ret.push_back(*def.spell); + break; + } + if (!has_def && var.declarations.size()) + ret.push_back(var.declarations[0]); + } + return ret; +} + +std::vector &GetNonDefDeclarations(DB *db, SymbolIdx sym) { + static std::vector empty; + switch (sym.kind) { + case Kind::Func: + return db->GetFunc(sym).declarations; + case Kind::Type: + return db->GetType(sym).declarations; + case Kind::Var: + return db->GetVar(sym).declarations; + default: + break; + } + return empty; +} + +std::vector GetUsesForAllBases(DB *db, QueryFunc &root) { + std::vector ret; + std::vector stack{&root}; + std::unordered_set seen; + seen.insert(root.usr); + while (!stack.empty()) { + QueryFunc &func = *stack.back(); + stack.pop_back(); + if (auto *def = func.AnyDef()) { + EachDefinedFunc(db, def->bases, [&](QueryFunc &func1) { + if (!seen.count(func1.usr)) { + seen.insert(func1.usr); + stack.push_back(&func1); + ret.insert(ret.end(), func1.uses.begin(), func1.uses.end()); + } + }); + } + } + + return ret; +} + +std::vector GetUsesForAllDerived(DB *db, QueryFunc &root) { + std::vector ret; + std::vector stack{&root}; + std::unordered_set seen; + seen.insert(root.usr); + while (!stack.empty()) { + QueryFunc &func = *stack.back(); + stack.pop_back(); + EachDefinedFunc(db, func.derived, [&](QueryFunc &func1) { + if (!seen.count(func1.usr)) { + seen.insert(func1.usr); + stack.push_back(&func1); + ret.insert(ret.end(), func1.uses.begin(), func1.uses.end()); + } + }); + } + + return ret; +} + +std::optional GetLsRange(WorkingFile *wfile, + const Range &location) { + if (!wfile || wfile->index_lines.empty()) + return lsRange{Position{location.start.line, location.start.column}, + Position{location.end.line, location.end.column}}; + + int start_column = location.start.column, end_column = location.end.column; + std::optional start = wfile->GetBufferPosFromIndexPos( + location.start.line, &start_column, false); + std::optional end = wfile->GetBufferPosFromIndexPos( + location.end.line, &end_column, true); + if (!start || !end) + return std::nullopt; + + // If remapping end fails (end can never be < start), just guess that the + // final location didn't move. This only screws up the highlighted code + // region if we guess wrong, so not a big deal. + // + // Remapping fails often in C++ since there are a lot of "};" at the end of + // class/struct definitions. + if (*end < *start) + *end = *start + (location.end.line - location.start.line); + if (*start == *end && start_column > end_column) + end_column = start_column; + + return lsRange{Position{*start, start_column}, Position{*end, end_column}}; +} + +DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path) { + QueryFile &file = db->files[file_id]; + if (file.def) { + *path = file.def->path; + return DocumentUri::FromPath(*path); + } else { + *path = ""; + return DocumentUri::FromPath(""); + } +} + +DocumentUri GetLsDocumentUri(DB *db, int file_id) { + QueryFile &file = db->files[file_id]; + if (file.def) { + return DocumentUri::FromPath(file.def->path); + } else { + return DocumentUri::FromPath(""); + } +} + +std::optional GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) { + std::string path; + DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path); + std::optional range = + GetLsRange(wfiles->GetFileByFilename(path), use.range); + if (!range) + return std::nullopt; + return Location{uri, *range}; +} + +std::optional GetLsLocation(DB *db, WorkingFiles *wfiles, + SymbolRef sym, int file_id) { + return GetLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id}); +} + +std::vector GetLsLocations(DB *db, WorkingFiles *wfiles, + const std::vector &uses) { + std::vector ret; + for (Use use : uses) + if (auto loc = GetLsLocation(db, wfiles, use)) + ret.push_back(*loc); + std::sort(ret.begin(), ret.end()); + ret.erase(std::unique(ret.begin(), ret.end()), ret.end()); + if (ret.size() > g_config->xref.maxNum) + ret.resize(g_config->xref.maxNum); + return ret; +} + +SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) { + SymbolKind ret; + if (sym.kind == Kind::File) + ret = SymbolKind::File; + else { + ret = SymbolKind::Unknown; + WithEntity(db, sym, [&](const auto &entity) { + for (auto &def : entity.def) { + ret = def.kind; + break; + } + }); + } + return ret; +} + +std::optional GetSymbolInfo(DB *db, SymbolIdx sym, + bool detailed) { + switch (sym.kind) { + case Kind::Invalid: + break; + case Kind::File: { + QueryFile &file = db->GetFile(sym); + if (!file.def) + break; + + SymbolInformation info; + info.name = file.def->path; + info.kind = SymbolKind::File; + return info; + } + default: { + SymbolInformation info; + EachEntityDef(db, sym, [&](const auto &def) { + if (detailed) + info.name = def.detailed_name; + else + info.name = def.Name(true); + info.kind = def.kind; + return false; + }); + return info; + } + } + + return std::nullopt; +} + +std::vector FindSymbolsAtLocation(WorkingFile *wfile, + QueryFile *file, Position &ls_pos, + bool smallest) { + std::vector symbols; + // If multiVersion > 0, index may not exist and thus index_lines is empty. + if (wfile && wfile->index_lines.size()) { + if (auto line = wfile->GetIndexPosFromBufferPos( + ls_pos.line, &ls_pos.character, false)) { + ls_pos.line = *line; + } else { + ls_pos.line = -1; + return {}; + } + } + + for (auto [sym, refcnt] : file->symbol2refcnt) + if (refcnt > 0 && sym.range.Contains(ls_pos.line, ls_pos.character)) + symbols.push_back(sym); + + // Order shorter ranges first, since they are more detailed/precise. This is + // important for macros which generate code so that we can resolving the + // macro argument takes priority over the entire macro body. + // + // Order Kind::Var before Kind::Type. Macro calls are treated as Var + // currently. If a macro expands to tokens led by a Kind::Type, the macro and + // the Type have the same range. We want to find the macro definition instead + // of the Type definition. + // + // Then order functions before other types, which makes goto definition work + // better on constructors. + std::sort( + symbols.begin(), symbols.end(), + [](const SymbolRef &a, const SymbolRef &b) { + int t = ComputeRangeSize(a.range) - ComputeRangeSize(b.range); + if (t) + return t < 0; + // MacroExpansion + if ((t = (a.role & Role::Dynamic) - (b.role & Role::Dynamic))) + return t > 0; + if ((t = (a.role & Role::Definition) - (b.role & Role::Definition))) + return t > 0; + // operator> orders Var/Func before Type. + t = static_cast(a.kind) - static_cast(b.kind); + if (t) + return t > 0; + return a.usr < b.usr; + }); + if (symbols.size() && smallest) { + SymbolRef sym = symbols[0]; + for (size_t i = 1; i < symbols.size(); i++) + if (!(sym.range == symbols[i].range && sym.kind == symbols[i].kind)) { + symbols.resize(i); + break; + } + } + + return symbols; +} } // namespace ccls diff --git a/src/query.hh b/src/query.hh index 3dbc1fe4..49e6558a 100644 --- a/src/query.hh +++ b/src/query.hh @@ -5,6 +5,7 @@ #include "indexer.hh" #include "serializer.hh" +#include "working_files.hh" #include #include @@ -185,4 +186,87 @@ struct DB { QueryType &GetType(SymbolIdx ref) { return Type(ref.usr); } QueryVar &GetVar(SymbolIdx ref) { return Var(ref.usr); } }; + +Maybe GetDefinitionSpell(DB *db, SymbolIdx sym); + +// Get defining declaration (if exists) or an arbitrary declaration (otherwise) +// for each id. +std::vector GetFuncDeclarations(DB *, const std::vector &); +std::vector GetTypeDeclarations(DB *, const std::vector &); +std::vector GetVarDeclarations(DB *, const std::vector &, unsigned); + +// Get non-defining declarations. +std::vector &GetNonDefDeclarations(DB *db, SymbolIdx sym); + +std::vector GetUsesForAllBases(DB *db, QueryFunc &root); +std::vector GetUsesForAllDerived(DB *db, QueryFunc &root); +std::optional GetLsRange(WorkingFile *working_file, + const Range &location); +DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path); +DocumentUri GetLsDocumentUri(DB *db, int file_id); + +std::optional GetLsLocation(DB *db, WorkingFiles *wfiles, Use use); +std::optional GetLsLocation(DB *db, WorkingFiles *wfiles, + SymbolRef sym, int file_id); +std::vector GetLsLocations(DB *db, WorkingFiles *wfiles, + const std::vector &uses); +// Returns a symbol. The symbol will *NOT* have a location assigned. +std::optional GetSymbolInfo(DB *db, SymbolIdx sym, + bool detailed); + +std::vector FindSymbolsAtLocation(WorkingFile *working_file, + QueryFile *file, + Position &ls_pos, + bool smallest = false); + +template void WithEntity(DB *db, SymbolIdx sym, Fn &&fn) { + switch (sym.kind) { + case Kind::Invalid: + case Kind::File: + break; + case Kind::Func: + fn(db->GetFunc(sym)); + break; + case Kind::Type: + fn(db->GetType(sym)); + break; + case Kind::Var: + fn(db->GetVar(sym)); + break; + } +} + +template void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) { + WithEntity(db, sym, [&](const auto &entity) { + for (auto &def : entity.def) + if (!fn(def)) + break; + }); +} + +template +void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) { + WithEntity(db, sym, [&](const auto &entity) { + for (Use use : entity.uses) + fn(use); + if (include_decl) { + for (auto &def : entity.def) + if (def.spell) + fn(*def.spell); + for (Use use : entity.declarations) + fn(use); + } + }); +} + +SymbolKind GetSymbolKind(DB *db, SymbolIdx sym); + +template +void EachDefinedFunc(DB *db, const std::vector &usrs, Fn &&fn) { + for (Usr usr : usrs) { + auto &obj = db->Func(usr); + if (!obj.def.empty()) + fn(obj); + } +} } // namespace ccls diff --git a/src/query_utils.cc b/src/query_utils.cc deleted file mode 100644 index 049e7809..00000000 --- a/src/query_utils.cc +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#include "query_utils.hh" - -#include "pipeline.hh" - -#include -#include - -namespace ccls { -namespace { - -// Computes roughly how long |range| is. -int ComputeRangeSize(const Range &range) { - if (range.start.line != range.end.line) - return INT_MAX; - return range.end.column - range.start.column; -} - -template -std::vector -GetDeclarations(llvm::DenseMap &entity_usr, - std::vector &entities, const std::vector &usrs) { - std::vector ret; - ret.reserve(usrs.size()); - for (Usr usr : usrs) { - Q &entity = entities[entity_usr[{usr}]]; - bool has_def = false; - for (auto &def : entity.def) - if (def.spell) { - ret.push_back(*def.spell); - has_def = true; - break; - } - if (!has_def && entity.declarations.size()) - ret.push_back(entity.declarations[0]); - } - return ret; -} - -} // namespace - -Maybe GetDefinitionSpell(DB *db, SymbolIdx sym) { - Maybe ret; - EachEntityDef(db, sym, [&](const auto &def) { return !(ret = def.spell); }); - return ret; -} - -std::vector GetFuncDeclarations(DB *db, const std::vector &usrs) { - return GetDeclarations(db->func_usr, db->funcs, usrs); -} -std::vector GetTypeDeclarations(DB *db, const std::vector &usrs) { - return GetDeclarations(db->type_usr, db->types, usrs); -} -std::vector GetVarDeclarations(DB *db, const std::vector &usrs, - unsigned kind) { - std::vector ret; - ret.reserve(usrs.size()); - for (Usr usr : usrs) { - QueryVar &var = db->Var(usr); - bool has_def = false; - for (auto &def : var.def) - if (def.spell) { - has_def = true; - // See messages/ccls_vars.cc - if (def.kind == SymbolKind::Field) { - if (!(kind & 1)) - break; - } else if (def.kind == SymbolKind::Variable) { - if (!(kind & 2)) - break; - } else if (def.kind == SymbolKind::Parameter) { - if (!(kind & 4)) - break; - } - ret.push_back(*def.spell); - break; - } - if (!has_def && var.declarations.size()) - ret.push_back(var.declarations[0]); - } - return ret; -} - -std::vector &GetNonDefDeclarations(DB *db, SymbolIdx sym) { - static std::vector empty; - switch (sym.kind) { - case Kind::Func: - return db->GetFunc(sym).declarations; - case Kind::Type: - return db->GetType(sym).declarations; - case Kind::Var: - return db->GetVar(sym).declarations; - default: - break; - } - return empty; -} - -std::vector GetUsesForAllBases(DB *db, QueryFunc &root) { - std::vector ret; - std::vector stack{&root}; - std::unordered_set seen; - seen.insert(root.usr); - while (!stack.empty()) { - QueryFunc &func = *stack.back(); - stack.pop_back(); - if (auto *def = func.AnyDef()) { - EachDefinedFunc(db, def->bases, [&](QueryFunc &func1) { - if (!seen.count(func1.usr)) { - seen.insert(func1.usr); - stack.push_back(&func1); - ret.insert(ret.end(), func1.uses.begin(), func1.uses.end()); - } - }); - } - } - - return ret; -} - -std::vector GetUsesForAllDerived(DB *db, QueryFunc &root) { - std::vector ret; - std::vector stack{&root}; - std::unordered_set seen; - seen.insert(root.usr); - while (!stack.empty()) { - QueryFunc &func = *stack.back(); - stack.pop_back(); - EachDefinedFunc(db, func.derived, [&](QueryFunc &func1) { - if (!seen.count(func1.usr)) { - seen.insert(func1.usr); - stack.push_back(&func1); - ret.insert(ret.end(), func1.uses.begin(), func1.uses.end()); - } - }); - } - - return ret; -} - -std::optional GetLsRange(WorkingFile *wfile, - const Range &location) { - if (!wfile || wfile->index_lines.empty()) - return lsRange{Position{location.start.line, location.start.column}, - Position{location.end.line, location.end.column}}; - - int start_column = location.start.column, end_column = location.end.column; - std::optional start = wfile->GetBufferPosFromIndexPos( - location.start.line, &start_column, false); - std::optional end = wfile->GetBufferPosFromIndexPos( - location.end.line, &end_column, true); - if (!start || !end) - return std::nullopt; - - // If remapping end fails (end can never be < start), just guess that the - // final location didn't move. This only screws up the highlighted code - // region if we guess wrong, so not a big deal. - // - // Remapping fails often in C++ since there are a lot of "};" at the end of - // class/struct definitions. - if (*end < *start) - *end = *start + (location.end.line - location.start.line); - if (*start == *end && start_column > end_column) - end_column = start_column; - - return lsRange{Position{*start, start_column}, Position{*end, end_column}}; -} - -DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path) { - QueryFile &file = db->files[file_id]; - if (file.def) { - *path = file.def->path; - return DocumentUri::FromPath(*path); - } else { - *path = ""; - return DocumentUri::FromPath(""); - } -} - -DocumentUri GetLsDocumentUri(DB *db, int file_id) { - QueryFile &file = db->files[file_id]; - if (file.def) { - return DocumentUri::FromPath(file.def->path); - } else { - return DocumentUri::FromPath(""); - } -} - -std::optional GetLsLocation(DB *db, WorkingFiles *wfiles, Use use) { - std::string path; - DocumentUri uri = GetLsDocumentUri(db, use.file_id, &path); - std::optional range = - GetLsRange(wfiles->GetFileByFilename(path), use.range); - if (!range) - return std::nullopt; - return Location{uri, *range}; -} - -std::optional GetLsLocation(DB *db, WorkingFiles *wfiles, - SymbolRef sym, int file_id) { - return GetLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id}); -} - -std::vector GetLsLocations(DB *db, WorkingFiles *wfiles, - const std::vector &uses) { - std::vector ret; - for (Use use : uses) - if (auto loc = GetLsLocation(db, wfiles, use)) - ret.push_back(*loc); - std::sort(ret.begin(), ret.end()); - ret.erase(std::unique(ret.begin(), ret.end()), ret.end()); - if (ret.size() > g_config->xref.maxNum) - ret.resize(g_config->xref.maxNum); - return ret; -} - -SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) { - SymbolKind ret; - if (sym.kind == Kind::File) - ret = SymbolKind::File; - else { - ret = SymbolKind::Unknown; - WithEntity(db, sym, [&](const auto &entity) { - for (auto &def : entity.def) { - ret = def.kind; - break; - } - }); - } - return ret; -} - -std::optional GetSymbolInfo(DB *db, SymbolIdx sym, - bool detailed) { - switch (sym.kind) { - case Kind::Invalid: - break; - case Kind::File: { - QueryFile &file = db->GetFile(sym); - if (!file.def) - break; - - SymbolInformation info; - info.name = file.def->path; - info.kind = SymbolKind::File; - return info; - } - default: { - SymbolInformation info; - EachEntityDef(db, sym, [&](const auto &def) { - if (detailed) - info.name = def.detailed_name; - else - info.name = def.Name(true); - info.kind = def.kind; - return false; - }); - return info; - } - } - - return std::nullopt; -} - -std::vector FindSymbolsAtLocation(WorkingFile *wfile, - QueryFile *file, Position &ls_pos, - bool smallest) { - std::vector symbols; - // If multiVersion > 0, index may not exist and thus index_lines is empty. - if (wfile && wfile->index_lines.size()) { - if (auto line = wfile->GetIndexPosFromBufferPos( - ls_pos.line, &ls_pos.character, false)) { - ls_pos.line = *line; - } else { - ls_pos.line = -1; - return {}; - } - } - - for (auto [sym, refcnt] : file->symbol2refcnt) - if (refcnt > 0 && sym.range.Contains(ls_pos.line, ls_pos.character)) - symbols.push_back(sym); - - // Order shorter ranges first, since they are more detailed/precise. This is - // important for macros which generate code so that we can resolving the - // macro argument takes priority over the entire macro body. - // - // Order Kind::Var before Kind::Type. Macro calls are treated as Var - // currently. If a macro expands to tokens led by a Kind::Type, the macro and - // the Type have the same range. We want to find the macro definition instead - // of the Type definition. - // - // Then order functions before other types, which makes goto definition work - // better on constructors. - std::sort( - symbols.begin(), symbols.end(), - [](const SymbolRef &a, const SymbolRef &b) { - int t = ComputeRangeSize(a.range) - ComputeRangeSize(b.range); - if (t) - return t < 0; - // MacroExpansion - if ((t = (a.role & Role::Dynamic) - (b.role & Role::Dynamic))) - return t > 0; - if ((t = (a.role & Role::Definition) - (b.role & Role::Definition))) - return t > 0; - // operator> orders Var/Func before Type. - t = static_cast(a.kind) - static_cast(b.kind); - if (t) - return t > 0; - return a.usr < b.usr; - }); - if (symbols.size() && smallest) { - SymbolRef sym = symbols[0]; - for (size_t i = 1; i < symbols.size(); i++) - if (!(sym.range == symbols[i].range && sym.kind == symbols[i].kind)) { - symbols.resize(i); - break; - } - } - - return symbols; -} -} // namespace ccls diff --git a/src/query_utils.hh b/src/query_utils.hh deleted file mode 100644 index 9803350f..00000000 --- a/src/query_utils.hh +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2017-2018 ccls Authors -// SPDX-License-Identifier: Apache-2.0 - -#pragma once - -#include "query.hh" -#include "working_files.hh" - -#include - -namespace ccls { -Maybe GetDefinitionSpell(DB *db, SymbolIdx sym); - -// Get defining declaration (if exists) or an arbitrary declaration (otherwise) -// for each id. -std::vector GetFuncDeclarations(DB *, const std::vector &); -std::vector GetTypeDeclarations(DB *, const std::vector &); -std::vector GetVarDeclarations(DB *, const std::vector &, unsigned); - -// Get non-defining declarations. -std::vector &GetNonDefDeclarations(DB *db, SymbolIdx sym); - -std::vector GetUsesForAllBases(DB *db, QueryFunc &root); -std::vector GetUsesForAllDerived(DB *db, QueryFunc &root); -std::optional GetLsRange(WorkingFile *working_file, - const Range &location); -DocumentUri GetLsDocumentUri(DB *db, int file_id, std::string *path); -DocumentUri GetLsDocumentUri(DB *db, int file_id); - -std::optional GetLsLocation(DB *db, WorkingFiles *wfiles, Use use); -std::optional GetLsLocation(DB *db, WorkingFiles *wfiles, - SymbolRef sym, int file_id); -std::vector GetLsLocations(DB *db, WorkingFiles *wfiles, - const std::vector &uses); -// Returns a symbol. The symbol will *NOT* have a location assigned. -std::optional GetSymbolInfo(DB *db, SymbolIdx sym, - bool detailed); - -std::vector FindSymbolsAtLocation(WorkingFile *working_file, - QueryFile *file, - Position &ls_pos, - bool smallest = false); - -template void WithEntity(DB *db, SymbolIdx sym, Fn &&fn) { - switch (sym.kind) { - case Kind::Invalid: - case Kind::File: - break; - case Kind::Func: - fn(db->GetFunc(sym)); - break; - case Kind::Type: - fn(db->GetType(sym)); - break; - case Kind::Var: - fn(db->GetVar(sym)); - break; - } -} - -template void EachEntityDef(DB *db, SymbolIdx sym, Fn &&fn) { - WithEntity(db, sym, [&](const auto &entity) { - for (auto &def : entity.def) - if (!fn(def)) - break; - }); -} - -template -void EachOccurrence(DB *db, SymbolIdx sym, bool include_decl, Fn &&fn) { - WithEntity(db, sym, [&](const auto &entity) { - for (Use use : entity.uses) - fn(use); - if (include_decl) { - for (auto &def : entity.def) - if (def.spell) - fn(*def.spell); - for (Use use : entity.declarations) - fn(use); - } - }); -} - -SymbolKind GetSymbolKind(DB *db, SymbolIdx sym); - -template -void EachDefinedFunc(DB *db, const std::vector &usrs, Fn &&fn) { - for (Usr usr : usrs) { - auto &obj = db->Func(usr); - if (!obj.def.empty()) - fn(obj); - } -} -} // namespace ccls