From 862f09be9ca61a5c6794dfa13bfb6e14d5401402 Mon Sep 17 00:00:00 2001 From: Arthur Grillo Date: Sat, 21 Sep 2024 01:42:50 -0300 Subject: [PATCH] cli: Explicitly add a Help command to accept the early args after it The default clap's help command doesn't have the ability to accept flags (e.g --no-pager). The recommended way[1] to solve this is to manually implement it. [1]: https://github.com/clap-rs/clap/discussions/5332 Fixes: #4501 --- cli/src/commands/help.rs | 66 ++++++++++++++++++++++++++++++++ cli/src/commands/mod.rs | 5 ++- cli/tests/cli-reference@.md.snap | 14 +++++++ cli/tests/test_global_opts.rs | 4 ++ 4 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 cli/src/commands/help.rs diff --git a/cli/src/commands/help.rs b/cli/src/commands/help.rs new file mode 100644 index 0000000000..5615b86aed --- /dev/null +++ b/cli/src/commands/help.rs @@ -0,0 +1,66 @@ +// Copyright 2024 The Jujutsu Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use tracing::instrument; + +use crate::cli_util::CommandHelper; +use crate::command_error; +use crate::command_error::CommandError; +use crate::ui::Ui; + +/// Print this message or the help of the given subcommand(s) +#[derive(clap::Args, Clone, Debug)] +pub(crate) struct HelpArgs { + /// The command to show help + command: Option>, +} + +#[instrument(skip_all)] +pub(crate) fn cmd_help( + _ui: &mut Ui, + command: &CommandHelper, + args: &HelpArgs, +) -> Result<(), CommandError> { + let cmd_names = if let Some(cmd_to_show_help) = &args.command { + let mut ret = vec![]; + let mut curr_command = command.app(); + + for cmd_to_show_help in cmd_to_show_help { + curr_command = curr_command + .find_subcommand(cmd_to_show_help.clone()) + .ok_or(command.app().clone().error( + clap::error::ErrorKind::InvalidValue, + format!("no subcomand with name {}", cmd_to_show_help), + ))?; + + ret.push(curr_command.get_name()); + } + + ret + } else { + vec![] + }; + + let mut args_to_show_help = vec![command.app().get_name()]; + args_to_show_help.extend(cmd_names); + args_to_show_help.push("--help"); + + let help_err = command + .app() + .clone() + .try_get_matches_from(args_to_show_help) + .expect_err("Clap library should return a DisplayHelp error in this context"); + + Err(command_error::cli_error(help_err)) +} diff --git a/cli/src/commands/mod.rs b/cli/src/commands/mod.rs index 52c03c1743..5c4eb073f2 100644 --- a/cli/src/commands/mod.rs +++ b/cli/src/commands/mod.rs @@ -30,6 +30,7 @@ mod evolog; mod file; mod fix; mod git; +mod help; mod init; mod interdiff; mod log; @@ -109,6 +110,7 @@ enum Command { Fix(fix::FixArgs), #[command(subcommand)] Git(git::GitCommand), + Help(help::HelpArgs), Init(init::InitArgs), Interdiff(interdiff::InterdiffArgs), Log(log::LogArgs), @@ -173,7 +175,7 @@ struct DummyCommandArgs { } pub fn default_app() -> clap::Command { - Command::augment_subcommands(Args::command()) + Command::augment_subcommands(Args::command()).disable_help_subcommand(true) } #[instrument(skip_all)] @@ -213,6 +215,7 @@ pub fn run_command(ui: &mut Ui, command_helper: &CommandHelper) -> Result<(), Co } Command::Fix(args) => fix::cmd_fix(ui, command_helper, args), Command::Git(args) => git::cmd_git(ui, command_helper, args), + Command::Help(args) => help::cmd_help(ui, command_helper, args), Command::Init(args) => init::cmd_init(ui, command_helper, args), Command::Interdiff(args) => interdiff::cmd_interdiff(ui, command_helper, args), Command::Log(args) => log::cmd_log(ui, command_helper, args), diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index d2a4d709aa..7729f1ceb1 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -56,6 +56,7 @@ This document contains the help content for the `jj` command-line program. * [`jj git remote remove`↴](#jj-git-remote-remove) * [`jj git remote rename`↴](#jj-git-remote-rename) * [`jj git remote set-url`↴](#jj-git-remote-set-url) +* [`jj help`↴](#jj-help) * [`jj init`↴](#jj-init) * [`jj interdiff`↴](#jj-interdiff) * [`jj log`↴](#jj-log) @@ -126,6 +127,7 @@ To get started, see the tutorial at https://martinvonz.github.io/jj/latest/tutor * `file` — File operations * `fix` — Update files with formatting fixes or other changes * `git` — Commands for working with Git remotes and the underlying Git repo +* `help` — Print this message or the help of the given subcommand(s) * `init` — Create a new repo in the given directory * `interdiff` — Compare the changes of two commits * `log` — Show revision history @@ -1150,6 +1152,18 @@ Set the URL of a Git remote +## `jj help` + +Print this message or the help of the given subcommand(s) + +**Usage:** `jj help [COMMAND]...` + +###### **Arguments:** + +* `` — The command to show help + + + ## `jj init` Create a new repo in the given directory diff --git a/cli/tests/test_global_opts.rs b/cli/tests/test_global_opts.rs index b275f7109f..8fe2097295 100644 --- a/cli/tests/test_global_opts.rs +++ b/cli/tests/test_global_opts.rs @@ -530,6 +530,10 @@ fn test_early_args() { let stdout = test_env.jj_cmd_success(test_env.env_root(), &["--color=always", "help"]); insta::assert_snapshot!(stdout.lines().find(|l| l.contains("Commands:")).unwrap(), @"Commands:"); + // Check that early args are accepted after the help command + let stdout = test_env.jj_cmd_success(test_env.env_root(), &["help", "--color=always"]); + insta::assert_snapshot!(stdout.lines().find(|l| l.contains("Commands:")).unwrap(), @"Commands:"); + // Early args are parsed with clap's ignore_errors(), but there is a known // bug that causes defaults to be unpopulated. Test that the early args are // tolerant of this bug and don't cause a crash.