2017-12-29 16:29:47 +00:00
|
|
|
#include "clang_complete.h"
|
2017-12-06 03:32:33 +00:00
|
|
|
#include "message_handler.h"
|
|
|
|
#include "query_utils.h"
|
2017-12-29 16:29:47 +00:00
|
|
|
#include "queue_manager.h"
|
2017-12-06 03:32:33 +00:00
|
|
|
|
2017-12-06 05:03:38 +00:00
|
|
|
namespace {
|
2017-12-06 04:39:44 +00:00
|
|
|
struct lsDocumentCodeLensParams {
|
|
|
|
lsTextDocumentIdentifier textDocument;
|
|
|
|
};
|
|
|
|
MAKE_REFLECT_STRUCT(lsDocumentCodeLensParams, textDocument);
|
|
|
|
|
|
|
|
struct lsCodeLensUserData {};
|
|
|
|
MAKE_REFLECT_EMPTY_STRUCT(lsCodeLensUserData);
|
|
|
|
|
|
|
|
struct lsCodeLensCommandArguments {
|
|
|
|
lsDocumentUri uri;
|
|
|
|
lsPosition position;
|
2017-12-12 05:20:29 +00:00
|
|
|
std::vector<lsLocation> locations;
|
2017-12-06 04:39:44 +00:00
|
|
|
};
|
|
|
|
void Reflect(Writer& visitor, lsCodeLensCommandArguments& value) {
|
2018-01-07 00:22:46 +00:00
|
|
|
visitor.StartArray(3);
|
2017-12-06 04:39:44 +00:00
|
|
|
Reflect(visitor, value.uri);
|
|
|
|
Reflect(visitor, value.position);
|
|
|
|
Reflect(visitor, value.locations);
|
|
|
|
visitor.EndArray();
|
|
|
|
}
|
2017-12-06 17:10:58 +00:00
|
|
|
#if false
|
2017-12-06 04:39:44 +00:00
|
|
|
void Reflect(Reader& visitor, lsCodeLensCommandArguments& value) {
|
|
|
|
auto it = visitor.Begin();
|
|
|
|
Reflect(*it, value.uri);
|
|
|
|
++it;
|
|
|
|
Reflect(*it, value.position);
|
|
|
|
++it;
|
|
|
|
Reflect(*it, value.locations);
|
|
|
|
}
|
2017-12-06 17:10:58 +00:00
|
|
|
#endif
|
2017-12-06 04:39:44 +00:00
|
|
|
|
|
|
|
using TCodeLens = lsCodeLens<lsCodeLensUserData, lsCodeLensCommandArguments>;
|
2018-01-19 08:56:09 +00:00
|
|
|
struct Ipc_TextDocumentCodeLens
|
|
|
|
: public RequestMessage<Ipc_TextDocumentCodeLens> {
|
2017-12-06 04:39:44 +00:00
|
|
|
const static IpcId kIpcId = IpcId::TextDocumentCodeLens;
|
|
|
|
lsDocumentCodeLensParams params;
|
|
|
|
};
|
|
|
|
MAKE_REFLECT_STRUCT(Ipc_TextDocumentCodeLens, id, params);
|
|
|
|
REGISTER_IPC_MESSAGE(Ipc_TextDocumentCodeLens);
|
|
|
|
|
|
|
|
struct Out_TextDocumentCodeLens
|
|
|
|
: public lsOutMessage<Out_TextDocumentCodeLens> {
|
|
|
|
lsRequestId id;
|
2017-12-12 05:20:29 +00:00
|
|
|
std::vector<lsCodeLens<lsCodeLensUserData, lsCodeLensCommandArguments>>
|
2017-12-06 04:39:44 +00:00
|
|
|
result;
|
|
|
|
};
|
|
|
|
MAKE_REFLECT_STRUCT(Out_TextDocumentCodeLens, jsonrpc, id, result);
|
|
|
|
|
|
|
|
#if false
|
|
|
|
struct Ipc_CodeLensResolve : public IpcMessage<Ipc_CodeLensResolve> {
|
|
|
|
const static IpcId kIpcId = IpcId::CodeLensResolve;
|
|
|
|
|
|
|
|
lsRequestId id;
|
|
|
|
TCodeLens params;
|
|
|
|
};
|
|
|
|
MAKE_REFLECT_STRUCT(Ipc_CodeLensResolve, id, params);
|
|
|
|
REGISTER_IPC_MESSAGE(Ipc_CodeLensResolve);
|
|
|
|
|
|
|
|
struct Out_CodeLensResolve : public lsOutMessage<Out_CodeLensResolve> {
|
|
|
|
lsRequestId id;
|
|
|
|
TCodeLens result;
|
|
|
|
};
|
|
|
|
MAKE_REFLECT_STRUCT(Out_CodeLensResolve, jsonrpc, id, result);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
struct CommonCodeLensParams {
|
|
|
|
std::vector<TCodeLens>* result;
|
|
|
|
QueryDatabase* db;
|
|
|
|
WorkingFiles* working_files;
|
|
|
|
WorkingFile* working_file;
|
|
|
|
};
|
|
|
|
|
2018-02-10 03:07:45 +00:00
|
|
|
SymbolRef OffsetStartColumn(SymbolRef sym, int16_t offset) {
|
|
|
|
sym.range.start.column += offset;
|
|
|
|
return sym;
|
|
|
|
}
|
|
|
|
|
2017-12-06 04:39:44 +00:00
|
|
|
void AddCodeLens(const char* singular,
|
|
|
|
const char* plural,
|
|
|
|
CommonCodeLensParams* common,
|
2018-02-10 03:07:45 +00:00
|
|
|
SymbolRef loc,
|
2018-02-10 20:53:18 +00:00
|
|
|
const std::vector<Use>& uses,
|
2017-12-06 04:39:44 +00:00
|
|
|
bool force_display) {
|
|
|
|
TCodeLens code_lens;
|
|
|
|
optional<lsRange> range = GetLsRange(common->working_file, loc.range);
|
|
|
|
if (!range)
|
|
|
|
return;
|
2018-02-11 21:42:48 +00:00
|
|
|
Maybe<QueryFileId> file_id = common->db->GetFileId(loc);
|
|
|
|
if (!file_id)
|
|
|
|
return;
|
2017-12-06 04:39:44 +00:00
|
|
|
code_lens.range = *range;
|
|
|
|
code_lens.command = lsCommand<lsCodeLensCommandArguments>();
|
|
|
|
code_lens.command->command = "cquery.showReferences";
|
2018-02-11 21:42:48 +00:00
|
|
|
code_lens.command->arguments.uri = GetLsDocumentUri(common->db, *file_id);
|
2017-12-06 04:39:44 +00:00
|
|
|
code_lens.command->arguments.position = code_lens.range.start;
|
|
|
|
|
|
|
|
// Add unique uses.
|
|
|
|
std::unordered_set<lsLocation> unique_uses;
|
2018-02-10 20:53:18 +00:00
|
|
|
for (Use use : uses) {
|
2017-12-06 04:39:44 +00:00
|
|
|
optional<lsLocation> location =
|
|
|
|
GetLsLocation(common->db, common->working_files, use);
|
|
|
|
if (!location)
|
|
|
|
continue;
|
|
|
|
unique_uses.insert(*location);
|
|
|
|
}
|
|
|
|
code_lens.command->arguments.locations.assign(unique_uses.begin(),
|
|
|
|
unique_uses.end());
|
|
|
|
|
|
|
|
// User visible label
|
|
|
|
size_t num_usages = unique_uses.size();
|
|
|
|
code_lens.command->title = std::to_string(num_usages) + " ";
|
|
|
|
if (num_usages == 1)
|
|
|
|
code_lens.command->title += singular;
|
|
|
|
else
|
|
|
|
code_lens.command->title += plural;
|
|
|
|
|
|
|
|
if (force_display || unique_uses.size() > 0)
|
|
|
|
common->result->push_back(code_lens);
|
|
|
|
}
|
|
|
|
|
2017-12-06 03:32:33 +00:00
|
|
|
struct TextDocumentCodeLensHandler
|
|
|
|
: BaseMessageHandler<Ipc_TextDocumentCodeLens> {
|
|
|
|
void Run(Ipc_TextDocumentCodeLens* request) override {
|
|
|
|
Out_TextDocumentCodeLens out;
|
|
|
|
out.id = request->id;
|
|
|
|
|
|
|
|
lsDocumentUri file_as_uri = request->params.textDocument.uri;
|
|
|
|
std::string path = file_as_uri.GetPath();
|
|
|
|
|
|
|
|
clang_complete->NotifyView(path);
|
|
|
|
|
|
|
|
QueryFile* file;
|
2017-12-31 03:18:33 +00:00
|
|
|
if (!FindFileOrFail(db, project, request->id,
|
2017-12-06 03:32:33 +00:00
|
|
|
request->params.textDocument.uri.GetPath(), &file)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
CommonCodeLensParams common;
|
|
|
|
common.result = &out.result;
|
|
|
|
common.db = db;
|
|
|
|
common.working_files = working_files;
|
|
|
|
common.working_file = working_files->GetFileByFilename(file->def->path);
|
|
|
|
|
2018-02-09 17:42:10 +00:00
|
|
|
for (SymbolRef sym : file->def->outline) {
|
2017-12-06 03:32:33 +00:00
|
|
|
// NOTE: We OffsetColumn so that the code lens always show up in a
|
|
|
|
// predictable order. Otherwise, the client may randomize it.
|
|
|
|
|
2018-02-09 17:42:10 +00:00
|
|
|
switch (sym.kind) {
|
2017-12-06 03:32:33 +00:00
|
|
|
case SymbolKind::Type: {
|
2018-02-10 03:07:45 +00:00
|
|
|
QueryType& type = db->GetType(sym);
|
2017-12-06 03:32:33 +00:00
|
|
|
if (!type.def)
|
|
|
|
continue;
|
2018-01-25 19:22:25 +00:00
|
|
|
if (type.def->kind == ClangSymbolKind::Namespace)
|
|
|
|
continue;
|
2018-02-10 03:07:45 +00:00
|
|
|
AddCodeLens("ref", "refs", &common, OffsetStartColumn(sym, 0),
|
2018-02-10 01:30:22 +00:00
|
|
|
type.uses, true /*force_display*/);
|
2018-02-10 03:07:45 +00:00
|
|
|
AddCodeLens("derived", "derived", &common, OffsetStartColumn(sym, 1),
|
2018-02-10 20:53:18 +00:00
|
|
|
ToUses(db, type.derived), false /*force_display*/);
|
2018-02-10 03:07:45 +00:00
|
|
|
AddCodeLens("var", "vars", &common, OffsetStartColumn(sym, 2),
|
2018-02-10 20:53:18 +00:00
|
|
|
ToUses(db, type.instances), false /*force_display*/);
|
2017-12-06 03:32:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind::Func: {
|
2018-02-10 03:07:45 +00:00
|
|
|
QueryFunc& func = db->GetFunc(sym);
|
2017-12-06 03:32:33 +00:00
|
|
|
if (!func.def)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int16_t offset = 0;
|
|
|
|
|
2018-01-30 00:27:43 +00:00
|
|
|
// For functions, the outline will report a location that is using the
|
|
|
|
// extent since that is better for outline. This tries to convert the
|
|
|
|
// extent location to the spelling location.
|
2018-01-25 03:15:18 +00:00
|
|
|
auto try_ensure_spelling = [&](SymbolRef sym) {
|
2018-02-11 04:30:27 +00:00
|
|
|
Maybe<Use> def = GetDefinitionSpellingOfSymbol(db, sym);
|
2018-02-10 01:30:22 +00:00
|
|
|
if (!def || db->GetFileId(*def) != db->GetFileId(sym) ||
|
2018-02-09 17:42:10 +00:00
|
|
|
def->range.start.line != sym.range.start.line) {
|
|
|
|
return sym;
|
2018-01-25 03:15:18 +00:00
|
|
|
}
|
2018-02-10 01:30:22 +00:00
|
|
|
return SymbolRef(*def);
|
2018-01-25 03:15:18 +00:00
|
|
|
};
|
|
|
|
|
2018-02-10 20:53:18 +00:00
|
|
|
std::vector<Use> base_callers =
|
2017-12-06 03:32:33 +00:00
|
|
|
GetCallersForAllBaseFunctions(db, func);
|
2018-02-10 20:53:18 +00:00
|
|
|
std::vector<Use> derived_callers =
|
2017-12-06 03:32:33 +00:00
|
|
|
GetCallersForAllDerivedFunctions(db, func);
|
|
|
|
if (base_callers.empty() && derived_callers.empty()) {
|
2018-02-09 17:42:10 +00:00
|
|
|
SymbolRef loc = try_ensure_spelling(sym);
|
2017-12-06 03:32:33 +00:00
|
|
|
AddCodeLens("call", "calls", &common,
|
2018-02-10 20:53:18 +00:00
|
|
|
OffsetStartColumn(loc, offset++), func.uses,
|
|
|
|
true /*force_display*/);
|
2017-12-06 03:32:33 +00:00
|
|
|
} else {
|
2018-02-09 17:42:10 +00:00
|
|
|
SymbolRef loc = try_ensure_spelling(sym);
|
2017-12-06 03:32:33 +00:00
|
|
|
AddCodeLens("direct call", "direct calls", &common,
|
2018-02-10 20:53:18 +00:00
|
|
|
OffsetStartColumn(loc, offset++), func.uses,
|
|
|
|
false /*force_display*/);
|
2017-12-06 03:32:33 +00:00
|
|
|
if (!base_callers.empty())
|
|
|
|
AddCodeLens("base call", "base calls", &common,
|
2018-02-10 20:53:18 +00:00
|
|
|
OffsetStartColumn(loc, offset++), base_callers,
|
2017-12-06 03:32:33 +00:00
|
|
|
false /*force_display*/);
|
|
|
|
if (!derived_callers.empty())
|
|
|
|
AddCodeLens("derived call", "derived calls", &common,
|
2018-02-10 20:53:18 +00:00
|
|
|
OffsetStartColumn(loc, offset++), derived_callers,
|
2017-12-06 03:32:33 +00:00
|
|
|
false /*force_display*/);
|
|
|
|
}
|
|
|
|
|
|
|
|
AddCodeLens("derived", "derived", &common,
|
2018-02-10 03:07:45 +00:00
|
|
|
OffsetStartColumn(sym, offset++),
|
2018-02-10 20:53:18 +00:00
|
|
|
ToUses(db, func.derived), false /*force_display*/);
|
2017-12-06 03:32:33 +00:00
|
|
|
|
|
|
|
// "Base"
|
2017-12-19 06:15:46 +00:00
|
|
|
if (func.def->base.size() == 1) {
|
2018-02-11 04:30:27 +00:00
|
|
|
Maybe<Use> base_loc =
|
2018-02-05 18:12:28 +00:00
|
|
|
GetDefinitionSpellingOfSymbol(db, func.def->base[0]);
|
2017-12-19 06:15:46 +00:00
|
|
|
if (base_loc) {
|
|
|
|
optional<lsLocation> ls_base =
|
2017-12-23 16:01:43 +00:00
|
|
|
GetLsLocation(db, working_files, *base_loc);
|
2017-12-19 06:15:46 +00:00
|
|
|
if (ls_base) {
|
|
|
|
optional<lsRange> range =
|
2018-02-09 17:42:10 +00:00
|
|
|
GetLsRange(common.working_file, sym.range);
|
2017-12-19 06:15:46 +00:00
|
|
|
if (range) {
|
|
|
|
TCodeLens code_lens;
|
|
|
|
code_lens.range = *range;
|
|
|
|
code_lens.range.start.character += offset++;
|
|
|
|
code_lens.command = lsCommand<lsCodeLensCommandArguments>();
|
|
|
|
code_lens.command->title = "Base";
|
|
|
|
code_lens.command->command = "cquery.goto";
|
|
|
|
code_lens.command->arguments.uri = ls_base->uri;
|
|
|
|
code_lens.command->arguments.position = ls_base->range.start;
|
|
|
|
out.result.push_back(code_lens);
|
|
|
|
}
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
}
|
2017-12-19 06:15:46 +00:00
|
|
|
} else {
|
2018-02-10 03:07:45 +00:00
|
|
|
AddCodeLens("base", "base", &common, OffsetStartColumn(sym, 1),
|
2018-02-10 20:53:18 +00:00
|
|
|
ToUses(db, func.def->base),
|
2017-12-23 16:01:43 +00:00
|
|
|
false /*force_display*/);
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind::Var: {
|
2018-02-10 03:07:45 +00:00
|
|
|
QueryVar& var = db->GetVar(sym);
|
2017-12-06 03:32:33 +00:00
|
|
|
if (!var.def)
|
|
|
|
continue;
|
|
|
|
|
2017-12-24 00:49:11 +00:00
|
|
|
if (var.def->is_local() && !config->codeLensOnLocalVariables)
|
2017-12-06 03:32:33 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
bool force_display = true;
|
|
|
|
// Do not show 0 refs on macro with no uses, as it is most likely
|
|
|
|
// a header guard.
|
2017-12-24 00:49:11 +00:00
|
|
|
if (var.def->is_macro())
|
2017-12-06 03:32:33 +00:00
|
|
|
force_display = false;
|
|
|
|
|
2018-02-10 03:07:45 +00:00
|
|
|
AddCodeLens("ref", "refs", &common, OffsetStartColumn(sym, 0),
|
2018-02-10 01:30:22 +00:00
|
|
|
var.uses, force_display);
|
2017-12-06 03:32:33 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case SymbolKind::File:
|
|
|
|
case SymbolKind::Invalid: {
|
|
|
|
assert(false && "unexpected");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2017-12-24 00:25:18 +00:00
|
|
|
QueueManager::WriteStdout(IpcId::TextDocumentCodeLens, out);
|
2017-12-06 03:32:33 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
REGISTER_MESSAGE_HANDLER(TextDocumentCodeLensHandler);
|
2017-12-06 17:10:58 +00:00
|
|
|
} // namespace
|