mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-28 18:41:57 +00:00
Add callHierarchy
This commit is contained in:
parent
74458915b3
commit
8a93950fb9
@ -28,6 +28,7 @@ REFLECT_STRUCT(TextDocumentContentChangeEvent, range, rangeLength, text);
|
||||
REFLECT_STRUCT(TextDocumentDidChangeParam, textDocument, contentChanges);
|
||||
REFLECT_STRUCT(TextDocumentPositionParam, textDocument, position);
|
||||
REFLECT_STRUCT(RenameParam, textDocument, position, newName);
|
||||
REFLECT_STRUCT(CallsParam, item);
|
||||
|
||||
// completion
|
||||
REFLECT_UNDERLYING(CompletionTriggerKind);
|
||||
@ -162,6 +163,8 @@ MessageHandler::MessageHandler() {
|
||||
bind("$ccls/navigate", &MessageHandler::ccls_navigate);
|
||||
bind("$ccls/reload", &MessageHandler::ccls_reload);
|
||||
bind("$ccls/vars", &MessageHandler::ccls_vars);
|
||||
bind("callHierarchy/incomingCalls", &MessageHandler::callHierarchy_incomingCalls);
|
||||
bind("callHierarchy/outgoingCalls", &MessageHandler::callHierarchy_outgoingCalls);
|
||||
bind("exit", &MessageHandler::exit);
|
||||
bind("initialize", &MessageHandler::initialize);
|
||||
bind("initialized", &MessageHandler::initialized);
|
||||
@ -183,6 +186,7 @@ MessageHandler::MessageHandler() {
|
||||
bind("textDocument/hover", &MessageHandler::textDocument_hover);
|
||||
bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
|
||||
bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
|
||||
bind("textDocument/prepareCallHierarchy", &MessageHandler::textDocument_prepareCallHierarchy);
|
||||
bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
|
||||
bind("textDocument/references", &MessageHandler::textDocument_references);
|
||||
bind("textDocument/rename", &MessageHandler::textDocument_rename);
|
||||
|
@ -58,6 +58,22 @@ struct WorkspaceEdit {
|
||||
};
|
||||
REFLECT_STRUCT(WorkspaceEdit, documentChanges);
|
||||
|
||||
struct CallHierarchyItem {
|
||||
std::string name;
|
||||
SymbolKind kind;
|
||||
std::string detail;
|
||||
DocumentUri uri;
|
||||
lsRange range;
|
||||
lsRange selectionRange;
|
||||
std::string data;
|
||||
};
|
||||
REFLECT_STRUCT(CallHierarchyItem, name, kind, detail, uri, range,
|
||||
selectionRange, data);
|
||||
|
||||
struct CallsParam {
|
||||
CallHierarchyItem item;
|
||||
};
|
||||
|
||||
// completion
|
||||
enum class CompletionTriggerKind {
|
||||
Invoked = 1,
|
||||
@ -259,6 +275,8 @@ private:
|
||||
void ccls_navigate(JsonReader &, ReplyOnce &);
|
||||
void ccls_reload(JsonReader &);
|
||||
void ccls_vars(JsonReader &, ReplyOnce &);
|
||||
void callHierarchy_incomingCalls(CallsParam ¶m, ReplyOnce &);
|
||||
void callHierarchy_outgoingCalls(CallsParam ¶m, ReplyOnce &);
|
||||
void exit(EmptyParam &);
|
||||
void initialize(JsonReader &, ReplyOnce &);
|
||||
void initialized(EmptyParam &);
|
||||
@ -281,6 +299,8 @@ private:
|
||||
void textDocument_implementation(TextDocumentPositionParam &, ReplyOnce &);
|
||||
void textDocument_onTypeFormatting(DocumentOnTypeFormattingParam &,
|
||||
ReplyOnce &);
|
||||
void textDocument_prepareCallHierarchy(TextDocumentPositionParam &,
|
||||
ReplyOnce &);
|
||||
void textDocument_rangeFormatting(DocumentRangeFormattingParam &,
|
||||
ReplyOnce &);
|
||||
void textDocument_references(JsonReader &, ReplyOnce &);
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include "pipeline.hh"
|
||||
#include "query.hh"
|
||||
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace ccls {
|
||||
@ -60,6 +61,18 @@ struct Out_cclsCall {
|
||||
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren,
|
||||
children);
|
||||
|
||||
struct Out_incomingCall {
|
||||
CallHierarchyItem from;
|
||||
std::vector<lsRange> fromRanges;
|
||||
};
|
||||
REFLECT_STRUCT(Out_incomingCall, from, fromRanges);
|
||||
|
||||
struct Out_outgoingCall {
|
||||
CallHierarchyItem to;
|
||||
std::vector<lsRange> fromRanges;
|
||||
};
|
||||
REFLECT_STRUCT(Out_outgoingCall, to, fromRanges);
|
||||
|
||||
bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
||||
CallType call_type, bool qualified, int levels) {
|
||||
const QueryFunc &func = m->db->getFunc(entry->usr);
|
||||
@ -205,4 +218,122 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
||||
else
|
||||
reply(flattenHierarchy(result));
|
||||
}
|
||||
|
||||
void MessageHandler::textDocument_prepareCallHierarchy(
|
||||
TextDocumentPositionParam ¶m, ReplyOnce &reply) {
|
||||
std::string path = param.textDocument.uri.getPath();
|
||||
auto [file, wf] = findOrFail(path, reply);
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
std::vector<CallHierarchyItem> result;
|
||||
for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position)) {
|
||||
if (sym.kind != Kind::Func)
|
||||
continue;
|
||||
const auto *def = db->getFunc(sym.usr).anyDef();
|
||||
if (!def)
|
||||
continue;
|
||||
auto r = getLsRange(wf, sym.range);
|
||||
if (!r)
|
||||
continue;
|
||||
CallHierarchyItem &item = result.emplace_back();
|
||||
item.name = def->name(false);
|
||||
item.kind = def->kind;
|
||||
item.detail = def->name(true);
|
||||
item.uri = DocumentUri::fromPath(path);
|
||||
item.range = item.selectionRange = *r;
|
||||
item.data = std::to_string(sym.usr);
|
||||
}
|
||||
reply(result);
|
||||
}
|
||||
|
||||
static lsRange toLsRange(Range r) {
|
||||
return {{r.start.line, r.start.column}, {r.end.line, r.end.column}};
|
||||
}
|
||||
|
||||
static void
|
||||
add(std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> &sym2ranges,
|
||||
SymbolRef sym, int file_id) {
|
||||
auto [it, inserted] = sym2ranges.try_emplace(SymbolIdx{sym.usr, sym.kind});
|
||||
if (inserted)
|
||||
it->second.first = file_id;
|
||||
if (it->second.first == file_id)
|
||||
it->second.second.push_back(toLsRange(sym.range));
|
||||
}
|
||||
|
||||
template <typename Out>
|
||||
static std::vector<Out> toCallResult(
|
||||
DB *db,
|
||||
const std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> &sym2ranges) {
|
||||
std::vector<Out> result;
|
||||
for (auto &[sym, ranges] : sym2ranges) {
|
||||
CallHierarchyItem item;
|
||||
item.uri = getLsDocumentUri(db, ranges.first);
|
||||
auto r = ranges.second[0];
|
||||
item.range = {{uint16_t(r.start.line), int16_t(r.start.character)},
|
||||
{uint16_t(r.end.line), int16_t(r.end.character)}};
|
||||
item.selectionRange = item.range;
|
||||
switch (sym.kind) {
|
||||
default:
|
||||
continue;
|
||||
case Kind::Func: {
|
||||
auto idx = db->func_usr[sym.usr];
|
||||
const QueryFunc &func = db->funcs[idx];
|
||||
const QueryFunc::Def *def = func.anyDef();
|
||||
if (!def)
|
||||
continue;
|
||||
item.name = def->name(false);
|
||||
item.kind = def->kind;
|
||||
item.detail = def->name(true);
|
||||
item.data = std::to_string(sym.usr);
|
||||
}
|
||||
}
|
||||
|
||||
result.push_back({std::move(item), std::move(ranges.second)});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void MessageHandler::callHierarchy_incomingCalls(CallsParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
Usr usr;
|
||||
try {
|
||||
usr = std::stoull(param.item.data);
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
const QueryFunc &func = db->getFunc(usr);
|
||||
std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> sym2ranges;
|
||||
for (Use use : func.uses) {
|
||||
const QueryFile &file = db->files[use.file_id];
|
||||
Maybe<ExtentRef> best;
|
||||
for (auto [sym, refcnt] : file.symbol2refcnt)
|
||||
if (refcnt > 0 && sym.extent.valid() && sym.kind == Kind::Func &&
|
||||
sym.extent.start <= use.range.start &&
|
||||
use.range.end <= sym.extent.end &&
|
||||
(!best || best->extent.start < sym.extent.start))
|
||||
best = sym;
|
||||
if (best)
|
||||
add(sym2ranges, *best, use.file_id);
|
||||
}
|
||||
reply(toCallResult<Out_incomingCall>(db, sym2ranges));
|
||||
}
|
||||
|
||||
void MessageHandler::callHierarchy_outgoingCalls(CallsParam ¶m,
|
||||
ReplyOnce &reply) {
|
||||
Usr usr;
|
||||
try {
|
||||
usr = std::stoull(param.item.data);
|
||||
} catch (...) {
|
||||
return;
|
||||
}
|
||||
const QueryFunc &func = db->getFunc(usr);
|
||||
std::map<SymbolIdx, std::pair<int, std::vector<lsRange>>> sym2ranges;
|
||||
if (const auto *def = func.anyDef())
|
||||
for (SymbolRef sym : def->callees)
|
||||
if (sym.kind == Kind::Func) {
|
||||
add(sym2ranges, sym, def->file_id);
|
||||
}
|
||||
reply(toCallResult<Out_outgoingCall>(db, sym2ranges));
|
||||
}
|
||||
} // namespace ccls
|
||||
|
@ -88,6 +88,7 @@ struct ServerCap {
|
||||
struct ExecuteCommandOptions {
|
||||
std::vector<const char *> commands = {ccls_xref};
|
||||
} executeCommandProvider;
|
||||
bool callHierarchyProvider = true;
|
||||
Config::ServerCap::Workspace workspace;
|
||||
};
|
||||
REFLECT_STRUCT(ServerCap::CodeActionOptions, codeActionKinds);
|
||||
@ -109,7 +110,7 @@ REFLECT_STRUCT(ServerCap, textDocumentSync, hoverProvider, completionProvider,
|
||||
documentRangeFormattingProvider,
|
||||
documentOnTypeFormattingProvider, renameProvider,
|
||||
documentLinkProvider, foldingRangeProvider,
|
||||
executeCommandProvider, workspace);
|
||||
executeCommandProvider, callHierarchyProvider, workspace);
|
||||
|
||||
struct DynamicReg {
|
||||
bool dynamicRegistration = false;
|
||||
|
Loading…
Reference in New Issue
Block a user