ccls/src/messages/text_document_code_lens.cc

232 lines
8.4 KiB
C++
Raw Normal View History

2017-12-29 16:29:47 +00:00
#include "clang_complete.h"
2018-03-02 04:33:21 +00:00
#include "lsp_code_action.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
namespace {
MethodType kMethodType = "textDocument/codeLens";
2017-12-06 04:39:44 +00:00
struct lsDocumentCodeLensParams {
lsTextDocumentIdentifier textDocument;
};
MAKE_REFLECT_STRUCT(lsDocumentCodeLensParams, textDocument);
using TCodeLens = lsCodeLens<lsCodeLensUserData, lsCodeLensCommandArguments>;
2018-03-22 05:01:21 +00:00
struct In_TextDocumentCodeLens : public RequestInMessage {
MethodType GetMethodType() const override { return kMethodType; }
2017-12-06 04:39:44 +00:00
lsDocumentCodeLensParams params;
};
MAKE_REFLECT_STRUCT(In_TextDocumentCodeLens, id, params);
REGISTER_IN_MESSAGE(In_TextDocumentCodeLens);
2017-12-06 04:39:44 +00:00
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);
struct CommonCodeLensParams {
std::vector<TCodeLens>* result;
QueryDatabase* db;
WorkingFiles* working_files;
WorkingFile* working_file;
};
2018-02-18 18:07:13 +00:00
Use OffsetStartColumn(Use use, int16_t offset) {
use.range.start.column += offset;
return use;
2018-02-10 03:07:45 +00:00
}
2017-12-06 04:39:44 +00:00
void AddCodeLens(const char* singular,
const char* plural,
CommonCodeLensParams* common,
2018-02-18 18:07:13 +00:00
Use use,
const std::vector<Use>& uses,
2017-12-06 04:39:44 +00:00
bool force_display) {
TCodeLens code_lens;
2018-03-31 03:16:33 +00:00
std::optional<lsRange> range = GetLsRange(common->working_file, use.range);
2017-12-06 04:39:44 +00:00
if (!range)
return;
2018-02-18 18:07:13 +00:00
if (use.file == QueryFileId())
return;
2017-12-06 04:39:44 +00:00
code_lens.range = *range;
code_lens.command = lsCommand<lsCodeLensCommandArguments>();
2018-03-31 03:16:33 +00:00
code_lens.command->command = "ccls.showReferences";
2018-02-18 18:07:13 +00:00
code_lens.command->arguments.uri = GetLsDocumentUri(common->db, use.file);
2017-12-06 04:39:44 +00:00
code_lens.command->arguments.position = code_lens.range.start;
// Add unique uses.
std::vector<lsLocation> unique_uses;
2018-02-18 18:07:13 +00:00
for (Use use1 : uses) {
if (auto ls_loc = GetLsLocation(common->db, common->working_files, use1))
unique_uses.push_back(*ls_loc);
2017-12-06 04:39:44 +00:00
}
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);
}
struct Handler_TextDocumentCodeLens
: BaseMessageHandler<In_TextDocumentCodeLens> {
MethodType GetMethodType() const override { return kMethodType; }
void Run(In_TextDocumentCodeLens* request) override {
2017-12-06 03:32:33 +00:00
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;
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-18 18:07:13 +00:00
Use use(sym.range, sym.id, sym.kind, sym.role, file->def->file);
2017-12-06 03:32:33 +00:00
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);
const QueryType::Def* def = type.AnyDef();
if (!def || def->kind == lsSymbolKind::Namespace)
continue;
2018-02-18 18:07:13 +00:00
AddCodeLens("ref", "refs", &common, OffsetStartColumn(use, 0),
type.uses, true /*force_display*/);
2018-02-18 18:07:13 +00:00
AddCodeLens("derived", "derived", &common, OffsetStartColumn(use, 1),
2018-03-20 02:51:42 +00:00
GetDeclarations(db, type.derived),
false /*force_display*/);
2018-02-18 18:07:13 +00:00
AddCodeLens("var", "vars", &common, OffsetStartColumn(use, 2),
2018-03-20 02:51:42 +00:00
GetDeclarations(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);
const QueryFunc::Def* def = func.AnyDef();
if (!def)
2017-12-06 03:32:33 +00:00
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-02-18 18:07:13 +00:00
auto try_ensure_spelling = [&](Use use) {
Maybe<Use> def = GetDefinitionSpell(db, use);
2018-02-18 18:07:13 +00:00
if (!def || def->range.start.line != use.range.start.line) {
return use;
}
2018-02-18 18:07:13 +00:00
return *def;
};
std::vector<Use> base_callers = GetUsesForAllBases(db, func);
std::vector<Use> derived_callers = GetUsesForAllDerived(db, func);
2017-12-06 03:32:33 +00:00
if (base_callers.empty() && derived_callers.empty()) {
2018-02-18 18:07:13 +00:00
Use loc = try_ensure_spelling(use);
2017-12-06 03:32:33 +00:00
AddCodeLens("call", "calls", &common,
OffsetStartColumn(loc, offset++), func.uses,
true /*force_display*/);
2017-12-06 03:32:33 +00:00
} else {
2018-02-18 18:07:13 +00:00
Use loc = try_ensure_spelling(use);
2017-12-06 03:32:33 +00:00
AddCodeLens("direct call", "direct calls", &common,
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,
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,
OffsetStartColumn(loc, offset++), derived_callers,
2017-12-06 03:32:33 +00:00
false /*force_display*/);
}
2018-03-20 02:51:42 +00:00
AddCodeLens(
"derived", "derived", &common, OffsetStartColumn(use, offset++),
GetDeclarations(db, func.derived), false /*force_display*/);
2017-12-06 03:32:33 +00:00
// "Base"
if (def->bases.size() == 1) {
Maybe<Use> base_loc = GetDefinitionSpell(
db, SymbolIdx{def->bases[0], SymbolKind::Func});
2017-12-19 06:15:46 +00:00
if (base_loc) {
2018-03-31 03:16:33 +00:00
std::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) {
2018-03-31 03:16:33 +00:00
std::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";
2018-03-31 03:16:33 +00:00
code_lens.command->command = "ccls.goto";
2017-12-19 06:15:46 +00:00
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-18 18:07:13 +00:00
AddCodeLens("base", "base", &common, OffsetStartColumn(use, 1),
2018-03-20 02:51:42 +00:00
GetDeclarations(db, def->bases),
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);
const QueryVar::Def* def = var.AnyDef();
2018-04-04 06:05:41 +00:00
if (!def || (def->is_local() && !g_config->codeLens.localVariables))
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.
2018-02-20 03:06:48 +00:00
if (def->kind == lsSymbolKind::Macro)
2017-12-06 03:32:33 +00:00
force_display = false;
2018-02-18 18:07:13 +00:00
AddCodeLens("ref", "refs", &common, OffsetStartColumn(use, 0),
var.uses, force_display);
2017-12-06 03:32:33 +00:00
break;
}
case SymbolKind::File:
case SymbolKind::Invalid: {
assert(false && "unexpected");
break;
}
};
}
QueueManager::WriteStdout(kMethodType, out);
2017-12-06 03:32:33 +00:00
}
};
REGISTER_MESSAGE_HANDLER(Handler_TextDocumentCodeLens);
2017-12-06 17:10:58 +00:00
} // namespace