Browse Source

$ccls/publishSemanticHighlighting: use pair<int,int> in place of lsRange

pull/517/head
Fangrui Song 6 years ago
parent
commit
d816e1b918
  1. 4
      src/indexer.cc
  2. 264
      src/message_handler.cc
  3. 5
      src/message_handler.h

4
src/indexer.cc

@ -531,11 +531,13 @@ public:
off |= 1u << 31; off |= 1u << 31;
Loc = SourceLocation::getFromRawEncoding(off); Loc = SourceLocation::getFromRawEncoding(off);
} }
#else
FileID LocFID;
#endif #endif
SourceLocation Spell = SM.getSpellingLoc(Loc); SourceLocation Spell = SM.getSpellingLoc(Loc);
Loc = SM.getFileLoc(Loc); Loc = SM.getFileLoc(Loc);
Range loc = FromTokenRange(SM, Lang, SourceRange(Loc, Loc)); Range loc = FromTokenRange(SM, Lang, SourceRange(Loc, Loc));
FileID LocFID = SM.getFileID(Loc); LocFID = SM.getFileID(Loc);
const FileEntry *FE = SM.getFileEntryForID(LocFID); const FileEntry *FE = SM.getFileEntryForID(LocFID);
if (!FE) { if (!FE) {
// TODO // TODO

264
src/message_handler.cc

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

5
src/message_handler.h

@ -64,7 +64,10 @@ struct Out_CclsPublishSemanticHighlighting
lsSymbolKind parentKind; lsSymbolKind parentKind;
lsSymbolKind kind; lsSymbolKind kind;
clang::StorageClass storage; clang::StorageClass storage;
std::vector<lsRange> ranges; std::vector<std::pair<int, int>> ranges;
// `lsRanges` is used to compute `ranges`.
std::vector<lsRange> lsRanges;
}; };
struct Params { struct Params {
lsDocumentUri uri; lsDocumentUri uri;

Loading…
Cancel
Save