2018-08-21 05:27:52 +00:00
|
|
|
// Copyright 2017-2018 ccls Authors
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
#include "message_handler.hh"
|
2017-12-05 07:57:41 +00:00
|
|
|
|
2018-05-27 19:24:56 +00:00
|
|
|
#include "log.hh"
|
2018-10-29 04:21:21 +00:00
|
|
|
#include "match.hh"
|
2018-07-09 02:56:33 +00:00
|
|
|
#include "pipeline.hh"
|
2018-10-28 17:49:31 +00:00
|
|
|
#include "project.hh"
|
2018-10-29 04:21:21 +00:00
|
|
|
#include "query_utils.hh"
|
|
|
|
#include "serializers/json.hh"
|
2017-12-06 03:32:33 +00:00
|
|
|
|
2018-06-01 21:22:55 +00:00
|
|
|
using namespace clang;
|
|
|
|
|
2018-01-10 03:04:08 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);
|
2018-04-08 17:32:08 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
namespace ccls {
|
|
|
|
MAKE_REFLECT_STRUCT(EmptyParam, placeholder);
|
|
|
|
MAKE_REFLECT_STRUCT(TextDocumentParam, textDocument);
|
|
|
|
MAKE_REFLECT_STRUCT(DidOpenTextDocumentParam, textDocument);
|
|
|
|
MAKE_REFLECT_STRUCT(TextDocumentContentChangeEvent, range, rangeLength, text);
|
|
|
|
MAKE_REFLECT_STRUCT(TextDocumentDidChangeParam, textDocument, contentChanges);
|
|
|
|
MAKE_REFLECT_STRUCT(TextDocumentPositionParam, textDocument, position);
|
|
|
|
MAKE_REFLECT_STRUCT(RenameParam, textDocument, position, newName);
|
|
|
|
|
|
|
|
// code*
|
|
|
|
MAKE_REFLECT_STRUCT(CodeActionParam::Context, diagnostics);
|
|
|
|
MAKE_REFLECT_STRUCT(CodeActionParam, textDocument, range, context);
|
|
|
|
|
|
|
|
// completion
|
|
|
|
MAKE_REFLECT_TYPE_PROXY(lsCompletionTriggerKind);
|
|
|
|
MAKE_REFLECT_STRUCT(lsCompletionContext, triggerKind, triggerCharacter);
|
|
|
|
MAKE_REFLECT_STRUCT(lsCompletionParams, textDocument, position, context);
|
|
|
|
|
|
|
|
// formatting
|
|
|
|
MAKE_REFLECT_STRUCT(FormattingOptions, tabSize, insertSpaces);
|
|
|
|
MAKE_REFLECT_STRUCT(DocumentFormattingParam, textDocument, options);
|
|
|
|
MAKE_REFLECT_STRUCT(DocumentOnTypeFormattingParam, textDocument, position,
|
|
|
|
ch, options);
|
|
|
|
MAKE_REFLECT_STRUCT(DocumentRangeFormattingParam, textDocument, range, options);
|
2018-02-23 23:27:21 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
// workspace
|
|
|
|
MAKE_REFLECT_TYPE_PROXY(FileChangeType);
|
|
|
|
MAKE_REFLECT_STRUCT(DidChangeWatchedFilesParam::Event, uri, type);
|
|
|
|
MAKE_REFLECT_STRUCT(DidChangeWatchedFilesParam, changes);
|
|
|
|
MAKE_REFLECT_STRUCT(DidChangeWorkspaceFoldersParam::Event, added, removed);
|
|
|
|
MAKE_REFLECT_STRUCT(DidChangeWorkspaceFoldersParam, event);
|
|
|
|
MAKE_REFLECT_STRUCT(WorkspaceSymbolParam, query);
|
|
|
|
|
|
|
|
namespace {
|
2019-01-09 07:19:17 +00:00
|
|
|
struct CclsSemanticHighlightSymbol {
|
|
|
|
int id = 0;
|
|
|
|
lsSymbolKind parentKind;
|
|
|
|
lsSymbolKind kind;
|
|
|
|
uint8_t storage;
|
|
|
|
std::vector<std::pair<int, int>> ranges;
|
|
|
|
|
|
|
|
// `lsRanges` is used to compute `ranges`.
|
|
|
|
std::vector<lsRange> lsRanges;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct CclsSemanticHighlightParams {
|
|
|
|
lsDocumentUri uri;
|
|
|
|
std::vector<CclsSemanticHighlightSymbol> symbols;
|
2018-02-23 23:27:21 +00:00
|
|
|
};
|
2019-01-09 07:19:17 +00:00
|
|
|
MAKE_REFLECT_STRUCT(CclsSemanticHighlightSymbol, id, parentKind, kind, storage,
|
|
|
|
ranges, lsRanges);
|
|
|
|
MAKE_REFLECT_STRUCT(CclsSemanticHighlightParams, uri, symbols);
|
|
|
|
|
|
|
|
struct CclsSetSkippedRangesParams {
|
|
|
|
lsDocumentUri uri;
|
|
|
|
std::vector<lsRange> skippedRanges;
|
|
|
|
};
|
|
|
|
MAKE_REFLECT_STRUCT(CclsSetSkippedRangesParams, uri, skippedRanges);
|
2018-02-23 23:27:21 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
|
2018-01-10 03:04:08 +00:00
|
|
|
struct ScanLineEvent {
|
|
|
|
lsPosition pos;
|
2018-07-09 02:56:33 +00:00
|
|
|
lsPosition end_pos; // Second key when there is a tie for insertion events.
|
2018-01-10 03:04:08 +00:00
|
|
|
int id;
|
2019-01-09 07:19:17 +00:00
|
|
|
CclsSemanticHighlightSymbol *symbol;
|
2018-07-09 02:56:33 +00:00
|
|
|
bool operator<(const ScanLineEvent &other) const {
|
2018-01-10 03:04:08 +00:00
|
|
|
// See the comments below when insertion/deletion events are inserted.
|
2018-02-22 17:43:53 +00:00
|
|
|
if (!(pos == other.pos))
|
|
|
|
return pos < other.pos;
|
|
|
|
if (!(other.end_pos == end_pos))
|
|
|
|
return other.end_pos < end_pos;
|
|
|
|
// This comparison essentially order Macro after non-Macro,
|
|
|
|
// So that macros will not be rendered as Var/Type/...
|
|
|
|
return symbol->kind < other.symbol->kind;
|
2018-01-10 03:04:08 +00:00
|
|
|
}
|
|
|
|
};
|
2018-07-09 02:56:33 +00:00
|
|
|
} // namespace
|
2018-01-10 03:04:08 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
void MessageHandler::Bind(const char *method, void (MessageHandler::*handler)(Reader &)) {
|
|
|
|
method2notification[method] = [this, handler](Reader &reader) {
|
|
|
|
(this->*handler)(reader);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename Param>
|
|
|
|
void MessageHandler::Bind(const char *method,
|
|
|
|
void (MessageHandler::*handler)(Param &)) {
|
|
|
|
method2notification[method] = [this, handler](Reader &reader) {
|
|
|
|
Param param{};
|
|
|
|
Reflect(reader, param);
|
|
|
|
(this->*handler)(param);
|
|
|
|
};
|
2017-12-05 07:57:41 +00:00
|
|
|
}
|
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
void MessageHandler::Bind(const char *method,
|
|
|
|
void (MessageHandler::*handler)(Reader &,
|
|
|
|
ReplyOnce &)) {
|
|
|
|
method2request[method] = [this, handler](Reader &reader, ReplyOnce &reply) {
|
|
|
|
(this->*handler)(reader, reply);
|
|
|
|
};
|
|
|
|
}
|
2018-07-09 02:56:33 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
template <typename Param>
|
|
|
|
void MessageHandler::Bind(const char *method,
|
|
|
|
void (MessageHandler::*handler)(Param &,
|
|
|
|
ReplyOnce &)) {
|
|
|
|
method2request[method] = [this, handler](Reader &reader, ReplyOnce &reply) {
|
|
|
|
Param param{};
|
|
|
|
Reflect(reader, param);
|
|
|
|
(this->*handler)(param, reply);
|
|
|
|
};
|
|
|
|
}
|
2017-12-06 03:32:33 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
MessageHandler::MessageHandler() {
|
|
|
|
Bind("$ccls/call", &MessageHandler::ccls_call);
|
|
|
|
Bind("$ccls/fileInfo", &MessageHandler::ccls_fileInfo);
|
|
|
|
Bind("$ccls/info", &MessageHandler::ccls_info);
|
|
|
|
Bind("$ccls/inheritance", &MessageHandler::ccls_inheritance);
|
|
|
|
Bind("$ccls/member", &MessageHandler::ccls_member);
|
|
|
|
Bind("$ccls/navigate", &MessageHandler::ccls_navigate);
|
|
|
|
Bind("$ccls/reload", &MessageHandler::ccls_reload);
|
|
|
|
Bind("$ccls/vars", &MessageHandler::ccls_vars);
|
|
|
|
Bind("exit", &MessageHandler::exit);
|
|
|
|
Bind("initialize", &MessageHandler::initialize);
|
|
|
|
Bind("shutdown", &MessageHandler::shutdown);
|
|
|
|
Bind("textDocument/codeAction", &MessageHandler::textDocument_codeAction);
|
|
|
|
Bind("textDocument/codeLens", &MessageHandler::textDocument_codeLens);
|
|
|
|
Bind("textDocument/completion", &MessageHandler::textDocument_completion);
|
|
|
|
Bind("textDocument/definition", &MessageHandler::textDocument_definition);
|
|
|
|
Bind("textDocument/didChange", &MessageHandler::textDocument_didChange);
|
|
|
|
Bind("textDocument/didClose", &MessageHandler::textDocument_didClose);
|
|
|
|
Bind("textDocument/didOpen", &MessageHandler::textDocument_didOpen);
|
|
|
|
Bind("textDocument/didSave", &MessageHandler::textDocument_didSave);
|
|
|
|
Bind("textDocument/documentHighlight",
|
|
|
|
&MessageHandler::textDocument_documentHighlight);
|
|
|
|
Bind("textDocument/documentLink", &MessageHandler::textDocument_documentLink);
|
|
|
|
Bind("textDocument/documentSymbol",
|
|
|
|
&MessageHandler::textDocument_documentSymbol);
|
|
|
|
Bind("textDocument/foldingRange", &MessageHandler::textDocument_foldingRange);
|
|
|
|
Bind("textDocument/formatting", &MessageHandler::textDocument_formatting);
|
|
|
|
Bind("textDocument/hover", &MessageHandler::textDocument_hover);
|
|
|
|
Bind("textDocument/implementation",
|
|
|
|
&MessageHandler::textDocument_implementation);
|
|
|
|
Bind("textDocument/onTypeFormatting",
|
|
|
|
&MessageHandler::textDocument_onTypeFormatting);
|
|
|
|
Bind("textDocument/rangeFormatting",
|
|
|
|
&MessageHandler::textDocument_rangeFormatting);
|
|
|
|
Bind("textDocument/references", &MessageHandler::textDocument_references);
|
|
|
|
Bind("textDocument/rename", &MessageHandler::textDocument_rename);
|
|
|
|
Bind("textDocument/signatureHelp",
|
|
|
|
&MessageHandler::textDocument_signatureHelp);
|
|
|
|
Bind("textDocument/typeDefinition",
|
|
|
|
&MessageHandler::textDocument_typeDefinition);
|
|
|
|
Bind("workspace/didChangeConfiguration",
|
|
|
|
&MessageHandler::workspace_didChangeConfiguration);
|
|
|
|
Bind("workspace/didChangeWatchedFiles",
|
|
|
|
&MessageHandler::workspace_didChangeWatchedFiles);
|
|
|
|
Bind("workspace/didChangeWorkspaceFolders",
|
|
|
|
&MessageHandler::workspace_didChangeWorkspaceFolders);
|
|
|
|
Bind("workspace/executeCommand", &MessageHandler::workspace_executeCommand);
|
|
|
|
Bind("workspace/symbol", &MessageHandler::workspace_symbol);
|
|
|
|
}
|
|
|
|
|
|
|
|
void MessageHandler::Run(InMessage &msg) {
|
|
|
|
rapidjson::Document &doc = *msg.document;
|
|
|
|
rapidjson::Value param;
|
|
|
|
auto it = doc.FindMember("params");
|
|
|
|
if (it != doc.MemberEnd())
|
|
|
|
param = it->value;
|
|
|
|
JsonReader reader(¶m);
|
|
|
|
if (msg.id.Valid()) {
|
|
|
|
ReplyOnce reply{msg.id};
|
|
|
|
auto it = method2request.find(msg.method);
|
|
|
|
if (it != method2request.end()) {
|
|
|
|
try {
|
|
|
|
it->second(reader, reply);
|
|
|
|
} catch (...) {
|
|
|
|
LOG_S(ERROR) << "failed to process request " << msg.method;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
lsResponseError err;
|
|
|
|
err.code = lsErrorCodes::MethodNotFound;
|
|
|
|
err.message = "unknown request " + msg.method;
|
|
|
|
reply.Error(err);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
auto it = method2notification.find(msg.method);
|
|
|
|
if (it != method2notification.end())
|
|
|
|
try {
|
|
|
|
it->second(reader);
|
|
|
|
} catch (...) {
|
|
|
|
LOG_S(ERROR) << "failed to process " << msg.method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QueryFile *MessageHandler::FindFile(ReplyOnce &reply,
|
|
|
|
const std::string &path,
|
|
|
|
int *out_file_id) {
|
|
|
|
QueryFile *ret = nullptr;
|
|
|
|
auto it = db->name2file_id.find(LowerPathIfInsensitive(path));
|
2018-04-30 04:49:03 +00:00
|
|
|
if (it != db->name2file_id.end()) {
|
2018-07-09 02:56:33 +00:00
|
|
|
QueryFile &file = db->files[it->second];
|
2017-12-06 03:32:33 +00:00
|
|
|
if (file.def) {
|
2018-10-28 17:49:31 +00:00
|
|
|
ret = &file;
|
2017-12-06 03:32:33 +00:00
|
|
|
if (out_file_id)
|
2018-04-30 04:49:03 +00:00
|
|
|
*out_file_id = it->second;
|
2018-10-28 17:49:31 +00:00
|
|
|
return ret;
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (out_file_id)
|
2018-04-30 04:49:03 +00:00
|
|
|
*out_file_id = -1;
|
2017-12-06 03:32:33 +00:00
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
if (reply.id.Valid()) {
|
|
|
|
bool has_entry = false;
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(project->mutex_);
|
|
|
|
for (auto &[root, folder] : project->root2folder)
|
|
|
|
has_entry |= folder.path2entry_index.count(path);
|
|
|
|
}
|
2019-01-09 07:19:17 +00:00
|
|
|
lsResponseError err;
|
2018-10-08 05:02:28 +00:00
|
|
|
if (has_entry) {
|
2019-01-09 07:19:17 +00:00
|
|
|
err.code = lsErrorCodes::ServerNotInitialized;
|
2018-10-28 17:49:31 +00:00
|
|
|
err.message = path + " is being indexed";
|
2017-12-31 03:18:33 +00:00
|
|
|
} else {
|
2019-01-09 07:19:17 +00:00
|
|
|
err.code = lsErrorCodes::InternalError;
|
2018-10-28 17:49:31 +00:00
|
|
|
err.message = "unable to find " + path;
|
2017-12-31 03:18:33 +00:00
|
|
|
}
|
2018-10-28 17:49:31 +00:00
|
|
|
reply.Error(err);
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
|
2018-10-28 17:49:31 +00:00
|
|
|
return ret;
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
|
2019-01-09 07:19:17 +00:00
|
|
|
void EmitSkippedRanges(WorkingFile *wfile, QueryFile &file) {
|
|
|
|
CclsSetSkippedRangesParams params;
|
|
|
|
params.uri = lsDocumentUri::FromPath(wfile->filename);
|
|
|
|
for (Range skipped : file.def->skipped_ranges)
|
|
|
|
if (auto ls_skipped = GetLsRange(wfile, skipped))
|
|
|
|
params.skippedRanges.push_back(*ls_skipped);
|
|
|
|
pipeline::Notify("$ccls/publishSkippedRanges", params);
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
|
2019-01-09 07:19:17 +00:00
|
|
|
void EmitSemanticHighlight(DB *db, WorkingFile *wfile, QueryFile &file) {
|
2018-09-24 17:56:29 +00:00
|
|
|
static GroupMatch match(g_config->highlight.whitelist,
|
|
|
|
g_config->highlight.blacklist);
|
2019-01-09 07:19:17 +00:00
|
|
|
assert(file.def);
|
2018-09-30 03:26:55 +00:00
|
|
|
if (wfile->buffer_content.size() > g_config->highlight.largeFileSize ||
|
2019-01-09 07:19:17 +00:00
|
|
|
!match.IsMatch(file.def->path))
|
2018-03-09 08:23:32 +00:00
|
|
|
return;
|
2017-12-06 03:32:33 +00:00
|
|
|
|
|
|
|
// Group symbols together.
|
2019-01-09 07:19:17 +00:00
|
|
|
std::unordered_map<SymbolIdx, CclsSemanticHighlightSymbol> grouped_symbols;
|
|
|
|
for (auto [sym, refcnt] : file.symbol2refcnt) {
|
2018-10-05 06:31:59 +00:00
|
|
|
if (refcnt <= 0) continue;
|
2018-01-31 04:59:31 +00:00
|
|
|
std::string_view detailed_name;
|
2018-03-07 08:29:53 +00:00
|
|
|
lsSymbolKind parent_kind = lsSymbolKind::Unknown;
|
2018-02-18 19:29:38 +00:00
|
|
|
lsSymbolKind kind = lsSymbolKind::Unknown;
|
2018-07-20 03:50:52 +00:00
|
|
|
uint8_t storage = SC_None;
|
2018-09-24 17:56:29 +00:00
|
|
|
int idx;
|
2017-12-06 03:32:33 +00:00
|
|
|
// This switch statement also filters out symbols that are not highlighted.
|
2018-02-09 17:42:10 +00:00
|
|
|
switch (sym.kind) {
|
2018-07-09 02:56:33 +00:00
|
|
|
case SymbolKind::Func: {
|
2018-09-24 17:56:29 +00:00
|
|
|
idx = db->func_usr[sym.usr];
|
|
|
|
const QueryFunc &func = db->funcs[idx];
|
2018-07-09 02:56:33 +00:00
|
|
|
const QueryFunc::Def *def = func.AnyDef();
|
|
|
|
if (!def)
|
|
|
|
continue; // applies to for loop
|
|
|
|
// 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
|
|
|
|
kind = def->kind;
|
|
|
|
storage = def->storage;
|
|
|
|
detailed_name = short_name;
|
2018-10-04 23:13:30 +00:00
|
|
|
parent_kind = def->parent_kind;
|
2018-07-09 02:56:33 +00:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
2018-09-24 17:56:29 +00:00
|
|
|
case SymbolKind::Type: {
|
|
|
|
idx = db->type_usr[sym.usr];
|
|
|
|
const QueryType &type = db->types[idx];
|
|
|
|
for (auto &def : type.def) {
|
2018-07-09 02:56:33 +00:00
|
|
|
kind = def.kind;
|
|
|
|
detailed_name = def.detailed_name;
|
|
|
|
if (def.spell) {
|
2018-10-04 23:13:30 +00:00
|
|
|
parent_kind = def.parent_kind;
|
2018-07-09 02:56:33 +00:00
|
|
|
break;
|
2018-02-18 05:52:14 +00:00
|
|
|
}
|
2018-07-09 02:56:33 +00:00
|
|
|
}
|
|
|
|
break;
|
2018-09-24 17:56:29 +00:00
|
|
|
}
|
2018-07-09 02:56:33 +00:00
|
|
|
case SymbolKind::Var: {
|
2018-09-24 17:56:29 +00:00
|
|
|
idx = db->var_usr[sym.usr];
|
|
|
|
const QueryVar &var = db->vars[idx];
|
2018-07-09 02:56:33 +00:00
|
|
|
for (auto &def : var.def) {
|
|
|
|
kind = def.kind;
|
|
|
|
storage = def.storage;
|
|
|
|
detailed_name = def.detailed_name;
|
|
|
|
if (def.spell) {
|
2018-10-04 23:13:30 +00:00
|
|
|
parent_kind = def.parent_kind;
|
2018-07-09 02:56:33 +00:00
|
|
|
break;
|
2018-03-07 08:56:47 +00:00
|
|
|
}
|
|
|
|
}
|
2018-07-09 02:56:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
continue; // applies to for loop
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
|
2018-06-01 21:22:55 +00:00
|
|
|
std::optional<lsRange> loc = GetLsRange(wfile, sym.range);
|
2017-12-06 03:32:33 +00:00
|
|
|
if (loc) {
|
2018-02-11 18:25:37 +00:00
|
|
|
auto it = grouped_symbols.find(sym);
|
2017-12-06 03:32:33 +00:00
|
|
|
if (it != grouped_symbols.end()) {
|
2018-07-09 02:56:33 +00:00
|
|
|
it->second.lsRanges.push_back(*loc);
|
2017-12-06 03:32:33 +00:00
|
|
|
} else {
|
2019-01-09 07:19:17 +00:00
|
|
|
CclsSemanticHighlightSymbol symbol;
|
|
|
|
symbol.id = idx;
|
2018-01-28 20:34:31 +00:00
|
|
|
symbol.parentKind = parent_kind;
|
2018-01-17 01:48:22 +00:00
|
|
|
symbol.kind = kind;
|
2018-01-26 17:28:29 +00:00
|
|
|
symbol.storage = storage;
|
2018-07-09 02:56:33 +00:00
|
|
|
symbol.lsRanges.push_back(*loc);
|
2018-02-11 18:25:37 +00:00
|
|
|
grouped_symbols[sym] = symbol;
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-01-10 03:04:08 +00:00
|
|
|
// Make ranges non-overlapping using a scan line algorithm.
|
|
|
|
std::vector<ScanLineEvent> events;
|
|
|
|
int id = 0;
|
2018-07-09 02:56:33 +00:00
|
|
|
for (auto &entry : grouped_symbols) {
|
2019-01-09 07:19:17 +00:00
|
|
|
CclsSemanticHighlightSymbol &symbol = entry.second;
|
2018-07-09 02:56:33 +00:00
|
|
|
for (auto &loc : symbol.lsRanges) {
|
2018-01-11 02:43:01 +00:00
|
|
|
// For ranges sharing the same start point, the one with leftmost end
|
|
|
|
// point comes first.
|
2018-01-10 03:04:08 +00:00
|
|
|
events.push_back({loc.start, loc.end, id, &symbol});
|
|
|
|
// For ranges sharing the same end point, their relative order does not
|
|
|
|
// matter, therefore we arbitrarily assign loc.end to them. We use
|
|
|
|
// negative id to indicate a deletion event.
|
|
|
|
events.push_back({loc.end, loc.end, ~id, &symbol});
|
|
|
|
id++;
|
|
|
|
}
|
2018-07-09 02:56:33 +00:00
|
|
|
symbol.lsRanges.clear();
|
2018-01-10 03:04:08 +00:00
|
|
|
}
|
|
|
|
std::sort(events.begin(), events.end());
|
|
|
|
|
|
|
|
std::vector<uint8_t> deleted(id, 0);
|
|
|
|
int top = 0;
|
|
|
|
for (size_t i = 0; i < events.size(); i++) {
|
|
|
|
while (top && deleted[events[top - 1].id])
|
|
|
|
top--;
|
|
|
|
// Order [a, b0) after [a, b1) if b0 < b1. The range comes later overrides
|
|
|
|
// the ealier. The order of [a0, b) [a1, b) does not matter.
|
|
|
|
// The order of [a, b) [b, c) does not as long as we do not emit empty
|
|
|
|
// ranges.
|
2018-01-11 02:43:01 +00:00
|
|
|
// Attribute range [events[i-1].pos, events[i].pos) to events[top-1].symbol
|
|
|
|
// .
|
2018-01-10 03:04:08 +00:00
|
|
|
if (top && !(events[i - 1].pos == events[i].pos))
|
2018-07-09 02:56:33 +00:00
|
|
|
events[top - 1].symbol->lsRanges.push_back(
|
|
|
|
{events[i - 1].pos, events[i].pos});
|
2018-01-10 03:04:08 +00:00
|
|
|
if (events[i].id >= 0)
|
|
|
|
events[top++] = events[i];
|
|
|
|
else
|
|
|
|
deleted[~events[i].id] = 1;
|
|
|
|
}
|
|
|
|
|
2019-01-09 07:19:17 +00:00
|
|
|
CclsSemanticHighlightParams params;
|
|
|
|
params.uri = lsDocumentUri::FromPath(wfile->filename);
|
2018-07-09 02:56:33 +00:00
|
|
|
// Transform lsRange into pair<int, int> (offset pairs)
|
2018-07-14 20:56:00 +00:00
|
|
|
if (!g_config->highlight.lsRanges) {
|
2019-01-09 07:19:17 +00:00
|
|
|
std::vector<std::pair<lsRange, CclsSemanticHighlightSymbol *>> scratch;
|
2018-07-14 20:56:00 +00:00
|
|
|
for (auto &entry : grouped_symbols) {
|
2018-07-09 02:56:33 +00:00
|
|
|
for (auto &range : entry.second.lsRanges)
|
|
|
|
scratch.emplace_back(range, &entry.second);
|
2018-07-14 20:56:00 +00:00
|
|
|
entry.second.lsRanges.clear();
|
|
|
|
}
|
2018-07-09 02:56:33 +00:00
|
|
|
std::sort(scratch.begin(), scratch.end(),
|
2018-08-09 17:08:14 +00:00
|
|
|
[](auto &l, auto &r) { return l.first.start < r.first.start; });
|
2018-07-09 02:56:33 +00:00
|
|
|
const auto &buf = wfile->buffer_content;
|
2018-07-10 06:40:26 +00:00
|
|
|
int l = 0, c = 0, i = 0, p = 0;
|
2018-07-09 02:56:33 +00:00
|
|
|
auto mov = [&](int line, int col) {
|
|
|
|
if (l < line)
|
|
|
|
c = 0;
|
2018-07-10 06:40:26 +00:00
|
|
|
for (; l < line && i < buf.size(); i++) {
|
2018-07-09 02:56:33 +00:00
|
|
|
if (buf[i] == '\n')
|
|
|
|
l++;
|
2018-07-10 06:40:26 +00:00
|
|
|
if (uint8_t(buf[i]) < 128 || 192 <= uint8_t(buf[i]))
|
|
|
|
p++;
|
|
|
|
}
|
2018-08-09 17:08:14 +00:00
|
|
|
if (l < line)
|
|
|
|
return true;
|
2018-07-10 06:40:26 +00:00
|
|
|
for (; c < col && i < buf.size() && buf[i] != '\n'; c++)
|
|
|
|
if (p++, uint8_t(buf[i++]) >= 128)
|
2018-07-09 02:56:33 +00:00
|
|
|
// Skip 0b10xxxxxx
|
2018-08-09 17:08:14 +00:00
|
|
|
while (i < buf.size() && uint8_t(buf[i]) >= 128 &&
|
|
|
|
uint8_t(buf[i]) < 192)
|
2018-07-09 02:56:33 +00:00
|
|
|
i++;
|
|
|
|
return c < col;
|
|
|
|
};
|
|
|
|
for (auto &entry : scratch) {
|
|
|
|
lsRange &r = entry.first;
|
|
|
|
if (mov(r.start.line, r.start.character))
|
|
|
|
continue;
|
2018-07-10 06:40:26 +00:00
|
|
|
int beg = p;
|
2018-07-09 02:56:33 +00:00
|
|
|
if (mov(r.end.line, r.end.character))
|
|
|
|
continue;
|
2018-07-10 06:40:26 +00:00
|
|
|
entry.second->ranges.emplace_back(beg, p);
|
2018-07-09 02:56:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (auto &entry : grouped_symbols)
|
2018-07-14 20:56:00 +00:00
|
|
|
if (entry.second.ranges.size() || entry.second.lsRanges.size())
|
2019-01-09 07:19:17 +00:00
|
|
|
params.symbols.push_back(std::move(entry.second));
|
|
|
|
pipeline::Notify("$ccls/publishSemanticHighlight", params);
|
2017-12-07 01:00:19 +00:00
|
|
|
}
|
2018-10-28 17:49:31 +00:00
|
|
|
} // namespace ccls
|