From 0f2715203f8272a2740beee5d583588cf48c4ff5 Mon Sep 17 00:00:00 2001 From: jyn Date: Sun, 4 Feb 2024 14:49:20 -0500 Subject: [PATCH] cli: use a positional argument for `jj util completion` this has two main advantages: - it makes it clear that the shells are mutually exclusive - it allows us to extend the command with shell-specific options in the future if necessary as a happy accident, it also adds support for `elvish` and `powershell`. for backwards compatibility, this also keeps the existing options as hidden flags. i am not super happy with how the new help looks; the instructions for setting up the shell are squished together and IMO a little harder to read. i'm open to suggestions. --- CHANGELOG.md | 6 +++ cli/src/commands/util.rs | 71 +++++++++++++++++++----------- cli/tests/cli-reference@.md.snap | 23 ++++++++-- cli/tests/test_shell_completion.rs | 30 +++++++++++++ 4 files changed, 100 insertions(+), 30 deletions(-) create mode 100644 cli/tests/test_shell_completion.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index f8c8cefce..e553c5d57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -39,8 +39,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 of `R` for "Removed". @joyously pointed out that `R` could also mean "Renamed". +* `jj util completion` now takes the shell as a positional argument, not a flag. + the previous behavior is deprecated, but supported for now. it will be removed + in the future. + ### New features +* `jj util completion` now supports powershell and elvish. + * Official binaries for macOS running on Apple Silicon (`aarch64-apple-darwin`) are now available, alongside the existing macOS x86 binaries. diff --git a/cli/src/commands/util.rs b/cli/src/commands/util.rs index aefae92c1..f6e3ef445 100644 --- a/cli/src/commands/util.rs +++ b/cli/src/commands/util.rs @@ -17,6 +17,7 @@ use std::slice; use std::time::{Duration, SystemTime}; use clap::Subcommand; +use clap_complete::Shell; use jj_lib::repo::Repo; use tracing::instrument; @@ -34,30 +35,26 @@ pub(crate) enum UtilCommand { } /// Print a command-line-completion script +/// +/// Apply it by running this: +/// bash: source <(jj util completion) +/// fish: jj util completion --fish | source +/// zsh: +/// autoload -U compinit +/// compinit +/// source <(jj util completion --zsh) #[derive(clap::Args, Clone, Debug)] +#[command(verbatim_doc_comment)] pub(crate) struct UtilCompletionArgs { - /// Print a completion script for Bash (default) - /// - /// Apply it by running this: - /// - /// source <(jj util completion) - #[arg(long, verbatim_doc_comment)] + shell: Option, + /// Deprecated. Use the SHELL positional argument instead. + #[arg(long, hide = true)] bash: bool, - /// Print a completion script for Fish - /// - /// Apply it by running this: - /// - /// jj util completion --fish | source - #[arg(long, verbatim_doc_comment)] + /// Deprecated. Use the SHELL positional argument instead. + #[arg(long, hide = true)] fish: bool, - /// Print a completion script for Zsh - /// - /// Apply it by running this: - /// - /// autoload -U compinit - /// compinit - /// source <(jj util completion --zsh) - #[arg(long, verbatim_doc_comment)] + /// Deprecated. Use the SHELL positional argument instead. + #[arg(long, hide = true)] zsh: bool, } @@ -109,13 +106,35 @@ fn cmd_util_completion( args: &UtilCompletionArgs, ) -> Result<(), CommandError> { let mut app = command.app().clone(); + let warn = |shell| { + writeln!( + ui.warning(), + "warning: `jj util completion --{shell}` will be removed in a future version, and \ + this will be a hard error" + ) + }; let mut buf = vec![]; - let shell = if args.zsh { - clap_complete::Shell::Zsh - } else if args.fish { - clap_complete::Shell::Fish - } else { - clap_complete::Shell::Bash + let shell = match (args.shell, args.fish, args.zsh, args.bash) { + (Some(s), false, false, false) => s, + // allow `--fish` and `--zsh` for back-compat, but don't allow them to be combined + (None, true, false, false) => { + warn("fish")?; + Shell::Fish + } + (None, false, true, false) => { + warn("zsh")?; + Shell::Zsh + } + // default to bash for back-compat. TODO: consider making `shell` a required argument + (None, false, false, _) => { + warn("bash")?; + Shell::Bash + } + _ => { + return Err(user_error( + "cannot generate completion for multiple shells at once", + )) + } }; clap_complete::generate(shell, &mut app, "jj", &mut buf); ui.stdout_formatter().write_all(&buf)?; diff --git a/cli/tests/cli-reference@.md.snap b/cli/tests/cli-reference@.md.snap index 09efbe8a6..e2d392921 100644 --- a/cli/tests/cli-reference@.md.snap +++ b/cli/tests/cli-reference@.md.snap @@ -1708,19 +1708,34 @@ Infrequently used commands such as for generating shell completions Print a command-line-completion script -**Usage:** `jj util completion [OPTIONS]` +Apply it by running this: +bash: source <(jj util completion) +fish: jj util completion --fish | source +zsh: +autoload -U compinit +compinit +source <(jj util completion --zsh) + +**Usage:** `jj util completion [SHELL]` + +###### **Arguments:** + +* `` + + Possible values: `bash`, `elvish`, `fish`, `powershell`, `zsh` + ###### **Options:** -* `--bash` — Print a completion script for Bash (default) +* `--bash` — Deprecated. Use the SHELL positional argument instead Possible values: `true`, `false` -* `--fish` — Print a completion script for Fish +* `--fish` — Deprecated. Use the SHELL positional argument instead Possible values: `true`, `false` -* `--zsh` — Print a completion script for Zsh +* `--zsh` — Deprecated. Use the SHELL positional argument instead Possible values: `true`, `false` diff --git a/cli/tests/test_shell_completion.rs b/cli/tests/test_shell_completion.rs new file mode 100644 index 000000000..29da5ba79 --- /dev/null +++ b/cli/tests/test_shell_completion.rs @@ -0,0 +1,30 @@ +// 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 crate::common::TestEnvironment; + +pub mod common; + +#[test] +fn test_deprecated_flags() { + let test_env = TestEnvironment::default(); + let (stdout, stderr) = + test_env.jj_cmd_ok(test_env.env_root(), &["util", "completion", "--bash"]); + assert_eq!( + stderr, + "warning: `jj util completion --bash` will be removed in a future version, and this will \ + be a hard error\n" + ); + assert!(stdout.contains("COMPREPLY")); +}