|
@ -1,9 +1,9 @@ |
|
|
#include "message_handler.h" |
|
|
#include "message_handler.h" |
|
|
|
|
|
|
|
|
#include "log.hh" |
|
|
#include "log.hh" |
|
|
|
|
|
#include "pipeline.hh" |
|
|
#include "project.h" |
|
|
#include "project.h" |
|
|
#include "query_utils.h" |
|
|
#include "query_utils.h" |
|
|
#include "pipeline.hh" |
|
|
|
|
|
using namespace ccls; |
|
|
using namespace ccls; |
|
|
|
|
|
|
|
|
using namespace clang; |
|
|
using namespace clang; |
|
@ -28,10 +28,10 @@ MAKE_REFLECT_STRUCT(Out_CclsSetSkippedRanges, jsonrpc, method, params); |
|
|
|
|
|
|
|
|
struct ScanLineEvent { |
|
|
struct ScanLineEvent { |
|
|
lsPosition pos; |
|
|
lsPosition pos; |
|
|
lsPosition end_pos; // Second key when there is a tie for insertion events.
|
|
|
lsPosition end_pos; // Second key when there is a tie for insertion events.
|
|
|
int id; |
|
|
int id; |
|
|
Out_CclsPublishSemanticHighlighting::Symbol* symbol; |
|
|
Out_CclsPublishSemanticHighlighting::Symbol *symbol; |
|
|
bool operator<(const ScanLineEvent& other) const { |
|
|
bool operator<(const ScanLineEvent &other) const { |
|
|
// See the comments below when insertion/deletion events are inserted.
|
|
|
// See the comments below when insertion/deletion events are inserted.
|
|
|
if (!(pos == other.pos)) |
|
|
if (!(pos == other.pos)) |
|
|
return pos < other.pos; |
|
|
return pos < other.pos; |
|
@ -42,17 +42,15 @@ struct ScanLineEvent { |
|
|
return symbol->kind < other.symbol->kind; |
|
|
return symbol->kind < other.symbol->kind; |
|
|
} |
|
|
} |
|
|
}; |
|
|
}; |
|
|
} // namespace
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
SemanticHighlightSymbolCache::Entry::Entry( |
|
|
SemanticHighlightSymbolCache::Entry::Entry( |
|
|
SemanticHighlightSymbolCache* all_caches, |
|
|
SemanticHighlightSymbolCache *all_caches, const std::string &path) |
|
|
const std::string& path) |
|
|
|
|
|
: all_caches_(all_caches), path(path) {} |
|
|
: all_caches_(all_caches), path(path) {} |
|
|
|
|
|
|
|
|
std::optional<int> SemanticHighlightSymbolCache::Entry::TryGetStableId( |
|
|
std::optional<int> SemanticHighlightSymbolCache::Entry::TryGetStableId( |
|
|
SymbolKind kind, |
|
|
SymbolKind kind, const std::string &detailed_name) { |
|
|
const std::string& detailed_name) { |
|
|
TNameToId *map = GetMapForSymbol_(kind); |
|
|
TNameToId* map = GetMapForSymbol_(kind); |
|
|
|
|
|
auto it = map->find(detailed_name); |
|
|
auto it = map->find(detailed_name); |
|
|
if (it != map->end()) |
|
|
if (it != map->end()) |
|
|
return it->second; |
|
|
return it->second; |
|
@ -61,14 +59,13 @@ std::optional<int> SemanticHighlightSymbolCache::Entry::TryGetStableId( |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
int SemanticHighlightSymbolCache::Entry::GetStableId( |
|
|
int SemanticHighlightSymbolCache::Entry::GetStableId( |
|
|
SymbolKind kind, |
|
|
SymbolKind kind, const std::string &detailed_name) { |
|
|
const std::string& detailed_name) { |
|
|
|
|
|
std::optional<int> id = TryGetStableId(kind, detailed_name); |
|
|
std::optional<int> id = TryGetStableId(kind, detailed_name); |
|
|
if (id) |
|
|
if (id) |
|
|
return *id; |
|
|
return *id; |
|
|
|
|
|
|
|
|
// Create a new id. First try to find a key in another map.
|
|
|
// Create a new id. First try to find a key in another map.
|
|
|
all_caches_->cache_.IterateValues([&](const std::shared_ptr<Entry>& entry) { |
|
|
all_caches_->cache_.IterateValues([&](const std::shared_ptr<Entry> &entry) { |
|
|
std::optional<int> other_id = entry->TryGetStableId(kind, detailed_name); |
|
|
std::optional<int> other_id = entry->TryGetStableId(kind, detailed_name); |
|
|
if (other_id) { |
|
|
if (other_id) { |
|
|
id = other_id; |
|
|
id = other_id; |
|
@ -78,24 +75,24 @@ int SemanticHighlightSymbolCache::Entry::GetStableId( |
|
|
}); |
|
|
}); |
|
|
|
|
|
|
|
|
// Create a new id.
|
|
|
// Create a new id.
|
|
|
TNameToId* map = GetMapForSymbol_(kind); |
|
|
TNameToId *map = GetMapForSymbol_(kind); |
|
|
if (!id) |
|
|
if (!id) |
|
|
id = all_caches_->next_stable_id_++; |
|
|
id = all_caches_->next_stable_id_++; |
|
|
return (*map)[detailed_name] = *id; |
|
|
return (*map)[detailed_name] = *id; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
SemanticHighlightSymbolCache::Entry::TNameToId* |
|
|
SemanticHighlightSymbolCache::Entry::TNameToId * |
|
|
SemanticHighlightSymbolCache::Entry::GetMapForSymbol_(SymbolKind kind) { |
|
|
SemanticHighlightSymbolCache::Entry::GetMapForSymbol_(SymbolKind kind) { |
|
|
switch (kind) { |
|
|
switch (kind) { |
|
|
case SymbolKind::Type: |
|
|
case SymbolKind::Type: |
|
|
return &detailed_type_name_to_stable_id; |
|
|
return &detailed_type_name_to_stable_id; |
|
|
case SymbolKind::Func: |
|
|
case SymbolKind::Func: |
|
|
return &detailed_func_name_to_stable_id; |
|
|
return &detailed_func_name_to_stable_id; |
|
|
case SymbolKind::Var: |
|
|
case SymbolKind::Var: |
|
|
return &detailed_var_name_to_stable_id; |
|
|
return &detailed_var_name_to_stable_id; |
|
|
case SymbolKind::File: |
|
|
case SymbolKind::File: |
|
|
case SymbolKind::Invalid: |
|
|
case SymbolKind::Invalid: |
|
|
break; |
|
|
break; |
|
|
} |
|
|
} |
|
|
assert(false); |
|
|
assert(false); |
|
|
return nullptr; |
|
|
return nullptr; |
|
@ -110,7 +107,7 @@ void SemanticHighlightSymbolCache::Init() { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
std::shared_ptr<SemanticHighlightSymbolCache::Entry> |
|
|
std::shared_ptr<SemanticHighlightSymbolCache::Entry> |
|
|
SemanticHighlightSymbolCache::GetCacheForFile(const std::string& path) { |
|
|
SemanticHighlightSymbolCache::GetCacheForFile(const std::string &path) { |
|
|
return cache_.Get( |
|
|
return cache_.Get( |
|
|
path, [&, this]() { return std::make_shared<Entry>(this, path); }); |
|
|
path, [&, this]() { return std::make_shared<Entry>(this, path); }); |
|
|
} |
|
|
} |
|
@ -119,24 +116,21 @@ MessageHandler::MessageHandler() { |
|
|
// Dynamically allocate |message_handlers|, otherwise there will be static
|
|
|
// Dynamically allocate |message_handlers|, otherwise there will be static
|
|
|
// initialization order races.
|
|
|
// initialization order races.
|
|
|
if (!message_handlers) |
|
|
if (!message_handlers) |
|
|
message_handlers = new std::vector<MessageHandler*>(); |
|
|
message_handlers = new std::vector<MessageHandler *>(); |
|
|
message_handlers->push_back(this); |
|
|
message_handlers->push_back(this); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// static
|
|
|
// static
|
|
|
std::vector<MessageHandler*>* MessageHandler::message_handlers = nullptr; |
|
|
std::vector<MessageHandler *> *MessageHandler::message_handlers = nullptr; |
|
|
|
|
|
|
|
|
bool FindFileOrFail(DB* db, |
|
|
bool FindFileOrFail(DB *db, Project *project, std::optional<lsRequestId> id, |
|
|
Project* project, |
|
|
const std::string &absolute_path, |
|
|
std::optional<lsRequestId> id, |
|
|
QueryFile **out_query_file, int *out_file_id) { |
|
|
const std::string& absolute_path, |
|
|
|
|
|
QueryFile** out_query_file, |
|
|
|
|
|
int* out_file_id) { |
|
|
|
|
|
*out_query_file = nullptr; |
|
|
*out_query_file = nullptr; |
|
|
|
|
|
|
|
|
auto it = db->name2file_id.find(LowerPathIfInsensitive(absolute_path)); |
|
|
auto it = db->name2file_id.find(LowerPathIfInsensitive(absolute_path)); |
|
|
if (it != db->name2file_id.end()) { |
|
|
if (it != db->name2file_id.end()) { |
|
|
QueryFile& file = db->files[it->second]; |
|
|
QueryFile &file = db->files[it->second]; |
|
|
if (file.def) { |
|
|
if (file.def) { |
|
|
*out_query_file = &file; |
|
|
*out_query_file = &file; |
|
|
if (out_file_id) |
|
|
if (out_file_id) |
|
@ -152,7 +146,7 @@ bool FindFileOrFail(DB* db, |
|
|
{ |
|
|
{ |
|
|
std::lock_guard<std::mutex> lock(project->mutex_); |
|
|
std::lock_guard<std::mutex> lock(project->mutex_); |
|
|
indexing = project->absolute_path_to_entry_index_.find(absolute_path) != |
|
|
indexing = project->absolute_path_to_entry_index_.find(absolute_path) != |
|
|
project->absolute_path_to_entry_index_.end(); |
|
|
project->absolute_path_to_entry_index_.end(); |
|
|
} |
|
|
} |
|
|
if (indexing) |
|
|
if (indexing) |
|
|
LOG_S(INFO) << "\"" << absolute_path << "\" is being indexed."; |
|
|
LOG_S(INFO) << "\"" << absolute_path << "\" is being indexed."; |
|
@ -187,10 +181,9 @@ void EmitSkippedRanges(WorkingFile *working_file, |
|
|
pipeline::WriteStdout(kMethodType_CclsPublishSkippedRanges, out); |
|
|
pipeline::WriteStdout(kMethodType_CclsPublishSkippedRanges, out); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void EmitSemanticHighlighting(DB* db, |
|
|
void EmitSemanticHighlighting(DB *db, |
|
|
SemanticHighlightSymbolCache* semantic_cache, |
|
|
SemanticHighlightSymbolCache *semantic_cache, |
|
|
WorkingFile* wfile, |
|
|
WorkingFile *wfile, QueryFile *file) { |
|
|
QueryFile* file) { |
|
|
|
|
|
assert(file->def); |
|
|
assert(file->def); |
|
|
if (wfile->buffer_content.size() > g_config->largeFileSize || |
|
|
if (wfile->buffer_content.size() > g_config->largeFileSize || |
|
|
!semantic_cache->match_->IsMatch(file->def->path)) |
|
|
!semantic_cache->match_->IsMatch(file->def->path)) |
|
@ -208,86 +201,86 @@ void EmitSemanticHighlighting(DB* db, |
|
|
StorageClass storage = SC_None; |
|
|
StorageClass storage = SC_None; |
|
|
// This switch statement also filters out symbols that are not highlighted.
|
|
|
// This switch statement also filters out symbols that are not highlighted.
|
|
|
switch (sym.kind) { |
|
|
switch (sym.kind) { |
|
|
case SymbolKind::Func: { |
|
|
case SymbolKind::Func: { |
|
|
const QueryFunc& func = db->GetFunc(sym); |
|
|
const QueryFunc &func = db->GetFunc(sym); |
|
|
const QueryFunc::Def* def = func.AnyDef(); |
|
|
const QueryFunc::Def *def = func.AnyDef(); |
|
|
if (!def) |
|
|
if (!def) |
|
|
continue; // applies to for loop
|
|
|
continue; // applies to for loop
|
|
|
if (def->spell) |
|
|
if (def->spell) |
|
|
parent_kind = GetSymbolKind(db, *def->spell); |
|
|
parent_kind = GetSymbolKind(db, *def->spell); |
|
|
if (parent_kind == lsSymbolKind::Unknown) { |
|
|
if (parent_kind == lsSymbolKind::Unknown) { |
|
|
for (Use use : func.declarations) { |
|
|
for (Use use : func.declarations) { |
|
|
parent_kind = GetSymbolKind(db, use); |
|
|
parent_kind = GetSymbolKind(db, use); |
|
|
break; |
|
|
break; |
|
|
} |
|
|
|
|
|
} |
|
|
} |
|
|
// Don't highlight overloadable operators or implicit lambda ->
|
|
|
|
|
|
// std::function constructor.
|
|
|
|
|
|
std::string_view short_name = def->Name(false); |
|
|
|
|
|
if (short_name.compare(0, 8, "operator") == 0) |
|
|
|
|
|
continue; // applies to for loop
|
|
|
|
|
|
if (def->spell) |
|
|
|
|
|
parent_kind = GetSymbolKind(db, *def->spell); |
|
|
|
|
|
kind = def->kind; |
|
|
|
|
|
storage = def->storage; |
|
|
|
|
|
detailed_name = short_name; |
|
|
|
|
|
|
|
|
|
|
|
// Check whether the function name is actually there.
|
|
|
|
|
|
// If not, do not publish the semantic highlight.
|
|
|
|
|
|
// E.g. copy-initialization of constructors should not be highlighted
|
|
|
|
|
|
// but we still want to keep the range for jumping to definition.
|
|
|
|
|
|
std::string_view concise_name = |
|
|
|
|
|
detailed_name.substr(0, detailed_name.find('<')); |
|
|
|
|
|
int16_t start_line = sym.range.start.line; |
|
|
|
|
|
int16_t start_col = sym.range.start.column; |
|
|
|
|
|
if (start_line < 0 || start_line >= wfile->index_lines.size()) |
|
|
|
|
|
continue; |
|
|
|
|
|
std::string_view line = wfile->index_lines[start_line]; |
|
|
|
|
|
sym.range.end.line = start_line; |
|
|
|
|
|
if (!(start_col + concise_name.size() <= line.size() && |
|
|
|
|
|
line.compare(start_col, concise_name.size(), concise_name) == 0)) |
|
|
|
|
|
continue; |
|
|
|
|
|
sym.range.end.column = start_col + concise_name.size(); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
case SymbolKind::Type: |
|
|
// Don't highlight overloadable operators or implicit lambda ->
|
|
|
for (auto& def : db->GetType(sym).def) { |
|
|
// std::function constructor.
|
|
|
kind = def.kind; |
|
|
std::string_view short_name = def->Name(false); |
|
|
detailed_name = def.detailed_name; |
|
|
if (short_name.compare(0, 8, "operator") == 0) |
|
|
if (def.spell) { |
|
|
continue; // applies to for loop
|
|
|
parent_kind = GetSymbolKind(db, *def.spell); |
|
|
if (def->spell) |
|
|
break; |
|
|
parent_kind = GetSymbolKind(db, *def->spell); |
|
|
} |
|
|
kind = def->kind; |
|
|
|
|
|
storage = def->storage; |
|
|
|
|
|
detailed_name = short_name; |
|
|
|
|
|
|
|
|
|
|
|
// Check whether the function name is actually there.
|
|
|
|
|
|
// If not, do not publish the semantic highlight.
|
|
|
|
|
|
// E.g. copy-initialization of constructors should not be highlighted
|
|
|
|
|
|
// but we still want to keep the range for jumping to definition.
|
|
|
|
|
|
std::string_view concise_name = |
|
|
|
|
|
detailed_name.substr(0, detailed_name.find('<')); |
|
|
|
|
|
int16_t start_line = sym.range.start.line; |
|
|
|
|
|
int16_t start_col = sym.range.start.column; |
|
|
|
|
|
if (start_line < 0 || start_line >= wfile->index_lines.size()) |
|
|
|
|
|
continue; |
|
|
|
|
|
std::string_view line = wfile->index_lines[start_line]; |
|
|
|
|
|
sym.range.end.line = start_line; |
|
|
|
|
|
if (!(start_col + concise_name.size() <= line.size() && |
|
|
|
|
|
line.compare(start_col, concise_name.size(), concise_name) == 0)) |
|
|
|
|
|
continue; |
|
|
|
|
|
sym.range.end.column = start_col + concise_name.size(); |
|
|
|
|
|
break; |
|
|
|
|
|
} |
|
|
|
|
|
case SymbolKind::Type: |
|
|
|
|
|
for (auto &def : db->GetType(sym).def) { |
|
|
|
|
|
kind = def.kind; |
|
|
|
|
|
detailed_name = def.detailed_name; |
|
|
|
|
|
if (def.spell) { |
|
|
|
|
|
parent_kind = GetSymbolKind(db, *def.spell); |
|
|
|
|
|
break; |
|
|
} |
|
|
} |
|
|
break; |
|
|
} |
|
|
case SymbolKind::Var: { |
|
|
break; |
|
|
const QueryVar& var = db->GetVar(sym); |
|
|
case SymbolKind::Var: { |
|
|
for (auto& def : var.def) { |
|
|
const QueryVar &var = db->GetVar(sym); |
|
|
kind = def.kind; |
|
|
for (auto &def : var.def) { |
|
|
storage = def.storage; |
|
|
kind = def.kind; |
|
|
detailed_name = def.detailed_name; |
|
|
storage = def.storage; |
|
|
if (def.spell) { |
|
|
detailed_name = def.detailed_name; |
|
|
parent_kind = GetSymbolKind(db, *def.spell); |
|
|
if (def.spell) { |
|
|
break; |
|
|
parent_kind = GetSymbolKind(db, *def.spell); |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
if (parent_kind == lsSymbolKind::Unknown) { |
|
|
} |
|
|
for (Use use : var.declarations) { |
|
|
if (parent_kind == lsSymbolKind::Unknown) { |
|
|
parent_kind = GetSymbolKind(db, use); |
|
|
for (Use use : var.declarations) { |
|
|
break; |
|
|
parent_kind = GetSymbolKind(db, use); |
|
|
} |
|
|
break; |
|
|
} |
|
|
} |
|
|
break; |
|
|
|
|
|
} |
|
|
} |
|
|
default: |
|
|
break; |
|
|
continue; // applies to for loop
|
|
|
} |
|
|
|
|
|
default: |
|
|
|
|
|
continue; // applies to for loop
|
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
std::optional<lsRange> loc = GetLsRange(wfile, sym.range); |
|
|
std::optional<lsRange> loc = GetLsRange(wfile, sym.range); |
|
|
if (loc) { |
|
|
if (loc) { |
|
|
auto it = grouped_symbols.find(sym); |
|
|
auto it = grouped_symbols.find(sym); |
|
|
if (it != grouped_symbols.end()) { |
|
|
if (it != grouped_symbols.end()) { |
|
|
it->second.ranges.push_back(*loc); |
|
|
it->second.lsRanges.push_back(*loc); |
|
|
} else { |
|
|
} else { |
|
|
Out_CclsPublishSemanticHighlighting::Symbol symbol; |
|
|
Out_CclsPublishSemanticHighlighting::Symbol symbol; |
|
|
symbol.stableId = semantic_cache_for_file->GetStableId( |
|
|
symbol.stableId = semantic_cache_for_file->GetStableId( |
|
@ -295,7 +288,7 @@ void EmitSemanticHighlighting(DB* db, |
|
|
symbol.parentKind = parent_kind; |
|
|
symbol.parentKind = parent_kind; |
|
|
symbol.kind = kind; |
|
|
symbol.kind = kind; |
|
|
symbol.storage = storage; |
|
|
symbol.storage = storage; |
|
|
symbol.ranges.push_back(*loc); |
|
|
symbol.lsRanges.push_back(*loc); |
|
|
grouped_symbols[sym] = symbol; |
|
|
grouped_symbols[sym] = symbol; |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
@ -304,9 +297,9 @@ void EmitSemanticHighlighting(DB* db, |
|
|
// Make ranges non-overlapping using a scan line algorithm.
|
|
|
// Make ranges non-overlapping using a scan line algorithm.
|
|
|
std::vector<ScanLineEvent> events; |
|
|
std::vector<ScanLineEvent> events; |
|
|
int id = 0; |
|
|
int id = 0; |
|
|
for (auto& entry : grouped_symbols) { |
|
|
for (auto &entry : grouped_symbols) { |
|
|
Out_CclsPublishSemanticHighlighting::Symbol& symbol = entry.second; |
|
|
Out_CclsPublishSemanticHighlighting::Symbol &symbol = entry.second; |
|
|
for (auto& loc : symbol.ranges) { |
|
|
for (auto &loc : symbol.lsRanges) { |
|
|
// For ranges sharing the same start point, the one with leftmost end
|
|
|
// For ranges sharing the same start point, the one with leftmost end
|
|
|
// point comes first.
|
|
|
// point comes first.
|
|
|
events.push_back({loc.start, loc.end, id, &symbol}); |
|
|
events.push_back({loc.start, loc.end, id, &symbol}); |
|
@ -316,7 +309,7 @@ void EmitSemanticHighlighting(DB* db, |
|
|
events.push_back({loc.end, loc.end, ~id, &symbol}); |
|
|
events.push_back({loc.end, loc.end, ~id, &symbol}); |
|
|
id++; |
|
|
id++; |
|
|
} |
|
|
} |
|
|
symbol.ranges.clear(); |
|
|
symbol.lsRanges.clear(); |
|
|
} |
|
|
} |
|
|
std::sort(events.begin(), events.end()); |
|
|
std::sort(events.begin(), events.end()); |
|
|
|
|
|
|
|
@ -332,19 +325,54 @@ void EmitSemanticHighlighting(DB* db, |
|
|
// Attribute range [events[i-1].pos, events[i].pos) to events[top-1].symbol
|
|
|
// Attribute range [events[i-1].pos, events[i].pos) to events[top-1].symbol
|
|
|
// .
|
|
|
// .
|
|
|
if (top && !(events[i - 1].pos == events[i].pos)) |
|
|
if (top && !(events[i - 1].pos == events[i].pos)) |
|
|
events[top - 1].symbol->ranges.push_back( |
|
|
events[top - 1].symbol->lsRanges.push_back( |
|
|
lsRange{events[i - 1].pos, events[i].pos}); |
|
|
{events[i - 1].pos, events[i].pos}); |
|
|
if (events[i].id >= 0) |
|
|
if (events[i].id >= 0) |
|
|
events[top++] = events[i]; |
|
|
events[top++] = events[i]; |
|
|
else |
|
|
else |
|
|
deleted[~events[i].id] = 1; |
|
|
deleted[~events[i].id] = 1; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// Publish.
|
|
|
|
|
|
Out_CclsPublishSemanticHighlighting out; |
|
|
Out_CclsPublishSemanticHighlighting out; |
|
|
out.params.uri = lsDocumentUri::FromPath(wfile->filename); |
|
|
out.params.uri = lsDocumentUri::FromPath(wfile->filename); |
|
|
for (auto& entry : grouped_symbols) |
|
|
// Transform lsRange into pair<int, int> (offset pairs)
|
|
|
|
|
|
{ |
|
|
|
|
|
std::vector<std::pair<lsRange, Out_CclsPublishSemanticHighlighting::Symbol *>> |
|
|
|
|
|
scratch; |
|
|
|
|
|
for (auto &entry : grouped_symbols) |
|
|
|
|
|
for (auto &range : entry.second.lsRanges) |
|
|
|
|
|
scratch.emplace_back(range, &entry.second); |
|
|
|
|
|
std::sort(scratch.begin(), scratch.end(), |
|
|
|
|
|
[](auto &l, auto &r) { return l.first.start < r.first.start; }); |
|
|
|
|
|
const auto &buf = wfile->buffer_content; |
|
|
|
|
|
int l = 0, c = 0, i = 0; |
|
|
|
|
|
auto mov = [&](int line, int col) { |
|
|
|
|
|
if (l < line) |
|
|
|
|
|
c = 0; |
|
|
|
|
|
for (; l < line && i < buf.size(); i++) |
|
|
|
|
|
if (buf[i] == '\n') |
|
|
|
|
|
l++; |
|
|
|
|
|
if (l < line) return true; |
|
|
|
|
|
for (; c < col && i < buf.size(); c++) |
|
|
|
|
|
if (uint8_t(buf[i++]) >= 128) |
|
|
|
|
|
// Skip 0b10xxxxxx
|
|
|
|
|
|
while (i < buf.size() && uint8_t(buf[i]) >= 128 && uint8_t(buf[i]) < 192) |
|
|
|
|
|
i++; |
|
|
|
|
|
return c < col; |
|
|
|
|
|
}; |
|
|
|
|
|
for (auto &entry : scratch) { |
|
|
|
|
|
lsRange &r = entry.first; |
|
|
|
|
|
if (mov(r.start.line, r.start.character)) |
|
|
|
|
|
continue; |
|
|
|
|
|
int beg = i; |
|
|
|
|
|
if (mov(r.end.line, r.end.character)) |
|
|
|
|
|
continue; |
|
|
|
|
|
entry.second->ranges.emplace_back(beg, i); |
|
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
for (auto &entry : grouped_symbols) |
|
|
if (entry.second.ranges.size()) |
|
|
if (entry.second.ranges.size()) |
|
|
out.params.symbols.push_back(entry.second); |
|
|
out.params.symbols.push_back(std::move(entry.second)); |
|
|
pipeline::WriteStdout(kMethodType_CclsPublishSemanticHighlighting, out); |
|
|
pipeline::WriteStdout(kMethodType_CclsPublishSemanticHighlighting, out); |
|
|
} |
|
|
} |
|
|