Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support the 'reference' codeActionKind #525

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/config.hh
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,11 @@ struct Config {
// If false, disable snippets and complete just the identifier part.
// TextDocumentClientCapabilities.completion.completionItem.snippetSupport
bool snippetSupport = true;

// List of supported CodeActionKinds. If empty, client does not
// have CodeActionLiteralSupport.
// TextDocumentClientCapabilities.codeAction.codeActionLiteralSupport.codeActionKind.valueSet
std::vector<std::string> codeActionKind;
} client;

struct CodeLens {
Expand Down
2 changes: 1 addition & 1 deletion src/message_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ using namespace clang;
MAKE_HASHABLE(ccls::SymbolIdx, t.usr, t.kind);

namespace ccls {
REFLECT_STRUCT(CodeActionParam::Context, diagnostics);
REFLECT_STRUCT(CodeActionParam::Context, diagnostics, only);
REFLECT_STRUCT(CodeActionParam, textDocument, range, context);
void reflect(JsonReader &, EmptyParam &) {}
REFLECT_STRUCT(TextDocumentParam, textDocument);
Expand Down
1 change: 1 addition & 0 deletions src/message_handler.hh
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct CodeActionParam {
lsRange range;
struct Context {
std::vector<Diagnostic> diagnostics;
std::vector<std::string> only;
} context;
};
struct EmptyParam {};
Expand Down
22 changes: 20 additions & 2 deletions src/messages/initialize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ struct ServerCap {
bool documentSymbolProvider = true;
bool workspaceSymbolProvider = true;
struct CodeActionOptions {
std::vector<const char *> codeActionKinds = {"quickfix"};
std::vector<const char *> codeActionKinds = {"quickfix", "reference"};
} codeActionProvider;
struct CodeLensOptions {
bool resolveProvider = false;
Expand Down Expand Up @@ -162,6 +162,15 @@ struct TextDocumentClientCap {
bool hierarchicalDocumentSymbolSupport = false;
} documentSymbol;

struct CodeAction {
bool dynamicRegistration = false;
struct CodeActionLiteralSupport {
struct CodeActionKind {
std::vector<std::string> valueSet;
} codeActionKind;
} codeActionLiteralSupport;
} codeAction;

struct PublishDiagnostics {
bool relatedInformation = false;
} publishDiagnostics;
Expand All @@ -173,9 +182,14 @@ REFLECT_STRUCT(TextDocumentClientCap::Completion, completionItem);
REFLECT_STRUCT(TextDocumentClientCap::DocumentSymbol,
hierarchicalDocumentSymbolSupport);
REFLECT_STRUCT(TextDocumentClientCap::LinkSupport, linkSupport);
REFLECT_STRUCT(TextDocumentClientCap::CodeAction::CodeActionLiteralSupport::CodeActionKind,
valueSet);
REFLECT_STRUCT(TextDocumentClientCap::CodeAction::CodeActionLiteralSupport, codeActionKind);
REFLECT_STRUCT(TextDocumentClientCap::CodeAction,
dynamicRegistration, codeActionLiteralSupport);
REFLECT_STRUCT(TextDocumentClientCap::PublishDiagnostics, relatedInformation);
REFLECT_STRUCT(TextDocumentClientCap, completion, definition, documentSymbol,
publishDiagnostics);
codeAction, publishDiagnostics);

struct ClientCap {
WorkspaceClientCap workspace;
Expand Down Expand Up @@ -322,6 +336,10 @@ void do_initialize(MessageHandler *m, InitializeParam &param,
if (!g_config->client.snippetSupport)
g_config->completion.duplicateOptional = false;

g_config->client.codeActionKind =
capabilities.textDocument
.codeAction.codeActionLiteralSupport.codeActionKind.valueSet;

// Ensure there is a resource directory.
if (g_config->clang.resourceDir.empty())
g_config->clang.resourceDir = getDefaultResourceDirectory();
Expand Down
139 changes: 109 additions & 30 deletions src/messages/textDocument_code.cc
Original file line number Diff line number Diff line change
Expand Up @@ -14,34 +14,117 @@

namespace ccls {
namespace {
struct Command {
std::string title;
std::string command;
std::vector<std::string> arguments;
};
struct CodeAction {
std::string title;
const char *kind = "quickfix";
std::string kind;
WorkspaceEdit edit;
Command command;
};
struct ReferenceCommand {
TextDocumentIdentifier textDocument;
Position position;
bool callee;
std::string direction;
bool derived;
int kind;
};
REFLECT_STRUCT(CodeAction, title, kind, edit);
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

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

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,
ReplyOnce &reply) {
WorkingFile *wf = findOrFail(param.textDocument.uri.getPath(), reply).second;
if (!wf)
return;
auto available_kinds = g_config->client.codeActionKind;
std::vector<std::string> requested_kinds = param.context.only;
std::vector<CodeAction> result;
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);
}))) {

if (should_send_action(available_kinds, requested_kinds, "quickfix")) {
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_;
}
}

if (should_send_action(available_kinds, requested_kinds, "reference")) {
auto add = [&, param = param] (
const char *title, const char *command_name,
const bool callee=false, const char *dir="",
const bool derived=false, int kind=0) {
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_;
}
ReferenceCommand rcmd;
rcmd.textDocument = param.textDocument;
rcmd.position = param.range.start;
rcmd.callee = callee;
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, "", false, 4);
add("member-fun", "$ccls/member", false, "", false, 3);
add("member-type", "$ccls/member", false, "", false, 2);
add("vars", "$ccls/vars");
}

reply(result);
}

Expand All @@ -51,27 +134,13 @@ struct Cmd_xref {
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;
Expand Down Expand Up @@ -215,6 +284,16 @@ void MessageHandler::workspace_executeCommand(JsonReader &reader,
break;
}
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