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.
This commit is contained in:
Fangrui Song 2018-12-16 19:53:00 -08:00
parent 926ea421e6
commit ba39be4bcd
10 changed files with 154 additions and 100 deletions

View File

@ -73,6 +73,8 @@ struct Config {
struct ClientCapability { struct ClientCapability {
// TextDocumentClientCapabilities.documentSymbol.hierarchicalDocumentSymbolSupport // TextDocumentClientCapabilities.documentSymbol.hierarchicalDocumentSymbolSupport
bool hierarchicalDocumentSymbolSupport = true; bool hierarchicalDocumentSymbolSupport = true;
// TextDocumentClientCapabilities.definition.linkSupport
bool linkSupport = true;
// TextDocumentClientCapabilities.completion.completionItem.snippetSupport // TextDocumentClientCapabilities.completion.completionItem.snippetSupport
bool snippetSupport = true; bool snippetSupport = true;
} client; } client;
@ -250,7 +252,7 @@ struct Config {
REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, pathMappings, REFLECT_STRUCT(Config::Clang, excludeArgs, extraArgs, pathMappings,
resourceDir); resourceDir);
REFLECT_STRUCT(Config::ClientCapability, hierarchicalDocumentSymbolSupport, REFLECT_STRUCT(Config::ClientCapability, hierarchicalDocumentSymbolSupport,
snippetSupport); linkSupport, snippetSupport);
REFLECT_STRUCT(Config::CodeLens, localVariables); REFLECT_STRUCT(Config::CodeLens, localVariables);
REFLECT_STRUCT(Config::Completion::Include, blacklist, maxPathSize, REFLECT_STRUCT(Config::Completion::Include, blacklist, maxPathSize,
suffixWhitelist, whitelist); suffixWhitelist, whitelist);
@ -259,8 +261,7 @@ REFLECT_STRUCT(Config::Completion, caseSensitivity, detailedLabel,
maxNum); maxNum);
REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave, REFLECT_STRUCT(Config::Diagnostics, blacklist, onChange, onOpen, onSave,
spellChecking, whitelist) spellChecking, whitelist)
REFLECT_STRUCT(Config::Highlight, largeFileSize, lsRanges, blacklist, REFLECT_STRUCT(Config::Highlight, largeFileSize, lsRanges, blacklist, whitelist)
whitelist)
REFLECT_STRUCT(Config::Index, blacklist, comments, initialBlacklist, REFLECT_STRUCT(Config::Index, blacklist, comments, initialBlacklist,
initialWhitelist, multiVersion, multiVersionBlacklist, initialWhitelist, multiVersion, multiVersionBlacklist,
multiVersionWhitelist, onChange, threads, trackDependency, multiVersionWhitelist, onChange, threads, trackDependency,
@ -268,10 +269,9 @@ REFLECT_STRUCT(Config::Index, blacklist, comments, initialBlacklist,
REFLECT_STRUCT(Config::Session, maxNum); REFLECT_STRUCT(Config::Session, maxNum);
REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort); REFLECT_STRUCT(Config::WorkspaceSymbol, caseSensitivity, maxNum, sort);
REFLECT_STRUCT(Config::Xref, maxNum); REFLECT_STRUCT(Config::Xref, maxNum);
REFLECT_STRUCT(Config, compilationDatabaseCommand, REFLECT_STRUCT(Config, compilationDatabaseCommand, compilationDatabaseDirectory,
compilationDatabaseDirectory, cacheDirectory, cacheFormat, cacheDirectory, cacheFormat, clang, client, codeLens, completion,
clang, client, codeLens, completion, diagnostics, highlight, diagnostics, highlight, index, session, workspaceSymbol, xref);
index, session, workspaceSymbol, xref);
extern Config *g_config; extern Config *g_config;

View File

@ -48,10 +48,6 @@ DocumentUri DocumentUri::FromPath(const std::string &path) {
return result; return result;
} }
bool DocumentUri::operator==(const DocumentUri &other) const {
return raw_uri == other.raw_uri;
}
void DocumentUri::SetPath(const std::string &path) { void DocumentUri::SetPath(const std::string &path) {
// file:///c%3A/Users/jacob/Desktop/superindex/indexer/full_tests // file:///c%3A/Users/jacob/Desktop/superindex/indexer/full_tests
raw_uri = path; raw_uri = path;

View File

@ -60,7 +60,8 @@ constexpr char window_showMessage[] = "window/showMessage";
struct DocumentUri { struct DocumentUri {
static DocumentUri FromPath(const std::string &path); 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); void SetPath(const std::string &path);
std::string GetPath() const; std::string GetPath() const;
@ -107,8 +108,26 @@ struct Location {
return uri == o.uri && range == o.range; return uri == o.uri && range == o.range;
} }
bool operator<(const Location &o) const { bool operator<(const Location &o) const {
return !(uri.raw_uri == o.uri.raw_uri) ? uri.raw_uri < o.uri.raw_uri return !(uri == o.uri) ? uri < o.uri : range < o.range;
: 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;
} }
}; };

View File

@ -104,6 +104,21 @@ void ReplyOnce::NotReady(bool file) {
Error(ErrorCode::InternalError, "not indexed"); Error(ErrorCode::InternalError, "not indexed");
} }
void ReplyOnce::ReplyLocationLink(std::vector<LocationLink> &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<Location> result1;
for (auto &loc : result)
result1.emplace_back(std::move(loc));
(*this)(result1);
}
}
void MessageHandler::Bind(const char *method, void MessageHandler::Bind(const char *method,
void (MessageHandler::*handler)(JsonReader &)) { void (MessageHandler::*handler)(JsonReader &)) {
method2notification[method] = [this, handler](JsonReader &reader) { method2notification[method] = [this, handler](JsonReader &reader) {
@ -143,6 +158,7 @@ void MessageHandler::Bind(const char *method,
} }
MessageHandler::MessageHandler() { MessageHandler::MessageHandler() {
// clang-format off
Bind("$ccls/call", &MessageHandler::ccls_call); Bind("$ccls/call", &MessageHandler::ccls_call);
Bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo); Bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo);
Bind("$ccls/info", &MessageHandler::ccls_info); Bind("$ccls/info", &MessageHandler::ccls_info);
@ -157,39 +173,31 @@ MessageHandler::MessageHandler() {
Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction); Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens); Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
Bind("textDocument/completion", &MessageHandler::textDocument_completion); Bind("textDocument/completion", &MessageHandler::textDocument_completion);
Bind("textDocument/declaration", &MessageHandler::textDocument_declaration);
Bind("textDocument/definition", &MessageHandler::textDocument_definition); Bind("textDocument/definition", &MessageHandler::textDocument_definition);
Bind("textDocument/didChange", &MessageHandler::textDocument_didChange); Bind("textDocument/didChange", &MessageHandler::textDocument_didChange);
Bind("textDocument/didClose", &MessageHandler::textDocument_didClose); Bind("textDocument/didClose", &MessageHandler::textDocument_didClose);
Bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen); Bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen);
Bind("textDocument/didSave", &MessageHandler::textDocument_didSave); Bind("textDocument/didSave", &MessageHandler::textDocument_didSave);
Bind("textDocument/documentHighlight", Bind("textDocument/documentHighlight", &MessageHandler::textDocument_documentHighlight);
&MessageHandler::textDocument_documentHighlight);
Bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink); Bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink);
Bind("textDocument/documentSymbol", Bind("textDocument/documentSymbol", &MessageHandler::textDocument_documentSymbol);
&MessageHandler::textDocument_documentSymbol);
Bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange); Bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange);
Bind("textDocument/formatting", &MessageHandler::textDocument_formatting); Bind("textDocument/formatting", &MessageHandler::textDocument_formatting);
Bind("textDocument/hover", &MessageHandler::textDocument_hover); Bind("textDocument/hover", &MessageHandler::textDocument_hover);
Bind("textDocument/implementation", Bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
&MessageHandler::textDocument_implementation); Bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
Bind("textDocument/onTypeFormatting", Bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
&MessageHandler::textDocument_onTypeFormatting);
Bind("textDocument/rangeFormatting",
&MessageHandler::textDocument_rangeFormatting);
Bind("textDocument/references", &MessageHandler::textDocument_references); Bind("textDocument/references", &MessageHandler::textDocument_references);
Bind("textDocument/rename", &MessageHandler::textDocument_rename); Bind("textDocument/rename", &MessageHandler::textDocument_rename);
Bind("textDocument/signatureHelp", Bind("textDocument/signatureHelp", &MessageHandler::textDocument_signatureHelp);
&MessageHandler::textDocument_signatureHelp); Bind("textDocument/typeDefinition", &MessageHandler::textDocument_typeDefinition);
Bind("textDocument/typeDefinition", Bind("workspace/didChangeConfiguration", &MessageHandler::workspace_didChangeConfiguration);
&MessageHandler::textDocument_typeDefinition); Bind("workspace/didChangeWatchedFiles", &MessageHandler::workspace_didChangeWatchedFiles);
Bind("workspace/didChangeConfiguration", Bind("workspace/didChangeWorkspaceFolders", &MessageHandler::workspace_didChangeWorkspaceFolders);
&MessageHandler::workspace_didChangeConfiguration);
Bind("workspace/didChangeWatchedFiles",
&MessageHandler::workspace_didChangeWatchedFiles);
Bind("workspace/didChangeWorkspaceFolders",
&MessageHandler::workspace_didChangeWorkspaceFolders);
Bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand); Bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand);
Bind("workspace/symbol", &MessageHandler::workspace_symbol); Bind("workspace/symbol", &MessageHandler::workspace_symbol);
// clang-format on
} }
void MessageHandler::Run(InMessage &msg) { void MessageHandler::Run(InMessage &msg) {

View File

@ -189,6 +189,7 @@ REFLECT_STRUCT(ResponseError, code, message);
REFLECT_STRUCT(Position, line, character); REFLECT_STRUCT(Position, line, character);
REFLECT_STRUCT(lsRange, start, end); REFLECT_STRUCT(lsRange, start, end);
REFLECT_STRUCT(Location, uri, range); REFLECT_STRUCT(Location, uri, range);
REFLECT_STRUCT(LocationLink, targetUri, targetRange, targetSelectionRange);
REFLECT_UNDERLYING_B(SymbolKind); REFLECT_UNDERLYING_B(SymbolKind);
REFLECT_STRUCT(TextDocumentIdentifier, uri); REFLECT_STRUCT(TextDocumentIdentifier, uri);
REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text); REFLECT_STRUCT(TextDocumentItem, uri, languageId, version, text);
@ -210,6 +211,7 @@ struct ReplyOnce {
pipeline::ReplyError(id, [&](JsonWriter &w) { Reflect(w, err); }); pipeline::ReplyError(id, [&](JsonWriter &w) { Reflect(w, err); });
} }
void NotReady(bool file); void NotReady(bool file);
void ReplyLocationLink(std::vector<LocationLink> &result);
}; };
struct MessageHandler { struct MessageHandler {
@ -252,6 +254,7 @@ private:
void textDocument_codeAction(CodeActionParam &, ReplyOnce &); void textDocument_codeAction(CodeActionParam &, ReplyOnce &);
void textDocument_codeLens(TextDocumentParam &, ReplyOnce &); void textDocument_codeLens(TextDocumentParam &, ReplyOnce &);
void textDocument_completion(CompletionParam &, ReplyOnce &); void textDocument_completion(CompletionParam &, ReplyOnce &);
void textDocument_declaration(TextDocumentPositionParam &, ReplyOnce &);
void textDocument_definition(TextDocumentPositionParam &, ReplyOnce &); void textDocument_definition(TextDocumentPositionParam &, ReplyOnce &);
void textDocument_didChange(TextDocumentDidChangeParam &); void textDocument_didChange(TextDocumentDidChangeParam &);
void textDocument_didClose(TextDocumentParam &); void textDocument_didClose(TextDocumentParam &);

View File

@ -39,13 +39,15 @@ void MessageHandler::ccls_vars(JsonReader &reader, ReplyOnce &reply) {
usr = def->type; usr = def->type;
[[fallthrough]]; [[fallthrough]];
} }
case Kind::Type: case Kind::Type: {
result = GetLsLocations( for (DeclRef dr :
db, wfiles, GetVarDeclarations(db, db->Type(usr).instances, param.kind))
GetVarDeclarations(db, db->Type(usr).instances, param.kind)); if (auto loc = GetLocationLink(db, wfiles, dr))
result.push_back(Location(std::move(loc)));
break; break;
} }
} }
}
reply(result); reply(result);
} }
} // namespace ccls } // namespace ccls

View File

@ -58,6 +58,7 @@ struct ServerCap {
struct SignatureHelpOptions { struct SignatureHelpOptions {
std::vector<const char *> triggerCharacters = {"(", ","}; std::vector<const char *> triggerCharacters = {"(", ","};
} signatureHelpProvider; } signatureHelpProvider;
bool declarationProvider = true;
bool definitionProvider = true; bool definitionProvider = true;
bool typeDefinitionProvider = true; bool typeDefinitionProvider = true;
bool implementationProvider = true; bool implementationProvider = true;
@ -109,7 +110,7 @@ REFLECT_STRUCT(ServerCap::Workspace::WorkspaceFolders, supported,
changeNotifications); changeNotifications);
REFLECT_STRUCT(ServerCap::Workspace, workspaceFolders); REFLECT_STRUCT(ServerCap::Workspace, workspaceFolders);
REFLECT_STRUCT(ServerCap, textDocumentSync, hoverProvider, completionProvider, REFLECT_STRUCT(ServerCap, textDocumentSync, hoverProvider, completionProvider,
signatureHelpProvider, definitionProvider, signatureHelpProvider, declarationProvider, definitionProvider,
implementationProvider, typeDefinitionProvider, implementationProvider, typeDefinitionProvider,
referencesProvider, documentHighlightProvider, referencesProvider, documentHighlightProvider,
documentSymbolProvider, workspaceSymbolProvider, documentSymbolProvider, workspaceSymbolProvider,
@ -161,6 +162,11 @@ struct TextDocumentClientCap {
} completionItem; } completionItem;
} completion; } completion;
// Ignore declaration, implementation, typeDefinition
struct LinkSupport {
bool linkSupport = false;
} definition;
struct DocumentSymbol { struct DocumentSymbol {
bool hierarchicalDocumentSymbolSupport = false; bool hierarchicalDocumentSymbolSupport = false;
} documentSymbol; } documentSymbol;
@ -171,7 +177,8 @@ REFLECT_STRUCT(TextDocumentClientCap::Completion::CompletionItem,
REFLECT_STRUCT(TextDocumentClientCap::Completion, completionItem); REFLECT_STRUCT(TextDocumentClientCap::Completion, completionItem);
REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol, REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol,
hierarchicalDocumentSymbolSupport); hierarchicalDocumentSymbolSupport);
REFLECT_STRUCT(TextDocumentClientCap, completion, documentSymbol); REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport);
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol);
struct ClientCap { struct ClientCap {
WorkspaceClientCap workspace; WorkspaceClientCap workspace;
@ -272,11 +279,13 @@ void Initialize(MessageHandler *m, InitializeParam &param, ReplyOnce &reply) {
// Client capabilities // Client capabilities
const auto &capabilities = param.capabilities; const auto &capabilities = param.capabilities;
g_config->client.snippetSupport &=
capabilities.textDocument.completion.completionItem.snippetSupport;
g_config->client.hierarchicalDocumentSymbolSupport &= g_config->client.hierarchicalDocumentSymbolSupport &=
capabilities.textDocument.documentSymbol capabilities.textDocument.documentSymbol
.hierarchicalDocumentSymbolSupport; .hierarchicalDocumentSymbolSupport;
g_config->client.linkSupport &=
capabilities.textDocument.definition.linkSupport;
g_config->client.snippetSupport &=
capabilities.textDocument.completion.completionItem.snippetSupport;
// Ensure there is a resource directory. // Ensure there is a resource directory.
if (g_config->clang.resourceDir.empty()) if (g_config->clang.resourceDir.empty())

View File

@ -33,6 +33,27 @@ std::vector<DeclRef> GetNonDefDeclarationTargets(DB *db, SymbolRef sym) {
} }
} // namespace } // namespace
void MessageHandler::textDocument_declaration(TextDocumentPositionParam &param,
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<LocationLink> 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 &param, void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
int file_id; int file_id;
@ -43,54 +64,52 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
return; return;
} }
std::vector<Location> result; std::vector<LocationLink> result;
Maybe<Use> on_def; Maybe<DeclRef> on_def;
Position &ls_pos = param.position; Position &ls_pos = param.position;
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, ls_pos, true)) { for (SymbolRef sym : FindSymbolsAtLocation(wf, file, ls_pos, true)) {
// Special cases which are handled: // Special cases which are handled:
// - symbol has declaration but no definition (ie, pure virtual) // - symbol has declaration but no definition (ie, pure virtual)
// - goto declaration while in definition of recursive type // - goto declaration while in definition of recursive type
std::vector<Use> uses; std::vector<DeclRef> drs;
EachEntityDef(db, sym, [&](const auto &def) { EachEntityDef(db, sym, [&](const auto &def) {
if (def.spell) { if (def.spell) {
Use spell = *def.spell; DeclRef spell = *def.spell;
if (spell.file_id == file_id && if (spell.file_id == file_id &&
spell.range.Contains(ls_pos.line, ls_pos.character)) { spell.range.Contains(ls_pos.line, ls_pos.character)) {
on_def = spell; on_def = spell;
uses.clear(); drs.clear();
return false; return false;
} }
uses.push_back(spell); drs.push_back(spell);
} }
return true; return true;
}); });
// |uses| is empty if on a declaration/definition, otherwise it includes // |uses| is empty if on a declaration/definition, otherwise it includes
// all declarations/definitions. // all declarations/definitions.
if (uses.empty()) { if (drs.empty()) {
for (Use use : GetNonDefDeclarationTargets(db, sym)) for (DeclRef dr : GetNonDefDeclarationTargets(db, sym))
if (!(use.file_id == file_id && if (!(dr.file_id == file_id &&
use.range.Contains(ls_pos.line, ls_pos.character))) dr.range.Contains(ls_pos.line, ls_pos.character)))
uses.push_back(use); drs.push_back(dr);
// There is no declaration but the cursor is on a definition. // There is no declaration but the cursor is on a definition.
if (uses.empty() && on_def) if (drs.empty() && on_def)
uses.push_back(*on_def); drs.push_back(*on_def);
} }
auto locs = GetLsLocations(db, wfiles, uses); for (DeclRef dr : drs)
result.insert(result.end(), locs.begin(), locs.end()); if (auto loc = GetLocationLink(db, wfiles, dr))
result.push_back(loc);
} }
if (result.size()) { if (result.empty()) {
std::sort(result.begin(), result.end());
result.erase(std::unique(result.begin(), result.end()), result.end());
} else {
Maybe<Range> range; Maybe<Range> range;
// Check #include // Check #include
for (const IndexInclude &include : file->def->includes) { for (const IndexInclude &include : file->def->includes) {
if (include.line == ls_pos.line) { if (include.line == ls_pos.line) {
result.push_back( result.push_back(
Location{DocumentUri::FromPath(include.resolved_path)}); {DocumentUri::FromPath(include.resolved_path).raw_uri});
range = {{0, 0}, {0, 0}}; range = {{0, 0}, {0, 0}};
break; break;
} }
@ -148,13 +167,13 @@ void MessageHandler::textDocument_definition(TextDocumentPositionParam &param,
if (best_sym.kind != Kind::Invalid) { if (best_sym.kind != Kind::Invalid) {
Maybe<DeclRef> dr = GetDefinitionSpell(db, best_sym); Maybe<DeclRef> dr = GetDefinitionSpell(db, best_sym);
assert(dr); assert(dr);
if (auto loc = GetLsLocation(db, wfiles, *dr)) if (auto loc = GetLocationLink(db, wfiles, *dr))
result.push_back(*loc); result.push_back(loc);
} }
} }
} }
reply(result); reply.ReplyLocationLink(result);
} }
void MessageHandler::textDocument_typeDefinition( void MessageHandler::textDocument_typeDefinition(
@ -166,17 +185,16 @@ void MessageHandler::textDocument_typeDefinition(
return; return;
} }
std::vector<Location> result; std::vector<LocationLink> result;
auto Add = [&](const QueryType &type) { auto Add = [&](const QueryType &type) {
for (const auto &def : type.def) for (const auto &def : type.def)
if (def.spell) { if (def.spell)
if (auto ls_loc = GetLsLocation(db, wfiles, *def.spell)) if (auto loc = GetLocationLink(db, wfiles, *def.spell))
result.push_back(*ls_loc); result.push_back(loc);
}
if (result.empty()) if (result.empty())
for (const DeclRef &dr : type.declarations) for (const DeclRef &dr : type.declarations)
if (auto ls_loc = GetLsLocation(db, wfiles, dr)) if (auto loc = GetLocationLink(db, wfiles, dr))
result.push_back(*ls_loc); result.push_back(loc);
}; };
for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) { for (SymbolRef sym : FindSymbolsAtLocation(wf, file, param.position)) {
switch (sym.kind) { switch (sym.kind) {
@ -199,8 +217,6 @@ void MessageHandler::textDocument_typeDefinition(
} }
} }
std::sort(result.begin(), result.end()); reply.ReplyLocationLink(result);
result.erase(std::unique(result.begin(), result.end()), result.end());
reply(result);
} }
} // namespace ccls } // namespace ccls

View File

@ -520,9 +520,9 @@ std::vector<Use> GetFuncDeclarations(DB *db, const std::vector<Usr> &usrs) {
std::vector<Use> GetTypeDeclarations(DB *db, const std::vector<Usr> &usrs) { std::vector<Use> GetTypeDeclarations(DB *db, const std::vector<Usr> &usrs) {
return GetDeclarations(db->type_usr, db->types, usrs); return GetDeclarations(db->type_usr, db->types, usrs);
} }
std::vector<Use> GetVarDeclarations(DB *db, const std::vector<Usr> &usrs, std::vector<DeclRef> GetVarDeclarations(DB *db, const std::vector<Usr> &usrs,
unsigned kind) { unsigned kind) {
std::vector<Use> ret; std::vector<DeclRef> ret;
ret.reserve(usrs.size()); ret.reserve(usrs.size());
for (Usr usr : usrs) { for (Usr usr : usrs) {
QueryVar &var = db->Var(usr); QueryVar &var = db->Var(usr);
@ -669,18 +669,19 @@ std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles,
return GetLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id}); return GetLsLocation(db, wfiles, Use{{sym.range, sym.role}, file_id});
} }
std::vector<Location> GetLsLocations(DB *db, WorkingFiles *wfiles, LocationLink GetLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr) {
const std::vector<Use> &uses) { std::string path;
std::vector<Location> ret; DocumentUri uri = GetLsDocumentUri(db, dr.file_id, &path);
for (Use use : uses) if (auto range = GetLsRange(wfiles->GetFile(path), dr.range))
if (auto loc = GetLsLocation(db, wfiles, use)) if (auto extent = GetLsRange(wfiles->GetFile(path), dr.extent)) {
ret.push_back(*loc); LocationLink ret;
std::sort(ret.begin(), ret.end()); ret.targetUri = uri.raw_uri;
ret.erase(std::unique(ret.begin(), ret.end()), ret.end()); ret.targetSelectionRange = *range;
if (ret.size() > g_config->xref.maxNum) ret.targetRange = extent->Includes(*range) ? *extent : *range;
ret.resize(g_config->xref.maxNum);
return ret; return ret;
} }
return {};
}
SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) { SymbolKind GetSymbolKind(DB *db, SymbolIdx sym) {
SymbolKind ret; SymbolKind ret;

View File

@ -189,7 +189,7 @@ Maybe<DeclRef> GetDefinitionSpell(DB *db, SymbolIdx sym);
// for each id. // for each id.
std::vector<Use> GetFuncDeclarations(DB *, const std::vector<Usr> &); std::vector<Use> GetFuncDeclarations(DB *, const std::vector<Usr> &);
std::vector<Use> GetTypeDeclarations(DB *, const std::vector<Usr> &); std::vector<Use> GetTypeDeclarations(DB *, const std::vector<Usr> &);
std::vector<Use> GetVarDeclarations(DB *, const std::vector<Usr> &, unsigned); std::vector<DeclRef> GetVarDeclarations(DB *, const std::vector<Usr> &, unsigned);
// Get non-defining declarations. // Get non-defining declarations.
std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym); std::vector<DeclRef> &GetNonDefDeclarations(DB *db, SymbolIdx sym);
@ -204,8 +204,8 @@ DocumentUri GetLsDocumentUri(DB *db, int file_id);
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use); std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, Use use);
std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles, std::optional<Location> GetLsLocation(DB *db, WorkingFiles *wfiles,
SymbolRef sym, int file_id); SymbolRef sym, int file_id);
std::vector<Location> GetLsLocations(DB *db, WorkingFiles *wfiles, LocationLink GetLocationLink(DB *db, WorkingFiles *wfiles, DeclRef dr);
const std::vector<Use> &uses);
// Returns a symbol. The symbol will *NOT* have a location assigned. // Returns a symbol. The symbol will *NOT* have a location assigned.
std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym, std::optional<SymbolInformation> GetSymbolInfo(DB *db, SymbolIdx sym,
bool detailed); bool detailed);