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

feat: Add nushell completion #1599

Merged
merged 5 commits into from
Jul 16, 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
11 changes: 11 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
50 changes: 43 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
69 changes: 50 additions & 19 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
69 changes: 54 additions & 15 deletions src/cli/completion.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<clap_complete::Shell>,
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),
};

Expand All @@ -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")
Expand Down Expand Up @@ -181,15 +220,15 @@ _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);
}

#[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);
}
Expand Down
Loading