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:
Jacob Dufault 2017-04-19 00:52:48 -07:00
parent 20864e422a
commit ea045499b6
3 changed files with 89 additions and 21 deletions

View File

@ -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;

View File

@ -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();

View File

@ -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;
};