| 77 | } // namespace |
| 78 | |
| 79 | void MessageHandler::textDocument_codeLens(TextDocumentParam ¶m, ReplyOnce &reply) { |
| 80 | auto [file, wf] = findOrFail(param.textDocument.uri.getPath(), reply); |
| 81 | if (!wf) |
| 82 | return; |
| 83 | |
| 84 | std::vector<CodeLens> result; |
| 85 | auto add = [&, wf = wf](const char *singular, Cmd_xref show, Range range, int num, bool force_display = false) { |
| 86 | if (!num && !force_display) |
| 87 | return; |
| 88 | std::optional<lsRange> ls_range = getLsRange(wf, range); |
| 89 | if (!ls_range) |
| 90 | return; |
| 91 | CodeLens &code_lens = result.emplace_back(); |
| 92 | code_lens.range = *ls_range; |
| 93 | code_lens.command = Command(); |
| 94 | code_lens.command->command = std::string(ccls_xref); |
| 95 | bool plural = num > 1 && singular[strlen(singular) - 1] != 'd'; |
| 96 | code_lens.command->title = llvm::formatv("{0} {1}{2}", num, singular, plural ? "s" : "").str(); |
| 97 | code_lens.command->arguments.push_back(toString(show)); |
| 98 | }; |
| 99 | |
| 100 | std::unordered_set<Range> seen; |
| 101 | for (auto [sym, refcnt] : file->symbol2refcnt) { |
| 102 | if (refcnt <= 0 || !sym.extent.valid() || !seen.insert(sym.range).second) |
| 103 | continue; |
| 104 | switch (sym.kind) { |
| 105 | case Kind::Func: { |
| 106 | QueryFunc &func = db->getFunc(sym); |
| 107 | const QueryFunc::Def *def = func.anyDef(); |
| 108 | if (!def) |
| 109 | continue; |
| 110 | std::vector<Use> base_uses = getUsesForAllBases(db, func); |
| 111 | std::vector<Use> derived_uses = getUsesForAllDerived(db, func); |
| 112 | add("ref", {sym.usr, Kind::Func, "uses"}, sym.range, func.uses.size(), base_uses.empty()); |
| 113 | if (base_uses.size()) |
| 114 | add("b.ref", {sym.usr, Kind::Func, "bases uses"}, sym.range, base_uses.size()); |
| 115 | if (derived_uses.size()) |
| 116 | add("d.ref", {sym.usr, Kind::Func, "derived uses"}, sym.range, derived_uses.size()); |
| 117 | if (base_uses.empty()) |
| 118 | add("base", {sym.usr, Kind::Func, "bases"}, sym.range, def->bases.size()); |
| 119 | add("derived", {sym.usr, Kind::Func, "derived"}, sym.range, func.derived.size()); |
| 120 | break; |
| 121 | } |
| 122 | case Kind::Type: { |
| 123 | QueryType &type = db->getType(sym); |
| 124 | add("ref", {sym.usr, Kind::Type, "uses"}, sym.range, type.uses.size(), true); |
| 125 | add("derived", {sym.usr, Kind::Type, "derived"}, sym.range, type.derived.size()); |
| 126 | add("var", {sym.usr, Kind::Type, "instances"}, sym.range, type.instances.size()); |
| 127 | break; |
| 128 | } |
| 129 | case Kind::Var: { |
| 130 | QueryVar &var = db->getVar(sym); |
| 131 | const QueryVar::Def *def = var.anyDef(); |
| 132 | if (!def || (def->is_local() && !g_config->codeLens.localVariables)) |
| 133 | continue; |
| 134 | add("ref", {sym.usr, Kind::Var, "uses"}, sym.range, var.uses.size(), def->kind != SymbolKind::Macro); |
| 135 | break; |
| 136 | } |
nothing calls this directly
no test coverage detected