mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-25 00:55:08 +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(TextDocumentDidChangeParam, textDocument, contentChanges);
|
||||||
REFLECT_STRUCT(TextDocumentPositionParam, textDocument, position);
|
REFLECT_STRUCT(TextDocumentPositionParam, textDocument, position);
|
||||||
REFLECT_STRUCT(RenameParam, textDocument, position, newName);
|
REFLECT_STRUCT(RenameParam, textDocument, position, newName);
|
||||||
|
REFLECT_STRUCT(CallsParam, item);
|
||||||
|
|
||||||
// completion
|
// completion
|
||||||
REFLECT_UNDERLYING(CompletionTriggerKind);
|
REFLECT_UNDERLYING(CompletionTriggerKind);
|
||||||
@ -162,6 +163,8 @@ MessageHandler::MessageHandler() {
|
|||||||
bind("$ccls/navigate", &MessageHandler::ccls_navigate);
|
bind("$ccls/navigate", &MessageHandler::ccls_navigate);
|
||||||
bind("$ccls/reload", &MessageHandler::ccls_reload);
|
bind("$ccls/reload", &MessageHandler::ccls_reload);
|
||||||
bind("$ccls/vars", &MessageHandler::ccls_vars);
|
bind("$ccls/vars", &MessageHandler::ccls_vars);
|
||||||
|
bind("callHierarchy/incomingCalls", &MessageHandler::callHierarchy_incomingCalls);
|
||||||
|
bind("callHierarchy/outgoingCalls", &MessageHandler::callHierarchy_outgoingCalls);
|
||||||
bind("exit", &MessageHandler::exit);
|
bind("exit", &MessageHandler::exit);
|
||||||
bind("initialize", &MessageHandler::initialize);
|
bind("initialize", &MessageHandler::initialize);
|
||||||
bind("initialized", &MessageHandler::initialized);
|
bind("initialized", &MessageHandler::initialized);
|
||||||
@ -183,6 +186,7 @@ MessageHandler::MessageHandler() {
|
|||||||
bind("textDocument/hover", &MessageHandler::textDocument_hover);
|
bind("textDocument/hover", &MessageHandler::textDocument_hover);
|
||||||
bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
|
bind("textDocument/implementation", &MessageHandler::textDocument_implementation);
|
||||||
bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
|
bind("textDocument/onTypeFormatting", &MessageHandler::textDocument_onTypeFormatting);
|
||||||
|
bind("textDocument/prepareCallHierarchy", &MessageHandler::textDocument_prepareCallHierarchy);
|
||||||
bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
|
bind("textDocument/rangeFormatting", &MessageHandler::textDocument_rangeFormatting);
|
||||||
bind("textDocument/references", &MessageHandler::textDocument_references);
|
bind("textDocument/references", &MessageHandler::textDocument_references);
|
||||||
bind("textDocument/rename", &MessageHandler::textDocument_rename);
|
bind("textDocument/rename", &MessageHandler::textDocument_rename);
|
||||||
|
@ -58,6 +58,22 @@ struct WorkspaceEdit {
|
|||||||
};
|
};
|
||||||
REFLECT_STRUCT(WorkspaceEdit, documentChanges);
|
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
|
// completion
|
||||||
enum class CompletionTriggerKind {
|
enum class CompletionTriggerKind {
|
||||||
Invoked = 1,
|
Invoked = 1,
|
||||||
@ -259,6 +275,8 @@ private:
|
|||||||
void ccls_navigate(JsonReader &, ReplyOnce &);
|
void ccls_navigate(JsonReader &, ReplyOnce &);
|
||||||
void ccls_reload(JsonReader &);
|
void ccls_reload(JsonReader &);
|
||||||
void ccls_vars(JsonReader &, ReplyOnce &);
|
void ccls_vars(JsonReader &, ReplyOnce &);
|
||||||
|
void callHierarchy_incomingCalls(CallsParam ¶m, ReplyOnce &);
|
||||||
|
void callHierarchy_outgoingCalls(CallsParam ¶m, ReplyOnce &);
|
||||||
void exit(EmptyParam &);
|
void exit(EmptyParam &);
|
||||||
void initialize(JsonReader &, ReplyOnce &);
|
void initialize(JsonReader &, ReplyOnce &);
|
||||||
void initialized(EmptyParam &);
|
void initialized(EmptyParam &);
|
||||||
@ -281,6 +299,8 @@ private:
|
|||||||
void textDocument_implementation(TextDocumentPositionParam &, ReplyOnce &);
|
void textDocument_implementation(TextDocumentPositionParam &, ReplyOnce &);
|
||||||
void textDocument_onTypeFormatting(DocumentOnTypeFormattingParam &,
|
void textDocument_onTypeFormatting(DocumentOnTypeFormattingParam &,
|
||||||
ReplyOnce &);
|
ReplyOnce &);
|
||||||
|
void textDocument_prepareCallHierarchy(TextDocumentPositionParam &,
|
||||||
|
ReplyOnce &);
|
||||||
void textDocument_rangeFormatting(DocumentRangeFormattingParam &,
|
void textDocument_rangeFormatting(DocumentRangeFormattingParam &,
|
||||||
ReplyOnce &);
|
ReplyOnce &);
|
||||||
void textDocument_references(JsonReader &, ReplyOnce &);
|
void textDocument_references(JsonReader &, ReplyOnce &);
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
#include "pipeline.hh"
|
#include "pipeline.hh"
|
||||||
#include "query.hh"
|
#include "query.hh"
|
||||||
|
|
||||||
|
#include <map>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace ccls {
|
namespace ccls {
|
||||||
@ -60,6 +61,18 @@ struct Out_cclsCall {
|
|||||||
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren,
|
REFLECT_STRUCT(Out_cclsCall, id, name, location, callType, numChildren,
|
||||||
children);
|
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,
|
bool expand(MessageHandler *m, Out_cclsCall *entry, bool callee,
|
||||||
CallType call_type, bool qualified, int levels) {
|
CallType call_type, bool qualified, int levels) {
|
||||||
const QueryFunc &func = m->db->getFunc(entry->usr);
|
const QueryFunc &func = m->db->getFunc(entry->usr);
|
||||||
@ -205,4 +218,122 @@ void MessageHandler::ccls_call(JsonReader &reader, ReplyOnce &reply) {
|
|||||||
else
|
else
|
||||||
reply(flattenHierarchy(result));
|
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
|
} // namespace ccls
|
||||||
|
@ -88,6 +88,7 @@ struct ServerCap {
|
|||||||
struct ExecuteCommandOptions {
|
struct ExecuteCommandOptions {
|
||||||
std::vector<const char *> commands = {ccls_xref};
|
std::vector<const char *> commands = {ccls_xref};
|
||||||
} executeCommandProvider;
|
} executeCommandProvider;
|
||||||
|
bool callHierarchyProvider = true;
|
||||||
Config::ServerCap::Workspace workspace;
|
Config::ServerCap::Workspace workspace;
|
||||||
};
|
};
|
||||||
REFLECT_STRUCT(ServerCap::CodeActionOptions, codeActionKinds);
|
REFLECT_STRUCT(ServerCap::CodeActionOptions, codeActionKinds);
|
||||||
@ -109,7 +110,7 @@ REFLECT_STRUCT(ServerCap, textDocumentSync, hoverProvider, completionProvider,
|
|||||||
documentRangeFormattingProvider,
|
documentRangeFormattingProvider,
|
||||||
documentOnTypeFormattingProvider, renameProvider,
|
documentOnTypeFormattingProvider, renameProvider,
|
||||||
documentLinkProvider, foldingRangeProvider,
|
documentLinkProvider, foldingRangeProvider,
|
||||||
executeCommandProvider, workspace);
|
executeCommandProvider, callHierarchyProvider, workspace);
|
||||||
|
|
||||||
struct DynamicReg {
|
struct DynamicReg {
|
||||||
bool dynamicRegistration = false;
|
bool dynamicRegistration = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user