mirror of
https://github.com/astral-sh/uv.git
synced 2025-12-10 11:59:07 +00:00
Generate shell completion for uvx (#7388)
## Summary Generate shell completion for uvx. Create a `uvx` toplevel command just for completion by combining `uv tool uvx` (hidden alias for `uv tool run`) with global arguments. This explicit combination is needed otherwise global arguments are missing (if they are missing, clap debug assertions fail when `uv tool run` arguments refer to global arguments in directives like conflicts with). Fixes #7258 ## Test Plan - Tested using bash using `eval "$(cargo run --bin uv generate-shell-completion bash)"`
This commit is contained in:
parent
d1c7cb8bc2
commit
e9378be919
3 changed files with 62 additions and 13 deletions
|
|
@ -76,6 +76,13 @@ pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
pub command: Box<Commands>,
|
pub command: Box<Commands>,
|
||||||
|
|
||||||
|
#[command(flatten)]
|
||||||
|
pub top_level: TopLevelArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Parser)]
|
||||||
|
#[command(disable_help_flag = true, disable_version_flag = true)]
|
||||||
|
pub struct TopLevelArgs {
|
||||||
#[command(flatten)]
|
#[command(flatten)]
|
||||||
pub cache_args: Box<CacheArgs>,
|
pub cache_args: Box<CacheArgs>,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ use uv_cli::{
|
||||||
compat::CompatArgs, CacheCommand, CacheNamespace, Cli, Commands, PipCommand, PipNamespace,
|
compat::CompatArgs, CacheCommand, CacheNamespace, Cli, Commands, PipCommand, PipNamespace,
|
||||||
ProjectCommand,
|
ProjectCommand,
|
||||||
};
|
};
|
||||||
use uv_cli::{PythonCommand, PythonNamespace, ToolCommand, ToolNamespace};
|
use uv_cli::{PythonCommand, PythonNamespace, ToolCommand, ToolNamespace, TopLevelArgs};
|
||||||
#[cfg(feature = "self-update")]
|
#[cfg(feature = "self-update")]
|
||||||
use uv_cli::{SelfCommand, SelfNamespace, SelfUpdateArgs};
|
use uv_cli::{SelfCommand, SelfNamespace, SelfUpdateArgs};
|
||||||
use uv_fs::CWD;
|
use uv_fs::CWD;
|
||||||
|
|
@ -58,17 +58,17 @@ pub(crate) mod version;
|
||||||
#[instrument(skip_all)]
|
#[instrument(skip_all)]
|
||||||
async fn run(cli: Cli) -> Result<ExitStatus> {
|
async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
// Enable flag to pick up warnings generated by workspace loading.
|
// Enable flag to pick up warnings generated by workspace loading.
|
||||||
if !cli.global_args.quiet {
|
if !cli.top_level.global_args.quiet {
|
||||||
uv_warnings::enable();
|
uv_warnings::enable();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Switch directories as early as possible.
|
// Switch directories as early as possible.
|
||||||
if let Some(directory) = cli.global_args.directory.as_ref() {
|
if let Some(directory) = cli.top_level.global_args.directory.as_ref() {
|
||||||
std::env::set_current_dir(directory)?;
|
std::env::set_current_dir(directory)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The `--isolated` argument is deprecated on preview APIs, and warns on non-preview APIs.
|
// The `--isolated` argument is deprecated on preview APIs, and warns on non-preview APIs.
|
||||||
let deprecated_isolated = if cli.global_args.isolated {
|
let deprecated_isolated = if cli.top_level.global_args.isolated {
|
||||||
match &*cli.command {
|
match &*cli.command {
|
||||||
// Supports `--isolated` as its own argument, so we can't warn either way.
|
// Supports `--isolated` as its own argument, so we can't warn either way.
|
||||||
Commands::Tool(ToolNamespace {
|
Commands::Tool(ToolNamespace {
|
||||||
|
|
@ -106,7 +106,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
// If found, this file is combined with the user configuration file.
|
// If found, this file is combined with the user configuration file.
|
||||||
// 3. The nearest configuration file (`uv.toml` or `pyproject.toml`) in the directory tree,
|
// 3. The nearest configuration file (`uv.toml` or `pyproject.toml`) in the directory tree,
|
||||||
// starting from the current directory.
|
// starting from the current directory.
|
||||||
let filesystem = if let Some(config_file) = cli.config_file.as_ref() {
|
let filesystem = if let Some(config_file) = cli.top_level.config_file.as_ref() {
|
||||||
if config_file
|
if config_file
|
||||||
.file_name()
|
.file_name()
|
||||||
.is_some_and(|file_name| file_name == "pyproject.toml")
|
.is_some_and(|file_name| file_name == "pyproject.toml")
|
||||||
|
|
@ -114,7 +114,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
warn_user!("The `--config-file` argument expects to receive a `uv.toml` file, not a `pyproject.toml`. If you're trying to run a command from another project, use the `--directory` argument instead.");
|
warn_user!("The `--config-file` argument expects to receive a `uv.toml` file, not a `pyproject.toml`. If you're trying to run a command from another project, use the `--directory` argument instead.");
|
||||||
}
|
}
|
||||||
Some(FilesystemOptions::from_file(config_file)?)
|
Some(FilesystemOptions::from_file(config_file)?)
|
||||||
} else if deprecated_isolated || cli.no_config {
|
} else if deprecated_isolated || cli.top_level.no_config {
|
||||||
None
|
None
|
||||||
} else if matches!(&*cli.command, Commands::Tool(_)) {
|
} else if matches!(&*cli.command, Commands::Tool(_)) {
|
||||||
// For commands that operate at the user-level, ignore local configuration.
|
// For commands that operate at the user-level, ignore local configuration.
|
||||||
|
|
@ -175,10 +175,10 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
.combine(filesystem);
|
.combine(filesystem);
|
||||||
|
|
||||||
// Resolve the global settings.
|
// Resolve the global settings.
|
||||||
let globals = GlobalSettings::resolve(&cli.global_args, filesystem.as_ref());
|
let globals = GlobalSettings::resolve(&cli.top_level.global_args, filesystem.as_ref());
|
||||||
|
|
||||||
// Resolve the cache settings.
|
// Resolve the cache settings.
|
||||||
let cache_settings = CacheSettings::resolve(*cli.cache_args, filesystem.as_ref());
|
let cache_settings = CacheSettings::resolve(*cli.top_level.cache_args, filesystem.as_ref());
|
||||||
|
|
||||||
// Configure the `tracing` crate, which controls internal logging.
|
// Configure the `tracing` crate, which controls internal logging.
|
||||||
#[cfg(feature = "tracing-durations-export")]
|
#[cfg(feature = "tracing-durations-export")]
|
||||||
|
|
@ -687,7 +687,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
args.hash_checking,
|
args.hash_checking,
|
||||||
args.python,
|
args.python,
|
||||||
args.settings,
|
args.settings,
|
||||||
cli.no_config,
|
cli.top_level.no_config,
|
||||||
globals.python_preference,
|
globals.python_preference,
|
||||||
globals.python_downloads,
|
globals.python_downloads,
|
||||||
globals.connectivity,
|
globals.connectivity,
|
||||||
|
|
@ -743,7 +743,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
args.settings.exclude_newer,
|
args.settings.exclude_newer,
|
||||||
globals.concurrency,
|
globals.concurrency,
|
||||||
globals.native_tls,
|
globals.native_tls,
|
||||||
cli.no_config,
|
cli.top_level.no_config,
|
||||||
args.no_project,
|
args.no_project,
|
||||||
&cache,
|
&cache,
|
||||||
printer,
|
printer,
|
||||||
|
|
@ -757,7 +757,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
run_command,
|
run_command,
|
||||||
script,
|
script,
|
||||||
globals,
|
globals,
|
||||||
cli.no_config,
|
cli.top_level.no_config,
|
||||||
filesystem,
|
filesystem,
|
||||||
cache,
|
cache,
|
||||||
printer,
|
printer,
|
||||||
|
|
@ -777,7 +777,30 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
Ok(ExitStatus::Success)
|
Ok(ExitStatus::Success)
|
||||||
}
|
}
|
||||||
Commands::GenerateShellCompletion(args) => {
|
Commands::GenerateShellCompletion(args) => {
|
||||||
|
// uv
|
||||||
args.shell.generate(&mut Cli::command(), &mut stdout());
|
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)
|
Ok(ExitStatus::Success)
|
||||||
}
|
}
|
||||||
Commands::Tool(ToolNamespace {
|
Commands::Tool(ToolNamespace {
|
||||||
|
|
@ -974,7 +997,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
globals.python_downloads,
|
globals.python_downloads,
|
||||||
globals.native_tls,
|
globals.native_tls,
|
||||||
globals.connectivity,
|
globals.connectivity,
|
||||||
cli.no_config,
|
cli.top_level.no_config,
|
||||||
printer,
|
printer,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
|
|
@ -1000,7 +1023,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
|
||||||
commands::python_find(
|
commands::python_find(
|
||||||
args.request,
|
args.request,
|
||||||
args.no_project,
|
args.no_project,
|
||||||
cli.no_config,
|
cli.top_level.no_config,
|
||||||
args.system,
|
args.system,
|
||||||
globals.python_preference,
|
globals.python_preference,
|
||||||
&cache,
|
&cache,
|
||||||
|
|
|
||||||
|
|
@ -181,6 +181,25 @@ To enable shell autocompletion for uv commands, run one of the following:
|
||||||
|
|
||||||
Then restart the shell or source the shell config file.
|
Then restart the shell or source the shell config file.
|
||||||
|
|
||||||
|
You can also enable shell autocompletion for uvx by running the same commands, replacing `uv` with
|
||||||
|
`uvx`:
|
||||||
|
|
||||||
|
=== "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'
|
||||||
|
```
|
||||||
|
|
||||||
## Uninstallation
|
## Uninstallation
|
||||||
|
|
||||||
If you need to remove uv from your system, just remove the `uv` and `uvx` binaries:
|
If you need to remove uv from your system, just remove the `uv` and `uvx` binaries:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue