Skip to content

Commit

Permalink
Add callHierarchy
Browse files Browse the repository at this point in the history
  • Loading branch information
MaskRay committed Nov 3, 2022
1 parent 7445891 commit 8a93950
Show file tree
Hide file tree
Showing 4 changed files with 157 additions and 1 deletion.
4 changes: 4 additions & 0 deletions src/message_handler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
Expand Down
20 changes: 20 additions & 0 deletions src/message_handler.hh
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -259,6 +275,8 @@ private:
void ccls_navigate(JsonReader &, ReplyOnce &);
void ccls_reload(JsonReader &);
void ccls_vars(JsonReader &, ReplyOnce &);
void callHierarchy_incomingCalls(CallsParam &param, ReplyOnce &);
void callHierarchy_outgoingCalls(CallsParam &param, ReplyOnce &);
void exit(EmptyParam &);
void initialize(JsonReader &, ReplyOnce &);
void initialized(EmptyParam &);
Expand All @@ -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 &);
Expand Down
131 changes: 131 additions & 0 deletions src/messages/ccls_call.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "pipeline.hh"
#include "query.hh"

#include <map>
#include <unordered_set>

namespace ccls {
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -205,4 +218,122 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
else
reply(flattenHierarchy(result));
}

void MessageHandler::textDocument_prepareCallHierarchy(
TextDocumentPositionParam &param, 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 &param,
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 &param,
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
3 changes: 2 additions & 1 deletion src/messages/initialize.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down

0 comments on commit 8a93950

Please sign in to comment.