diff --git a/src/command_line.cc b/src/command_line.cc index eadad5c4..31a4edc4 100644 --- a/src/command_line.cc +++ b/src/command_line.cc @@ -533,8 +533,8 @@ optional GetLsRange(WorkingFile* working_file, const Range& location) { lsPosition(location.end.line - 1, location.end.column - 1)); } - optional start = working_file->GetBufferLineFromDiskLine(location.start.line); - optional end = working_file->GetBufferLineFromDiskLine(location.end.line); + optional start = working_file->GetBufferLineFromIndexLine(location.start.line); + optional end = working_file->GetBufferLineFromIndexLine(location.end.line); if (!start || !end) return nullopt; @@ -687,14 +687,18 @@ lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db, WorkingFiles* working_file return edit; } -std::vector FindSymbolsAtLocation(QueryFile* file, lsPosition position) { +std::vector FindSymbolsAtLocation(WorkingFile* working_file, QueryFile* file, lsPosition position) { std::vector symbols; symbols.reserve(1); - // TODO: This needs to use the WorkingFile to convert buffer location to - // indexed location. int target_line = position.line + 1; int target_column = position.character + 1; + if (working_file) { + optional index_line = working_file->GetIndexLineFromBufferLine(target_line); + if (index_line) + target_line = *index_line; + } + for (const SymbolRef& ref : file->def.all_symbols) { if (ref.loc.range.Contains(target_line, target_column)) symbols.push_back(ref); @@ -1192,10 +1196,12 @@ void QueryDbMainLoop( std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; break; } + WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); + Out_TextDocumentRename response; response.id = msg->id; - for (const SymbolRef& ref : FindSymbolsAtLocation(file, msg->params.position)) { + for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { // Found symbol. Return references to rename. std::vector uses = GetUsesOfSymbol(db, ref.idx); response.result = BuildWorkspaceEdit(db, working_files, uses, msg->params.newName); @@ -1241,6 +1247,7 @@ void QueryDbMainLoop( std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; break; } + WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); Out_TextDocumentDefinition response; response.id = msg->id; @@ -1248,7 +1255,7 @@ void QueryDbMainLoop( int target_line = msg->params.position.line + 1; int target_column = msg->params.position.character + 1; - for (const SymbolRef& ref : FindSymbolsAtLocation(file, msg->params.position)) { + for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { // Found symbol. Return definition. // Special cases which are handled: @@ -1304,10 +1311,12 @@ void QueryDbMainLoop( std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; break; } + WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); + Out_TextDocumentDocumentHighlight response; response.id = msg->id; - for (const SymbolRef& ref : FindSymbolsAtLocation(file, msg->params.position)) { + for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { // Found symbol. Return references to highlight. std::vector uses = GetUsesOfSymbol(db, ref.idx); response.result.reserve(uses.size()); @@ -1339,10 +1348,12 @@ void QueryDbMainLoop( std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; break; } + WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); + Out_TextDocumentHover response; response.id = msg->id; - for (const SymbolRef& ref : FindSymbolsAtLocation(file, msg->params.position)) { + for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { // Found symbol. Return hover. optional ls_range = GetLsRange(working_files->GetFileByFilename(file->def.path), ref.loc.range); if (!ls_range) @@ -1365,11 +1376,12 @@ void QueryDbMainLoop( std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl; break; } + WorkingFile* working_file = working_files->GetFileByFilename(file->def.path); Out_TextDocumentReferences response; response.id = msg->id; - for (const SymbolRef& ref : FindSymbolsAtLocation(file, msg->params.position)) { + for (const SymbolRef& ref : FindSymbolsAtLocation(working_file, file, msg->params.position)) { optional excluded_declaration; if (!msg->params.context.includeDeclaration) { std::cerr << "Excluding declaration in references" << std::endl; diff --git a/src/working_files.cc b/src/working_files.cc index b0c1ada2..cf789f3a 100644 --- a/src/working_files.cc +++ b/src/working_files.cc @@ -30,26 +30,39 @@ WorkingFile::WorkingFile(const std::string& filename, const std::string& buffer_ void WorkingFile::SetIndexContent(const std::string& index_content) { index_lines = ToLines(index_content, true /*trim_whitespace*/); + + // Build lookup buffer. + index_lines_lookup.clear(); + index_lines_lookup.reserve(index_lines.size()); + for (int i = 0; i < index_lines.size(); ++i) { + const std::string& index_line = index_lines[i]; + + auto it = index_lines_lookup.find(index_line); + if (it == index_lines_lookup.end()) + index_lines_lookup[index_line] = { i + 1 }; + else + it->second.push_back(i + 1); + } } void WorkingFile::OnBufferContentUpdated() { all_buffer_lines = ToLines(buffer_content, true /*trim_whitespace*/); // Build lookup buffer. - buffer_line_lookup.clear(); - buffer_line_lookup.reserve(all_buffer_lines.size()); + all_buffer_lines_lookup.clear(); + all_buffer_lines_lookup.reserve(all_buffer_lines.size()); for (int i = 0; i < all_buffer_lines.size(); ++i) { const std::string& buffer_line = all_buffer_lines[i]; - auto it = buffer_line_lookup.find(buffer_line); - if (it == buffer_line_lookup.end()) - buffer_line_lookup[buffer_line] = { i + 1 }; + auto it = all_buffer_lines_lookup.find(buffer_line); + if (it == all_buffer_lines_lookup.end()) + all_buffer_lines_lookup[buffer_line] = { i + 1 }; else it->second.push_back(i + 1); } } -optional WorkingFile::GetBufferLineFromDiskLine(int index_line) const { +optional WorkingFile::GetBufferLineFromIndexLine(int index_line) const { // The implementation is simple but works pretty well for most cases. We // lookup the line contents in the indexed file contents, and try to find the // most similar line in the current buffer file. @@ -68,8 +81,8 @@ optional WorkingFile::GetBufferLineFromDiskLine(int index_line) const { // Find the line in the cached index file. We'll try to find the most similar line // in the buffer and return the index for that. std::string index = index_lines[index_line - 1]; - auto buffer_it = buffer_line_lookup.find(index); - if (buffer_it == buffer_line_lookup.end()) { + auto buffer_it = all_buffer_lines_lookup.find(index); + if (buffer_it == all_buffer_lines_lookup.end()) { // TODO: Use levenshtein distance to find the best match (but only to an // extent) return nullopt; @@ -91,6 +104,41 @@ optional WorkingFile::GetBufferLineFromDiskLine(int index_line) const { return closest_buffer_line; } +optional WorkingFile::GetIndexLineFromBufferLine(int buffer_line) const { + // See GetBufferLineFromIndexLine for additional comments. + + // Note: |index_line| and |buffer_line| are 1-based. + + // TODO: Consider making this an assert. + if (buffer_line < 1 || buffer_line > all_buffer_lines.size()) + return nullopt; + + // Find the line in the index file. We'll try to find the most similar line + // in the index file and return the index for that. + std::string buffer = all_buffer_lines[buffer_line - 1]; + auto index_it = index_lines_lookup.find(buffer); + if (index_it == index_lines_lookup.end()) { + // TODO: Use levenshtein distance to find the best match (but only to an + // extent) + return nullopt; + } + + // From all the identical lines, return the one which is closest to + // |index_line|. There will usually only be one identical line. + assert(!index_it->second.empty()); + int closest_dist = INT_MAX; + int closest_index_line = INT_MIN; + for (int index_line : index_it->second) { + int dist = std::abs(buffer_line - index_line); + if (dist <= closest_dist) { + closest_dist = dist; + closest_index_line = index_line; + } + } + + return closest_index_line; +} + CXUnsavedFile WorkingFile::AsUnsavedFile() const { CXUnsavedFile result; result.Filename = filename.c_str(); diff --git a/src/working_files.h b/src/working_files.h index 773ac6ca..bd4c27ed 100644 --- a/src/working_files.h +++ b/src/working_files.h @@ -20,8 +20,13 @@ struct WorkingFile { std::vector index_lines; // Note: This assumes 0-based lines (1-based lines are normally assumed). std::vector all_buffer_lines; - // Note: The items in the value entry are 1-based lines.s - std::unordered_map> buffer_line_lookup; + // This map goes from disk-line -> indicies+1 in index_lines. + // Note: The items in the value entry are 1-based lines. + std::unordered_map> index_lines_lookup; + // This map goes from buffer-line -> indices+1 in all_buffer_lines. + // Note: The items in the value entry are 1-based liness. + std::unordered_map> all_buffer_lines_lookup; + WorkingFile(const std::string& filename, const std::string& buffer_content); @@ -32,7 +37,10 @@ struct WorkingFile { // Find the buffer-line which should be shown for |indexed_line|. This // accepts and returns 1-based lines. - optional GetBufferLineFromDiskLine(int indexed_line) const; + optional GetBufferLineFromIndexLine(int indexed_line) const; + // Find the indexed-line which should be shown for |buffer_line|. This + // accepts and returns 1-based lines. + optional GetIndexLineFromBufferLine(int buffer_line) const; CXUnsavedFile AsUnsavedFile() const; };