Implement textDocument/references

This commit is contained in:
Jacob Dufault 2017-04-09 22:34:06 -07:00
parent 88e0d224e3
commit e9f0c57708
8 changed files with 169 additions and 11 deletions

View File

@ -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.

View File

@ -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()) {

View File

@ -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:

View File

@ -16,6 +16,7 @@ enum class IpcId : int {
TextDocumentDidSave,
TextDocumentCompletion,
TextDocumentDefinition,
TextDocumentReferences,
TextDocumentDocumentSymbol,
TextDocumentCodeLens,
CodeLensResolve,

View File

@ -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;

View File

@ -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;

View File

@ -7,7 +7,6 @@ void caller() {
}
/*
// TODO FIXME: called() has no callers.
OUTPUT:
{
"funcs": [{

View 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"]
}]
}
*/