From 5c4f586558372155c8d570ee2f5f11d9a83827bf Mon Sep 17 00:00:00 2001 From: cojmeister <51219103+cojmeister@users.noreply.github.com> Date: Wed, 13 Aug 2025 10:27:08 +0300 Subject: [PATCH] Added completions for NuShell (#513) * Added a CompletionShell enum and implemented required methods for it * CompletionsOpts now uses CompletionShell enum to be able to call NuShell as well * CompletionsOpts now uses CompletionShell enum to be able to call NuShell as well * CompletionShell implements the generate method - simplifies the code * Added clap_complete_nushell as a dependency * Updated readme * Added nushell completion * Cargo fmt errors --- CHANGELOG.md | 1 + Cargo.lock | 11 +++++++ Cargo.toml | 1 + README.md | 8 +++-- src/cli.rs | 4 +-- src/completion_shell.rs | 72 +++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + src/main.rs | 3 +- 8 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 src/completion_shell.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 27bdd8f..a787a91 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Added option to specify Crosstool-NG version, using `-c` or `--crosstools-toolchain-version` +- Added an option for [NuShell](https://www.nushell.sh/) completion ### Fixed diff --git a/Cargo.lock b/Cargo.lock index ccafd42..d7b4e7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -292,6 +292,16 @@ dependencies = [ "clap", ] +[[package]] +name = "clap_complete_nushell" +version = "4.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a0c951694691e65bf9d421d597d68416c22de9632e884c28412cb8cd8b73dce" +dependencies = [ + "clap", + "clap_complete", +] + [[package]] name = "clap_derive" version = "4.5.41" @@ -597,6 +607,7 @@ dependencies = [ "bytes", "clap", "clap_complete", + "clap_complete_nushell", "directories", "env_logger", "flate2", diff --git a/Cargo.toml b/Cargo.toml index 1dd077f..f307315 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -18,6 +18,7 @@ async-trait = "0.1.88" bytes = "1.10.1" clap = { version = "4.5.38", features = ["derive", "env"] } clap_complete = "4.5.50" +clap_complete_nushell = "4.5.8" directories = "6.0.0" env_logger = "0.11.8" flate2 = "1.1.1" diff --git a/README.md b/README.md index 2940d52..5bca54e 100644 --- a/README.md +++ b/README.md @@ -116,13 +116,13 @@ Options: ``` ### Completions Subcommand -For detailed instructions on how to enable tab completion, see [Enable tab completion for Bash, Fish, Zsh, or PowerShell](#enable-tab-completion-for-bash-fish-zsh-or-powershell) section. +For detailed instructions on how to enable tab completion, see [Enable tab completion for Bash, Fish, Zsh, PowerShell or NuShell](#enable-tab-completion-for-bash-fish-zsh-or-powershell) section. ``` Usage: espup completions [OPTIONS] Arguments: - Shell to generate completions for [possible values: bash, elvish, fish, powershell, zsh] + Shell to generate completions for [possible values: bash, zsh, fish, powershell, elvish, nushell] Options: -l, --log-level Verbosity level of the logs [default: info] [possible values: debug, info, warn, error] @@ -295,6 +295,10 @@ $ espup completions zsh > ~/.zfunc/_espup $ espup completions powershell >> $PROFILE.CurrentUserCurrentHost # or $ espup completions powershell | Out-String | Invoke-Expression + +# NuShell +$ mkdir -p ~/.config/nushell/completions +$ espup completions nushell > ~/.config/nushell/completions/espup.nu ``` **Note**: you may need to restart your shell in order for the changes to take diff --git a/src/cli.rs b/src/cli.rs index 12fd978..06f621a 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,8 +1,8 @@ //! Command line interface. +use crate::completion_shell::CompletionShell; use crate::targets::{Target, parse_targets}; use clap::Parser; -use clap_complete::Shell; use std::{collections::HashSet, path::PathBuf}; #[derive(Debug, Parser)] @@ -11,7 +11,7 @@ pub struct CompletionsOpts { #[arg(short = 'l', long, default_value = "info", value_parser = ["debug", "info", "warn", "error"])] pub log_level: String, /// Shell to generate completions for. - pub shell: Shell, + pub shell: CompletionShell, } #[derive(Debug, Parser)] diff --git a/src/completion_shell.rs b/src/completion_shell.rs new file mode 100644 index 0000000..3a6f22a --- /dev/null +++ b/src/completion_shell.rs @@ -0,0 +1,72 @@ +use clap::ValueEnum; +use clap_complete::Shell; +use clap_complete_nushell::Nushell; +use std::fmt; +use std::str::FromStr; + +#[derive(Debug, Clone)] +pub enum CompletionShell { + Clap(Shell), + Nushell, +} + +impl CompletionShell { + pub fn generate(&self, cmd: &mut clap::Command, bin_name: &str, out: &mut dyn std::io::Write) { + match self { + CompletionShell::Clap(sh) => { + clap_complete::generate(*sh, cmd, bin_name, out); + } + CompletionShell::Nushell => { + clap_complete::generate(Nushell, cmd, bin_name, out); + } + } + } +} + +impl ValueEnum for CompletionShell { + fn value_variants<'a>() -> &'a [Self] { + use Shell::*; + // Static list of variants, one for each possible shell + const VARIANTS: &[CompletionShell] = &[ + CompletionShell::Clap(Bash), + CompletionShell::Clap(Zsh), + CompletionShell::Clap(Fish), + CompletionShell::Clap(PowerShell), + CompletionShell::Clap(Elvish), + CompletionShell::Nushell, + ]; + VARIANTS + } + + fn to_possible_value(&self) -> Option { + Some(match self { + CompletionShell::Clap(sh) => sh.to_possible_value()?, + CompletionShell::Nushell => clap::builder::PossibleValue::new("nushell"), + }) + } +} + +impl fmt::Display for CompletionShell { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CompletionShell::Clap(shell) => write!(f, "{shell}"), + CompletionShell::Nushell => write!(f, "nushell"), + } + } +} + +impl FromStr for CompletionShell { + type Err = String; + + fn from_str(input: &str) -> Result { + match input.to_ascii_lowercase().as_str() { + "bash" | "zsh" | "fish" | "powershell" | "elvish" => input + .parse::() + .map(CompletionShell::Clap) + .map_err(|e| e.to_string()), + + "nushell" => Ok(CompletionShell::Nushell), + _ => Err(format!("unsupported shell: {input}")), + } + } +} diff --git a/src/lib.rs b/src/lib.rs index c4f394d..54fa3a8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,5 @@ pub mod cli; +mod completion_shell; pub mod env; pub mod error; pub mod host_triple; diff --git a/src/main.rs b/src/main.rs index 922e3d4..2cba005 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,7 +43,8 @@ async fn completions(args: CompletionsOpts) -> Result<()> { info!("Generating completions for {} shell", args.shell); - clap_complete::generate(args.shell, &mut Cli::command(), "espup", &mut stdout()); + args.shell + .generate(&mut Cli::command(), "espup", &mut stdout()); info!("Completions successfully generated!");