Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Generate shell completion for uvx #7388

Merged
merged 3 commits into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 {
bluss marked this conversation as resolved.
Show resolved Hide resolved
#[command(flatten)]
pub cache_args: Box<CacheArgs>,

Expand Down
49 changes: 36 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,30 @@ 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());

bluss marked this conversation as resolved.
Show resolved Hide resolved
Ok(ExitStatus::Success)
}
Commands::Tool(ToolNamespace {
Expand Down Expand Up @@ -974,7 +997,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 +1023,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
19 changes: 19 additions & 0 deletions docs/getting-started/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

You can also enable shell autocompletion for uvx by running the same commands, replacing `uv` with
Copy link
Contributor

@ilyagr ilyagr Sep 17, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not the author, but my understanding is that uv generate-shell-completion bash currently generates the completions for both uv and uvx. I'm not sure that uvx generate-shell-completion bash will work at all.

If my understanding is correct, these docs are misleading, and there isn't really need for docs. The existing instructions will cause completion to work for both uv and uvx.

It wouldn't be hard to modify this code to generate the completions separately, e.g. something like uv generate-shell-completion --uvx-only bash. Update: (But I don't think there's a real need for this until somebody asks for it)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You’re right, I misread the instructions in the summary. Will revert in the morning (or anyone is welcome to PR).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could expand the docs with the statement that uv and uvx completions are included in the same output.

Not sure if it's worth providing further hints, like if you use a per command file for completions, duplicate the output in both files or symlink uvx -> uv. Could be discussed in future docs changes.

`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

If you need to remove uv from your system, just remove the `uv` and `uvx` binaries:
Expand Down
Loading