mirror of
https://github.com/MaskRay/ccls.git
synced 2025-03-30 13:32:13 +00:00
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:
parent
6e90f8db45
commit
43ad87ab8d
@ -33,7 +33,6 @@ const int kNumIndexers = 8 - 1;
|
|||||||
const int kQueueSizeBytes = 1024 * 8;
|
const int kQueueSizeBytes = 1024 * 8;
|
||||||
const int kMaxWorkspaceSearchResults = 1000;
|
const int kMaxWorkspaceSearchResults = 1000;
|
||||||
|
|
||||||
|
|
||||||
QueryableFile* FindFile(QueryableDatabase* db, const std::string& filename) {
|
QueryableFile* FindFile(QueryableDatabase* db, const std::string& filename) {
|
||||||
auto it = db->usr_to_symbol.find(filename);
|
auto it = db->usr_to_symbol.find(filename);
|
||||||
if (it != db->usr_to_symbol.end())
|
if (it != db->usr_to_symbol.end())
|
||||||
@ -82,13 +81,45 @@ optional<QueryableLocation> GetDefinitionSpellingOfSymbol(QueryableDatabase* db,
|
|||||||
return nullopt;
|
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) {
|
if (!working_file) {
|
||||||
return lsRange(
|
return lsRange(
|
||||||
lsPosition(location.start.line - 1, location.start.column - 1),
|
lsPosition(location.start.line - 1, location.start.column - 1),
|
||||||
lsPosition(location.end.line - 1, location.end.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(
|
return lsRange(
|
||||||
lsPosition(working_file->GetBufferLineFromDiskLine(location.start.line) - 1, location.start.column - 1),
|
lsPosition(working_file->GetBufferLineFromDiskLine(location.start.line) - 1, location.start.column - 1),
|
||||||
lsPosition(working_file->GetBufferLineFromDiskLine(location.end.line) - 1, location.end.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);
|
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;
|
std::string path;
|
||||||
lsDocumentUri uri = GetLsDocumentUri(db, location.path, &path);
|
lsDocumentUri uri = GetLsDocumentUri(db, location.path, &path);
|
||||||
lsRange range = GetLsRange(working_files->GetFileByFilename(path), location.range);
|
optional<lsRange> range = GetLsRange(working_files->GetFileByFilename(path), location.range);
|
||||||
return lsLocation(uri, range);
|
if (!range)
|
||||||
|
return nullopt;
|
||||||
|
return lsLocation(uri, *range);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns a symbol. The symbol will have the location pointing to the
|
// Returns a symbol. The symbol will have *NOT* have a location assigned.
|
||||||
// definition.
|
|
||||||
lsSymbolInformation GetSymbolInfo(QueryableDatabase* db, WorkingFiles* working_files, SymbolIdx symbol) {
|
lsSymbolInformation GetSymbolInfo(QueryableDatabase* db, WorkingFiles* working_files, SymbolIdx symbol) {
|
||||||
lsSymbolInformation info;
|
lsSymbolInformation info;
|
||||||
|
|
||||||
@ -121,15 +153,12 @@ lsSymbolInformation GetSymbolInfo(QueryableDatabase* db, WorkingFiles* working_f
|
|||||||
QueryableFile* def = symbol.ResolveFile(db);
|
QueryableFile* def = symbol.ResolveFile(db);
|
||||||
info.name = def->def.usr;
|
info.name = def->def.usr;
|
||||||
info.kind = lsSymbolKind::File;
|
info.kind = lsSymbolKind::File;
|
||||||
info.location.uri.SetPath(def->def.usr);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SymbolKind::Type: {
|
case SymbolKind::Type: {
|
||||||
QueryableTypeDef* def = symbol.ResolveType(db);
|
QueryableTypeDef* def = symbol.ResolveType(db);
|
||||||
info.name = def->def.qualified_name;
|
info.name = def->def.qualified_name;
|
||||||
info.kind = lsSymbolKind::Class;
|
info.kind = lsSymbolKind::Class;
|
||||||
if (def->def.definition_extent.has_value())
|
|
||||||
info.location = GetLsLocation(db, working_files, def->def.definition_extent.value());
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SymbolKind::Func: {
|
case SymbolKind::Func: {
|
||||||
@ -142,20 +171,12 @@ lsSymbolInformation GetSymbolInfo(QueryableDatabase* db, WorkingFiles* working_f
|
|||||||
else {
|
else {
|
||||||
info.kind = lsSymbolKind::Function;
|
info.kind = lsSymbolKind::Function;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (def->def.definition_extent.has_value()) {
|
|
||||||
info.location = GetLsLocation(db, working_files, def->def.definition_extent.value());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SymbolKind::Var: {
|
case SymbolKind::Var: {
|
||||||
QueryableVarDef* def = symbol.ResolveVar(db);
|
QueryableVarDef* def = symbol.ResolveVar(db);
|
||||||
info.name = def->def.qualified_name;
|
info.name = def->def.qualified_name;
|
||||||
info.kind = lsSymbolKind::Variable;
|
info.kind = lsSymbolKind::Variable;
|
||||||
|
|
||||||
if (def->def.definition_extent.has_value()) {
|
|
||||||
info.location = GetLsLocation(db, working_files, def->def.definition_extent.value());
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SymbolKind::Invalid: {
|
case SymbolKind::Invalid: {
|
||||||
@ -180,7 +201,10 @@ void AddCodeLens(
|
|||||||
const char* singular,
|
const char* singular,
|
||||||
const char* plural) {
|
const char* plural) {
|
||||||
TCodeLens code_lens;
|
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 = lsCommand<lsCodeLensCommandArguments>();
|
||||||
code_lens.command->command = "superindex.showReferences";
|
code_lens.command->command = "superindex.showReferences";
|
||||||
code_lens.command->arguments.uri = GetLsDocumentUri(db, loc.path);
|
code_lens.command->arguments.uri = GetLsDocumentUri(db, loc.path);
|
||||||
@ -193,7 +217,10 @@ void AddCodeLens(
|
|||||||
continue;
|
continue;
|
||||||
if (only_interesting && !use.range.interesting)
|
if (only_interesting && !use.range.interesting)
|
||||||
continue;
|
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(),
|
code_lens.command->arguments.locations.assign(unique_uses.begin(),
|
||||||
unique_uses.end());
|
unique_uses.end());
|
||||||
@ -527,7 +554,7 @@ void QueryDbMainLoop(
|
|||||||
|
|
||||||
std::vector<std::unique_ptr<BaseIpcMessage>> messages = language_client->GetMessages(&language_client->for_server);
|
std::vector<std::unique_ptr<BaseIpcMessage>> messages = language_client->GetMessages(&language_client->for_server);
|
||||||
for (auto& message : messages) {
|
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) {
|
switch (message->method_id) {
|
||||||
case IpcId::Quit: {
|
case IpcId::Quit: {
|
||||||
@ -636,8 +663,11 @@ void QueryDbMainLoop(
|
|||||||
if (ref.loc.range.start.line >= target_line && ref.loc.range.end.line <= target_line &&
|
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) {
|
ref.loc.range.start.column <= target_column && ref.loc.range.end.column >= target_column) {
|
||||||
optional<QueryableLocation> location = GetDefinitionSpellingOfSymbol(db, ref.idx);
|
optional<QueryableLocation> location = GetDefinitionSpellingOfSymbol(db, ref.idx);
|
||||||
if (location)
|
if (location) {
|
||||||
response.result.push_back(GetLsLocation(db, working_files, location.value()));
|
optional<lsLocation> ls_location = GetLsLocation(db, working_files, location.value());
|
||||||
|
if (ls_location)
|
||||||
|
response.result.push_back(*ls_location);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -661,7 +691,10 @@ void QueryDbMainLoop(
|
|||||||
std::cerr << "File outline size is " << file->def.outline.size() << std::endl;
|
std::cerr << "File outline size is " << file->def.outline.size() << std::endl;
|
||||||
for (SymbolRef ref : file->def.outline) {
|
for (SymbolRef ref : file->def.outline) {
|
||||||
lsSymbolInformation info = GetSymbolInfo(db, working_files, ref.idx);
|
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);
|
response.result.push_back(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,8 +786,17 @@ void QueryDbMainLoop(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (db->qualified_names[i].find(query) != std::string::npos)
|
if (db->qualified_names[i].find(query) != std::string::npos) {
|
||||||
response.result.push_back(GetSymbolInfo(db, working_files, db->symbols[i]));
|
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);
|
SendOutMessageToClient(language_client, response);
|
||||||
@ -917,7 +959,7 @@ void LanguageServerStdinLoop(IpcMessageQueue* ipc) {
|
|||||||
case IpcId::TextDocumentDocumentSymbol:
|
case IpcId::TextDocumentDocumentSymbol:
|
||||||
case IpcId::TextDocumentCodeLens:
|
case IpcId::TextDocumentCodeLens:
|
||||||
case IpcId::WorkspaceSymbol:
|
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());
|
ipc->SendMessage(&ipc->for_server, message->method_id, *message.get());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -27,23 +27,19 @@ int LineCount(std::string::const_iterator start, const std::string::const_iterat
|
|||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool IsNextTokenNewline(const std::string& str, int start) {
|
bool IsPreviousTokenNewline(const std::string& str, int start) {
|
||||||
while (start < str.size()) {
|
return start == 0 || str[start - 1] == '\n';
|
||||||
// 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;
|
|
||||||
|
|
||||||
if (str[start] == '\n')
|
int DetermineDiskLineForChange(WorkingFile* f, int start_offset, int buffer_line, int line_delta) {
|
||||||
return true;
|
// 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 (!isalpha(str[start]))
|
if (!IsPreviousTokenNewline(f->content, start_offset)) {
|
||||||
return false;
|
//std::cerr << " Applying newline workaround" << std::endl;
|
||||||
|
++buffer_line;
|
||||||
++start;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return f->GetDiskLineFromBufferLine(buffer_line);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
@ -186,7 +182,7 @@ void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
|
|||||||
return;
|
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) {
|
for (const Ipc_TextDocumentDidChange::lsTextDocumentContentChangeEvent& diff : change.contentChanges) {
|
||||||
//std::cerr << "Applying rangeLength=" << diff.rangeLength;
|
//std::cerr << "Applying rangeLength=" << diff.rangeLength;
|
||||||
@ -197,26 +193,28 @@ void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
int start_offset = GetOffsetForPosition(diff.range.start, file->content);
|
int start_offset = GetOffsetForPosition(diff.range.start, file->content);
|
||||||
int previous_line_count = diff.range.end.line - diff.range.start.line;
|
int old_line_count = diff.range.end.line - diff.range.start.line;
|
||||||
int replace_line_count = LineCount(diff.text.begin(), diff.text.end());
|
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) {
|
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
|
// language server stores line counts starting at 0, we start at 1
|
||||||
int buffer_line = diff.range.start.line + 1;
|
int buffer_line = diff.range.start.line + 1;
|
||||||
|
int disk_line = DetermineDiskLineForChange(file, start_offset, buffer_line, line_delta);
|
||||||
// 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);
|
|
||||||
|
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (int i = 0; i < file->changes.size(); ++i) {
|
for (int i = 0; i < file->changes.size(); ++i) {
|
||||||
auto& change = file->changes[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;
|
change.delta += line_delta;
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
@ -227,14 +225,16 @@ void WorkingFiles::OnChange(const Ipc_TextDocumentDidChange::Params& change) {
|
|||||||
if (!found)
|
if (!found)
|
||||||
file->changes.push_back(WorkingFile::Change(disk_line, line_delta));
|
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(),
|
std::sort(file->changes.begin(), file->changes.end(),
|
||||||
[](const WorkingFile::Change& a, const WorkingFile::Change& b) {
|
[](const WorkingFile::Change& a, const WorkingFile::Change& b) {
|
||||||
return a.disk_line < b.disk_line;
|
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);
|
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();
|
TEST_SUITE_END();
|
Loading…
Reference in New Issue
Block a user