Some delta tracking improvements. Still not perfect but probably good enough. Also hide refs that have been deleted in source code.

This commit is contained in:
Jacob Dufault 2017-04-09 15:16:06 -07:00
parent 6e90f8db45
commit 43ad87ab8d
2 changed files with 162 additions and 58 deletions

View File

@ -33,7 +33,6 @@ const int kNumIndexers = 8 - 1;
const int kQueueSizeBytes = 1024 * 8;
const int kMaxWorkspaceSearchResults = 1000;
QueryableFile* FindFile(QueryableDatabase* db, const std::string& filename) {
auto it = db->usr_to_symbol.find(filename);
if (it != db->usr_to_symbol.end())
@ -82,13 +81,45 @@ optional<QueryableLocation> GetDefinitionSpellingOfSymbol(QueryableDatabase* db,
return nullopt;
}
lsRange GetLsRange(WorkingFile* working_file, const Range& location) {
optional<QueryableLocation> GetDefinitionExtentOfSymbol(QueryableDatabase* db, const QueryTypeId& id) {
return GetQueryable(db, id)->def.definition_extent;
}
optional<QueryableLocation> GetDefinitionExtentOfSymbol(QueryableDatabase* db, const QueryFuncId& id) {
return GetQueryable(db, id)->def.definition_extent;
}
optional<QueryableLocation> GetDefinitionExtentOfSymbol(QueryableDatabase* db, const QueryVarId& id) {
return GetQueryable(db, id)->def.definition_extent;
}
optional<QueryableLocation> GetDefinitionExtentOfSymbol(QueryableDatabase* db, const SymbolIdx& symbol) {
switch (symbol.kind) {
case SymbolKind::File:
// TODO: If line 1 is deleted the file won't show up in, ie, workspace symbol search results.
return QueryableLocation(QueryFileId(symbol.idx), Range(false /*is_interesting*/, Position(1, 1), Position(1, 1)));
case SymbolKind::Type:
return db->types[symbol.idx].def.definition_extent;
case SymbolKind::Func:
return db->funcs[symbol.idx].def.definition_extent;
case SymbolKind::Var:
return db->vars[symbol.idx].def.definition_extent;
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
}
}
return nullopt;
}
optional<lsRange> GetLsRange(WorkingFile* working_file, const Range& location) {
if (!working_file) {
return lsRange(
lsPosition(location.start.line - 1, location.start.column - 1),
lsPosition(location.end.line - 1, location.end.column - 1));
}
// TODO: Should we also consider location.end.line?
if (working_file->IsDeletedDiskLine(location.start.line))
return nullopt;
return lsRange(
lsPosition(working_file->GetBufferLineFromDiskLine(location.start.line) - 1, location.start.column - 1),
lsPosition(working_file->GetBufferLineFromDiskLine(location.end.line) - 1, location.end.column - 1));
@ -104,15 +135,16 @@ lsDocumentUri GetLsDocumentUri(QueryableDatabase* db, QueryFileId file_id) {
return lsDocumentUri::FromPath(path);
}
lsLocation GetLsLocation(QueryableDatabase* db, WorkingFiles* working_files, const QueryableLocation& location) {
optional<lsLocation> GetLsLocation(QueryableDatabase* db, WorkingFiles* working_files, const QueryableLocation& location) {
std::string path;
lsDocumentUri uri = GetLsDocumentUri(db, location.path, &path);
lsRange range = GetLsRange(working_files->GetFileByFilename(path), location.range);
return lsLocation(uri, range);
optional<lsRange> range = GetLsRange(working_files->GetFileByFilename(path), location.range);
if (!range)
return nullopt;
return lsLocation(uri, *range);
}
// Returns a symbol. The symbol will have the location pointing to the
// definition.
// Returns a symbol. The symbol will have *NOT* have a location assigned.
lsSymbolInformation GetSymbolInfo(QueryableDatabase* db, WorkingFiles* working_files, SymbolIdx symbol) {
lsSymbolInformation info;
@ -121,15 +153,12 @@ lsSymbolInformation GetSymbolInfo(QueryableDatabase* db, WorkingFiles* working_f
QueryableFile* def = symbol.ResolveFile(db);
info.name = def->def.usr;
info.kind = lsSymbolKind::File;
info.location.uri.SetPath(def->def.usr);
break;
}
case SymbolKind::Type: {
QueryableTypeDef* def = symbol.ResolveType(db);
info.name = def->def.qualified_name;
info.kind = lsSymbolKind::Class;
if (def->def.definition_extent.has_value())
info.location = GetLsLocation(db, working_files, def->def.definition_extent.value());
break;
}
case SymbolKind::Func: {
@ -142,20 +171,12 @@ lsSymbolInformation GetSymbolInfo(QueryableDatabase* db, WorkingFiles* working_f
else {
info.kind = lsSymbolKind::Function;
}
if (def->def.definition_extent.has_value()) {
info.location = GetLsLocation(db, working_files, def->def.definition_extent.value());
}
break;
}
case SymbolKind::Var: {
QueryableVarDef* def = symbol.ResolveVar(db);
info.name = def->def.qualified_name;
info.kind = lsSymbolKind::Variable;
if (def->def.definition_extent.has_value()) {
info.location = GetLsLocation(db, working_files, def->def.definition_extent.value());
}
break;
}
case SymbolKind::Invalid: {
@ -180,7 +201,10 @@ void AddCodeLens(
const char* singular,
const char* plural) {
TCodeLens code_lens;
code_lens.range = GetLsRange(working_file, loc.range);
optional<lsRange> range = GetLsRange(working_file, loc.range);
if (!range)
return;
code_lens.range = *range;
code_lens.command = lsCommand<lsCodeLensCommandArguments>();
code_lens.command->command = "superindex.showReferences";
code_lens.command->arguments.uri = GetLsDocumentUri(db, loc.path);
@ -193,7 +217,10 @@ void AddCodeLens(
continue;
if (only_interesting && !use.range.interesting)
continue;
unique_uses.insert(GetLsLocation(db, working_files, use));
optional<lsLocation> location = GetLsLocation(db, working_files, use);
if (!location)
continue;
unique_uses.insert(*location);
}
code_lens.command->arguments.locations.assign(unique_uses.begin(),
unique_uses.end());
@ -527,7 +554,7 @@ void QueryDbMainLoop(
std::vector<std::unique_ptr<BaseIpcMessage>> messages = language_client->GetMessages(&language_client->for_server);
for (auto& message : messages) {
std::cerr << "[querydb] Processing message " << static_cast<int>(message->method_id) << std::endl;
//std::cerr << "[querydb] Processing message " << static_cast<int>(message->method_id) << std::endl;
switch (message->method_id) {
case IpcId::Quit: {
@ -636,8 +663,11 @@ void QueryDbMainLoop(
if (ref.loc.range.start.line >= target_line && ref.loc.range.end.line <= target_line &&
ref.loc.range.start.column <= target_column && ref.loc.range.end.column >= target_column) {
optional<QueryableLocation> location = GetDefinitionSpellingOfSymbol(db, ref.idx);
if (location)
response.result.push_back(GetLsLocation(db, working_files, location.value()));
if (location) {
optional<lsLocation> ls_location = GetLsLocation(db, working_files, location.value());
if (ls_location)
response.result.push_back(*ls_location);
}
break;
}
}
@ -661,7 +691,10 @@ void QueryDbMainLoop(
std::cerr << "File outline size is " << file->def.outline.size() << std::endl;
for (SymbolRef ref : file->def.outline) {
lsSymbolInformation info = GetSymbolInfo(db, working_files, ref.idx);
info.location = GetLsLocation(db, working_files, ref.loc);
optional<lsLocation> location = GetLsLocation(db, working_files, ref.loc);
if (!location)
continue;
info.location = *location;
response.result.push_back(info);
}
@ -753,8 +786,17 @@ void QueryDbMainLoop(
break;
}
if (db->qualified_names[i].find(query) != std::string::npos)
response.result.push_back(GetSymbolInfo(db, working_files, db->symbols[i]));
if (db->qualified_names[i].find(query) != std::string::npos) {
lsSymbolInformation info = GetSymbolInfo(db, working_files, db->symbols[i]);
optional<QueryableLocation> location = GetDefinitionExtentOfSymbol(db, db->symbols[i]);
if (!location)
continue;
optional<lsLocation> ls_location = GetLsLocation(db, working_files, *location);
if (!ls_location)
continue;
info.location = *ls_location;
response.result.push_back(info);
}
}
SendOutMessageToClient(language_client, response);
@ -917,7 +959,7 @@ void LanguageServerStdinLoop(IpcMessageQueue* ipc) {
case IpcId::TextDocumentDocumentSymbol:
case IpcId::TextDocumentCodeLens:
case IpcId::WorkspaceSymbol:
std::cerr << "Spending message " << (int)message->method_id << std::endl;
//std::cerr << "Sending message " << (int)message->method_id << std::endl;
ipc->SendMessage(&ipc->for_server, message->method_id, *message.get());
break;
}

View File

@ -27,23 +27,19 @@ int LineCount(std::string::const_iterator start, const std::string::const_iterat
return count;
}
bool IsNextTokenNewline(const std::string& str, int start) {
while (start < str.size()) {
// Peek a token ahead for \r\n. We cannot check a token back because
// isalpha('\r') returns false.
if ((start + 1 < str.size()) && str[start] == '\r' && str[start + 1] == '\n')
return true;
bool IsPreviousTokenNewline(const std::string& str, int start) {
return start == 0 || str[start - 1] == '\n';
}
if (str[start] == '\n')
return true;
if (!isalpha(str[start]))
return false;
++start;
int DetermineDiskLineForChange(WorkingFile* f, int start_offset, int buffer_line, int line_delta) {
// If we're adding a newline that means we will introduce a new virtual newline
// below. That's the case *except* if we are moving the current line down.
if (!IsPreviousTokenNewline(f->content, start_offset)) {
//std::cerr << " Applying newline workaround" << std::endl;
++buffer_line;
}
return true;
return f->GetDiskLineFromBufferLine(buffer_line);
}
} // namespace
@ -186,7 +182,7 @@ void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
return;
}
// TODO: we should probably pay attention to versioning.
//std::cerr << "VERSION " << change.textDocument.version << std::endl;
for (const Ipc_TextDocumentDidChange::lsTextDocumentContentChangeEvent& diff : change.contentChanges) {
//std::cerr << "Applying rangeLength=" << diff.rangeLength;
@ -197,26 +193,28 @@ void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
}
else {
int start_offset = GetOffsetForPosition(diff.range.start, file->content);
int previous_line_count = diff.range.end.line - diff.range.start.line;
int replace_line_count = LineCount(diff.text.begin(), diff.text.end());
int old_line_count = diff.range.end.line - diff.range.start.line;
int new_line_count = LineCount(diff.text.begin(), diff.text.end());
int line_delta = replace_line_count - previous_line_count;
assert(old_line_count == LineCount(file->content.begin() + start_offset, file->content.begin() + start_offset + diff.rangeLength + 1));
int line_delta = new_line_count - old_line_count;
if (line_delta != 0) {
//std::cerr << " diff.range.start.line=" << diff.range.start.line+1 << ", diff.range.start.character=" << diff.range.start.character << std::endl;
//std::cerr << " diff.range.end.line=" << diff.range.end.line+1 << ", diff.range.end.character=" << diff.range.end.character << std::endl;
//std::cerr << " old_line_count=" << old_line_count << ", new_line_count=" << new_line_count << std::endl;
//std::cerr << " disk_line(diff.range.start.line)=" << file->GetDiskLineFromBufferLine(diff.range.start.line+1) << std::endl;
//std::cerr << " disk_line(diff.range.end.line)=" << file->GetDiskLineFromBufferLine(diff.range.end.line+1) << std::endl;
// language server stores line counts starting at 0, we start at 1
int buffer_line = diff.range.start.line + 1;
// If we're adding a newline but not actually shifting any of the
// contents of this line, we should mark the text prepended on the next
// line.
if (IsNextTokenNewline(file->content, start_offset))
++buffer_line;
int disk_line = file->GetDiskLineFromBufferLine(buffer_line);
int disk_line = DetermineDiskLineForChange(file, start_offset, buffer_line, line_delta);
bool found = false;
for (int i = 0; i < file->changes.size(); ++i) {
auto& change = file->changes[i];
if (change.disk_line == disk_line) {
if (disk_line == change.disk_line ||
(line_delta >= 1 && disk_line - 1 == change.disk_line && change.delta < 0) /* handles joining two lines and then resplitting them */ ) {
change.delta += line_delta;
found = true;
}
@ -227,14 +225,16 @@ void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
if (!found)
file->changes.push_back(WorkingFile::Change(disk_line, line_delta));
//std::cerr << "(changes.size()=" << file->changes.size() << ") Inserted delta=" << line_delta << " at disk_line=" << disk_line << " with buffer_line=" << buffer_line << std::endl;
//for (auto& change : file->changes)
// std::cerr << " disk_line=" << change.disk_line << " delta=" << change.delta << std::endl;
std::sort(file->changes.begin(), file->changes.end(),
[](const WorkingFile::Change& a, const WorkingFile::Change& b) {
return a.disk_line < b.disk_line;
});
//std::cerr << " APPLIED" << std::endl;
//std::cerr << " Inserted delta=" << line_delta << " at disk_line=" << disk_line << " with buffer_line=" << buffer_line << std::endl;
//std::cerr << " changes.size()=" << file->changes.size() << std::endl;
//for (auto& change : file->changes)
// std::cerr << " disk_line=" << change.disk_line << " delta=" << change.delta << std::endl;
}
@ -557,4 +557,66 @@ TEST_CASE("deleted_line past-start") {
CHECK(f.IsDeletedDiskLine(5) == false);
}
TEST_CASE("wip") {
/*
VERSION 4
diff.range.start.line=2, diff.range.start.character=0
diff.range.end.line= 3, diff.range.end.character= 0
diff.text=""
Applying newline workaround
APPLIED
Inserted delta=-1 at disk_line=4 with buffer_line=4
changes.size()=1
disk_line=4 delta=-1
VERSION 7
diff.range.start.line=2, diff.range.start.character=0
diff.range.end.line= 2, diff.range.end.character= 0
diff.text="daaa\n"
Applying newline workaround
APPLIED
Inserted delta=1 at disk_line=5 with buffer_line=4
changes.size()=2
disk_line=4 delta=-1
disk_line=5 delta=1
*/
WorkingFile f("", "");
/*
delete line 4
buffer line 4 -> disk line 5
*/
f.changes.push_back(WorkingFile::Change(4, -1));
CHECK(f.GetDiskLineFromBufferLine(4) == 5);
}
TEST_CASE("DetermineDiskLineForChange") {
// aaa
// bbb
WorkingFile f("", "aaa\nbbb");
// Adding a line so we have
// aaa
//
// bbb
CHECK(DetermineDiskLineForChange(&f, 3 /*start_offset*/, 1 /*buffer_line*/, 1 /*line_delta*/) == 2);
// Adding a line so we have
//
// aaa
// bbb
CHECK(DetermineDiskLineForChange(&f, 0 /*start_offset*/, 1 /*buffer_line*/, 1 /*line_delta*/) == 1);
// Adding a line so we have
// a
// aa
// bbb
CHECK(DetermineDiskLineForChange(&f, 1 /*start_offset*/, 1 /*buffer_line*/, 1 /*line_delta*/) == 2);
}
TEST_SUITE_END();