#include "fuzzy_match.h" #include "lex_utils.h" #include "message_handler.h" #include "query_utils.h" #include "queue_manager.h" #include #include namespace { void PushBack(std::vector* result, optional location) { if (location) result->push_back(*location); } struct Ipc_TextDocumentDefinition : public RequestMessage { const static IpcId kIpcId = IpcId::TextDocumentDefinition; lsTextDocumentPositionParams params; }; MAKE_REFLECT_STRUCT(Ipc_TextDocumentDefinition, id, params); REGISTER_IPC_MESSAGE(Ipc_TextDocumentDefinition); struct Out_TextDocumentDefinition : public lsOutMessage { lsRequestId id; std::vector result; }; MAKE_REFLECT_STRUCT(Out_TextDocumentDefinition, jsonrpc, id, result); std::vector GetGotoDefinitionTargets(QueryDatabase* db, SymbolRef sym) { switch (sym.kind) { // Returns GetDeclarationsOfSymbolForGotoDefinition and // variable type definition. case SymbolKind::Var: { std::vector ret = GetDeclarationsOfSymbolForGotoDefinition(db, sym); QueryVar& var = db->GetVar(sym); if (var.def && var.def->type) { std::vector types = GetDeclarationsOfSymbolForGotoDefinition( db, SymbolIdx{*var.def->type, SymbolKind::Type}); ret.insert(ret.end(), types.begin(), types.end()); } return ret; } default: return GetDeclarationsOfSymbolForGotoDefinition(db, sym); } } struct TextDocumentDefinitionHandler : BaseMessageHandler { void Run(Ipc_TextDocumentDefinition* request) override { QueryFileId file_id; QueryFile* file; if (!FindFileOrFail(db, project, request->id, request->params.textDocument.uri.GetPath(), &file, &file_id)) { return; } WorkingFile* working_file = working_files->GetFileByFilename(file->def->path); Out_TextDocumentDefinition out; out.id = request->id; bool has_symbol = false; int target_line = request->params.position.line; int target_column = request->params.position.character; for (SymbolRef sym : FindSymbolsAtLocation(working_file, file, request->params.position)) { // Found symbol. Return definition. has_symbol = true; // Special cases which are handled: // - symbol has declaration but no definition (ie, pure virtual) // - start at spelling but end at extent for better mouse tooltip // - goto declaration while in definition of recursive type Maybe def_loc = GetDefinitionSpellingOfSymbol(db, sym); // We use spelling start and extent end because this causes vscode to // highlight the entire definition when previewing / hoving with the // mouse. Maybe extent = GetDefinitionExtentOfSymbol(db, sym); if (def_loc && extent) def_loc->range.end = extent->range.end; // If the cursor is currently at or in the definition we should goto // the declaration if possible. We also want to use declarations if // we're pointing to, ie, a pure virtual function which has no // definition. if (!def_loc || (def_loc->file == file_id && def_loc->range.Contains(target_line, target_column))) { // Goto declaration. std::vector targets = GetGotoDefinitionTargets(db, sym); for (Use target : targets) { optional ls_target = GetLsLocation(db, working_files, target); if (ls_target) out.result.push_back(*ls_target); } // We found some declarations. Break so we don't add the definition // location. if (!out.result.empty()) break; } if (def_loc) { PushBack(&out.result, GetLsLocation(db, working_files, *def_loc)); } if (!out.result.empty()) break; } // No symbols - check for includes. if (out.result.empty()) { for (const IndexInclude& include : file->def->includes) { if (include.line == target_line) { lsLocation result; result.uri = lsDocumentUri::FromPath(include.resolved_path); out.result.push_back(result); break; } } // Find the best match of the identifier at point. if (!has_symbol && db->symbols.size()) { const std::string& buffer = working_file->buffer_content; int start = GetOffsetForPosition(request->params.position, buffer); int end = start; while (start > 0 && isalnum(buffer[start - 1])) start--; while (isalnum(buffer[end])) end++; auto query = std::string_view(buffer).substr(start, end - start); int best_score = kMinScore; int best_i = 0; std::vector score, dp; for (int i = 0; i < (int)db->symbols.size(); ++i) { std::string_view short_name = db->GetSymbolShortName(i); if (short_name.size() > score.size()) { score.resize(short_name.size()); dp.resize(short_name.size()); } int t = FuzzyEvaluate(query, short_name, score, dp); if (t > best_score) { best_score = t; best_i = i; } } Maybe use = GetDefinitionSpellingOfSymbol(db, db->symbols[best_i]); if (use) { optional ls_loc = GetLsLocation(db, working_files, *use); if (ls_loc) out.result.push_back(*ls_loc); } } } QueueManager::WriteStdout(IpcId::TextDocumentDefinition, out); } }; REGISTER_MESSAGE_HANDLER(TextDocumentDefinitionHandler); } // namespace