mirror of
https://github.com/MaskRay/ccls.git
synced 2025-03-30 13:32:13 +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));
|
lsPosition(location.end.line - 1, location.end.column - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
optional<int> start = working_file->GetBufferLineFromDiskLine(location.start.line);
|
optional<int> start = working_file->GetBufferLineFromIndexLine(location.start.line);
|
||||||
optional<int> end = working_file->GetBufferLineFromDiskLine(location.end.line);
|
optional<int> end = working_file->GetBufferLineFromIndexLine(location.end.line);
|
||||||
if (!start || !end)
|
if (!start || !end)
|
||||||
return nullopt;
|
return nullopt;
|
||||||
|
|
||||||
@ -687,14 +687,18 @@ lsWorkspaceEdit BuildWorkspaceEdit(QueryDatabase* db, WorkingFiles* working_file
|
|||||||
return edit;
|
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;
|
std::vector<SymbolRef> symbols;
|
||||||
symbols.reserve(1);
|
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_line = position.line + 1;
|
||||||
int target_column = position.character + 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) {
|
for (const SymbolRef& ref : file->def.all_symbols) {
|
||||||
if (ref.loc.range.Contains(target_line, target_column))
|
if (ref.loc.range.Contains(target_line, target_column))
|
||||||
symbols.push_back(ref);
|
symbols.push_back(ref);
|
||||||
@ -1192,10 +1196,12 @@ void QueryDbMainLoop(
|
|||||||
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
WorkingFile* working_file = working_files->GetFileByFilename(file->def.path);
|
||||||
|
|
||||||
Out_TextDocumentRename response;
|
Out_TextDocumentRename response;
|
||||||
response.id = msg->id;
|
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.
|
// Found symbol. Return references to rename.
|
||||||
std::vector<QueryLocation> uses = GetUsesOfSymbol(db, ref.idx);
|
std::vector<QueryLocation> uses = GetUsesOfSymbol(db, ref.idx);
|
||||||
response.result = BuildWorkspaceEdit(db, working_files, uses, msg->params.newName);
|
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;
|
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
WorkingFile* working_file = working_files->GetFileByFilename(file->def.path);
|
||||||
|
|
||||||
Out_TextDocumentDefinition response;
|
Out_TextDocumentDefinition response;
|
||||||
response.id = msg->id;
|
response.id = msg->id;
|
||||||
@ -1248,7 +1255,7 @@ void QueryDbMainLoop(
|
|||||||
int target_line = msg->params.position.line + 1;
|
int target_line = msg->params.position.line + 1;
|
||||||
int target_column = msg->params.position.character + 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.
|
// Found symbol. Return definition.
|
||||||
|
|
||||||
// Special cases which are handled:
|
// Special cases which are handled:
|
||||||
@ -1304,10 +1311,12 @@ void QueryDbMainLoop(
|
|||||||
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
WorkingFile* working_file = working_files->GetFileByFilename(file->def.path);
|
||||||
|
|
||||||
Out_TextDocumentDocumentHighlight response;
|
Out_TextDocumentDocumentHighlight response;
|
||||||
response.id = msg->id;
|
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.
|
// Found symbol. Return references to highlight.
|
||||||
std::vector<QueryLocation> uses = GetUsesOfSymbol(db, ref.idx);
|
std::vector<QueryLocation> uses = GetUsesOfSymbol(db, ref.idx);
|
||||||
response.result.reserve(uses.size());
|
response.result.reserve(uses.size());
|
||||||
@ -1339,10 +1348,12 @@ void QueryDbMainLoop(
|
|||||||
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
WorkingFile* working_file = working_files->GetFileByFilename(file->def.path);
|
||||||
|
|
||||||
Out_TextDocumentHover response;
|
Out_TextDocumentHover response;
|
||||||
response.id = msg->id;
|
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.
|
// Found symbol. Return hover.
|
||||||
optional<lsRange> ls_range = GetLsRange(working_files->GetFileByFilename(file->def.path), ref.loc.range);
|
optional<lsRange> ls_range = GetLsRange(working_files->GetFileByFilename(file->def.path), ref.loc.range);
|
||||||
if (!ls_range)
|
if (!ls_range)
|
||||||
@ -1365,11 +1376,12 @@ void QueryDbMainLoop(
|
|||||||
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
std::cerr << "Unable to find file " << msg->params.textDocument.uri.GetPath() << std::endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
WorkingFile* working_file = working_files->GetFileByFilename(file->def.path);
|
||||||
|
|
||||||
Out_TextDocumentReferences response;
|
Out_TextDocumentReferences response;
|
||||||
response.id = msg->id;
|
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;
|
optional<QueryLocation> excluded_declaration;
|
||||||
if (!msg->params.context.includeDeclaration) {
|
if (!msg->params.context.includeDeclaration) {
|
||||||
std::cerr << "Excluding declaration in references" << std::endl;
|
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) {
|
void WorkingFile::SetIndexContent(const std::string& index_content) {
|
||||||
index_lines = ToLines(index_content, true /*trim_whitespace*/);
|
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() {
|
void WorkingFile::OnBufferContentUpdated() {
|
||||||
all_buffer_lines = ToLines(buffer_content, true /*trim_whitespace*/);
|
all_buffer_lines = ToLines(buffer_content, true /*trim_whitespace*/);
|
||||||
|
|
||||||
// Build lookup buffer.
|
// Build lookup buffer.
|
||||||
buffer_line_lookup.clear();
|
all_buffer_lines_lookup.clear();
|
||||||
buffer_line_lookup.reserve(all_buffer_lines.size());
|
all_buffer_lines_lookup.reserve(all_buffer_lines.size());
|
||||||
for (int i = 0; i < all_buffer_lines.size(); ++i) {
|
for (int i = 0; i < all_buffer_lines.size(); ++i) {
|
||||||
const std::string& buffer_line = all_buffer_lines[i];
|
const std::string& buffer_line = all_buffer_lines[i];
|
||||||
|
|
||||||
auto it = buffer_line_lookup.find(buffer_line);
|
auto it = all_buffer_lines_lookup.find(buffer_line);
|
||||||
if (it == buffer_line_lookup.end())
|
if (it == all_buffer_lines_lookup.end())
|
||||||
buffer_line_lookup[buffer_line] = { i + 1 };
|
all_buffer_lines_lookup[buffer_line] = { i + 1 };
|
||||||
else
|
else
|
||||||
it->second.push_back(i + 1);
|
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
|
// 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
|
// lookup the line contents in the indexed file contents, and try to find the
|
||||||
// most similar line in the current buffer file.
|
// 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
|
// 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.
|
// in the buffer and return the index for that.
|
||||||
std::string index = index_lines[index_line - 1];
|
std::string index = index_lines[index_line - 1];
|
||||||
auto buffer_it = buffer_line_lookup.find(index);
|
auto buffer_it = all_buffer_lines_lookup.find(index);
|
||||||
if (buffer_it == buffer_line_lookup.end()) {
|
if (buffer_it == all_buffer_lines_lookup.end()) {
|
||||||
// TODO: Use levenshtein distance to find the best match (but only to an
|
// TODO: Use levenshtein distance to find the best match (but only to an
|
||||||
// extent)
|
// extent)
|
||||||
return nullopt;
|
return nullopt;
|
||||||
@ -91,6 +104,41 @@ optional<int> WorkingFile::GetBufferLineFromDiskLine(int index_line) const {
|
|||||||
return closest_buffer_line;
|
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 WorkingFile::AsUnsavedFile() const {
|
||||||
CXUnsavedFile result;
|
CXUnsavedFile result;
|
||||||
result.Filename = filename.c_str();
|
result.Filename = filename.c_str();
|
||||||
|
@ -20,8 +20,13 @@ struct WorkingFile {
|
|||||||
std::vector<std::string> index_lines;
|
std::vector<std::string> index_lines;
|
||||||
// Note: This assumes 0-based lines (1-based lines are normally assumed).
|
// Note: This assumes 0-based lines (1-based lines are normally assumed).
|
||||||
std::vector<std::string> all_buffer_lines;
|
std::vector<std::string> all_buffer_lines;
|
||||||
// Note: The items in the value entry are 1-based lines.s
|
// This map goes from disk-line -> indicies+1 in index_lines.
|
||||||
std::unordered_map<std::string, std::vector<int>> buffer_line_lookup;
|
// 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);
|
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
|
// Find the buffer-line which should be shown for |indexed_line|. This
|
||||||
// accepts and returns 1-based lines.
|
// 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;
|
CXUnsavedFile AsUnsavedFile() const;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user