From ba39be4bcdf52017928522561b24d755460e2ba7 Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Sun, 16 Dec 2018 19:53:00 -0800 Subject: [PATCH] Support textDocument/declaration & LocationLink textDocument/{declaration,definition,typeDefinition} return either LocationLink[] or Location[] Add an initialization option client.linkSupport . When it is false, ccls will return Location[] disregarding client's linkSupport. `struct LocationLink` does not include originSelectionRange as it is wasteful. --- src/config.hh | 30 ++++----- src/lsp.cc | 4 -- src/lsp.hh | 25 +++++++- src/message_handler.cc | 48 +++++++++------ src/message_handler.hh | 3 + src/messages/ccls_vars.cc | 10 +-- src/messages/initialize.cc | 17 +++-- src/messages/textDocument_definition.cc | 82 +++++++++++++++---------- src/query.cc | 29 ++++----- src/query.hh | 6 +- 10 files changed, 154 insertions(+), 100 deletions(-) diff --git a/src/config.hh b/src/config.hh index 7bfea4af..055da31c 100644 --- a/src/config.hh +++ b/src/config.hh @@ -73,6 +73,8 @@ struct Config { struct ClientCapability { // TextDocumentClientCapabilities.documentSymbol.hierarchicalDocumentSymbolSupport bool hierarchicalDocumentSymbolSupport = true; + // TextDocumentClientCapabilities.definition.linkSupport + bool linkSupport = true; // TextDocumentClientCapabilities.completion.completionItem.snippetSupport bool snippetSupport = true; } client; @@ -248,30 +250,28 @@ struct Config { } xref; }; REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, pathMappings, - resourceDir); + resourceDir); REFLECT_STRUCT(Config::ClientCapability, hierarchicalDocumentSymbolSupport, - snippetSupport); + linkSupport, snippetSupport); REFLECT_STRUCT(Config::CodeLens, localVariables); REFLECT_STRUCT(Config::Completion::Include, blacklist, maxPathSize, - suffixWhitelist, whitelist); + suffixWhitelist, whitelist); REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel, - dropOldRequests, duplicateOptional, filterAndSort, include, - maxNum); + dropOldRequests, duplicateOptional, filterAndSort, include, + maxNum); REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave, - spellChecking, whitelist) -REFLECT_STRUCT(Config::Highlight, largeFileSize, lsRanges, blacklist, - whitelist) + spellChecking, whitelist) +REFLECT_STRUCT(Config::Highlight, largeFileSize, lsRanges, blacklist, whitelist) REFLECT_STRUCT(Config::Index, blacklist, comments, initialBlacklist, - initialWhitelist, multiVersion, multiVersionBlacklist, - multiVersionWhitelist, onChange, threads, trackDependency, - whitelist); + initialWhitelist, multiVersion, multiVersionBlacklist, + multiVersionWhitelist, onChange, threads, trackDependency, + whitelist); REFLECT_STRUCT(Config::Session, maxNum); REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort); REFLECT_STRUCT(Config::Xref, maxNum); -REFLECT_STRUCT(Config, compilationDatabaseCommand, - compilationDatabaseDirectory, cacheDirectory, cacheFormat, - clang, client, codeLens, completion, diagnostics, highlight, - index, session, workspaceSymbol, xref); +REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory, + cacheDirectory, cacheFormat, clang, client, codeLens, completion, + diagnostics, highlight, index, session, workspaceSymbol, xref); extern Config *g_config; diff --git a/src/lsp.cc b/src/lsp.cc index 73ad18d8..182fd2eb 100644 --- a/src/lsp.cc +++ b/src/lsp.cc @@ -48,10 +48,6 @@ DocumentUri DocumentUri::FromPath(const std::string &path) { return result; } -bool DocumentUri::operator==(const DocumentUri &other) const { - return raw_uri == other.raw_uri; -} - void DocumentUri::SetPath(const std::string &path) { // file:///c%3A/Users/jacob/Desktop/superindex/indexer/full_tests raw_uri = path; diff --git a/src/lsp.hh b/src/lsp.hh index f822bed3..212d49e0 100644 --- a/src/lsp.hh +++ b/src/lsp.hh @@ -60,7 +60,8 @@ constexpr char window_showMessage[] = "window/showMessage"; struct DocumentUri { static DocumentUri FromPath(const std::string &path); - bool operator==(const DocumentUri &other) const; + bool operator==(const DocumentUri &o) const { return raw_uri == o.raw_uri; } + bool operator<(const DocumentUri &o) const { return raw_uri < o.raw_uri; } void SetPath(const std::string &path); std::string GetPath() const; @@ -107,8 +108,26 @@ struct Location { return uri == o.uri && range == o.range; } bool operator<(const Location &o) const { - return !(uri.raw_uri == o.uri.raw_uri) ? uri.raw_uri < o.uri.raw_uri - : range < o.range; + return !(uri == o.uri) ? uri < o.uri : range < o.range; + } +}; + +struct LocationLink { + std::string targetUri; + lsRange targetRange; + lsRange targetSelectionRange; + explicit operator bool() const { return targetUri.size(); } + explicit operator Location() && { + return {DocumentUri{std::move(targetUri)}, targetSelectionRange}; + } + bool operator==(const LocationLink &o) const { + return targetUri == o.targetUri && + targetSelectionRange == o.targetSelectionRange; + } + bool operator<(const LocationLink &o) const { + return !(targetUri == o.targetUri) + ? targetUri < o.targetUri + : targetSelectionRange < o.targetSelectionRange; } }; diff --git a/src/message_handler.cc b/src/message_handler.cc index f3f536ad..478267c9 100644 --- a/src/message_handler.cc +++ b/src/message_handler.cc @@ -104,6 +104,21 @@ void ReplyOnce::NotReady(bool file) { Error(ErrorCode::InternalError, "not indexed"); } +void ReplyOnce::ReplyLocationLink(std::vector &result) { + std::sort(result.begin(), result.end()); + result.erase(std::unique(result.begin(), result.end()), result.end()); + if (result.size() > g_config->xref.maxNum) + result.resize(g_config->xref.maxNum); + if (g_config->client.linkSupport) { + (*this)(result); + } else { + std::vector result1; + for (auto &loc : result) + result1.emplace_back(std::move(loc)); + (*this)(result1); + } +} + void MessageHandler::Bind(const char *method, void (MessageHandler::*handler)(JsonReader &)) { method2notification[method] = [this, handler](JsonReader &reader) { @@ -143,6 +158,7 @@ void MessageHandler::Bind(const char *method, } MessageHandler::MessageHandler() { + // clang-format off Bind("$ccls/call", &MessageHandler::ccls_call); Bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo); Bind("$ccls/info", &MessageHandler::ccls_info); @@ -157,39 +173,31 @@ MessageHandler::MessageHandler() { Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction); Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens); Bind("textDocument/completion", &MessageHandler::textDocument_completion); + Bind("textDocument/declaration", &MessageHandler::textDocument_declaration); Bind("textDocument/definition", &MessageHandler::textDocument_definition); Bind("textDocument/didChange", &MessageHandler::textDocument_didChange); Bind("textDocument/didClose", &MessageHandler::textDocument_didClose); Bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen); Bind("textDocument/didSave", &MessageHandler::textDocument_didSave); - Bind("textDocument/documentHighlight", - &MessageHandler::textDocument_documentHighlight); + Bind("textDocument/documentHighlight", &MessageHandler::textDocument_documentHighlight); Bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink); - Bind("textDocument/documentSymbol", - &MessageHandler::textDocument_documentSymbol); + Bind("textDocument/documentSymbol", &MessageHandler::textDocument_documentSymbol); Bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange); Bind("textDocument/formatting", &MessageHandler::textDocument_formatting); Bind("textDocument/hover", &MessageHandler::textDocument_hover); - Bind("textDocument/implementation", - &MessageHandler::textDocument_implementation); - Bind("textDocument/onTypeFormatting", - &MessageHandler::textDocument_onTypeFormatting); - Bind("textDocument/rangeFormatting", - &MessageHandler::textDocument_rangeFormatting); + Bind("textDocument/implementation", &MessageHandler::textDocument_implementation); + Bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting); + Bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting); Bind("textDocument/references", &MessageHandler::textDocument_references); Bind("textDocument/rename", &MessageHandler::textDocument_rename); - Bind("textDocument/signatureHelp", - &MessageHandler::textDocument_signatureHelp); - Bind("textDocument/typeDefinition", - &MessageHandler::textDocument_typeDefinition); - Bind("workspace/didChangeConfiguration", - &MessageHandler::workspace_didChangeConfiguration); - Bind("workspace/didChangeWatchedFiles", - &MessageHandler::workspace_didChangeWatchedFiles); - Bind("workspace/didChangeWorkspaceFolders", - &MessageHandler::workspace_didChangeWorkspaceFolders); + Bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp); + Bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition); + Bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration); + Bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles); + Bind("workspace/didChangeWorkspaceFolders", &MessageHandler::workspace_didChangeWorkspaceFolders); Bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand); Bind("workspace/symbol", &MessageHandler::workspace_symbol); + // clang-format on } void MessageHandler::Run(InMessage &msg) { diff --git a/src/message_handler.hh b/src/message_handler.hh index a0b6f1ce..8ccde4a5 100644 --- a/src/message_handler.hh +++ b/src/message_handler.hh @@ -189,6 +189,7 @@ REFLECT_STRUCT(ResponseError, code, message); REFLECT_STRUCT(Position, line, character); REFLECT_STRUCT(lsRange, start, end); REFLECT_STRUCT(Location, uri, range); +REFLECT_STRUCT(LocationLink, targetUri, targetRange, targetSelectionRange); REFLECT_UNDERLYING_B(SymbolKind); REFLECT_STRUCT(TextDocumentIdentifier, uri); REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text); @@ -210,6 +211,7 @@ struct ReplyOnce { pipeline::ReplyError(id, [&](JsonWriter &w) { Reflect(w, err); }); } void NotReady(bool file); + void ReplyLocationLink(std::vector &result); }; struct MessageHandler { @@ -252,6 +254,7 @@ private: void textDocument_codeAction(CodeActionParam &, ReplyOnce &); void textDocument_codeLens(TextDocumentParam &, ReplyOnce &); void textDocument_completion(CompletionParam &, ReplyOnce &); + void textDocument_declaration(TextDocumentPositionParam &, ReplyOnce &); void textDocument_definition(TextDocumentPositionParam &, ReplyOnce &); void textDocument_didChange(TextDocumentDidChangeParam &); void textDocument_didClose(TextDocumentParam &); diff --git a/src/messages/ccls_vars.cc b/src/messages/ccls_vars.cc index 5153f28b..01c8f403 100644 --- a/src/messages/ccls_vars.cc +++ b/src/messages/ccls_vars.cc @@ -39,12 +39,14 @@ void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) { usr = def->type; [[fallthrough]]; } - case Kind::Type: - result = GetLsLocations( - db, wfiles, - GetVarDeclarations(db, db->Type(usr).instances, param.kind)); + case Kind::Type: { + for (DeclRef dr : + GetVarDeclarations(db, db->Type(usr).instances, param.kind)) + if (auto loc = GetLocationLink(db, wfiles, dr)) + result.push_back(Location(std::move(loc))); break; } + } } reply(result); } diff --git a/src/messages/initialize.cc b/src/messages/initialize.cc index a2d15848..a62d43e9 100644 --- a/src/messages/initialize.cc +++ b/src/messages/initialize.cc @@ -58,6 +58,7 @@ struct ServerCap { struct SignatureHelpOptions { std::vector triggerCharacters = {"(", ","}; } signatureHelpProvider; + bool declarationProvider = true; bool definitionProvider = true; bool typeDefinitionProvider = true; bool implementationProvider = true; @@ -109,7 +110,7 @@ REFLECT_STRUCT(ServerCap::Workspace::WorkspaceFolders, supported, changeNotifications); REFLECT_STRUCT(ServerCap::Workspace, workspaceFolders); REFLECT_STRUCT(ServerCap, textDocumentSync, hoverProvider, completionProvider, - signatureHelpProvider, definitionProvider, + signatureHelpProvider, declarationProvider, definitionProvider, implementationProvider, typeDefinitionProvider, referencesProvider, documentHighlightProvider, documentSymbolProvider, workspaceSymbolProvider, @@ -161,6 +162,11 @@ struct TextDocumentClientCap { } completionItem; } completion; + // Ignore declaration, implementation, typeDefinition + struct LinkSupport { + bool linkSupport = false; + } definition; + struct DocumentSymbol { bool hierarchicalDocumentSymbolSupport = false; } documentSymbol; @@ -171,7 +177,8 @@ REFLECT_STRUCT(TextDocumentClientCap::Completion::CompletionItem, REFLECT_STRUCT(TextDocumentClientCap::Completion, completionItem); REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol, hierarchicalDocumentSymbolSupport); -REFLECT_STRUCT(TextDocumentClientCap, completion, documentSymbol); +REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport); +REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol); struct ClientCap { WorkspaceClientCap workspace; @@ -272,11 +279,13 @@ void Initialize(MessageHandler *m, InitializeParam ¶m, ReplyOnce &reply) { // Client capabilities const auto &capabilities = param.capabilities; - g_config->client.snippetSupport &= - capabilities.textDocument.completion.completionItem.snippetSupport; g_config->client.hierarchicalDocumentSymbolSupport &= capabilities.textDocument.documentSymbol .hierarchicalDocumentSymbolSupport; + g_config->client.linkSupport &= + capabilities.textDocument.definition.linkSupport; + g_config->client.snippetSupport &= + capabilities.textDocument.completion.completionItem.snippetSupport; // Ensure there is a resource directory. if (g_config->clang.resourceDir.empty()) diff --git a/src/messages/textDocument_definition.cc b/src/messages/textDocument_definition.cc index 9a321a1c..b643b93d 100644 --- a/src/messages/textDocument_definition.cc +++ b/src/messages/textDocument_definition.cc @@ -33,6 +33,27 @@ std::vector GetNonDefDeclarationTargets(DB *db, SymbolRef sym) { } } // namespace +void MessageHandler::textDocument_declaration(TextDocumentPositionParam ¶m, + ReplyOnce &reply) { + int file_id; + QueryFile *file = FindFile(param.textDocument.uri.GetPath(), &file_id); + WorkingFile *wf = file ? wfiles->GetFile(file->def->path) : nullptr; + if (!wf) { + reply.NotReady(file); + return; + } + + std::vector result; + Position &ls_pos = param.position; + for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) + for (DeclRef dr : GetNonDefDeclarations(db, sym)) + if (!(dr.file_id == file_id && + dr.range.Contains(ls_pos.line, ls_pos.character))) + if (auto loc = GetLocationLink(db, wfiles, dr)) + result.push_back(loc); + reply.ReplyLocationLink(result); +} + void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m, ReplyOnce &reply) { int file_id; @@ -43,54 +64,52 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m, return; } - std::vector result; - Maybe on_def; + std::vector result; + Maybe on_def; Position &ls_pos = param.position; for (SymbolRef sym : FindSymbolsAtLocation(wf, file, ls_pos, true)) { // Special cases which are handled: // - symbol has declaration but no definition (ie, pure virtual) // - goto declaration while in definition of recursive type - std::vector uses; + std::vector drs; EachEntityDef(db, sym, [&](const auto &def) { if (def.spell) { - Use spell = *def.spell; + DeclRef spell = *def.spell; if (spell.file_id == file_id && spell.range.Contains(ls_pos.line, ls_pos.character)) { on_def = spell; - uses.clear(); + drs.clear(); return false; } - uses.push_back(spell); + drs.push_back(spell); } return true; }); // |uses| is empty if on a declaration/definition, otherwise it includes // all declarations/definitions. - if (uses.empty()) { - for (Use use : GetNonDefDeclarationTargets(db, sym)) - if (!(use.file_id == file_id && - use.range.Contains(ls_pos.line, ls_pos.character))) - uses.push_back(use); + if (drs.empty()) { + for (DeclRef dr : GetNonDefDeclarationTargets(db, sym)) + if (!(dr.file_id == file_id && + dr.range.Contains(ls_pos.line, ls_pos.character))) + drs.push_back(dr); // There is no declaration but the cursor is on a definition. - if (uses.empty() && on_def) - uses.push_back(*on_def); + if (drs.empty() && on_def) + drs.push_back(*on_def); } - auto locs = GetLsLocations(db, wfiles, uses); - result.insert(result.end(), locs.begin(), locs.end()); + for (DeclRef dr : drs) + if (auto loc = GetLocationLink(db, wfiles, dr)) + result.push_back(loc); } - if (result.size()) { - std::sort(result.begin(), result.end()); - result.erase(std::unique(result.begin(), result.end()), result.end()); - } else { + if (result.empty()) { Maybe range; // Check #include for (const IndexInclude &include : file->def->includes) { if (include.line == ls_pos.line) { result.push_back( - Location{DocumentUri::FromPath(include.resolved_path)}); + {DocumentUri::FromPath(include.resolved_path).raw_uri}); range = {{0, 0}, {0, 0}}; break; } @@ -148,13 +167,13 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam ¶m, if (best_sym.kind != Kind::Invalid) { Maybe dr = GetDefinitionSpell(db, best_sym); assert(dr); - if (auto loc = GetLsLocation(db, wfiles, *dr)) - result.push_back(*loc); + if (auto loc = GetLocationLink(db, wfiles, *dr)) + result.push_back(loc); } } } - reply(result); + reply.ReplyLocationLink(result); } void MessageHandler::textDocument_typeDefinition( @@ -166,17 +185,16 @@ void MessageHandler::textDocument_typeDefinition( return; } - std::vector result; + std::vector result; auto Add = [&](const QueryType &type) { for (const auto &def : type.def) - if (def.spell) { - if (auto ls_loc = GetLsLocation(db, wfiles, *def.spell)) - result.push_back(*ls_loc); - } + if (def.spell) + if (auto loc = GetLocationLink(db, wfiles, *def.spell)) + result.push_back(loc); if (result.empty()) for (const DeclRef &dr : type.declarations) - if (auto ls_loc = GetLsLocation(db, wfiles, dr)) - result.push_back(*ls_loc); + if (auto loc = GetLocationLink(db, wfiles, dr)) + result.push_back(loc); }; for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { switch (sym.kind) { @@ -199,8 +217,6 @@ void MessageHandler::textDocument_typeDefinition( } } - std::sort(result.begin(), result.end()); - result.erase(std::unique(result.begin(), result.end()), result.end()); - reply(result); + reply.ReplyLocationLink(result); } } // namespace ccls diff --git a/src/query.cc b/src/query.cc index 3c706e84..6bc3ae11 100644 --- a/src/query.cc +++ b/src/query.cc @@ -520,9 +520,9 @@ std::vector GetFuncDeclarations(DB *db, const std::vector &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; +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); @@ -669,17 +669,18 @@ std::optional GetLsLocation(DB *db, WorkingFiles *wfiles, 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; +LocationLink GetLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr) { + std::string path; + DocumentUri uri = GetLsDocumentUri(db, dr.file_id, &path); + if (auto range = GetLsRange(wfiles->GetFile(path), dr.range)) + if (auto extent = GetLsRange(wfiles->GetFile(path), dr.extent)) { + LocationLink ret; + ret.targetUri = uri.raw_uri; + ret.targetSelectionRange = *range; + ret.targetRange = extent->Includes(*range) ? *extent : *range; + return ret; + } + return {}; } SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) { diff --git a/src/query.hh b/src/query.hh index c8e3a659..2277f90e 100644 --- a/src/query.hh +++ b/src/query.hh @@ -189,7 +189,7 @@ Maybe GetDefinitionSpell(DB *db, SymbolIdx sym); // 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); +std::vector GetVarDeclarations(DB *, const std::vector &, unsigned); // Get non-defining declarations. std::vector &GetNonDefDeclarations(DB *db, SymbolIdx sym); @@ -204,8 +204,8 @@ 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); +LocationLink GetLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr); + // Returns a symbol. The symbol will *NOT* have a location assigned. std::optional GetSymbolInfo(DB *db, SymbolIdx sym, bool detailed);