mirror of
https://github.com/MaskRay/ccls.git
synced 2025-02-16 13:48:04 +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,
|
||||
// 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.
|
||||
struct Buffer {
|
||||
// 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];
|
||||
}
|
||||
|
||||
#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) {
|
||||
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_TextDocumentComplete>(ipc.get());
|
||||
RegisterId<Ipc_TextDocumentDefinition>(ipc.get());
|
||||
RegisterId<Ipc_TextDocumentReferences>(ipc.get());
|
||||
RegisterId<Ipc_TextDocumentDocumentSymbol>(ipc.get());
|
||||
RegisterId<Ipc_TextDocumentCodeLens>(ipc.get());
|
||||
RegisterId<Ipc_CodeLensResolve>(ipc.get());
|
||||
@ -401,6 +439,7 @@ void RegisterMessageTypes() {
|
||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDidSave>();
|
||||
MessageRegistry::instance()->Register<Ipc_TextDocumentComplete>();
|
||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDefinition>();
|
||||
MessageRegistry::instance()->Register<Ipc_TextDocumentReferences>();
|
||||
MessageRegistry::instance()->Register<Ipc_TextDocumentDocumentSymbol>();
|
||||
MessageRegistry::instance()->Register<Ipc_TextDocumentCodeLens>();
|
||||
MessageRegistry::instance()->Register<Ipc_CodeLensResolve>();
|
||||
@ -669,6 +708,39 @@ void QueryDbMainLoop(
|
||||
Out_TextDocumentDefinition response;
|
||||
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
|
||||
// if we store range information instead of hacking it.
|
||||
int target_line = msg->params.position.line + 1;
|
||||
@ -676,10 +748,22 @@ void QueryDbMainLoop(
|
||||
|
||||
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) {
|
||||
optional<QueryableLocation> location = GetDefinitionSpellingOfSymbol(db, ref.idx);
|
||||
if (location) {
|
||||
optional<lsLocation> ls_location = GetLsLocation(db, working_files, location.value());
|
||||
ref.loc.range.start.column <= target_column && ref.loc.range.end.column >= target_column) {
|
||||
|
||||
optional<QueryableLocation> excluded_declaration;
|
||||
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)
|
||||
response.result.push_back(*ls_location);
|
||||
}
|
||||
@ -946,9 +1030,8 @@ void LanguageServerStdinLoop(IpcMessageQueue* ipc) {
|
||||
response.result.capabilities.codeLensProvider->resolveProvider = false;
|
||||
|
||||
response.result.capabilities.definitionProvider = true;
|
||||
|
||||
response.result.capabilities.referencesProvider = true;
|
||||
response.result.capabilities.documentSymbolProvider = true;
|
||||
|
||||
response.result.capabilities.workspaceSymbolProvider = true;
|
||||
|
||||
//response.Write(std::cerr);
|
||||
@ -972,6 +1055,7 @@ void LanguageServerStdinLoop(IpcMessageQueue* ipc) {
|
||||
case IpcId::TextDocumentDidSave:
|
||||
case IpcId::TextDocumentCompletion:
|
||||
case IpcId::TextDocumentDefinition:
|
||||
case IpcId::TextDocumentReferences:
|
||||
case IpcId::TextDocumentDocumentSymbol:
|
||||
case IpcId::TextDocumentCodeLens:
|
||||
case IpcId::WorkspaceSymbol: {
|
||||
@ -1080,7 +1164,7 @@ int main(int argc, char** argv) {
|
||||
if (context.shouldExit())
|
||||
return res;
|
||||
|
||||
//RunTests();
|
||||
RunTests();
|
||||
return 0;
|
||||
}
|
||||
else if (options.find("--help") != options.end()) {
|
||||
|
@ -22,6 +22,8 @@ const char* IpcIdToString(IpcId id) {
|
||||
return "textDocument/completion";
|
||||
case IpcId::TextDocumentDefinition:
|
||||
return "textDocument/definition";
|
||||
case IpcId::TextDocumentReferences:
|
||||
return "textDocument/references";
|
||||
case IpcId::TextDocumentDocumentSymbol:
|
||||
return "textDocument/documentSymbol";
|
||||
case IpcId::TextDocumentCodeLens:
|
||||
|
@ -16,6 +16,7 @@ enum class IpcId : int {
|
||||
TextDocumentDidSave,
|
||||
TextDocumentCompletion,
|
||||
TextDocumentDefinition,
|
||||
TextDocumentReferences,
|
||||
TextDocumentDocumentSymbol,
|
||||
TextDocumentCodeLens,
|
||||
CodeLensResolve,
|
||||
|
@ -1131,6 +1131,32 @@ struct Out_TextDocumentDefinition : public lsOutMessage<Out_TextDocumentDefiniti
|
||||
};
|
||||
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.
|
||||
struct lsDocumentSymbolParams {
|
||||
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/multi_file/header.h") 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/_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/rapidjson/include",
|
||||
"-IC:/Users/jacob/Desktop/superindex/indexer/src"
|
||||
}, false /*dump_ast*/);
|
||||
}, true /*dump_ast*/);
|
||||
|
||||
for (auto& entry : all_expected_output) {
|
||||
const std::string& expected_path = entry.first;
|
||||
|
@ -7,7 +7,6 @@ void caller() {
|
||||
}
|
||||
|
||||
/*
|
||||
// TODO FIXME: called() has no callers.
|
||||
OUTPUT:
|
||||
{
|
||||
"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