mirror of
https://github.com/MaskRay/ccls.git
synced 2025-04-21 08:03:02 +00:00
Implement textDocument/references
This commit is contained in:
parent
88e0d224e3
commit
e9f0c57708
@ -8,7 +8,7 @@ struct ScopedLock {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Points to a generic block of memory. Note that |data| is relocatable, ie,
|
// Points to a generic block of memory. Note that |data| is relocatable, ie,
|
||||||
// multiple Buffer instantations may point to the same underlying block of
|
// multiple Buffer instantiations may point to the same underlying block of
|
||||||
// memory but the data pointer has different values.
|
// memory but the data pointer has different values.
|
||||||
struct Buffer {
|
struct Buffer {
|
||||||
// Create a new buffer of the given capacity using process-local memory.
|
// Create a new buffer of the given capacity using process-local memory.
|
||||||
|
@ -55,6 +55,43 @@ QueryableVarDef* GetQueryable(QueryableDatabase* db, const QueryVarId& id) {
|
|||||||
return &db->vars[id.id];
|
return &db->vars[id.id];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if false
|
||||||
|
optional<QueryableLocation> GetDeclarationOfSymbol(QueryableDatabase* db, const SymbolIdx& symbol) {
|
||||||
|
switch (symbol.kind) {
|
||||||
|
case SymbolKind::Type:
|
||||||
|
return db->types[symbol.idx].def.definition_spelling;
|
||||||
|
case SymbolKind::Func:
|
||||||
|
return db->funcs[symbol.idx].;
|
||||||
|
case SymbolKind::Var:
|
||||||
|
return db->vars[symbol.idx].uses;
|
||||||
|
case SymbolKind::File:
|
||||||
|
case SymbolKind::Invalid: {
|
||||||
|
assert(false && "unexpected");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
std::vector<QueryableLocation> GetUsesOfSymbol(QueryableDatabase* db, const SymbolIdx& symbol) {
|
||||||
|
switch (symbol.kind) {
|
||||||
|
case SymbolKind::Type:
|
||||||
|
return db->types[symbol.idx].uses;
|
||||||
|
case SymbolKind::Func:
|
||||||
|
return db->funcs[symbol.idx].uses;
|
||||||
|
case SymbolKind::Var:
|
||||||
|
return db->vars[symbol.idx].uses;
|
||||||
|
case SymbolKind::File:
|
||||||
|
case SymbolKind::Invalid: {
|
||||||
|
assert(false && "unexpected");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
optional<QueryableLocation> GetDefinitionSpellingOfSymbol(QueryableDatabase* db, const QueryTypeId& id) {
|
optional<QueryableLocation> GetDefinitionSpellingOfSymbol(QueryableDatabase* db, const QueryTypeId& id) {
|
||||||
return GetQueryable(db, id)->def.definition_spelling;
|
return GetQueryable(db, id)->def.definition_spelling;
|
||||||
}
|
}
|
||||||
@ -380,6 +417,7 @@ std::unique_ptr<IpcMessageQueue> BuildIpcMessageQueue(const std::string& name, s
|
|||||||
RegisterId<Ipc_TextDocumentDidSave>(ipc.get());
|
RegisterId<Ipc_TextDocumentDidSave>(ipc.get());
|
||||||
RegisterId<Ipc_TextDocumentComplete>(ipc.get());
|
RegisterId<Ipc_TextDocumentComplete>(ipc.get());
|
||||||
RegisterId<Ipc_TextDocumentDefinition>(ipc.get());
|
RegisterId<Ipc_TextDocumentDefinition>(ipc.get());
|
||||||
|
RegisterId<Ipc_TextDocumentReferences>(ipc.get());
|
||||||
RegisterId<Ipc_TextDocumentDocumentSymbol>(ipc.get());
|
RegisterId<Ipc_TextDocumentDocumentSymbol>(ipc.get());
|
||||||
RegisterId<Ipc_TextDocumentCodeLens>(ipc.get());
|
RegisterId<Ipc_TextDocumentCodeLens>(ipc.get());
|
||||||
RegisterId<Ipc_CodeLensResolve>(ipc.get());
|
RegisterId<Ipc_CodeLensResolve>(ipc.get());
|
||||||
@ -401,6 +439,7 @@ void RegisterMessageTypes() {
|
|||||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDidSave>();
|
MessageRegistry::instance()->Register<Ipc_TextDocumentDidSave>();
|
||||||
MessageRegistry::instance()->Register<Ipc_TextDocumentComplete>();
|
MessageRegistry::instance()->Register<Ipc_TextDocumentComplete>();
|
||||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDefinition>();
|
MessageRegistry::instance()->Register<Ipc_TextDocumentDefinition>();
|
||||||
|
MessageRegistry::instance()->Register<Ipc_TextDocumentReferences>();
|
||||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDocumentSymbol>();
|
MessageRegistry::instance()->Register<Ipc_TextDocumentDocumentSymbol>();
|
||||||
MessageRegistry::instance()->Register<Ipc_TextDocumentCodeLens>();
|
MessageRegistry::instance()->Register<Ipc_TextDocumentCodeLens>();
|
||||||
MessageRegistry::instance()->Register<Ipc_CodeLensResolve>();
|
MessageRegistry::instance()->Register<Ipc_CodeLensResolve>();
|
||||||
@ -669,6 +708,39 @@ void QueryDbMainLoop(
|
|||||||
Out_TextDocumentDefinition response;
|
Out_TextDocumentDefinition response;
|
||||||
response.id = msg->id;
|
response.id = msg->id;
|
||||||
|
|
||||||
|
int target_line = msg->params.position.line + 1;
|
||||||
|
int target_column = msg->params.position.character + 1;
|
||||||
|
|
||||||
|
for (const SymbolRef& ref : file->def.all_symbols) {
|
||||||
|
if (ref.loc.range.start.line >= target_line && ref.loc.range.end.line <= target_line &&
|
||||||
|
ref.loc.range.start.column <= target_column && ref.loc.range.end.column >= target_column) {
|
||||||
|
// Found symbol. Return definition.
|
||||||
|
optional<QueryableLocation> location = GetDefinitionSpellingOfSymbol(db, ref.idx);
|
||||||
|
if (location) {
|
||||||
|
optional<lsLocation> ls_location = GetLsLocation(db, working_files, location.value());
|
||||||
|
if (ls_location)
|
||||||
|
response.result.push_back(*ls_location);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SendOutMessageToClient(language_client, response);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case IpcId::TextDocumentReferences: {
|
||||||
|
auto msg = static_cast<Ipc_TextDocumentReferences*>(message.get());
|
||||||
|
|
||||||
|
QueryableFile* file = FindFile(db, msg->params.textDocument.uri.GetPath());
|
||||||
|
if (!file) {
|
||||||
|
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Out_TextDocumentReferences response;
|
||||||
|
response.id = msg->id;
|
||||||
|
|
||||||
// TODO: Edge cases (whitespace, etc) will work a lot better
|
// TODO: Edge cases (whitespace, etc) will work a lot better
|
||||||
// if we store range information instead of hacking it.
|
// if we store range information instead of hacking it.
|
||||||
int target_line = msg->params.position.line + 1;
|
int target_line = msg->params.position.line + 1;
|
||||||
@ -677,9 +749,21 @@ void QueryDbMainLoop(
|
|||||||
for (const SymbolRef& ref : file->def.all_symbols) {
|
for (const SymbolRef& ref : file->def.all_symbols) {
|
||||||
if (ref.loc.range.start.line >= target_line && ref.loc.range.end.line <= target_line &&
|
if (ref.loc.range.start.line >= target_line && ref.loc.range.end.line <= target_line &&
|
||||||
ref.loc.range.start.column <= target_column && ref.loc.range.end.column >= target_column) {
|
ref.loc.range.start.column <= target_column && ref.loc.range.end.column >= target_column) {
|
||||||
optional<QueryableLocation> location = GetDefinitionSpellingOfSymbol(db, ref.idx);
|
|
||||||
if (location) {
|
optional<QueryableLocation> excluded_declaration;
|
||||||
optional<lsLocation> ls_location = GetLsLocation(db, working_files, location.value());
|
if (!msg->params.context.includeDeclaration) {
|
||||||
|
std::cerr << "Excluding declaration in references" << std::endl;
|
||||||
|
excluded_declaration = GetDefinitionSpellingOfSymbol(db, ref.idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Found symbol. Return references.
|
||||||
|
std::vector<QueryableLocation> uses = GetUsesOfSymbol(db, ref.idx);
|
||||||
|
response.result.reserve(uses.size());
|
||||||
|
for (const QueryableLocation& use : uses) {
|
||||||
|
if (excluded_declaration.has_value() && use == *excluded_declaration)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
optional<lsLocation> ls_location = GetLsLocation(db, working_files, use);
|
||||||
if (ls_location)
|
if (ls_location)
|
||||||
response.result.push_back(*ls_location);
|
response.result.push_back(*ls_location);
|
||||||
}
|
}
|
||||||
@ -946,9 +1030,8 @@ void LanguageServerStdinLoop(IpcMessageQueue* ipc) {
|
|||||||
response.result.capabilities.codeLensProvider->resolveProvider = false;
|
response.result.capabilities.codeLensProvider->resolveProvider = false;
|
||||||
|
|
||||||
response.result.capabilities.definitionProvider = true;
|
response.result.capabilities.definitionProvider = true;
|
||||||
|
response.result.capabilities.referencesProvider = true;
|
||||||
response.result.capabilities.documentSymbolProvider = true;
|
response.result.capabilities.documentSymbolProvider = true;
|
||||||
|
|
||||||
response.result.capabilities.workspaceSymbolProvider = true;
|
response.result.capabilities.workspaceSymbolProvider = true;
|
||||||
|
|
||||||
//response.Write(std::cerr);
|
//response.Write(std::cerr);
|
||||||
@ -972,6 +1055,7 @@ void LanguageServerStdinLoop(IpcMessageQueue* ipc) {
|
|||||||
case IpcId::TextDocumentDidSave:
|
case IpcId::TextDocumentDidSave:
|
||||||
case IpcId::TextDocumentCompletion:
|
case IpcId::TextDocumentCompletion:
|
||||||
case IpcId::TextDocumentDefinition:
|
case IpcId::TextDocumentDefinition:
|
||||||
|
case IpcId::TextDocumentReferences:
|
||||||
case IpcId::TextDocumentDocumentSymbol:
|
case IpcId::TextDocumentDocumentSymbol:
|
||||||
case IpcId::TextDocumentCodeLens:
|
case IpcId::TextDocumentCodeLens:
|
||||||
case IpcId::WorkspaceSymbol: {
|
case IpcId::WorkspaceSymbol: {
|
||||||
@ -1080,7 +1164,7 @@ int main(int argc, char** argv) {
|
|||||||
if (context.shouldExit())
|
if (context.shouldExit())
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
//RunTests();
|
RunTests();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
else if (options.find("--help") != options.end()) {
|
else if (options.find("--help") != options.end()) {
|
||||||
|
@ -22,6 +22,8 @@ const char* IpcIdToString(IpcId id) {
|
|||||||
return "textDocument/completion";
|
return "textDocument/completion";
|
||||||
case IpcId::TextDocumentDefinition:
|
case IpcId::TextDocumentDefinition:
|
||||||
return "textDocument/definition";
|
return "textDocument/definition";
|
||||||
|
case IpcId::TextDocumentReferences:
|
||||||
|
return "textDocument/references";
|
||||||
case IpcId::TextDocumentDocumentSymbol:
|
case IpcId::TextDocumentDocumentSymbol:
|
||||||
return "textDocument/documentSymbol";
|
return "textDocument/documentSymbol";
|
||||||
case IpcId::TextDocumentCodeLens:
|
case IpcId::TextDocumentCodeLens:
|
||||||
|
@ -16,6 +16,7 @@ enum class IpcId : int {
|
|||||||
TextDocumentDidSave,
|
TextDocumentDidSave,
|
||||||
TextDocumentCompletion,
|
TextDocumentCompletion,
|
||||||
TextDocumentDefinition,
|
TextDocumentDefinition,
|
||||||
|
TextDocumentReferences,
|
||||||
TextDocumentDocumentSymbol,
|
TextDocumentDocumentSymbol,
|
||||||
TextDocumentCodeLens,
|
TextDocumentCodeLens,
|
||||||
CodeLensResolve,
|
CodeLensResolve,
|
||||||
|
@ -1131,6 +1131,32 @@ struct Out_TextDocumentDefinition : public lsOutMessage<Out_TextDocumentDefiniti
|
|||||||
};
|
};
|
||||||
MAKE_REFLECT_STRUCT(Out_TextDocumentDefinition, jsonrpc, id, result);
|
MAKE_REFLECT_STRUCT(Out_TextDocumentDefinition, jsonrpc, id, result);
|
||||||
|
|
||||||
|
// References
|
||||||
|
struct Ipc_TextDocumentReferences : public IpcMessage<Ipc_TextDocumentReferences> {
|
||||||
|
struct lsReferenceContext {
|
||||||
|
// Include the declaration of the current symbol.
|
||||||
|
bool includeDeclaration;
|
||||||
|
};
|
||||||
|
struct lsReferenceParams : public lsTextDocumentPositionParams {
|
||||||
|
lsTextDocumentIdentifier textDocument;
|
||||||
|
lsPosition position;
|
||||||
|
lsReferenceContext context;
|
||||||
|
};
|
||||||
|
|
||||||
|
const static IpcId kIpcId = IpcId::TextDocumentReferences;
|
||||||
|
|
||||||
|
lsRequestId id;
|
||||||
|
lsReferenceParams params;
|
||||||
|
};
|
||||||
|
MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences::lsReferenceContext, includeDeclaration);
|
||||||
|
MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences::lsReferenceParams, textDocument, position, context);
|
||||||
|
MAKE_REFLECT_STRUCT(Ipc_TextDocumentReferences, id, params);
|
||||||
|
struct Out_TextDocumentReferences : public lsOutMessage<Out_TextDocumentReferences> {
|
||||||
|
lsRequestId id;
|
||||||
|
NonElidedVector<lsLocation> result;
|
||||||
|
};
|
||||||
|
MAKE_REFLECT_STRUCT(Out_TextDocumentReferences, jsonrpc, id, result);
|
||||||
|
|
||||||
// List symbols in a document.
|
// List symbols in a document.
|
||||||
struct lsDocumentSymbolParams {
|
struct lsDocumentSymbolParams {
|
||||||
lsTextDocumentIdentifier textDocument;
|
lsTextDocumentIdentifier textDocument;
|
||||||
|
@ -119,7 +119,7 @@ void RunTests() {
|
|||||||
//if (path != "tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc") continue;
|
//if (path != "tests/templates/namespace_template_class_template_func_usage_folded_into_one.cc") continue;
|
||||||
//if (path != "tests/multi_file/header.h") continue;
|
//if (path != "tests/multi_file/header.h") continue;
|
||||||
//if (path != "tests/multi_file/impl.cc") continue;
|
//if (path != "tests/multi_file/impl.cc") continue;
|
||||||
//if (path != "tests/usage/func_called_from_macro_argument.cc") continue;
|
if (path != "tests/usage/func_called_from_template.cc") continue;
|
||||||
//if (path != "tests/templates/implicit_variable_instantiation.cc") continue;
|
//if (path != "tests/templates/implicit_variable_instantiation.cc") continue;
|
||||||
//if (path != "tests/_empty_test.cc") continue;
|
//if (path != "tests/_empty_test.cc") continue;
|
||||||
|
|
||||||
@ -141,7 +141,7 @@ void RunTests() {
|
|||||||
"-IC:/Users/jacob/Desktop/superindex/indexer/third_party/doctest/",
|
"-IC:/Users/jacob/Desktop/superindex/indexer/third_party/doctest/",
|
||||||
"-IC:/Users/jacob/Desktop/superindex/indexer/third_party/rapidjson/include",
|
"-IC:/Users/jacob/Desktop/superindex/indexer/third_party/rapidjson/include",
|
||||||
"-IC:/Users/jacob/Desktop/superindex/indexer/src"
|
"-IC:/Users/jacob/Desktop/superindex/indexer/src"
|
||||||
}, false /*dump_ast*/);
|
}, true /*dump_ast*/);
|
||||||
|
|
||||||
for (auto& entry : all_expected_output) {
|
for (auto& entry : all_expected_output) {
|
||||||
const std::string& expected_path = entry.first;
|
const std::string& expected_path = entry.first;
|
||||||
|
@ -7,7 +7,6 @@ void caller() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
// TODO FIXME: called() has no callers.
|
|
||||||
OUTPUT:
|
OUTPUT:
|
||||||
{
|
{
|
||||||
"funcs": [{
|
"funcs": [{
|
||||||
|
46
tests/usage/func_called_from_template.cc
Normal file
46
tests/usage/func_called_from_template.cc
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
void called();
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void caller() {
|
||||||
|
called();
|
||||||
|
}
|
||||||
|
|
||||||
|
void foo() {
|
||||||
|
caller<int>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// NOTE: without caller<int>() instantation caller() is never visited so
|
||||||
|
// called() is never referenced.
|
||||||
|
OUTPUT:
|
||||||
|
{
|
||||||
|
"funcs": [{
|
||||||
|
"id": 0,
|
||||||
|
"usr": "c:@F@called#",
|
||||||
|
"short_name": "called",
|
||||||
|
"qualified_name": "called",
|
||||||
|
"declarations": ["1:6-1:12"],
|
||||||
|
"callers": ["1@5:3-5:9"],
|
||||||
|
"uses": ["1:6-1:12", "5:3-5:9"]
|
||||||
|
}, {
|
||||||
|
"id": 1,
|
||||||
|
"usr": "c:@FT@>1#Tcaller#v#",
|
||||||
|
"short_name": "caller",
|
||||||
|
"qualified_name": "caller",
|
||||||
|
"definition_spelling": "4:6-4:12",
|
||||||
|
"definition_extent": "4:1-6:2",
|
||||||
|
"callers": ["2@9:3-9:9"],
|
||||||
|
"callees": ["0@5:3-5:9"],
|
||||||
|
"uses": ["4:6-4:12", "9:3-9:9"]
|
||||||
|
}, {
|
||||||
|
"id": 2,
|
||||||
|
"usr": "c:@F@foo#",
|
||||||
|
"short_name": "foo",
|
||||||
|
"qualified_name": "foo",
|
||||||
|
"definition_spelling": "8:6-8:9",
|
||||||
|
"definition_extent": "8:1-10:2",
|
||||||
|
"callees": ["1@9:3-9:9"],
|
||||||
|
"uses": ["8:6-8:9"]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
*/
|
Loading…
Reference in New Issue
Block a user