From cbf54c3415a0777a76ca5a5fcccd50437076c65a Mon Sep 17 00:00:00 2001 From: Miles Cranmer Date: Wed, 24 Apr 2024 18:21:56 +0100 Subject: [PATCH] Add nushell completion support (#1030) This PR adds clap-based [nushell](https://github.com/nushell/nushell/) completions. Within the clap repository there is the `clap_complete_nushell` crate: https://github.com/clap-rs/clap/tree/master/clap_complete_nushell. I'm not sure why they have put it in a separate crate, and this means it requires some extra logic, but it's not too bad and as you can see I didn't need to change much. This should be backwards compatible. I also update the docs and give an example.
Here what the output looks like (expand) ``` > rye self completion -s nushell ``` ```nushell module completions { # An Experimental Package Management Solution for Python export extern rye [ --env-file: string # Load one or more .env files --version # Print the version --help(-h) # Print help ] def "nu-complete rye add pin" [] { [ "equal" "tilde-equal" "greater-than-equal" ] } # Adds a Python package to this project export extern "rye add" [ ...requirements: string # The package to add as PEP 508 requirement string. e.g. 'flask==2.2.3' --git: string # Install the given package from this git repository --url: string # Install the given package from this URL --path: string # Install the given package from this local path --absolute # Force non interpolated absolute paths --tag: string # Install a specific tag --rev: string # Update to a specific git rev --branch: string # Update to a specific git branch --features: string # Adds a dependency with a specific feature --dev # Add this as dev dependency --excluded # Add this as an excluded dependency that will not be installed even if it's a sub dependency --optional: string # Add this to an optional dependency group --pre # Include pre-releases when finding a package version --pin: string@"nu-complete rye add pin" # Overrides the pin operator --sync # Runs `sync` even if auto-sync is disabled --no-sync # Does not run `sync` even if auto-sync is enabled --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help ] # Builds a package for distribution export extern "rye build" [ --sdist # Build a sdist --wheel # Build a wheel --all(-a) # Build all packages --package(-p): string # Build a specific package --out(-o): string # An output directory (defaults to `workspace/dist`) --pyproject: string # Use this pyproject.toml file --clean(-c) # Clean the output directory first --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help ] def "nu-complete rye config format" [] { [ "json" ] } # Reads or modifies the global `config.toml` file export extern "rye config" [ --show-path # Print the path to the config --format: string@"nu-complete rye config format" # Request parseable output format rather than lines --get: string # Reads a config key --set: string # Sets a config key to a string --set-int: string # Sets a config key to an integer --set-bool: string # Sets a config key to a bool --unset: string # Remove a config key --help(-h) # Print help (see more with '--help') ] # Fetches a Python interpreter for the local machine. This is an alias of `rye toolchain fetch` export extern "rye fetch" [ version?: string # The version of Python to fetch --force(-f) # Fetch the Python toolchain even if it is already installed --target-path: string # Fetches the Python toolchain into an explicit location rather --build-info # Fetches with build info --no-build-info # Fetches without build info --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help (see more with '--help') ] # Run the code formatter on the project export extern "rye fmt" [ ...paths: string # List of files or directories to limit the operation to --all(-a) # Perform the operation on all packages --package(-p): string # Perform the operation on a specific package --pyproject: string # Use this pyproject.toml file --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output ...extra_args: string # Extra arguments to ruff --check # Run format in check mode --help(-h) # Print help (see more with '--help') ] def "nu-complete rye init build_system" [] { [ "hatchling" "setuptools" "flit" "pdm" "maturin" ] } # Initialize a new or existing Python project with Rye export extern "rye init" [ path?: string # Where to place the project (defaults to current path) --lib # Generate a library project (default) --script # Generate an executable project --min-py: string # Minimal Python version supported by this project --py(-p): string # Python version to use for the virtualenv --no-readme # Do not create a readme --no-pin # Do not create .python-version file (requires-python will be used) --build-system: string@"nu-complete rye init build_system" # Which build system should be used (defaults to hatchling)? --license: string # Which license should be used (SPDX identifier)? --name: string # The name of the package --private # Set "Private :: Do Not Upload" classifier, used for private projects --no-import # Don't import from setup.cfg, setup.py, or requirements files --virtual # Initialize this as a virtual package --requirements(-r): string # Requirements files to initialize pyproject.toml with --dev-requirements: string # Development requirements files to initialize pyproject.toml with --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help (see more with '--help') ] # Installs a package as global tool. This is an alias of `rye tools install` export extern "rye install" [ requirement: string # The name of the package to install --git: string # Install the given package from this git repository --url: string # Install the given package from this URL --path: string # Install the given package from this local path --absolute # Force non interpolated absolute paths --tag: string # Install a specific tag --rev: string # Update to a specific git rev --branch: string # Update to a specific git branch --features: string # Adds a dependency with a specific feature --include-dep: string # Include scripts from a given dependency --extra-requirement: string # Additional dependencies to install that are not declared by the main package --python(-p): string # Optionally the Python version to use --force(-f) # Force install the package even if it's already there --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help ] # Updates the lockfiles without installing dependencies export extern "rye lock" [ --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --update: string # Update a specific package --update-all # Update all packages to the latest --pre # Update to pre-release versions --features: string # Extras/features to enable when locking the workspace --all-features # Enables all features --with-sources # Set to true to lock with sources in the lockfile --reset # Reset prior lock options --pyproject: string # Use this pyproject.toml file --help(-h) # Print help ] # Run the linter on the project export extern "rye lint" [ ...paths: string # List of files or directories to limit the operation to --all(-a) # Perform the operation on all packages --package(-p): string # Perform the operation on a specific package --pyproject: string # Use this pyproject.toml file --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output ...extra_args: string # Extra arguments to ruff --fix # Apply fixes --help(-h) # Print help (see more with '--help') ] # Builds and prints a PEP 508 requirement string from parts export extern "rye make-req" [ ...requirements: string # The package to add as PEP 508 requirement string. e.g. 'flask==2.2.3' --git: string # Install the given package from this git repository --url: string # Install the given package from this URL --path: string # Install the given package from this local path --absolute # Force non interpolated absolute paths --tag: string # Install a specific tag --rev: string # Update to a specific git rev --branch: string # Update to a specific git branch --features: string # Adds a dependency with a specific feature --help(-h) # Print help ] # Pins a Python version to this project export extern "rye pin" [ version: string # The version of Python to pin --relaxed # Issue a relaxed pin --no-update-requires-python # Prevent updating requires-python in the pyproject.toml --pyproject: string # Use this pyproject.toml file --help(-h) # Print help (see more with '--help') ] # Publish packages to a package repository export extern "rye publish" [ ...dist: string # The distribution files to upload to the repository (defaults to /dist/*) --repository(-r): string # The repository to publish to --repository-url: string # The repository url to publish to --username(-u): string # The username to authenticate to the repository with --token: string # An access token used for the upload --sign # Sign files to upload using GPG --identity(-i): string # GPG identity used to sign files --cert: string # Path to alternate CA bundle --skip-existing # Skip files that have already been published (only applies to repositories supporting this feature) --yes(-y) # Skip prompts --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help ] # Removes a package from this project export extern "rye remove" [ ...requirements: string # The packages to remove --dev # Remove this from dev dependencies --optional: string # Remove this from an optional dependency group --sync # Runs `sync` even if auto-sync is disabled --no-sync # Does not run `sync` even if auto-sync is enabled --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help ] # Runs a command installed into this package export extern "rye run" [ --list(-l) # List all commands --pyproject: string # Use this pyproject.toml file --help(-h) # Print help ] # Prints the current state of the project export extern "rye show" [ --installed-deps # Print the installed dependencies from the venv --pyproject: string # Use this pyproject.toml file --help(-h) # Print help ] # Updates the virtualenv based on the pyproject.toml export extern "rye sync" [ --force(-f) # Force the environment to be re-created --no-dev # Do not include dev dependencies --no-lock # Do not update the lockfile --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --update: string # Update a specific package --update-all # Update all packages to the latest --pre # Update to pre-release versions --features: string # Extras/features to enable when synching the workspace --all-features # Enables all features --with-sources # Set to true to lock with sources in the lockfile --pyproject: string # Use this pyproject.toml file --reset # Do not reuse (reset) prior lock options --help(-h) # Print help ] # Run the tests on the project export extern "rye test" [ --all(-a) # Perform the operation on all packages --package(-p): string # Perform the operation on a specific package --pyproject: string # Use this pyproject.toml file --no-capture(-s) --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output ...extra_args: string # Extra arguments to pytest --help(-h) # Print help (see more with '--help') ] # Helper utility to manage Python toolchains export extern "rye toolchain" [ --help(-h) # Print help ] # Fetches a Python interpreter for the local machine. This is an alias of `rye toolchain fetch` export extern "rye toolchain fetch" [ version?: string # The version of Python to fetch --force(-f) # Fetch the Python toolchain even if it is already installed --target-path: string # Fetches the Python toolchain into an explicit location rather --build-info # Fetches with build info --no-build-info # Fetches without build info --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help (see more with '--help') ] def "nu-complete rye toolchain list format" [] { [ "json" ] } # List all registered toolchains export extern "rye toolchain list" [ --include-downloadable # Also include non installed, but downloadable toolchains --format: string@"nu-complete rye toolchain list format" # Request parseable output format --help(-h) # Print help ] # Register a Python binary export extern "rye toolchain register" [ path: string # Path to the Python binary --name(-n): string # Name of the toolchain. If not provided a name is auto detected --help(-h) # Print help (see more with '--help') ] # Removes a toolchain export extern "rye toolchain remove" [ version: string # Name and version of the toolchain --force(-f) # Force removal even if the toolchain is in use --help(-h) # Print help ] # Print this message or the help of the given subcommand(s) export extern "rye toolchain help" [ ] # Fetches a Python interpreter for the local machine. This is an alias of `rye toolchain fetch` export extern "rye toolchain help fetch" [ ] # List all registered toolchains export extern "rye toolchain help list" [ ] # Register a Python binary export extern "rye toolchain help register" [ ] # Removes a toolchain export extern "rye toolchain help remove" [ ] # Print this message or the help of the given subcommand(s) export extern "rye toolchain help help" [ ] # Helper utility to manage global tools export extern "rye tools" [ --help(-h) # Print help ] # Installs a package as global tool. This is an alias of `rye tools install` export extern "rye tools install" [ requirement: string # The name of the package to install --git: string # Install the given package from this git repository --url: string # Install the given package from this URL --path: string # Install the given package from this local path --absolute # Force non interpolated absolute paths --tag: string # Install a specific tag --rev: string # Update to a specific git rev --branch: string # Update to a specific git branch --features: string # Adds a dependency with a specific feature --include-dep: string # Include scripts from a given dependency --extra-requirement: string # Additional dependencies to install that are not declared by the main package --python(-p): string # Optionally the Python version to use --force(-f) # Force install the package even if it's already there --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help ] # Uninstalls a global tool. This is an alias of `rye tools uninstall` export extern "rye tools uninstall" [ name: string # The package to uninstall --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help ] # List all registered tools export extern "rye tools list" [ --include-scripts(-s) # Show all the scripts installed by the tools --include-version(-v) # Show the version of tools --help(-h) # Print help ] # Print this message or the help of the given subcommand(s) export extern "rye tools help" [ ] # Installs a package as global tool. This is an alias of `rye tools install` export extern "rye tools help install" [ ] # Uninstalls a global tool. This is an alias of `rye tools uninstall` export extern "rye tools help uninstall" [ ] # List all registered tools export extern "rye tools help list" [ ] # Print this message or the help of the given subcommand(s) export extern "rye tools help help" [ ] # Rye self management export extern "rye self" [ --help(-h) # Print help ] def "nu-complete rye self completion shell" [] { [ "bash" "elvish" "fish" "power-shell" "zsh" "nushell" ] } # Generates a completion script for a shell export extern "rye self completion" [ --shell(-s): string@"nu-complete rye self completion shell" # The shell to generate a completion script for (defaults to 'bash') --help(-h) # Print help (see more with '--help') ] # Performs an update of rye export extern "rye self update" [ --version: string # Update to a specific version --tag: string # Update to a specific tag --rev: string # Update to a specific git rev --branch: string # Update to a specific git branch --force # Force reinstallation --help(-h) # Print help (see more with '--help') ] # Triggers the initial installation of Rye export extern "rye self install" [ --yes(-y) # Skip prompts --toolchain: string # Register a specific toolchain before bootstrap --toolchain-version: string # Use a specific toolchain version --modify-path # Always modify without asking the PATH environment variable --no-modify-path # Do not modify the PATH environment variable --help(-h) # Print help (see more with '--help') ] # Uninstalls rye again export extern "rye self uninstall" [ --yes(-y) # Skip safety check --help(-h) # Print help ] # Print this message or the help of the given subcommand(s) export extern "rye self help" [ ] # Generates a completion script for a shell export extern "rye self help completion" [ ] # Performs an update of rye export extern "rye self help update" [ ] # Triggers the initial installation of Rye export extern "rye self help install" [ ] # Uninstalls rye again export extern "rye self help uninstall" [ ] # Print this message or the help of the given subcommand(s) export extern "rye self help help" [ ] # Uninstalls a global tool. This is an alias of `rye tools uninstall` export extern "rye uninstall" [ name: string # The package to uninstall --verbose(-v) # Enables verbose diagnostics --quiet(-q) # Turns off all output --help(-h) # Print help ] def "nu-complete rye version bump" [] { [ "major" "minor" "patch" ] } # Get or set project version export extern "rye version" [ version?: string # The version to set --bump(-b): string@"nu-complete rye version bump" # The version bump to apply --help(-h) # Print help ] # Prints the currently installed packages export extern "rye list" [ --pyproject: string # Use this pyproject.toml file --help(-h) # Print help ] # The shell command was removed export extern "rye shell" [ --help(-h) # Print help ] # Print this message or the help of the given subcommand(s) export extern "rye help" [ ] # Adds a Python package to this project export extern "rye help add" [ ] # Builds a package for distribution export extern "rye help build" [ ] # Reads or modifies the global `config.toml` file export extern "rye help config" [ ] # Fetches a Python interpreter for the local machine. This is an alias of `rye toolchain fetch` export extern "rye help fetch" [ ] # Run the code formatter on the project export extern "rye help fmt" [ ] # Initialize a new or existing Python project with Rye export extern "rye help init" [ ] # Installs a package as global tool. This is an alias of `rye tools install` export extern "rye help install" [ ] # Updates the lockfiles without installing dependencies export extern "rye help lock" [ ] # Run the linter on the project export extern "rye help lint" [ ] # Builds and prints a PEP 508 requirement string from parts export extern "rye help make-req" [ ] # Pins a Python version to this project export extern "rye help pin" [ ] # Publish packages to a package repository export extern "rye help publish" [ ] # Removes a package from this project export extern "rye help remove" [ ] # Runs a command installed into this package export extern "rye help run" [ ] # Prints the current state of the project export extern "rye help show" [ ] # Updates the virtualenv based on the pyproject.toml export extern "rye help sync" [ ] # Run the tests on the project export extern "rye help test" [ ] # Helper utility to manage Python toolchains export extern "rye help toolchain" [ ] # Fetches a Python interpreter for the local machine. This is an alias of `rye toolchain fetch` export extern "rye help toolchain fetch" [ ] # List all registered toolchains export extern "rye help toolchain list" [ ] # Register a Python binary export extern "rye help toolchain register" [ ] # Removes a toolchain export extern "rye help toolchain remove" [ ] # Helper utility to manage global tools export extern "rye help tools" [ ] # Installs a package as global tool. This is an alias of `rye tools install` export extern "rye help tools install" [ ] # Uninstalls a global tool. This is an alias of `rye tools uninstall` export extern "rye help tools uninstall" [ ] # List all registered tools export extern "rye help tools list" [ ] # Rye self management export extern "rye help self" [ ] # Generates a completion script for a shell export extern "rye help self completion" [ ] # Performs an update of rye export extern "rye help self update" [ ] # Triggers the initial installation of Rye export extern "rye help self install" [ ] # Uninstalls rye again export extern "rye help self uninstall" [ ] # Uninstalls a global tool. This is an alias of `rye tools uninstall` export extern "rye help uninstall" [ ] # Get or set project version export extern "rye help version" [ ] # Prints the currently installed packages export extern "rye help list" [ ] # The shell command was removed export extern "rye help shell" [ ] # Print this message or the help of the given subcommand(s) export extern "rye help help" [ ] } export use completions * ```
--- Cargo.lock | 11 ++++++ docs/guide/commands/self/completion.md | 2 +- docs/guide/installation.md | 8 +++- rye/Cargo.toml | 1 + rye/src/cli/rye.rs | 51 ++++++++++++++++++++++++-- 5 files changed, 67 insertions(+), 6 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9748ae00e1..18b3bd8fe2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -322,6 +322,16 @@ dependencies = [ "clap", ] +[[package]] +name = "clap_complete_nushell" +version = "4.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d0e48e026ce7df2040239117d25e4e79714907420c70294a5ce4b6bbe6a7b6" +dependencies = [ + "clap", + "clap_complete", +] + [[package]] name = "clap_derive" version = "4.4.7" @@ -1652,6 +1662,7 @@ dependencies = [ "bzip2", "clap", "clap_complete", + "clap_complete_nushell", "configparser", "console", "ctrlc", diff --git a/docs/guide/commands/self/completion.md b/docs/guide/commands/self/completion.md index 7d8c63fc0e..2b3c56cf42 100644 --- a/docs/guide/commands/self/completion.md +++ b/docs/guide/commands/self/completion.md @@ -18,6 +18,6 @@ _no arguments_ * `-s, --shell `: The shell to generate a completion script for (defaults to 'bash') - [possible values: `bash`, `elvish`, `fish`, `powershell`, `zsh`] + [possible values: `bash`, `elvish`, `fish`, `powershell`, `zsh`, `nushell`] * `-h, --help`: Print help (see a summary with '-h') diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 25b62b8d51..1f883ee211 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -137,7 +137,7 @@ to learn more. ## Shell Completion -Rye supports generating completion scripts for Bash, Zsh, Fish or Powershell. Here are some common locations for each shell: +Rye supports generating completion scripts for Bash, Zsh, Fish, Powershell and Nushell. Here are some common locations for each shell: === "Bash" @@ -183,6 +183,12 @@ Rye supports generating completion scripts for Bash, Zsh, Fish or Powershell. He rye self completion -s powershell | Out-File -Encoding utf8 $PROFILE\..\Completions\rye_completion.ps1 ``` +=== "NuShell" + + ```nushell + rye self completion -s nushell | save --append $nu.env-path + ``` + ## Updating Rye To update rye to the latest version you can use `rye` itself: diff --git a/rye/Cargo.toml b/rye/Cargo.toml index 41bdab91e7..72431ba886 100644 --- a/rye/Cargo.toml +++ b/rye/Cargo.toml @@ -16,6 +16,7 @@ clap = { version = "4.3.5", default-features = false, features = [ "std", ] } clap_complete = "4.2.1" +clap_complete_nushell = "4.5.1" console = "0.15.7" curl = { version = "0.4.44", features = ["ssl", "static-curl", "static-ssl"] } flate2 = "1.0.25" diff --git a/rye/src/cli/rye.rs b/rye/src/cli/rye.rs index 3b6d59199c..ddcb6e9603 100644 --- a/rye/src/cli/rye.rs +++ b/rye/src/cli/rye.rs @@ -7,8 +7,9 @@ use std::sync::Arc; use std::{env, fs}; use anyhow::{anyhow, bail, Context, Error}; -use clap::{CommandFactory, Parser}; -use clap_complete::Shell; +use clap::{CommandFactory, Parser, ValueEnum}; +use clap_complete::{Generator, Shell}; +use clap_complete_nushell::Nushell; use console::style; use minijinja::render; use self_replace::self_delete_outside_path; @@ -52,12 +53,54 @@ pub struct Args { command: SubCommand, } +#[derive(Clone, Debug, ValueEnum)] +enum ShellCompletion { + /// Bourne Again SHell (bash) + Bash, + /// Elvish shell + Elvish, + /// Friendly Interactive SHell (fish) + Fish, + /// PowerShell + PowerShell, + /// Z SHell (zsh) + Zsh, + /// Nushell + Nushell, +} + +impl Generator for ShellCompletion { + /// Generate the file name for the completion script. + fn file_name(&self, name: &str) -> String { + match self { + ShellCompletion::Nushell => Nushell.file_name(name), + ShellCompletion::Bash => Shell::Bash.file_name(name), + ShellCompletion::Elvish => Shell::Elvish.file_name(name), + ShellCompletion::Fish => Shell::Fish.file_name(name), + ShellCompletion::PowerShell => Shell::PowerShell.file_name(name), + ShellCompletion::Zsh => Shell::Zsh.file_name(name), + } + } + + /// Generate the completion script for the shell. + fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) { + match self { + ShellCompletion::Nushell => Nushell.generate(cmd, buf), + ShellCompletion::Bash => Shell::Bash.generate(cmd, buf), + ShellCompletion::Elvish => Shell::Elvish.generate(cmd, buf), + ShellCompletion::Fish => Shell::Fish.generate(cmd, buf), + ShellCompletion::PowerShell => Shell::PowerShell.generate(cmd, buf), + ShellCompletion::Zsh => Shell::Zsh.generate(cmd, buf), + } + } +} + /// Generates a completion script for a shell. #[derive(Parser, Debug)] pub struct CompletionCommand { /// The shell to generate a completion script for (defaults to 'bash'). #[arg(short, long)] - shell: Option, + shell: Option, } /// Performs an update of rye. @@ -180,7 +223,7 @@ pub fn execute(cmd: Args) -> Result<(), Error> { fn completion(args: CompletionCommand) -> Result<(), Error> { clap_complete::generate( - args.shell.unwrap_or(Shell::Bash), + args.shell.unwrap_or(ShellCompletion::Bash), &mut super::Args::command(), "rye", &mut std::io::stdout(),