From c9a251b7ab30e55b30c3630310ce3f9590d87d75 Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Mon, 10 Apr 2017 22:43:01 -0700 Subject: [PATCH] Goto definition intelligently jumps to declaration --- src/command_line.cc | 86 ++++++++++++++++++++++++++++++++++----------- src/position.cc | 12 +++++++ src/position.h | 2 ++ 3 files changed, 80 insertions(+), 20 deletions(-) diff --git a/src/command_line.cc b/src/command_line.cc index 3d096dd3..f151a346 100644 --- a/src/command_line.cc +++ b/src/command_line.cc @@ -36,6 +36,18 @@ const int kNumIndexers = 8 - 1; const int kQueueSizeBytes = 1024 * 8; const int kMaxWorkspaceSearchResults = 1000; +QueryableFile* FindFile(QueryableDatabase* db, const std::string& filename, QueryFileId* file_id) { + auto it = db->usr_to_symbol.find(filename); + if (it != db->usr_to_symbol.end()) { + *file_id = QueryFileId(it->second.idx); + return &db->files[it->second.idx]; + } + + std::cerr << "Unable to find file " << filename << std::endl; + *file_id = QueryFileId(-1); + return nullptr; +} + QueryableFile* FindFile(QueryableDatabase* db, const std::string& filename) { auto it = db->usr_to_symbol.find(filename); if (it != db->usr_to_symbol.end()) @@ -132,23 +144,38 @@ optional GetDefinitionExtentOfSymbol(QueryableDatabase* db, c } optional GetDefinitionExtentOfSymbol(QueryableDatabase* db, const SymbolIdx& symbol) { switch (symbol.kind) { - case SymbolKind::File: - // TODO: If line 1 is deleted the file won't show up in, ie, workspace symbol search results. - return QueryableLocation(QueryFileId(symbol.idx), Range(false /*is_interesting*/, Position(1, 1), Position(1, 1))); - case SymbolKind::Type: - return db->types[symbol.idx].def.definition_extent; - case SymbolKind::Func: - return db->funcs[symbol.idx].def.definition_extent; - case SymbolKind::Var: - return db->vars[symbol.idx].def.definition_extent; - case SymbolKind::Invalid: { - assert(false && "unexpected"); - break; - } + case SymbolKind::File: + // TODO: If line 1 is deleted the file won't show up in, ie, workspace symbol search results. + return QueryableLocation(QueryFileId(symbol.idx), Range(false /*is_interesting*/, Position(1, 1), Position(1, 1))); + case SymbolKind::Type: + return db->types[symbol.idx].def.definition_extent; + case SymbolKind::Func: + return db->funcs[symbol.idx].def.definition_extent; + case SymbolKind::Var: + return db->vars[symbol.idx].def.definition_extent; + case SymbolKind::Invalid: { + assert(false && "unexpected"); + break; + } } return nullopt; } +std::vector GetDeclarationsOfSymbol(QueryableDatabase* db, const SymbolIdx& symbol) { + switch (symbol.kind) { + case SymbolKind::Func: + return db->funcs[symbol.idx].declarations; + case SymbolKind::Var: { + optional declaration = db->vars[symbol.idx].def.declaration; + if (declaration) + return { *declaration }; + break; + } + } + + return {}; +} + optional GetLsRange(WorkingFile* working_file, const Range& location) { if (!working_file) { return lsRange( @@ -738,7 +765,8 @@ void QueryDbMainLoop( case IpcId::TextDocumentDefinition: { auto msg = static_cast(message.get()); - QueryableFile* file = FindFile(db, msg->params.textDocument.uri.GetPath()); + QueryFileId file_id; + QueryableFile* file = FindFile(db, msg->params.textDocument.uri.GetPath(), &file_id); if (!file) { std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; break; @@ -751,19 +779,37 @@ void QueryDbMainLoop( 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) { + if (ref.loc.range.Contains(target_line, target_column)) { // Found symbol. Return definition. - optional spelling = GetDefinitionSpellingOfSymbol(db, ref.idx); - if (spelling) { + optional definition_spelling = GetDefinitionSpellingOfSymbol(db, ref.idx); + if (definition_spelling) { + + // If the cursor is currently at the definition we should goto the declaration if possible. + if (definition_spelling->path == file_id && definition_spelling->range.Contains(target_line, target_column)) { + // Goto declaration. + + std::vector declarations = GetDeclarationsOfSymbol(db, ref.idx); + for (auto declaration : declarations) { + optional ls_declaration = GetLsLocation(db, working_files, declaration); + if (ls_declaration) + response.result.push_back(*ls_declaration); + } + + if (!response.result.empty()) + break; + } + + + // Goto definition. + // We use spelling start and extent end because this causes vscode // to highlight the entire definition when previewing / hoving with // the mouse. optional extent = GetDefinitionExtentOfSymbol(db, ref.idx); if (extent) - spelling->range.end = extent->range.end; + definition_spelling->range.end = extent->range.end; - optional ls_location = GetLsLocation(db, working_files, spelling.value()); + optional ls_location = GetLsLocation(db, working_files, definition_spelling.value()); if (ls_location) response.result.push_back(*ls_location); } diff --git a/src/position.cc b/src/position.cc index f58723f5..043b331f 100644 --- a/src/position.cc +++ b/src/position.cc @@ -94,6 +94,18 @@ Range::Range(const char* encoded) { end.column = atoi(encoded); } +bool Range::Contains(int line, int column) const { + if (line == start.line && line == end.line) + return column >= start.column && column <= end.column; + if (line == start.line) + return column >= start.column; + if (line == end.line) + return column <= end.column; + if (line > start.line && line < end.line) + return true; + return false; +} + std::string Range::ToString() { // Output looks like this: // diff --git a/src/position.h b/src/position.h index 229d42af..f014461c 100644 --- a/src/position.h +++ b/src/position.h @@ -31,6 +31,8 @@ struct Range { Range(bool interesting, Position start, Position end); explicit Range(const char* encoded); + bool Contains(int line, int column) const; + std::string ToString(); Range WithInteresting(bool interesting);