1 // Copyright 2017-2018 ccls Authors
2 // SPDX-License-Identifier: Apache-2.0
3
4 #include "message_handler.hh"
5 #include "pipeline.hh"
6 #include "query.hh"
7
8 #include <llvm/Support/FormatVariadic.h>
9
10 #include <rapidjson/document.h>
11 #include <rapidjson/writer.h>
12
13 #include <unordered_set>
14
15 namespace ccls {
16 namespace {
17 struct CodeAction {
18 std::string title;
19 const char *kind = "quickfix";
20 WorkspaceEdit edit;
21 };
22 REFLECT_STRUCT(CodeAction, title, kind, edit);
23 } // namespace
textDocument_codeAction(CodeActionParam & param,ReplyOnce & reply)24 void MessageHandler::textDocument_codeAction(CodeActionParam ¶m,
25 ReplyOnce &reply) {
26 WorkingFile *wf = findOrFail(param.textDocument.uri.getPath(), reply).second;
27 if (!wf)
28 return;
29 std::vector<CodeAction> result;
30 std::vector<Diagnostic> diagnostics;
31 wfiles->withLock([&]() { diagnostics = wf->diagnostics; });
32 for (Diagnostic &diag : diagnostics)
33 if (diag.fixits_.size() &&
34 (param.range.intersects(diag.range) ||
35 llvm::any_of(diag.fixits_, [&](const TextEdit &edit) {
36 return param.range.intersects(edit.range);
37 }))) {
38 CodeAction &cmd = result.emplace_back();
39 cmd.title = "FixIt: " + diag.message;
40 auto &edit = cmd.edit.documentChanges.emplace_back();
41 edit.textDocument.uri = param.textDocument.uri;
42 edit.textDocument.version = wf->version;
43 edit.edits = diag.fixits_;
44 }
45 reply(result);
46 }
47
48 namespace {
49 struct Cmd_xref {
50 Usr usr;
51 Kind kind;
52 std::string field;
53 };
54 struct Command {
55 std::string title;
56 std::string command;
57 std::vector<std::string> arguments;
58 };
59 struct CodeLens {
60 lsRange range;
61 std::optional<Command> command;
62 };
63 REFLECT_STRUCT(Cmd_xref, usr, kind, field);
64 REFLECT_STRUCT(Command, title, command, arguments);
65 REFLECT_STRUCT(CodeLens, range, command);
66
toString(T & v)67 template <typename T> std::string toString(T &v) {
68 rapidjson::StringBuffer output;
69 rapidjson::Writer<rapidjson::StringBuffer> writer(output);
70 JsonWriter json_writer(&writer);
71 reflect(json_writer, v);
72 return output.GetString();
73 }
74
75 struct CommonCodeLensParams {
76 std::vector<CodeLens> *result;
77 DB *db;
78 WorkingFile *wfile;
79 };
80 } // namespace
81
textDocument_codeLens(TextDocumentParam & param,ReplyOnce & reply)82 void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m,
83 ReplyOnce &reply) {
84 auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply);
85 if (!wf)
86 return;
87
88 std::vector<CodeLens> result;
89 auto add = [&, wf = wf](const char *singular, Cmd_xref show, Range range,
90 int num, bool force_display = false) {
91 if (!num && !force_display)
92 return;
93 std::optional<lsRange> ls_range = getLsRange(wf, range);
94 if (!ls_range)
95 return;
96 CodeLens &code_lens = result.emplace_back();
97 code_lens.range = *ls_range;
98 code_lens.command = Command();
99 code_lens.command->command = std::string(ccls_xref);
100 bool plural = num > 1 && singular[strlen(singular) - 1] != 'd';
101 code_lens.command->title =
102 llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str();
103 code_lens.command->arguments.push_back(toString(show));
104 };
105
106 std::unordered_set<Range> seen;
107 for (auto [sym, refcnt] : file->symbol2refcnt) {
108 if (refcnt <= 0 || !sym.extent.valid() || !seen.insert(sym.range).second)
109 continue;
110 switch (sym.kind) {
111 case Kind::Func: {
112 QueryFunc &func = db->getFunc(sym);
113 const QueryFunc::Def *def = func.anyDef();
114 if (!def)
115 continue;
116 std::vector<Use> base_uses = getUsesForAllBases(db, func);
117 std::vector<Use> derived_uses = getUsesForAllDerived(db, func);
118 add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(),
119 base_uses.empty());
120 if (base_uses.size())
121 add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range,
122 base_uses.size());
123 if (derived_uses.size())
124 add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range,
125 derived_uses.size());
126 if (base_uses.empty())
127 add("base", {sym.usr, Kind::Func, "bases"}, sym.range,
128 def->bases.size());
129 add("derived", {sym.usr, Kind::Func, "derived"}, sym.range,
130 func.derived.size());
131 break;
132 }
133 case Kind::Type: {
134 QueryType &type = db->getType(sym);
135 add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(),
136 true);
137 add("derived", {sym.usr, Kind::Type, "derived"}, sym.range,
138 type.derived.size());
139 add("var", {sym.usr, Kind::Type, "instances"}, sym.range,
140 type.instances.size());
141 break;
142 }
143 case Kind::Var: {
144 QueryVar &var = db->getVar(sym);
145 const QueryVar::Def *def = var.anyDef();
146 if (!def || (def->is_local() && !g_config->codeLens.localVariables))
147 continue;
148 add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(),
149 def->kind != SymbolKind::Macro);
150 break;
151 }
152 case Kind::File:
153 case Kind::Invalid:
154 llvm_unreachable("");
155 };
156 }
157
158 reply(result);
159 }
160
workspace_executeCommand(JsonReader & reader,ReplyOnce & reply)161 void MessageHandler::workspace_executeCommand(JsonReader &reader,
162 ReplyOnce &reply) {
163 Command param;
164 reflect(reader, param);
165 if (param.arguments.empty()) {
166 return;
167 }
168 rapidjson::Document reader1;
169 reader1.Parse(param.arguments[0].c_str());
170 JsonReader json_reader{&reader1};
171 if (param.command == ccls_xref) {
172 Cmd_xref cmd;
173 reflect(json_reader, cmd);
174 std::vector<Location> result;
175 auto map = [&](auto &&uses) {
176 for (auto &use : uses)
177 if (auto loc = getLsLocation(db, wfiles, use))
178 result.push_back(std::move(*loc));
179 };
180 switch (cmd.kind) {
181 case Kind::Func: {
182 QueryFunc &func = db->getFunc(cmd.usr);
183 if (cmd.field == "bases") {
184 if (auto *def = func.anyDef())
185 map(getFuncDeclarations(db, def->bases));
186 } else if (cmd.field == "bases uses") {
187 map(getUsesForAllBases(db, func));
188 } else if (cmd.field == "derived") {
189 map(getFuncDeclarations(db, func.derived));
190 } else if (cmd.field == "derived uses") {
191 map(getUsesForAllDerived(db, func));
192 } else if (cmd.field == "uses") {
193 map(func.uses);
194 }
195 break;
196 }
197 case Kind::Type: {
198 QueryType &type = db->getType(cmd.usr);
199 if (cmd.field == "derived") {
200 map(getTypeDeclarations(db, type.derived));
201 } else if (cmd.field == "instances") {
202 map(getVarDeclarations(db, type.instances, 7));
203 } else if (cmd.field == "uses") {
204 map(type.uses);
205 }
206 break;
207 }
208 case Kind::Var: {
209 QueryVar &var = db->getVar(cmd.usr);
210 if (cmd.field == "uses")
211 map(var.uses);
212 break;
213 }
214 default:
215 break;
216 }
217 reply(result);
218 }
219 }
220 } // namespace ccls
221