#include "message_handler.h" #include "query_utils.h" #include "queue_manager.h" namespace { MethodType kMethodType = "$ccls/inheritanceHierarchy"; struct In_CclsInheritanceHierarchy : public RequestInMessage { MethodType GetMethodType() const override { return kMethodType; } struct Params { // If id+kind are specified, expand a node; otherwise textDocument+position // should be specified for building the root and |levels| of nodes below. lsTextDocumentIdentifier textDocument; lsPosition position; Maybe> id; SymbolKind kind = SymbolKind::Invalid; // true: derived classes/functions; false: base classes/functions bool derived = false; bool qualified = true; int levels = 1; }; Params params; }; MAKE_REFLECT_STRUCT(In_CclsInheritanceHierarchy::Params, textDocument, position, id, kind, derived, qualified, levels); MAKE_REFLECT_STRUCT(In_CclsInheritanceHierarchy, id, params); REGISTER_IN_MESSAGE(In_CclsInheritanceHierarchy); struct Out_CclsInheritanceHierarchy : public lsOutMessage { struct Entry { Id id; SymbolKind kind; std::string_view name; lsLocation location; // For unexpanded nodes, this is an upper bound because some entities may be // undefined. If it is 0, there are no members. int numChildren; // Empty if the |levels| limit is reached. std::vector children; }; lsRequestId id; std::optional result; }; MAKE_REFLECT_STRUCT(Out_CclsInheritanceHierarchy::Entry, id, kind, name, location, numChildren, children); MAKE_REFLECT_STRUCT(Out_CclsInheritanceHierarchy, jsonrpc, id, result); bool Expand(MessageHandler* m, Out_CclsInheritanceHierarchy::Entry* entry, bool derived, bool qualified, int levels); template bool ExpandHelper(MessageHandler* m, Out_CclsInheritanceHierarchy::Entry* entry, bool derived, bool qualified, int levels, Q& entity) { const auto* def = entity.AnyDef(); if (!def) { entry->numChildren = 0; return false; } entry->name = def->Name(qualified); if (def->spell) { if (std::optional loc = GetLsLocation(m->db, m->working_files, *def->spell)) entry->location = *loc; } if (derived) { if (levels > 0) { for (auto id : entity.derived) { Out_CclsInheritanceHierarchy::Entry entry1; entry1.id = id; entry1.kind = entry->kind; if (Expand(m, &entry1, derived, qualified, levels - 1)) entry->children.push_back(std::move(entry1)); } entry->numChildren = int(entry->children.size()); } else entry->numChildren = int(entity.derived.size()); } else { if (levels > 0) { for (auto id : def->bases) { Out_CclsInheritanceHierarchy::Entry entry1; entry1.id = id; entry1.kind = entry->kind; if (Expand(m, &entry1, derived, qualified, levels - 1)) entry->children.push_back(std::move(entry1)); } entry->numChildren = int(entry->children.size()); } else entry->numChildren = int(def->bases.size()); } return true; } bool Expand(MessageHandler* m, Out_CclsInheritanceHierarchy::Entry* entry, bool derived, bool qualified, int levels) { if (entry->kind == SymbolKind::Func) return ExpandHelper(m, entry, derived, qualified, levels, m->db->funcs[entry->id.id]); else return ExpandHelper(m, entry, derived, qualified, levels, m->db->types[entry->id.id]); } struct Handler_CclsInheritanceHierarchy : BaseMessageHandler { MethodType GetMethodType() const override { return kMethodType; } std::optional BuildInitial(SymbolRef sym, bool derived, bool qualified, int levels) { Out_CclsInheritanceHierarchy::Entry entry; entry.id = sym.id; entry.kind = sym.kind; Expand(this, &entry, derived, qualified, levels); return entry; } void Run(In_CclsInheritanceHierarchy* request) override { auto& params = request->params; Out_CclsInheritanceHierarchy out; out.id = request->id; if (params.id) { Out_CclsInheritanceHierarchy::Entry entry; entry.id = *params.id; entry.kind = params.kind; if (((entry.kind == SymbolKind::Func && entry.id.id < db->funcs.size()) || (entry.kind == SymbolKind::Type && entry.id.id < db->types.size())) && Expand(this, &entry, params.derived, params.qualified, params.levels)) out.result = std::move(entry); } else { QueryFile* file; if (!FindFileOrFail(db, project, request->id, params.textDocument.uri.GetPath(), &file)) return; WorkingFile* wfile = working_files->GetFileByFilename(file->def->path); for (SymbolRef sym : FindSymbolsAtLocation(wfile, file, params.position)) { if (sym.kind == SymbolKind::Func || sym.kind == SymbolKind::Type) { out.result = BuildInitial(sym, params.derived, params.qualified, params.levels); break; } } } QueueManager::WriteStdout(kMethodType, out); } }; REGISTER_MESSAGE_HANDLER(Handler_CclsInheritanceHierarchy); } // namespace