diff --git a/Cargo.lock b/Cargo.lock index dc975119d..d6ca803d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -820,6 +820,16 @@ dependencies = [ "clap", ] +[[package]] +name = "clap_complete_nushell" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1accf1b463dee0d3ab2be72591dccdab8bef314958340447c882c4c72acfe2a3" +dependencies = [ + "clap", + "clap_complete", +] + [[package]] name = "clap_derive" version = "4.5.4" @@ -3303,6 +3313,7 @@ dependencies = [ "clap", "clap-verbosity-flag", "clap_complete", + "clap_complete_nushell", "concat-idents", "console", "crossbeam-channel", diff --git a/Cargo.toml b/Cargo.toml index 3d6763d95..d900e5702 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -42,6 +42,7 @@ clap = { version = "4.5.4", default-features = false, features = [ ] } clap-verbosity-flag = "2.2.0" clap_complete = "4.5.2" +clap_complete_nushell = "4.5.2" concat-idents = "1.1.5" console = { version = "0.15.8", features = ["windows-console-colors"] } crossbeam-channel = "0.5.12" diff --git a/README.md b/README.md index 9385b1e14..d9bfa3889 100644 --- a/README.md +++ b/README.md @@ -107,23 +107,59 @@ winget install prefix-dev.pixi ### Autocompletion -To get autocompletion run: +To get autocompletion follow the instructions for your shell. +Afterwards, restart the shell or source the shell config file. -```shell -# On unix (MacOS or Linux), pick your shell (use `echo $SHELL` to find the shell you are using.): +#### Bash (default on most Linux systems) + +```bash echo 'eval "$(pixi completion --shell bash)"' >> ~/.bashrc +``` +#### Zsh (default on macOS) + +```zsh echo 'eval "$(pixi completion --shell zsh)"' >> ~/.zshrc -echo 'pixi completion --shell fish | source' >> ~/.config/fish/config.fish -echo 'eval (pixi completion --shell elvish | slurp)' >> ~/.elvish/rc.elv ``` -For PowerShell on Windows, run the following command and then restart the shell or source the shell config file: +#### PowerShell (pre-installed on all Windows systems) ```pwsh Add-Content -Path $PROFILE -Value '(& pixi completion --shell powershell) | Out-String | Invoke-Expression' ``` -And then restart the shell or source the shell config file. +If this fails with "Failure because no profile file exists", make sure your profile file exists. +If not, create it with: + +```PowerShell +New-Item -Path $PROFILE -ItemType File -Force +``` + +#### Fish + +```fish +echo 'pixi completion --shell fish | source' >> ~/.config/fish/config.fish +``` + +#### Nushell + +Add the following to the end of your Nushell env file (find it by running `$nu.env-path` in Nushell): + +```nushell +mkdir ~/.cache/pixi +pixi completion --shell nushell | save -f ~/.cache/pixi/completions.nu +``` + +And add the following to the end of your Nushell configuration (find it by running `$nu.config-path`): + +```nushell +use ~/.cache/pixi/completions.nu * +``` + +#### Elvish + +```elv +echo 'eval (pixi completion --shell elvish | slurp)' >> ~/.elvish/rc.elv +``` ### Distro Packages diff --git a/docs/index.md b/docs/index.md index d27a2fce3..bbe95990d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -47,29 +47,60 @@ You can find more options for the installation script [here](#installer-script-o ## Autocompletion -To get autocompletion run: +To get autocompletion follow the instructions for your shell. +Afterwards, restart the shell or source the shell config file. -=== "Linux & macOS" - ```shell - # Pick your shell (use `echo $SHELL` to find the shell you are using.): - echo 'eval "$(pixi completion --shell bash)"' >> ~/.bashrc - echo 'eval "$(pixi completion --shell zsh)"' >> ~/.zshrc - echo 'pixi completion --shell fish | source' >> ~/.config/fish/config.fish - echo 'eval (pixi completion --shell elvish | slurp)' >> ~/.elvish/rc.elv - ``` -=== "Windows" - PowerShell: - ```powershell - Add-Content -Path $PROFILE -Value '(& pixi completion --shell powershell) | Out-String | Invoke-Expression' +### Bash (default on most Linux systems) + +```bash +echo 'eval "$(pixi completion --shell bash)"' >> ~/.bashrc +``` +### Zsh (default on macOS) + +```zsh +echo 'eval "$(pixi completion --shell zsh)"' >> ~/.zshrc +``` + +### PowerShell (pre-installed on all Windows systems) + +```pwsh +Add-Content -Path $PROFILE -Value '(& pixi completion --shell powershell) | Out-String | Invoke-Expression' +``` + +!!! tip "Failure because no profile file exists" + Make sure your profile file exists, otherwise create it with: + ```PowerShell + New-Item -Path $PROFILE -ItemType File -Force ``` - !!! tip "Failure because no profile file exists" - Make sure your profile file exists, otherwise create it with: - ```PowerShell - New-Item -Path $PROFILE -ItemType File -Force - ``` -And then restart the shell or source the shell config file. + +### Fish + +```fish +echo 'pixi completion --shell fish | source' >> ~/.config/fish/config.fish +``` + +### Nushell + +Add the following to the end of your Nushell env file (find it by running `$nu.env-path` in Nushell): + +```nushell +mkdir ~/.cache/pixi +pixi completion --shell nushell | save -f ~/.cache/pixi/completions.nu +``` + +And add the following to the end of your Nushell configuration (find it by running `$nu.config-path`): + +```nushell +use ~/.cache/pixi/completions.nu * +``` + +### Elvish + +```elv +echo 'eval (pixi completion --shell elvish | slurp)' >> ~/.elvish/rc.elv +``` ## Alternative installation methods diff --git a/src/cli/completion.rs b/src/cli/completion.rs index 6d5d36d04..34b2b6c7e 100644 --- a/src/cli/completion.rs +++ b/src/cli/completion.rs @@ -1,5 +1,7 @@ use crate::cli::Args as CommandArgs; -use clap::{CommandFactory, Parser}; +use clap::{CommandFactory, Parser, ValueEnum}; +use clap_complete::{shells, Generator}; +use clap_complete_nushell::Nushell; use miette::IntoDiagnostic; use regex::Regex; use std::borrow::Cow; @@ -8,25 +10,62 @@ use std::io::Write; /// Generates a completion script for a shell. #[derive(Parser, Debug)] pub struct Args { - /// The shell to generate a completion script for (defaults to 'bash'). + /// The shell to generate a completion script for #[arg(short, long)] - shell: Option, + shell: Shell, +} + +/// Defines the shells for which we can provide completions +#[allow(clippy::enum_variant_names)] +#[derive(ValueEnum, Clone, Debug, Copy, Eq, Hash, PartialEq)] +enum Shell { + /// Bourne Again SHell (bash) + Bash, + /// Elvish shell + Elvish, + /// Friendly Interactive SHell (fish) + Fish, + /// Nushell + Nushell, + /// PowerShell + Powershell, + /// Z SHell (zsh) + Zsh, +} + +impl Generator for Shell { + fn file_name(&self, name: &str) -> String { + match self { + Shell::Bash => shells::Bash.file_name(name), + Shell::Elvish => shells::Elvish.file_name(name), + Shell::Fish => shells::Fish.file_name(name), + Shell::Nushell => Nushell.file_name(name), + Shell::Powershell => shells::PowerShell.file_name(name), + Shell::Zsh => shells::Zsh.file_name(name), + } + } + + fn generate(&self, cmd: &clap::Command, buf: &mut dyn std::io::Write) { + match self { + Shell::Bash => shells::Bash.generate(cmd, buf), + Shell::Elvish => shells::Elvish.generate(cmd, buf), + Shell::Fish => shells::Fish.generate(cmd, buf), + Shell::Nushell => Nushell.generate(cmd, buf), + Shell::Powershell => shells::PowerShell.generate(cmd, buf), + Shell::Zsh => shells::Zsh.generate(cmd, buf), + } + } } /// Generate completions for the pixi cli, and print those to the stdout pub(crate) fn execute(args: Args) -> miette::Result<()> { - let clap_shell = args - .shell - .or(clap_complete::Shell::from_env()) - .unwrap_or(clap_complete::Shell::Bash); - // Generate the original completion script. - let script = get_completion_script(clap_shell); + let script = get_completion_script(args.shell); // For supported shells, modify the script to include more context sensitive completions. - let script = match clap_shell { - clap_complete::Shell::Bash => replace_bash_completion(&script), - clap_complete::Shell::Zsh => replace_zsh_completion(&script), + let script = match args.shell { + Shell::Bash => replace_bash_completion(&script), + Shell::Zsh => replace_zsh_completion(&script), _ => Cow::Owned(script), }; @@ -39,7 +78,7 @@ pub(crate) fn execute(args: Args) -> miette::Result<()> { } /// Generate the completion script using clap_complete for a specified shell. -fn get_completion_script(shell: clap_complete::Shell) -> String { +fn get_completion_script(shell: Shell) -> String { let mut buf = vec![]; clap_complete::generate(shell, &mut CommandArgs::command(), "pixi", &mut buf); String::from_utf8(buf).expect("clap_complete did not generate a valid UTF8 script") @@ -181,7 +220,7 @@ _arguments "${_arguments_options[@]}" \ #[test] pub fn test_bash_completion_working_regex() { // Generate the original completion script. - let script = get_completion_script(clap_complete::Shell::Bash); + let script = get_completion_script(Shell::Bash); // Test if there was a replacement done on the clap generated completions assert_ne!(replace_bash_completion(&script), script); } @@ -189,7 +228,7 @@ _arguments "${_arguments_options[@]}" \ #[test] pub fn test_zsh_completion_working_regex() { // Generate the original completion script. - let script = get_completion_script(clap_complete::Shell::Zsh); + let script = get_completion_script(Shell::Zsh); // Test if there was a replacement done on the clap generated completions assert_ne!(replace_zsh_completion(&script), script); }