Skip to content

Commit

Permalink
Move uvx shell completion to uvx --generate-shell-completion (#7511)
Browse files Browse the repository at this point in the history
## 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
  • Loading branch information
bluss authored Sep 20, 2024
1 parent 99d354b commit 7a25a82
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 23 deletions.
19 changes: 19 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<clap_complete_command::Shell>,
}

#[derive(Args)]
Expand Down
47 changes: 24 additions & 23 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -780,30 +780,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
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 {
Expand All @@ -816,6 +793,30 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
_ => 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);
Expand Down
1 change: 1 addition & 0 deletions crates/uv/src/settings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ impl ToolRunSettings {
build,
refresh,
python,
generate_shell_completion: _,
} = args;

// If `--upgrade` was passed explicitly, warn.
Expand Down
18 changes: 18 additions & 0 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7a25a82

Please sign in to comment.