From 973bfa80bf5d6e62991456fab5313edf6f69c91e Mon Sep 17 00:00:00 2001 From: Sergio Valor Date: Sun, 17 May 2026 17:17:55 +0200 Subject: [PATCH] =?UTF-8?q?console=5Fcommands:=20load()=20delega=20a=20par?= =?UTF-8?q?sers=20i=20buildHelp=20(cognitive=2069=E2=86=92<25)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- source/game/ui/console_commands.cpp | 199 ++++++++++++++-------------- source/game/ui/console_commands.hpp | 1 + 2 files changed, 101 insertions(+), 99 deletions(-) diff --git a/source/game/ui/console_commands.cpp b/source/game/ui/console_commands.cpp index b47a40a..5c91455 100644 --- a/source/game/ui/console_commands.cpp +++ b/source/game/ui/console_commands.cpp @@ -1103,7 +1103,78 @@ void CommandRegistry::registerHandlers() { // NOLINT(readability-function-cogni #endif } -void CommandRegistry::load(const std::string& yaml_path) { // NOLINT(readability-function-cognitive-complexity) +namespace { + // Parseja un node "scope" (string o sequence) a un vector. Buit si node és buit. + auto parseScopeNode(const fkyaml::node& scope_node) -> std::vector { + std::vector result; + if (scope_node.is_sequence()) { + std::ranges::transform(scope_node, std::back_inserter(result), [](const auto& s) { return s.template get_value(); }); + } else { + result.push_back(scope_node.get_value()); + } + return result; + } + + // Parseja un mapping de path → [options] en l'unordered_map de destí + void parseCompletionsNode(const fkyaml::node& completions_node, std::unordered_map>& out) { + for (auto it = completions_node.begin(); it != completions_node.end(); ++it) { + auto path = it.key().get_value(); + std::vector opts; + std::ranges::transform(*it, std::back_inserter(opts), [](const auto& opt) { return opt.template get_value(); }); + out[path] = std::move(opts); + } + } + +#ifdef _DEBUG + // Aplica camps "debug_extras" sobre un CommandDef ja inicialitzat (només en _DEBUG) + void applyDebugExtras(const fkyaml::node& extras, CommandDef& def) { + if (extras.contains("description")) { def.description = extras["description"].get_value(); } + if (extras.contains("usage")) { def.usage = extras["usage"].get_value(); } + if (extras.contains("hidden")) { def.hidden = extras["hidden"].get_value(); } + if (extras.contains("help_hidden")) { def.help_hidden = extras["help_hidden"].get_value(); } + if (extras.contains("completions")) { + def.completions.clear(); + parseCompletionsNode(extras["completions"], def.completions); + } + } +#endif + + // Parseja un cmd_node a CommandDef. Hereta de la categoria si el comand no defineix scope/debug_only. + auto parseCommandDef(const fkyaml::node& cmd_node, const std::string& category, bool cat_debug_only, const std::vector& cat_scopes) -> CommandDef { + CommandDef def; + def.keyword = cmd_node["keyword"].get_value(); + def.handler_id = cmd_node["handler"].get_value(); + def.category = category; + def.description = cmd_node.contains("description") ? cmd_node["description"].get_value() : ""; + def.usage = cmd_node.contains("usage") ? cmd_node["usage"].get_value() : def.keyword; + def.instant = cmd_node.contains("instant") && cmd_node["instant"].get_value(); + def.hidden = cmd_node.contains("hidden") && cmd_node["hidden"].get_value(); + def.debug_only = cat_debug_only || (cmd_node.contains("debug_only") && cmd_node["debug_only"].get_value()); + def.help_hidden = cmd_node.contains("help_hidden") && cmd_node["help_hidden"].get_value(); + def.dynamic_completions = cmd_node.contains("dynamic_completions") && cmd_node["dynamic_completions"].get_value(); + + if (cmd_node.contains("scope")) { + def.scopes = parseScopeNode(cmd_node["scope"]); + } else if (!cat_scopes.empty()) { + def.scopes = cat_scopes; + } else { + def.scopes.emplace_back("global"); + } + + if (cmd_node.contains("completions")) { + parseCompletionsNode(cmd_node["completions"], def.completions); + } + +#ifdef _DEBUG + if (cmd_node.contains("debug_extras")) { + applyDebugExtras(cmd_node["debug_extras"], def); + } +#endif + return def; + } +} // namespace + +void CommandRegistry::load(const std::string& yaml_path) { registerHandlers(); // Cargar y parsear el YAML @@ -1127,115 +1198,21 @@ void CommandRegistry::load(const std::string& yaml_path) { // NOLINT(readabilit for (const auto& cat_node : yaml["categories"]) { const auto CATEGORY = cat_node["name"].get_value(); const bool CAT_DEBUG_ONLY = cat_node.contains("debug_only") && cat_node["debug_only"].get_value(); - - // Scopes por defecto de la categoría - std::vector cat_scopes; - if (cat_node.contains("scope")) { - const auto& scope_node = cat_node["scope"]; - if (scope_node.is_sequence()) { - std::ranges::transform(scope_node, std::back_inserter(cat_scopes), [](const auto& s) { return s.template get_value(); }); - } else { - cat_scopes.push_back(scope_node.get_value()); - } - } + const std::vector CAT_SCOPES = cat_node.contains("scope") ? parseScopeNode(cat_node["scope"]) : std::vector{}; if (!cat_node.contains("commands")) { continue; } for (const auto& cmd_node : cat_node["commands"]) { - CommandDef def; - def.keyword = cmd_node["keyword"].get_value(); - def.handler_id = cmd_node["handler"].get_value(); - def.category = CATEGORY; - def.description = cmd_node.contains("description") ? cmd_node["description"].get_value() : ""; - def.usage = cmd_node.contains("usage") ? cmd_node["usage"].get_value() : def.keyword; - def.instant = cmd_node.contains("instant") && cmd_node["instant"].get_value(); - def.hidden = cmd_node.contains("hidden") && cmd_node["hidden"].get_value(); - def.debug_only = CAT_DEBUG_ONLY || (cmd_node.contains("debug_only") && cmd_node["debug_only"].get_value()); - def.help_hidden = cmd_node.contains("help_hidden") && cmd_node["help_hidden"].get_value(); - def.dynamic_completions = cmd_node.contains("dynamic_completions") && cmd_node["dynamic_completions"].get_value(); - - // Scopes: del comando, o hereda de la categoría, o "global" por defecto - if (cmd_node.contains("scope")) { - const auto& scope_node = cmd_node["scope"]; - if (scope_node.is_sequence()) { - for (const auto& s : scope_node) { def.scopes.push_back(s.get_value()); } - } else { - def.scopes.push_back(scope_node.get_value()); - } - } else if (!cat_scopes.empty()) { - def.scopes = cat_scopes; - } else { - def.scopes.emplace_back("global"); - } - - // Completions estáticas - if (cmd_node.contains("completions")) { - auto completions_node = cmd_node["completions"]; - for (auto it = completions_node.begin(); it != completions_node.end(); ++it) { - auto path = it.key().get_value(); - std::vector opts; - std::ranges::transform(*it, std::back_inserter(opts), [](const auto& opt) { return opt.template get_value(); }); - def.completions[path] = std::move(opts); - } - } - - // Aplicar debug_extras en debug builds -#ifdef _DEBUG - if (cmd_node.contains("debug_extras")) { - const auto& extras = cmd_node["debug_extras"]; - if (extras.contains("description")) { def.description = extras["description"].get_value(); } - if (extras.contains("usage")) { def.usage = extras["usage"].get_value(); } - if (extras.contains("hidden")) { def.hidden = extras["hidden"].get_value(); } - if (extras.contains("help_hidden")) { def.help_hidden = extras["help_hidden"].get_value(); } - if (extras.contains("completions")) { - def.completions.clear(); - auto extras_completions = extras["completions"]; - for (auto it = extras_completions.begin(); it != extras_completions.end(); ++it) { - auto path = it.key().get_value(); - std::vector opts; - std::ranges::transform(*it, std::back_inserter(opts), [](const auto& opt) { return opt.template get_value(); }); - def.completions[path] = std::move(opts); - } - } - } -#endif - - // En Release: saltar comandos debug_only + CommandDef def = parseCommandDef(cmd_node, CATEGORY, CAT_DEBUG_ONLY, CAT_SCOPES); #ifndef _DEBUG if (def.debug_only) { continue; } #endif - commands_.push_back(std::move(def)); } } - // Registrar el handler de HELP (captura this) - handlers_["cmd_help"] = [this](const std::vector& args) -> std::string { - if (!args.empty()) { - // HELP : mostrar ayuda detallada de un comando - const auto* cmd = findCommand(args[0]); - if (cmd != nullptr) { - std::string kw_lower = cmd->keyword; - std::ranges::transform(kw_lower, kw_lower.begin(), ::tolower); - std::string result = kw_lower + ": " + cmd->description + "\n" + cmd->usage; - - // Listar subcomandos/opciones si hay completions - auto opts = getCompletions(cmd->keyword); - if (!opts.empty()) { - result += "\noptions:"; - for (const auto& opt : opts) { - std::string opt_lower = opt; - std::ranges::transform(opt_lower, opt_lower.begin(), ::tolower); - result += " " + opt_lower; - } - } - return result; - } - return "Unknown command: " + args[0]; - } - std::cout << generateTerminalHelp(); - return generateConsoleHelp(); - }; + // Registrar el handler de HELP (delega a buildHelp per mantenir baixa la complexitat de load) + handlers_["cmd_help"] = [this](const std::vector& args) -> std::string { return buildHelp(args); }; // Aplanar completions en el mapa global for (const auto& cmd : commands_) { @@ -1245,6 +1222,30 @@ void CommandRegistry::load(const std::string& yaml_path) { // NOLINT(readabilit } } +auto CommandRegistry::buildHelp(const std::vector& args) const -> std::string { + if (args.empty()) { + std::cout << generateTerminalHelp(); + return generateConsoleHelp(); + } + // HELP : ajuda detallada + const auto* cmd = findCommand(args[0]); + if (cmd == nullptr) { return "Unknown command: " + args[0]; } + + std::string kw_lower = cmd->keyword; + std::ranges::transform(kw_lower, kw_lower.begin(), ::tolower); + std::string result = kw_lower + ": " + cmd->description + "\n" + cmd->usage; + + const auto OPTS = getCompletions(cmd->keyword); + if (OPTS.empty()) { return result; } + result += "\noptions:"; + for (const auto& opt : OPTS) { + std::string opt_lower = opt; + std::ranges::transform(opt_lower, opt_lower.begin(), ::tolower); + result += " " + opt_lower; + } + return result; +} + auto CommandRegistry::findCommand(const std::string& keyword) const -> const CommandDef* { auto it = std::ranges::find_if(commands_, [&keyword](const auto& cmd) { return cmd.keyword == keyword; }); diff --git a/source/game/ui/console_commands.hpp b/source/game/ui/console_commands.hpp index b775402..1cf1b64 100644 --- a/source/game/ui/console_commands.hpp +++ b/source/game/ui/console_commands.hpp @@ -58,4 +58,5 @@ class CommandRegistry { void registerHandlers(); [[nodiscard]] auto isCommandVisible(const CommandDef& cmd) const -> bool; + [[nodiscard]] auto buildHelp(const std::vector& args) const -> std::string; // Cos del handler HELP (extret per reduir complexitat de load()) };