impr: Rework command line argument parsing (#2440)

The current CLI argument parsing did not work as documented and had a
number of issues related to multi-flag (subcommand) parsing. I've
reworked the logic in such a way that should maintain full compatibility
with any existing scripts/use-cases but with added functionality.

---------

Co-authored-by: Nik <werwolv98@gmail.com>
This commit is contained in:
Zackary Newman 2025-09-12 15:07:33 -04:00 committed by GitHub
parent 6169078c04
commit dcbba9cbfc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 65 additions and 56 deletions

View File

@ -13,6 +13,9 @@
namespace hex::subcommands {
std::optional<SubCommand> findSubCommand(const std::string &arg) {
if (arg == "-" || arg == "--")
return std::nullopt;
for (auto &plugin : PluginManager::getPlugins()) {
for (auto &subCommand : plugin.getSubCommands()) {
if (fmt::format("--{}", subCommand.commandLong) == arg || fmt::format("-{}", subCommand.commandShort) == arg) {
@ -35,46 +38,62 @@ namespace hex::subcommands {
auto argsIter = args.begin();
// Get subcommand associated with the first argument
std::optional<SubCommand> currentSubCommand = findSubCommand(*argsIter);
if (currentSubCommand) {
argsIter += 1;
// If it is a valid subcommand, remove it from the argument list
} else {
// If no (valid) subcommand was provided, the default one is --open
currentSubCommand = findSubCommand("--open");
}
// Arguments of the current subcommand
std::optional<SubCommand> currentSubCommand;
std::vector<std::string> currentSubCommandArgs;
// Compute all subcommands to run
while (argsIter != args.end()) {
const std::string &arg = *argsIter;
if (*argsIter == "--") {
// Treat the rest of the args as files
++argsIter;
std::vector<std::string> remainingArgs(argsIter, args.end());
subCommands.emplace_back(*findSubCommand("--open"), remainingArgs);
argsIter = args.end(); // Skip while loop
} else if (!findSubCommand(*argsIter)) {
// First argument not a subcommand, treat all args as files
subCommands.emplace_back(*findSubCommand("--open"), args);
argsIter = args.end(); // Skip while loop
}
if (!currentSubCommandArgs.empty() && arg.starts_with("--") && !(currentSubCommand.has_value() && currentSubCommand->type == SubCommand::Type::SubCommand)) {
// Save command to run
if (currentSubCommand) {
while (argsIter != args.end()) {
// Get subcommand associated with the argument
// Guaranteed to find a match on the first argument, as the other case has been handled above
const auto newSubCommand = findSubCommand(*argsIter);
if (*argsIter == "--") {
// Treat the rest of the args as files
subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);
++argsIter;
std::vector<std::string> remainingArgs(argsIter, args.end());
subCommands.emplace_back(*findSubCommand("--open"), remainingArgs);
currentSubCommand = std::nullopt;
currentSubCommandArgs.clear();
break;
}
// Will always take this `if` statement on the first time through the loop
if (newSubCommand.has_value()) {
if (currentSubCommand.has_value()) {
subCommands.emplace_back(*currentSubCommand, currentSubCommandArgs);
}
if (newSubCommand->type == SubCommand::Type::SubCommand) {
++argsIter;
std::vector<std::string> remainingArgs(argsIter, args.end());
subCommands.emplace_back(*newSubCommand, remainingArgs);
currentSubCommand = std::nullopt;
currentSubCommandArgs = { };
} else if (currentSubCommand) {
// Add current argument to the current command
currentSubCommandArgs.push_back(arg);
argsIter += 1;
} else {
// Get next subcommand from current argument
currentSubCommand = findSubCommand(arg);
if (!currentSubCommand) {
log::error("No subcommand named '{}' found", arg);
exit(EXIT_FAILURE);
currentSubCommandArgs.clear();
break;
}
argsIter += 1;
currentSubCommand = newSubCommand;
currentSubCommandArgs.clear();
} else {
currentSubCommandArgs.push_back(*argsIter);
}
++argsIter;
}
// Save last command to run
@ -83,7 +102,7 @@ namespace hex::subcommands {
}
// Run the subcommands
for (auto &[subcommand, subCommandArgs] : subCommands) {
for (const auto &[subcommand, subCommandArgs] : subCommands) {
subcommand.callback(subCommandArgs);
}

View File

@ -99,13 +99,7 @@ namespace hex::plugin::builtin {
}
std::vector<std::string> fullPaths;
bool doubleDashFound = false;
for (auto &arg : args) {
// Skip the first argument named `--`
if (arg == "--" && !doubleDashFound) {
doubleDashFound = true;
} else {
for (const auto &arg : args) {
try {
std::fs::path path;
@ -123,7 +117,6 @@ namespace hex::plugin::builtin {
log::error("Failed to open file '{}'\n {}", arg, e.what());
}
}
}
if (!fullPaths.empty())
hex::subcommands::forwardSubCommand("open", fullPaths);
@ -541,9 +534,6 @@ namespace hex::plugin::builtin {
void registerCommandForwarders() {
hex::subcommands::registerSubCommand("open", [](const std::vector<std::string> &args){
for (auto &arg : args) {
if (arg.starts_with("--"))
break;
RequestOpenFile::post(arg);
}
});