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,