From 99e79f123a92dc17c972cde18c655379008f60e3 Mon Sep 17 00:00:00 2001 From: Kalmarv <59708937+Kalmarv@users.noreply.github.com> Date: Wed, 14 Oct 2026 09:36:32 -0600 Subject: [PATCH] Console: "help perf" lists the perf commands, and a wrong word after a command says where to look help showed only that command when it was also the first word of others: help perf showed the HUD toggle and not perf hud, perf status, perf sample and the rest, and the same held for help log, music, reset, boardbreak and roadrash. help listed a family only when no command had the family's name. It now lists them after the command's own details, by name or by alias (Registry::under). "perf hdu" answered "Too many arguments. Usage: perf"; when the command has others under it, the error now ends "Use help perf for the commands under it." Co-Authored-By: Claude Opus 5.5 --- Engine/Core/Console/command_registry.h | 21 ++++++++++++++++++++- Extension/Console/commands.cpp | 23 +++++++++++++---------- 2 files changed, 33 insertions(+), 11 deletions(-) diff --git a/Engine/Core/Console/command_registry.h b/Engine/Core/Console/command_registry.h index 841aa0bf..eb75aa01 100644 --- a/Engine/Core/Console/command_registry.h +++ b/Engine/Core/Console/command_registry.h @@ -139,6 +139,23 @@ template class Registry { const auto it = lookup_.find(lower(name)); return it == lookup_.end() ? nullptr : &entries_[it->second]; } + // The other entries whose name or an alias goes on from a name by more words: + // `perf hud` and `perf status` under `perf`. + std::vector under(std::string_view name) const { + const auto prefix = std::string(name) + ' '; + const auto *self = find(name); // (`log` is also `log status`) + std::vector result; + for (const auto &entry : entries_) { + if (&entry == self) + continue; + bool below = ascii_case_insensitive_starts_with(entry.name, prefix); + for (const auto &alias : entry.aliases) + below = below || ascii_case_insensitive_starts_with(alias, prefix); + if (below) + result.push_back(&entry); + } + return result; + } static std::string usage(const Entry &entry) { std::string result = entry.name; for (const auto &arg : entry.arguments) { @@ -259,7 +276,9 @@ template class Registry { values.push_back(std::move(*value)); } if (index != args.size()) { - output("error: Too many arguments. Usage: " + usage(*entry)); + // `perf hdu`: neither an argument of perf nor a command under it. + output("error: Too many arguments. Usage: " + usage(*entry) + + (under(entry->name).empty() ? "" : ". Use help " + entry->name + " for the commands under it.")); return false; } if (!entry->run) { diff --git a/Extension/Console/commands.cpp b/Extension/Console/commands.cpp index 63f68c12..5cf3ad2c 100644 --- a/Extension/Console/commands.cpp +++ b/Extension/Console/commands.cpp @@ -44,24 +44,23 @@ void register_console_commands(Commands ®istry) { if (args.empty()) { out("Enter a variable by itself to read it, or follow it with a value: " "noclip 1, nobail 0, flyspeed 60."); - out("Use commands [prefix] to list entries, help for details, " - "and Tab to complete names and " - "arguments."); + out("Use commands [prefix] to list entries, help for details and the commands under it " + "(help perf, help mp, help trainer), and Tab to complete names and arguments."); out("Groups: Movement, Gameplay, World, Graphics, Progression, Objects, " "Engine, Console."); return; } const auto &name = std::get(args[0]); const auto *entry = registry.find(name); + const auto below = registry.under(entry ? entry->name : name); + const auto list_below = [&below, &out] { + for (const auto *item : below) + out(Commands::usage(*item) + (item->description.empty() ? "" : " - " + item->description)); + }; if (!entry) { - bool found = false; - for (const auto &item : registry.entries()) - if (ascii_case_insensitive_starts_with(item.name, name + " ")) { - out(Commands::usage(item) + (item.description.empty() ? "" : " - " + item.description)); - found = true; - } - if (!found) + if (below.empty()) out("error: Unknown help topic."); + list_below(); return; } out(Commands::usage(*entry)); @@ -83,6 +82,10 @@ void register_console_commands(Commands ®istry) { } if (entry->reset) out("Restore: reset " + entry->name); + // `perf` is a command of its own and the first word of `perf hud`, `perf status` and the rest. + if (!below.empty()) + out(entry->name + " commands:"); + list_below(); }; registry.add(std::move(help)); auto list = action("commands", "List commands and variables, optionally filtered by prefix", Group::console,