From 819f5198dee4e34cc8d4f8632fc173874a264a1b Mon Sep 17 00:00:00 2001 From: Fangrui Song Date: Wed, 17 Jan 2018 23:14:29 -0800 Subject: [PATCH] Add $cquery/memberHierarchy{Initial,Expand} to list member variables of a type --- src/command_line.cc | 2 + src/ipc.cc | 4 + src/ipc.h | 2 + src/messages/cquery_member_hierarchy.cc | 135 ++++++++++++++++++++++++ 4 files changed, 143 insertions(+) create mode 100644 src/messages/cquery_member_hierarchy.cc diff --git a/src/command_line.cc b/src/command_line.cc index 38cb1d1d..349eed6e 100644 --- a/src/command_line.cc +++ b/src/command_line.cc @@ -290,6 +290,8 @@ void LaunchStdinLoop(Config* config, case IpcId::CqueryTypeHierarchyTree: case IpcId::CqueryCallTreeInitial: case IpcId::CqueryCallTreeExpand: + case IpcId::CqueryMemberHierarchyInitial: + case IpcId::CqueryMemberHierarchyExpand: case IpcId::CqueryVars: case IpcId::CqueryCallers: case IpcId::CqueryBase: diff --git a/src/ipc.cc b/src/ipc.cc index 53919f7c..d20240e3 100644 --- a/src/ipc.cc +++ b/src/ipc.cc @@ -74,6 +74,10 @@ const char* IpcIdToString(IpcId id) { return "$cquery/callTreeInitial"; case IpcId::CqueryCallTreeExpand: return "$cquery/callTreeExpand"; + case IpcId::CqueryMemberHierarchyInitial: + return "$cquery/memberHierarchyInitial"; + case IpcId::CqueryMemberHierarchyExpand: + return "$cquery/memberHierarchyExpand"; case IpcId::CqueryVars: return "$cquery/vars"; case IpcId::CqueryCallers: diff --git a/src/ipc.h b/src/ipc.h index 714c6431..3ea9618a 100644 --- a/src/ipc.h +++ b/src/ipc.h @@ -48,6 +48,8 @@ enum class IpcId : int { CqueryTypeHierarchyTree, CqueryCallTreeInitial, CqueryCallTreeExpand, + CqueryMemberHierarchyInitial, + CqueryMemberHierarchyExpand, // These are like DocumentReferences but show different types of data. CqueryVars, // Show all variables of a type. CqueryCallers, // Show all callers of a function. diff --git a/src/messages/cquery_member_hierarchy.cc b/src/messages/cquery_member_hierarchy.cc new file mode 100644 index 00000000..df8b0ecd --- /dev/null +++ b/src/messages/cquery_member_hierarchy.cc @@ -0,0 +1,135 @@ +#include "message_handler.h" +#include "query_utils.h" +#include "queue_manager.h" + +namespace { +struct Ipc_CqueryMemberHierarchyInitial + : public IpcMessage { + const static IpcId kIpcId = IpcId::CqueryMemberHierarchyInitial; + lsRequestId id; + lsTextDocumentPositionParams params; +}; +MAKE_REFLECT_STRUCT(Ipc_CqueryMemberHierarchyInitial, id, params); +REGISTER_IPC_MESSAGE(Ipc_CqueryMemberHierarchyInitial); + +struct Ipc_CqueryMemberHierarchyExpand + : public IpcMessage { + const static IpcId kIpcId = IpcId::CqueryMemberHierarchyExpand; + lsRequestId id; + struct Params { + size_t type_id; + } params; +}; +MAKE_REFLECT_STRUCT(Ipc_CqueryMemberHierarchyExpand::Params, type_id); +MAKE_REFLECT_STRUCT(Ipc_CqueryMemberHierarchyExpand, id, params); +REGISTER_IPC_MESSAGE(Ipc_CqueryMemberHierarchyExpand); + +struct Out_CqueryMemberHierarchy + : public lsOutMessage { + struct Entry { + std::string name; + uint64_t type_id; + lsLocation location; + }; + lsRequestId id; + std::vector result; +}; +MAKE_REFLECT_STRUCT(Out_CqueryMemberHierarchy::Entry, + name, + type_id, + location); +MAKE_REFLECT_STRUCT(Out_CqueryMemberHierarchy, jsonrpc, id, result); + +std::vector BuildInitial( + QueryDatabase* db, + WorkingFiles* working_files, + QueryTypeId root) { + QueryType& root_type = db->types[root.id]; + if (!root_type.def || !root_type.def->definition_spelling) + return {}; + optional def_loc = + GetLsLocation(db, working_files, *root_type.def->definition_spelling); + if (!def_loc) + return {}; + + Out_CqueryMemberHierarchy::Entry entry; + entry.name = root_type.def->short_name; + entry.type_id = root.id; + entry.location = *def_loc; + return {entry}; +} + +std::vector +ExpandNode(QueryDatabase* db, + WorkingFiles* working_files, + QueryTypeId root) { + QueryType& root_type = db->types[root.id]; + if (!root_type.def) + return {}; + + std::vector ret; + for (auto& var_id : root_type.def->vars) { + QueryVar& var = db->vars[var_id.id]; + Out_CqueryMemberHierarchy::Entry entry; + entry.name = var.def->short_name; + entry.type_id = + var.def->variable_type ? var.def->variable_type->id : size_t(-1); + if (var.def->definition_spelling) { + optional loc = + GetLsLocation(db, working_files, *var.def->definition_spelling); + // TODO invalid location + if (loc) + entry.location = *loc; + } + ret.push_back(std::move(entry)); + } + return ret; +} + +struct CqueryMemberHierarchyInitialHandler + : BaseMessageHandler { + void Run(Ipc_CqueryMemberHierarchyInitial* request) override { + QueryFile* file; + if (!FindFileOrFail(db, project, request->id, + request->params.textDocument.uri.GetPath(), &file)) + return; + + WorkingFile* working_file = + working_files->GetFileByFilename(file->def->path); + Out_CqueryMemberHierarchy out; + out.id = request->id; + + for (const SymbolRef& ref : + FindSymbolsAtLocation(working_file, file, request->params.position)) { + if (ref.idx.kind == SymbolKind::Type) { + out.result = BuildInitial(db, working_files, QueryTypeId(ref.idx.idx)); + break; + } + if (ref.idx.kind == SymbolKind::Var) { + QueryVar& var = db->vars[ref.idx.idx]; + if (var.def && var.def->variable_type) + out.result = BuildInitial(db, working_files, *var.def->variable_type); + break; + } + } + + QueueManager::WriteStdout(IpcId::CqueryMemberHierarchyInitial, out); + } +}; +REGISTER_MESSAGE_HANDLER(CqueryMemberHierarchyInitialHandler); + +struct CqueryMemberHierarchyExpandHandler + : BaseMessageHandler { + void Run(Ipc_CqueryMemberHierarchyExpand* request) override { + Out_CqueryMemberHierarchy out; + out.id = request->id; + // |ExpandNode| uses -1 to indicate invalid |type_id|. + if (request->params.type_id != size_t(-1)) + out.result = + ExpandNode(db, working_files, QueryTypeId(request->params.type_id)); + + QueueManager::WriteStdout(IpcId::CqueryMemberHierarchyExpand, out); + } +}; +REGISTER_MESSAGE_HANDLER(CqueryMemberHierarchyExpandHandler); +} // namespace