mirror of
https://github.com/MaskRay/ccls.git
synced 2025-01-31 18:00:26 +00:00
When looking up symbols map buffer line to index line.
This makes editing experience a bit smoother, as you can goto definition before saving.
This commit is contained in:
parent
20864e422a
commit
ea045499b6
@ -533,8 +533,8 @@ optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& location) {
|
||||
lsPosition(location.end.line - 1, location.end.column - 1));
|
||||
}
|
||||
|
||||
optional<int> start = working_file->GetBufferLineFromDiskLine(location.start.line);
|
||||
optional<int> end = working_file->GetBufferLineFromDiskLine(location.end.line);
|
||||
optional<int> start = working_file->GetBufferLineFromIndexLine(location.start.line);
|
||||
optional<int> 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<SymbolRef> FindSymbolsAtLocation(QueryFile* file, lsPosition position) {
|
||||
std::vector<SymbolRef> FindSymbolsAtLocation(WorkingFile* working_file, QueryFile* file, lsPosition position) {
|
||||
std::vector<SymbolRef> 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<int> 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<QueryLocation> 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<QueryLocation> 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<lsRange> 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<QueryLocation> excluded_declaration;
|
||||
if (!msg->params.context.includeDeclaration) {
|
||||
std::cerr << "Excluding declaration in references" << std::endl;
|
||||
|
@ -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<int> WorkingFile::GetBufferLineFromDiskLine(int index_line) const {
|
||||
optional<int> 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<int> 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<int> WorkingFile::GetBufferLineFromDiskLine(int index_line) const {
|
||||
return closest_buffer_line;
|
||||
}
|
||||
|
||||
optional<int> 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();
|
||||
|
@ -20,8 +20,13 @@ struct WorkingFile {
|
||||
std::vector<std::string> index_lines;
|
||||
// Note: This assumes 0-based lines (1-based lines are normally assumed).
|
||||
std::vector<std::string> all_buffer_lines;
|
||||
// Note: The items in the value entry are 1-based lines.s
|
||||
std::unordered_map<std::string, std::vector<int>> 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<std::string, std::vector<int>> 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<std::string, std::vector<int>> 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<int> GetBufferLineFromDiskLine(int indexed_line) const;
|
||||
optional<int> GetBufferLineFromIndexLine(int indexed_line) const;
|
||||
// Find the indexed-line which should be shown for |buffer_line|. This
|
||||
// accepts and returns 1-based lines.
|
||||
optional<int> GetIndexLineFromBufferLine(int buffer_line) const;
|
||||
|
||||
CXUnsavedFile AsUnsavedFile() const;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user