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.
This commit is contained in:
jyn 2024-02-04 14:49:20 -05:00
parent 0773cefe32
commit 0f2715203f
4 changed files with 100 additions and 30 deletions

View file

@ -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 of `R` for "Removed". @joyously pointed out that `R` could also mean
"Renamed". "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 ### New features
* `jj util completion` now supports powershell and elvish.
* Official binaries for macOS running on Apple Silicon (`aarch64-apple-darwin`) * Official binaries for macOS running on Apple Silicon (`aarch64-apple-darwin`)
are now available, alongside the existing macOS x86 binaries. are now available, alongside the existing macOS x86 binaries.

View file

@ -17,6 +17,7 @@ use std::slice;
use std::time::{Duration, SystemTime}; use std::time::{Duration, SystemTime};
use clap::Subcommand; use clap::Subcommand;
use clap_complete::Shell;
use jj_lib::repo::Repo; use jj_lib::repo::Repo;
use tracing::instrument; use tracing::instrument;
@ -34,30 +35,26 @@ pub(crate) enum UtilCommand {
} }
/// Print a command-line-completion script /// 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)] #[derive(clap::Args, Clone, Debug)]
#[command(verbatim_doc_comment)]
pub(crate) struct UtilCompletionArgs { pub(crate) struct UtilCompletionArgs {
/// Print a completion script for Bash (default) shell: Option<Shell>,
/// /// Deprecated. Use the SHELL positional argument instead.
/// Apply it by running this: #[arg(long, hide = true)]
///
/// source <(jj util completion)
#[arg(long, verbatim_doc_comment)]
bash: bool, bash: bool,
/// Print a completion script for Fish /// Deprecated. Use the SHELL positional argument instead.
/// #[arg(long, hide = true)]
/// Apply it by running this:
///
/// jj util completion --fish | source
#[arg(long, verbatim_doc_comment)]
fish: bool, fish: bool,
/// Print a completion script for Zsh /// Deprecated. Use the SHELL positional argument instead.
/// #[arg(long, hide = true)]
/// Apply it by running this:
///
/// autoload -U compinit
/// compinit
/// source <(jj util completion --zsh)
#[arg(long, verbatim_doc_comment)]
zsh: bool, zsh: bool,
} }
@ -109,13 +106,35 @@ fn cmd_util_completion(
args: &UtilCompletionArgs, args: &UtilCompletionArgs,
) -> Result<(), CommandError> { ) -> Result<(), CommandError> {
let mut app = command.app().clone(); 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 mut buf = vec![];
let shell = if args.zsh { let shell = match (args.shell, args.fish, args.zsh, args.bash) {
clap_complete::Shell::Zsh (Some(s), false, false, false) => s,
} else if args.fish { // allow `--fish` and `--zsh` for back-compat, but don't allow them to be combined
clap_complete::Shell::Fish (None, true, false, false) => {
} else { warn("fish")?;
clap_complete::Shell::Bash 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); clap_complete::generate(shell, &mut app, "jj", &mut buf);
ui.stdout_formatter().write_all(&buf)?; ui.stdout_formatter().write_all(&buf)?;

View file

@ -1708,19 +1708,34 @@ Infrequently used commands such as for generating shell completions
Print a command-line-completion script 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:**
* `<SHELL>`
Possible values: `bash`, `elvish`, `fish`, `powershell`, `zsh`
###### **Options:** ###### **Options:**
* `--bash` — Print a completion script for Bash (default) * `--bash` — Deprecated. Use the SHELL positional argument instead
Possible values: `true`, `false` Possible values: `true`, `false`
* `--fish` — Print a completion script for Fish * `--fish` — Deprecated. Use the SHELL positional argument instead
Possible values: `true`, `false` Possible values: `true`, `false`
* `--zsh` — Print a completion script for Zsh * `--zsh` — Deprecated. Use the SHELL positional argument instead
Possible values: `true`, `false` Possible values: `true`, `false`

View file

@ -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"));
}