Skip to content

Commit

Permalink
Generate shell completion for uvx
Browse files Browse the repository at this point in the history
Create a dummy uvx top level command by grabbing the `uv tool uvx`
subcommand (hidden alias of `uv tool run`).

Global arguments need to be added to this dummy top level command; this
is accomplished by splitting Cli into subcommands and top level
arguments: then top level arguments can be added onto the dummy uvx top
level.

There is one conflict between global --isolated and uv tool run
--isolated; this must be handled (as in the code) to avoid debug asserts
when generating completion.
  • Loading branch information
bluss committed Sep 16, 2024
1 parent 3060fd2 commit 358f5fd
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 13 deletions.
7 changes: 7 additions & 0 deletions crates/uv-cli/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,13 @@ pub struct Cli {
#[command(subcommand)]
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)]
pub cache_args: Box<CacheArgs>,

Expand Down
48 changes: 35 additions & 13 deletions crates/uv/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use uv_cli::{
compat::CompatArgs, CacheCommand, CacheNamespace, Cli, Commands, PipCommand, PipNamespace,
ProjectCommand,
};
use uv_cli::{PythonCommand, PythonNamespace, ToolCommand, ToolNamespace};
use uv_cli::{PythonCommand, PythonNamespace, ToolCommand, ToolNamespace, TopLevelArgs};
#[cfg(feature = "self-update")]
use uv_cli::{SelfCommand, SelfNamespace, SelfUpdateArgs};
use uv_fs::CWD;
Expand Down Expand Up @@ -58,17 +58,17 @@ pub(crate) mod version;
#[instrument(skip_all)]
async fn run(cli: Cli) -> Result<ExitStatus> {
// 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();
}

// 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)?;
}

// 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 {
// Supports `--isolated` as its own argument, so we can't warn either way.
Commands::Tool(ToolNamespace {
Expand Down Expand Up @@ -106,15 +106,15 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
// 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,
// 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
.file_name()
.is_some_and(|file_name| file_name == "pyproject.toml")
{
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)?)
} else if deprecated_isolated || cli.no_config {
} else if deprecated_isolated || cli.top_level.no_config {
None
} else if matches!(&*cli.command, Commands::Tool(_)) {
// For commands that operate at the user-level, ignore local configuration.
Expand Down Expand Up @@ -175,10 +175,10 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
.combine(filesystem);

// 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.
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.
#[cfg(feature = "tracing-durations-export")]
Expand Down Expand Up @@ -687,7 +687,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
args.hash_checking,
args.python,
args.settings,
cli.no_config,
cli.top_level.no_config,
globals.python_preference,
globals.python_downloads,
globals.connectivity,
Expand Down Expand Up @@ -743,7 +743,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
args.settings.exclude_newer,
globals.concurrency,
globals.native_tls,
cli.no_config,
cli.top_level.no_config,
args.no_project,
&cache,
printer,
Expand All @@ -757,7 +757,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
run_command,
script,
globals,
cli.no_config,
cli.top_level.no_config,
filesystem,
cache,
printer,
Expand All @@ -777,7 +777,29 @@ 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 top level arguments
let mut uvx = Cli::command()
.find_subcommand("tool")
.unwrap()
.find_subcommand("uvx")
.unwrap()
.clone()
// avoid duplicating TopLevelArgs's help, version
.disable_help_flag(true)
.disable_version_flag(true)
.version("dummy_for_completion");

// like Args::augment_args but open coded 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 Down Expand Up @@ -974,7 +996,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
globals.python_downloads,
globals.native_tls,
globals.connectivity,
cli.no_config,
cli.top_level.no_config,
printer,
)
.await
Expand All @@ -1000,7 +1022,7 @@ async fn run(cli: Cli) -> Result<ExitStatus> {
commands::python_find(
args.request,
args.no_project,
cli.no_config,
cli.top_level.no_config,
args.system,
globals.python_preference,
&cache,
Expand Down

0 comments on commit 358f5fd

Please sign in to comment.