From 7a25a82fc908da5387e804691f2344fe731bb59f Mon Sep 17 00:00:00 2001 From: bluss Date: Fri, 20 Sep 2024 03:27:25 +0200 Subject: [PATCH] Move uvx shell completion to uvx --generate-shell-completion (#7511) ## Summary Because a problem was found with Powershell and combining the generated completion scripts for uv and uvx, let's try separating uv and uvx command completion scripts. The generated powershell script template can be seen in clap_complete source, and it starts with `using` directives, which makes it impossible (apparently) to concatenate two of those script outputs. As a side effect, this is available under `uv tool run --generate-shell-completion` too. Fixes #7482 ## Test Plan - `eval "$(cargo run --bin uvx -- --generate-shell-completion bash)"` - Test Powershell --- .github/workflows/ci.yml | 19 +++++++++++ crates/uv-cli/src/lib.rs | 3 ++ crates/uv/src/lib.rs | 47 ++++++++++++++-------------- crates/uv/src/settings.rs | 1 + docs/getting-started/installation.md | 18 +++++++++++ 5 files changed, 65 insertions(+), 23 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2f27c1687..3cc2cb9fa 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -197,6 +197,13 @@ jobs: $uv venv $uv pip install ruff + - name: "Smoke test completion" + run: | + uv="./target/debug/uv" + uvx="./target/debug/uvx" + eval "$($uv generate-shell-completion bash)" + eval "$($uvx --generate-shell-completion bash)" + cargo-test-macos: timeout-minutes: 10 needs: determine_changes @@ -296,6 +303,18 @@ jobs: uv venv uv pip install ruff + - name: "Smoke test completion" + working-directory: ${{ env.UV_WORKSPACE }} + shell: powershell + env: + # Avoid debug build stack overflows. + UV_STACK_SIZE: 2000000 + run: | + Set-Alias -Name uv -Value ./target/debug/uv + Set-Alias -Name uvx -Value ./target/debug/uvx + (& uv generate-shell-completion powershell) | Out-String | Invoke-Expression + (& uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression + # Separate jobs for the nightly crate windows-trampoline-check: timeout-minutes: 10 diff --git a/crates/uv-cli/src/lib.rs b/crates/uv-cli/src/lib.rs index e9515a9e1..587f0d833 100644 --- a/crates/uv-cli/src/lib.rs +++ b/crates/uv-cli/src/lib.rs @@ -3247,6 +3247,9 @@ pub struct ToolRunArgs { /// By default, environment modifications are omitted, but enabled under `--verbose`. #[arg(long, env = "UV_SHOW_RESOLUTION", value_parser = clap::builder::BoolishValueParser::new(), hide = true)] pub show_resolution: bool, + + #[arg(long, hide = true)] + pub generate_shell_completion: Option, } #[derive(Args)] diff --git a/crates/uv/src/lib.rs b/crates/uv/src/lib.rs index 80b788ec2..56e74e3be 100644 --- a/crates/uv/src/lib.rs +++ b/crates/uv/src/lib.rs @@ -780,30 +780,7 @@ async fn run(cli: Cli) -> Result { Ok(ExitStatus::Success) } Commands::GenerateShellCompletion(args) => { - // uv args.shell.generate(&mut Cli::command(), &mut stdout()); - - // uvx: combine `uv tool uvx` with the top-level arguments - let mut uvx = Cli::command() - .find_subcommand("tool") - .unwrap() - .find_subcommand("uvx") - .unwrap() - .clone() - // Avoid duplicating the `--help` and `--version` flags from the top-level arguments. - .disable_help_flag(true) - .disable_version_flag(true) - .version(env!("CARGO_PKG_VERSION")); - - // Copy the top-level arguments into the `uvx` command. (Like `Args::augment_args`, but - // expanded to skip collisions.) - for arg in TopLevelArgs::command().get_arguments() { - if arg.get_id() != "isolated" { - uvx = uvx.arg(arg); - } - } - args.shell.generate(&mut uvx, &mut stdout()); - Ok(ExitStatus::Success) } Commands::Tool(ToolNamespace { @@ -816,6 +793,30 @@ async fn run(cli: Cli) -> Result { _ => unreachable!(), }; + if let Some(shell) = args.generate_shell_completion { + // uvx: combine `uv tool uvx` with the top-level arguments + let mut uvx = Cli::command() + .find_subcommand("tool") + .unwrap() + .find_subcommand("uvx") + .unwrap() + .clone() + // Avoid duplicating the `--help` and `--version` flags from the top-level arguments. + .disable_help_flag(true) + .disable_version_flag(true) + .version(env!("CARGO_PKG_VERSION")); + + // Copy the top-level arguments into the `uvx` command. (Like `Args::augment_args`, but + // expanded to skip collisions.) + for arg in TopLevelArgs::command().get_arguments() { + if arg.get_id() != "isolated" { + uvx = uvx.arg(arg); + } + } + shell.generate(&mut uvx, &mut stdout()); + return Ok(ExitStatus::Success); + } + // Resolve the settings from the command-line arguments and workspace configuration. let args = settings::ToolRunSettings::resolve(args, filesystem, invocation_source); show_settings!(args); diff --git a/crates/uv/src/settings.rs b/crates/uv/src/settings.rs index c7d1eb626..641e780a7 100644 --- a/crates/uv/src/settings.rs +++ b/crates/uv/src/settings.rs @@ -319,6 +319,7 @@ impl ToolRunSettings { build, refresh, python, + generate_shell_completion: _, } = args; // If `--upgrade` was passed explicitly, warn. diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 87e530987..34e81fa34 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -186,6 +186,24 @@ To enable shell autocompletion for uv commands, run one of the following: Add-Content -Path $PROFILE -Value '(& uv generate-shell-completion powershell) | Out-String | Invoke-Expression' ``` +To enable shell autocompletion for uvx, run one of the following: + +=== "Linux and macOS" + + ```bash + # Determine your shell (e.g., with `echo $SHELL`), then run one of: + echo 'eval "$(uvx --generate-shell-completion bash)"' >> ~/.bashrc + echo 'eval "$(uvx --generate-shell-completion zsh)"' >> ~/.zshrc + echo 'uvx --generate-shell-completion fish | source' >> ~/.config/fish/config.fish + echo 'eval (uvx --generate-shell-completion elvish | slurp)' >> ~/.elvish/rc.elv + ``` + +=== "Windows" + + ```powershell + Add-Content -Path $PROFILE -Value '(& uvx --generate-shell-completion powershell) | Out-String | Invoke-Expression' + ``` + Then restart the shell or source the shell config file. ## Uninstallation