Support the 'reference' codeActionKind

https://github.com/joaotavora/eglot/issues/302#issuecomment-550225329
This commit is contained in:
Felicián Németh 2019-11-18 16:24:06 +01:00 committed by Fangrui Song
parent 8b4d0fc055
commit 17b769851f
2 changed files with 104 additions and 40 deletions

View File

@ -70,7 +70,7 @@ struct ServerCap {
bool documentSymbolProvider = true; bool documentSymbolProvider = true;
bool workspaceSymbolProvider = true; bool workspaceSymbolProvider = true;
struct CodeActionOptions { struct CodeActionOptions {
std::vector<const char *> codeActionKinds = {"quickfix"}; std::vector<const char *> codeActionKinds = {"quickfix", "reference"};
} codeActionProvider; } codeActionProvider;
struct CodeLensOptions { struct CodeLensOptions {
bool resolveProvider = false; bool resolveProvider = false;

View File

@ -14,49 +14,117 @@
namespace ccls { namespace ccls {
namespace { namespace {
struct Command {
std::string title;
std::string command;
std::vector<std::string> arguments;
};
struct CodeAction { struct CodeAction {
std::string title; std::string title;
const char *kind = "quickfix"; std::string kind;
WorkspaceEdit edit; WorkspaceEdit edit;
Command command;
}; };
REFLECT_STRUCT(CodeAction, title, kind, edit); struct ReferenceCommand {
TextDocumentIdentifier textDocument;
Position position;
bool callee;
std::string direction;
bool derived;
int kind;
};
REFLECT_STRUCT(Command, title, command, arguments);
REFLECT_STRUCT(CodeAction, title, kind, edit, command);
REFLECT_STRUCT(ReferenceCommand, textDocument, position,
callee, direction, derived, kind);
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();
}
} // namespace } // namespace
template <typename T> bool vec_has(const std::vector<T> &vec, const T &key) { 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); return std::find(std::begin(vec), std::end(vec), key) != std::end(vec);
} }
bool should_send_action(std::vector<std::string> available_kinds,
std::vector<std::string> requested_kinds,
std::string kind) {
if (!requested_kinds.empty() && !vec_has(requested_kinds, kind)) {
return false;
}
if (!available_kinds.empty() && !vec_has(available_kinds, kind)) {
return false;
}
return true;
}
void MessageHandler::textDocument_codeAction(CodeActionParam &param, void MessageHandler::textDocument_codeAction(CodeActionParam &param,
ReplyOnce &reply) { ReplyOnce &reply) {
WorkingFile *wf = findOrFail(param.textDocument.uri.getPath(), reply).second; WorkingFile *wf = findOrFail(param.textDocument.uri.getPath(), reply).second;
if (!wf) if (!wf)
return; return;
std::vector<std::string> only = param.context.only; auto available_kinds = g_config->client.codeActionKind;
std::vector<std::string> requested_kinds = param.context.only;
std::vector<CodeAction> result; std::vector<CodeAction> result;
if (!only.empty() && !vec_has(only, std::string("quickfix"))) {
reply(result); if (should_send_action(available_kinds, requested_kinds, "quickfix")) {
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;
cmd.kind = "quickfix";
auto &edit = cmd.edit.documentChanges.emplace_back();
edit.textDocument.uri = param.textDocument.uri;
edit.textDocument.version = wf->version;
edit.edits = diag.fixits_;
}
} }
auto kinds = g_config->client.codeActionKind;
if (!kinds.empty() && !vec_has(kinds, std::string("quickfix"))) { if (should_send_action(available_kinds, requested_kinds, "reference")) {
reply(result); auto add = [&, param = param] (
return; const char *title, const char *command_name,
} const bool callee=false, const char *dir="",
std::vector<Diagnostic> diagnostics; const bool derived=false, int kind=0) {
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(); CodeAction &cmd = result.emplace_back();
cmd.title = "FixIt: " + diag.message; ReferenceCommand rcmd;
auto &edit = cmd.edit.documentChanges.emplace_back(); rcmd.textDocument = param.textDocument;
edit.textDocument.uri = param.textDocument.uri; rcmd.position = param.range.start;
edit.textDocument.version = wf->version; rcmd.callee = callee;
edit.edits = diag.fixits_; rcmd.direction = dir;
} rcmd.derived = derived;
rcmd.kind = kind;
cmd.title = title;
cmd.kind = "reference";
cmd.command.title = title;
cmd.command.command = command_name;
cmd.command.arguments.push_back(toString(rcmd));
};
add("call", "$ccls/call");
add("callee", "$ccls/call", true);
add("navigate-up", "$ccls/navigate", false, "U");
add("navigate-down", "$ccls/navigate", false, "D");
add("navigate-right", "$ccls/navigate", false, "R");
add("navigate-left", "$ccls/navigate", false, "L");
add("inheritance", "$ccls/inheritance");
add("inheritance-derived", "$ccls/inheritance", false, "", true);
add("member-var", "$ccls/member", false, "", 4);
add("member-fun", "$ccls/member", false, "", 3);
add("member-type", "$ccls/member", false, "", 2);
add("vars", "$ccls/vars");
}
reply(result); reply(result);
} }
@ -66,27 +134,13 @@ struct Cmd_xref {
Kind kind; Kind kind;
std::string field; std::string field;
}; };
struct Command {
std::string title;
std::string command;
std::vector<std::string> arguments;
};
struct CodeLens { struct CodeLens {
lsRange range; lsRange range;
std::optional<Command> command; std::optional<Command> command;
}; };
REFLECT_STRUCT(Cmd_xref, usr, kind, field); REFLECT_STRUCT(Cmd_xref, usr, kind, field);
REFLECT_STRUCT(Command, title, command, arguments);
REFLECT_STRUCT(CodeLens, range, command); 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 { struct CommonCodeLensParams {
std::vector<CodeLens> *result; std::vector<CodeLens> *result;
DB *db; DB *db;
@ -230,6 +284,16 @@ void MessageHandler::workspace_executeCommand(JsonReader &reader,
break; break;
} }
reply(result); reply(result);
} else if (param.command == "$ccls/call") {
ccls_call(json_reader, reply);
} else if (param.command == "$ccls/navigate") {
ccls_navigate(json_reader, reply);
} else if (param.command == "$ccls/inheritance") {
ccls_inheritance(json_reader, reply);
} else if (param.command == "$ccls/member") {
ccls_member(json_reader, reply);
} else if (param.command == "$ccls/vars") {
ccls_vars(json_reader, reply);
} }
} }
} // namespace ccls } // namespace ccls