diff --git a/src/message_handler.cc b/src/message_handler.cc index 451fc525..e485f282 100644 --- a/src/message_handler.cc +++ b/src/message_handler.cc @@ -48,7 +48,7 @@ MAKE_REFLECT_STRUCT(DidChangeWatchedFilesParam::Event, uri, type); MAKE_REFLECT_STRUCT(DidChangeWatchedFilesParam, changes); MAKE_REFLECT_STRUCT(DidChangeWorkspaceFoldersParam::Event, added, removed); MAKE_REFLECT_STRUCT(DidChangeWorkspaceFoldersParam, event); -MAKE_REFLECT_STRUCT(WorkspaceSymbolParam, query); +MAKE_REFLECT_STRUCT(WorkspaceSymbolParam, query, folders); namespace { struct CclsSemanticHighlightSymbol { diff --git a/src/message_handler.hh b/src/message_handler.hh index 5eb7e608..91108a23 100644 --- a/src/message_handler.hh +++ b/src/message_handler.hh @@ -162,6 +162,9 @@ struct DidChangeWorkspaceFoldersParam { }; struct WorkspaceSymbolParam { std::string query; + + // ccls extensions + std::vector folders; }; // TODO llvm 8 llvm::unique_function diff --git a/src/messages/textDocument_definition.cc b/src/messages/textDocument_definition.cc index ed77efbf..40f78e5f 100644 --- a/src/messages/textDocument_definition.cc +++ b/src/messages/textDocument_definition.cc @@ -10,10 +10,10 @@ namespace ccls { namespace { -std::vector GetNonDefDeclarationTargets(DB *db, SymbolRef sym) { +std::vector GetNonDefDeclarationTargets(DB *db, SymbolRef sym) { switch (sym.kind) { case SymbolKind::Var: { - std::vector ret = GetNonDefDeclarations(db, sym); + std::vector ret = GetNonDefDeclarations(db, sym); // If there is no declaration, jump to its type. if (ret.empty()) { for (auto &def : db->GetVar(sym).def) diff --git a/src/messages/textDocument_references.cc b/src/messages/textDocument_references.cc index d8be0be4..3596113d 100644 --- a/src/messages/textDocument_references.cc +++ b/src/messages/textDocument_references.cc @@ -10,18 +10,23 @@ namespace ccls { namespace { struct ReferenceParam : public TextDocumentPositionParam { struct Context { - bool base = true; - // Exclude references with any |Role| bits set. - Role excludeRole = Role::None; // Include the declaration of the current symbol. bool includeDeclaration = false; - // Include references with all |Role| bits set. - Role role = Role::None; } context; + + // ccls extension + // If not empty, restrict to specified folders. + std::vector folders; + // For Type, also return references of base types. + bool base = true; + // Exclude references with any |Role| bits set. + Role excludeRole = Role::None; + // Include references with all |Role| bits set. + Role role = Role::None; }; -MAKE_REFLECT_STRUCT(ReferenceParam::Context, base, excludeRole, - includeDeclaration, role); -MAKE_REFLECT_STRUCT(ReferenceParam, textDocument, position, context); +MAKE_REFLECT_STRUCT(ReferenceParam::Context, includeDeclaration); +MAKE_REFLECT_STRUCT(ReferenceParam, textDocument, position, context, folders, + base, excludeRole, role); } // namespace void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) { @@ -30,10 +35,13 @@ void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) { QueryFile *file = FindFile(reply, param.textDocument.uri.GetPath()); if (!file) return; - std::vector result; WorkingFile *wfile = wfiles->GetFileByFilename(file->def->path); - if (!file) + if (!wfile) return; + for (auto &folder : param.folders) + EnsureEndsInSlash(folder); + std::vector file_set = db->GetFileSet(param.folders); + std::vector result; std::unordered_set seen_uses; int line = param.position.line; @@ -44,24 +52,23 @@ void MessageHandler::textDocument_references(Reader &reader, ReplyOnce &reply) { seen.insert(sym.usr); std::vector stack{sym.usr}; if (sym.kind != SymbolKind::Func) - param.context.base = false; + param.base = false; while (stack.size()) { sym.usr = stack.back(); stack.pop_back(); auto fn = [&](Use use, lsSymbolKind parent_kind) { - if (Role(use.role & param.context.role) == param.context.role && - !(use.role & param.context.excludeRole) && - seen_uses.insert(use).second) - if (auto loc = GetLsLocation(db, wfiles, use)) { + if (file_set[use.file_id] && + Role(use.role & param.role) == param.role && + !(use.role & param.excludeRole) && seen_uses.insert(use).second) + if (auto loc = GetLsLocation(db, wfiles, use)) result.push_back(*loc); - } }; WithEntity(db, sym, [&](const auto &entity) { lsSymbolKind parent_kind = lsSymbolKind::Unknown; for (auto &def : entity.def) if (def.spell) { parent_kind = GetSymbolKind(db, sym); - if (param.context.base) + if (param.base) for (Usr usr : def.GetBases()) if (!seen.count(usr)) { seen.insert(usr); diff --git a/src/messages/workspace.cc b/src/messages/workspace.cc index c07effe3..0abcd06c 100644 --- a/src/messages/workspace.cc +++ b/src/messages/workspace.cc @@ -10,6 +10,7 @@ #include "query_utils.hh" #include +#include #include #include @@ -84,23 +85,35 @@ void MessageHandler::workspace_didChangeWorkspaceFolders( namespace { // Lookup |symbol| in |db| and insert the value into |result|. bool AddSymbol( - DB *db, WorkingFiles *wfiles, SymbolIdx sym, bool use_detailed, + DB *db, WorkingFiles *wfiles, const std::vector &file_set, + SymbolIdx sym, bool use_detailed, std::vector> *result) { std::optional info = GetSymbolInfo(db, sym, true); if (!info) return false; - Use loc; - if (Maybe dr = GetDefinitionSpell(db, sym)) - loc = *dr; - else { - auto decls = GetNonDefDeclarations(db, sym); - if (decls.empty()) - return false; - loc = decls[0]; + Maybe dr; + bool in_folder = false; + WithEntity(db, sym, [&](const auto &entity) { + for (auto &def : entity.def) + if (def.spell) { + dr = def.spell; + if (!in_folder && (in_folder = file_set[def.spell->file_id])) + break; + } + }); + if (!dr) { + auto &decls = GetNonDefDeclarations(db, sym); + for (auto &dr1 : decls) { + dr = dr1; + if (!in_folder && (in_folder = file_set[dr1.file_id])) + break; + } } + if (!in_folder) + return false; - std::optional ls_location = GetLsLocation(db, wfiles, loc); + std::optional ls_location = GetLsLocation(db, wfiles, *dr); if (!ls_location) return false; info->location = *ls_location; @@ -112,7 +125,10 @@ bool AddSymbol( void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m, ReplyOnce &reply) { std::vector result; - std::string query = param.query; + const std::string &query = param.query; + for (auto &folder : param.folders) + EnsureEndsInSlash(folder); + std::vector file_set = db->GetFileSet(param.folders); // {symbol info, matching detailed_name or short_name, index} std::vector> cands; @@ -129,7 +145,7 @@ void MessageHandler::workspace_symbol(WorkspaceSymbolParam ¶m, std::string_view detailed_name = db->GetSymbolName(sym, true); int pos = ReverseSubseqMatch(query_without_space, detailed_name, sensitive); return pos >= 0 && - AddSymbol(db, wfiles, sym, + AddSymbol(db, wfiles, file_set, sym, detailed_name.find(':', pos) != std::string::npos, &cands) && cands.size() >= g_config->workspaceSymbol.maxNum; diff --git a/src/query.cc b/src/query.cc index b29b8d1c..f51e8726 100644 --- a/src/query.cc +++ b/src/query.cc @@ -456,4 +456,22 @@ std::string_view DB::GetSymbolName(SymbolIdx sym, bool qualified) { } return ""; } + +std::vector DB::GetFileSet(const std::vector &folders) { + if (folders.empty()) + return std::vector(files.size(), 1); + std::vector file_set(files.size()); + for (QueryFile &file : files) + if (file.def) { + bool ok = false; + for (auto &folder : folders) + if (llvm::StringRef(file.def->path).startswith(folder)) { + ok = true; + break; + } + if (ok) + file_set[file.id] = 1; + } + return file_set; +} } // namespace ccls diff --git a/src/query.hh b/src/query.hh index ad255373..88af54dd 100644 --- a/src/query.hh +++ b/src/query.hh @@ -170,6 +170,7 @@ struct DB { void Update(const Lid2file_id &, int file_id, std::vector> &&us); std::string_view GetSymbolName(SymbolIdx sym, bool qualified); + std::vector GetFileSet(const std::vector &folders); bool HasFunc(Usr usr) const { return func_usr.count(usr); } bool HasType(Usr usr) const { return type_usr.count(usr); } diff --git a/src/query_utils.cc b/src/query_utils.cc index d1c5ff2f..03ca15e8 100644 --- a/src/query_utils.cc +++ b/src/query_utils.cc @@ -83,25 +83,19 @@ std::vector GetVarDeclarations(DB *db, const std::vector &usrs, return ret; } -std::vector GetNonDefDeclarations(DB *db, SymbolIdx sym) { - std::vector ret; +std::vector &GetNonDefDeclarations(DB *db, SymbolIdx sym) { + static std::vector empty; switch (sym.kind) { case SymbolKind::Func: - for (auto &d : db->GetFunc(sym).declarations) - ret.push_back(d); - break; + return db->GetFunc(sym).declarations; case SymbolKind::Type: - for (auto &d : db->GetType(sym).declarations) - ret.push_back(d); - break; + return db->GetType(sym).declarations; case SymbolKind::Var: - for (auto &d : db->GetVar(sym).declarations) - ret.push_back(d); - break; + return db->GetVar(sym).declarations; default: break; } - return ret; + return empty; } std::vector GetUsesForAllBases(DB *db, QueryFunc &root) { diff --git a/src/query_utils.hh b/src/query_utils.hh index 3eb1e81a..e91a0f33 100644 --- a/src/query_utils.hh +++ b/src/query_utils.hh @@ -18,7 +18,7 @@ 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 &GetNonDefDeclarations(DB *db, SymbolIdx sym); std::vector GetUsesForAllBases(DB *db, QueryFunc &root); std::vector GetUsesForAllDerived(DB *db, QueryFunc &root);