mirror of
https://github.com/MaskRay/ccls.git
synced 2025-04-05 00:12:13 +00:00
236 lines
7.1 KiB
C++
236 lines
7.1 KiB
C++
// Copyright 2017-2018 ccls Authors
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
#include "message_handler.hh"
|
|
#include "pipeline.hh"
|
|
#include "query.hh"
|
|
|
|
#include <llvm/Support/FormatVariadic.h>
|
|
|
|
#include <rapidjson/document.h>
|
|
#include <rapidjson/writer.h>
|
|
|
|
#include <unordered_set>
|
|
|
|
namespace ccls {
|
|
namespace {
|
|
struct CodeAction {
|
|
std::string title;
|
|
const char *kind = "quickfix";
|
|
WorkspaceEdit edit;
|
|
};
|
|
REFLECT_STRUCT(CodeAction, title, kind, edit);
|
|
} // namespace
|
|
|
|
template <typename T> bool vec_has(const std::vector<T> &vec, const T &key) {
|
|
return std::find(std::begin(vec), std::end(vec), key) != std::end(vec);
|
|
}
|
|
|
|
void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
|
|
ReplyOnce &reply) {
|
|
WorkingFile *wf = findOrFail(param.textDocument.uri.getPath(), reply).second;
|
|
if (!wf)
|
|
return;
|
|
std::vector<std::string> only = param.context.only;
|
|
std::vector<CodeAction> result;
|
|
if (!only.empty() && !vec_has(only, std::string("quickfix"))) {
|
|
reply(result);
|
|
return;
|
|
}
|
|
auto kinds = g_config->client.codeActionKind;
|
|
if (!kinds.empty() && !vec_has(kinds, std::string("quickfix"))) {
|
|
reply(result);
|
|
return;
|
|
}
|
|
std::vector<Diagnostic> diagnostics;
|
|
wfiles->withLock([&]() { diagnostics = wf->diagnostics; });
|
|
for (Diagnostic &diag : diagnostics)
|
|
if (diag.fixits_.size() &&
|
|
(param.range.intersects(diag.range) ||
|
|
llvm::any_of(diag.fixits_, [&](const TextEdit &edit) {
|
|
return param.range.intersects(edit.range);
|
|
}))) {
|
|
CodeAction &cmd = result.emplace_back();
|
|
cmd.title = "FixIt: " + diag.message;
|
|
auto &edit = cmd.edit.documentChanges.emplace_back();
|
|
edit.textDocument.uri = param.textDocument.uri;
|
|
edit.textDocument.version = wf->version;
|
|
edit.edits = diag.fixits_;
|
|
}
|
|
reply(result);
|
|
}
|
|
|
|
namespace {
|
|
struct Cmd_xref {
|
|
Usr usr;
|
|
Kind kind;
|
|
std::string field;
|
|
};
|
|
struct Command {
|
|
std::string title;
|
|
std::string command;
|
|
std::vector<std::string> arguments;
|
|
};
|
|
struct CodeLens {
|
|
lsRange range;
|
|
std::optional<Command> command;
|
|
};
|
|
REFLECT_STRUCT(Cmd_xref, usr, kind, field);
|
|
REFLECT_STRUCT(Command, title, command, arguments);
|
|
REFLECT_STRUCT(CodeLens, range, command);
|
|
|
|
template <typename T> std::string toString(T &v) {
|
|
rapidjson::StringBuffer output;
|
|
rapidjson::Writer<rapidjson::StringBuffer> writer(output);
|
|
JsonWriter json_writer(&writer);
|
|
reflect(json_writer, v);
|
|
return output.GetString();
|
|
}
|
|
|
|
struct CommonCodeLensParams {
|
|
std::vector<CodeLens> *result;
|
|
DB *db;
|
|
WorkingFile *wfile;
|
|
};
|
|
} // namespace
|
|
|
|
void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
|
|
ReplyOnce &reply) {
|
|
auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
|
|
if (!wf)
|
|
return;
|
|
|
|
std::vector<CodeLens> result;
|
|
auto add = [&, wf = wf](const char *singular, Cmd_xref show, Range range,
|
|
int num, bool force_display = false) {
|
|
if (!num && !force_display)
|
|
return;
|
|
std::optional<lsRange> ls_range = getLsRange(wf, range);
|
|
if (!ls_range)
|
|
return;
|
|
CodeLens &code_lens = result.emplace_back();
|
|
code_lens.range = *ls_range;
|
|
code_lens.command = Command();
|
|
code_lens.command->command = std::string(ccls_xref);
|
|
bool plural = num > 1 && singular[strlen(singular) - 1] != 'd';
|
|
code_lens.command->title =
|
|
llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str();
|
|
code_lens.command->arguments.push_back(toString(show));
|
|
};
|
|
|
|
std::unordered_set<Range> seen;
|
|
for (auto [sym, refcnt] : file->symbol2refcnt) {
|
|
if (refcnt <= 0 || !sym.extent.valid() || !seen.insert(sym.range).second)
|
|
continue;
|
|
switch (sym.kind) {
|
|
case Kind::Func: {
|
|
QueryFunc &func = db->getFunc(sym);
|
|
const QueryFunc::Def *def = func.anyDef();
|
|
if (!def)
|
|
continue;
|
|
std::vector<Use> base_uses = getUsesForAllBases(db, func);
|
|
std::vector<Use> derived_uses = getUsesForAllDerived(db, func);
|
|
add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(),
|
|
base_uses.empty());
|
|
if (base_uses.size())
|
|
add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range,
|
|
base_uses.size());
|
|
if (derived_uses.size())
|
|
add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range,
|
|
derived_uses.size());
|
|
if (base_uses.empty())
|
|
add("base", {sym.usr, Kind::Func, "bases"}, sym.range,
|
|
def->bases.size());
|
|
add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
|
|
func.derived.size());
|
|
break;
|
|
}
|
|
case Kind::Type: {
|
|
QueryType &type = db->getType(sym);
|
|
add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(),
|
|
true);
|
|
add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
|
|
type.derived.size());
|
|
add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
|
|
type.instances.size());
|
|
break;
|
|
}
|
|
case Kind::Var: {
|
|
QueryVar &var = db->getVar(sym);
|
|
const QueryVar::Def *def = var.anyDef();
|
|
if (!def || (def->is_local() && !g_config->codeLens.localVariables))
|
|
continue;
|
|
add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(),
|
|
def->kind != SymbolKind::Macro);
|
|
break;
|
|
}
|
|
case Kind::File:
|
|
case Kind::Invalid:
|
|
llvm_unreachable("");
|
|
};
|
|
}
|
|
|
|
reply(result);
|
|
}
|
|
|
|
void MessageHandler::workspace_executeCommand(JsonReader &reader,
|
|
ReplyOnce &reply) {
|
|
Command param;
|
|
reflect(reader, param);
|
|
if (param.arguments.empty()) {
|
|
return;
|
|
}
|
|
rapidjson::Document reader1;
|
|
reader1.Parse(param.arguments[0].c_str());
|
|
JsonReader json_reader{&reader1};
|
|
if (param.command == ccls_xref) {
|
|
Cmd_xref cmd;
|
|
reflect(json_reader, cmd);
|
|
std::vector<Location> result;
|
|
auto map = [&](auto &&uses) {
|
|
for (auto &use : uses)
|
|
if (auto loc = getLsLocation(db, wfiles, use))
|
|
result.push_back(std::move(*loc));
|
|
};
|
|
switch (cmd.kind) {
|
|
case Kind::Func: {
|
|
QueryFunc &func = db->getFunc(cmd.usr);
|
|
if (cmd.field == "bases") {
|
|
if (auto *def = func.anyDef())
|
|
map(getFuncDeclarations(db, def->bases));
|
|
} else if (cmd.field == "bases uses") {
|
|
map(getUsesForAllBases(db, func));
|
|
} else if (cmd.field == "derived") {
|
|
map(getFuncDeclarations(db, func.derived));
|
|
} else if (cmd.field == "derived uses") {
|
|
map(getUsesForAllDerived(db, func));
|
|
} else if (cmd.field == "uses") {
|
|
map(func.uses);
|
|
}
|
|
break;
|
|
}
|
|
case Kind::Type: {
|
|
QueryType &type = db->getType(cmd.usr);
|
|
if (cmd.field == "derived") {
|
|
map(getTypeDeclarations(db, type.derived));
|
|
} else if (cmd.field == "instances") {
|
|
map(getVarDeclarations(db, type.instances, 7));
|
|
} else if (cmd.field == "uses") {
|
|
map(type.uses);
|
|
}
|
|
break;
|
|
}
|
|
case Kind::Var: {
|
|
QueryVar &var = db->getVar(cmd.usr);
|
|
if (cmd.field == "uses")
|
|
map(var.uses);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
reply(result);
|
|
}
|
|
}
|
|
} // namespace ccls
|