From 290ab4f69040ae18f2767cd03e9409ba5be79d34 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Fri, 13 Oct 2023 14:44:26 +0200 Subject: [PATCH 1/9] wip: autocompletion of pixi run with tasks --- Cargo.lock | 1 + Cargo.toml | 1 + src/cli/completions.rs | 22 +++++++++++++++++ src/cli/mod.rs | 55 ++++++++++++++++++++++++++++-------------- src/cli/task.rs | 27 +++++++++++++++------ 5 files changed, 81 insertions(+), 25 deletions(-) create mode 100644 src/cli/completions.rs diff --git a/Cargo.lock b/Cargo.lock index 323025192..2d4a1f2bb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2061,6 +2061,7 @@ dependencies = [ "rattler_shell", "rattler_solve", "rattler_virtual_packages", + "regex", "reqwest", "serde", "serde_json", diff --git a/Cargo.toml b/Cargo.toml index 223aaf9c2..7db21909b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ rattler_repodata_gateway = { version = "0.10.0", default-features = false, featu rattler_shell = { version = "0.10.0", default-features = false, features = ["sysinfo"] } rattler_solve = { version = "0.10.0", default-features = false, features = ["resolvo"] } rattler_virtual_packages = { version = "0.10.0", default-features = false } +regex = "1.9.5" reqwest = { version = "0.11.20", default-features = false } serde = "1.0.188" serde_json = "1.0.107" diff --git a/src/cli/completions.rs b/src/cli/completions.rs new file mode 100644 index 000000000..5c43127b0 --- /dev/null +++ b/src/cli/completions.rs @@ -0,0 +1,22 @@ +pub(crate) const BASH_COMPLETION_REPLACEMENTS: (&str, &str) = ( + r#"(?s)pixi__run\) + opts="(.*?)" + if \[\[ \${cur} == -\* \|\| \${COMP_CWORD} -eq 2 \]\] ; then + COMPREPLY=\( \$(compgen -W "\${opts}" -- "\${cur}") \) + return 0 + fi"#, + r#"pixi__run) + opts="$1" + if [[ ${cur} == -* ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + elif [[ ${COMP_CWORD} -eq 2 ]]; then + + local tasks=$(pixi task list --summary 2> /dev/null) + + if [[ $? -eq 0 ]]; then + COMPREPLY=( $(compgen -W "${tasks}" -- "${cur}") ) + return 0 + fi + fi"#, +); diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 7d96ec341..1084998c3 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -5,12 +5,15 @@ use clap_complete; use clap_verbosity_flag::Verbosity; use miette::IntoDiagnostic; use rattler_shell::shell::{Shell, ShellEnum}; +use regex::Regex; use std::io::{IsTerminal, Write}; +use std::ops::{Deref, DerefMut}; use std::str::FromStr; use tracing_subscriber::{filter::LevelFilter, util::SubscriberInitExt, EnvFilter}; pub mod add; pub mod auth; +mod completions; pub mod global; pub mod info; pub mod init; @@ -74,30 +77,46 @@ fn completion(args: CompletionCommand) -> miette::Result<()> { .shell .or(clap_complete::Shell::from_env()) .unwrap_or(clap_complete::Shell::Bash); + + let mut script = b""; clap_complete::generate( clap_shell, &mut Args::command(), "pixi", - &mut std::io::stdout(), + &mut script, // &mut std::io::stdout(), ); - // Create PS1 overwrite command - // TODO: Also make this work for non bourne shells. - let mut script = String::new(); - let shell = ShellEnum::from_str(clap_shell.to_string().as_str()).into_diagnostic()?; - // Generate a shell agnostic command to add the PIXI_PROMPT to the PS1 variable. - shell - .set_env_var( - &mut script, - "PS1", - format!( - "{}{}", - shell.format_env_var("PIXI_PROMPT"), - shell.format_env_var("PS1") - ) - .as_str(), - ) - .unwrap(); + let pattern = r#"(?s)pixi__run\) + opts="(.*?)" + if \[\[ \$\{cur\} == -\\* \|\| \$\{COMP_CWORD\} -eq 2 \]\] ; then + COMPREPLY=\( \$(compgen -W "\$\{opts\}" -- "\$\{cur\}") \) + return 0 + fi"#; + + let replacement = r#"pixi__run) + opts="$1" + if [[ ${cur} == -* ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + elif [[ ${COMP_CWORD} -eq 2 ]]; then + + local tasks=$(pixi task list --summary 2> /dev/null) + + if [[ $? -eq 0 ]]; then + COMPREPLY=( $(compgen -W "${tasks}" -- "${cur}") ) + return 0 + fi + fi"#; + + match clap_shell { + clap_complete::Shell::Bash => { + // let (pattern, replacement) = completions::BASH_COMPLETION_REPLACEMENTS; + let re = Regex::new(pattern).unwrap(); + script = re.replace(script.as_str(), replacement); + } + _ => {} + } + // Just like the clap autocompletion code write directly to the stdout std::io::stdout() .write_all(script.as_bytes()) diff --git a/src/cli/task.rs b/src/cli/task.rs index bf277c87d..c8c894e02 100644 --- a/src/cli/task.rs +++ b/src/cli/task.rs @@ -21,7 +21,7 @@ pub enum Operation { /// List all tasks #[clap(alias = "l")] - List, + List(ListArgs), } #[derive(Parser, Debug)] @@ -74,6 +74,12 @@ pub struct AliasArgs { pub platform: Option, } +#[derive(Parser, Debug, Clone)] +pub struct ListArgs { + #[arg(long, short)] + pub summary: bool, +} + impl From for Task { fn from(value: AddArgs) -> Self { let depends_on = value.depends_on.unwrap_or_default(); @@ -202,16 +208,23 @@ pub fn execute(args: Args) -> miette::Result<()> { task, ); } - Operation::List => { + Operation::List(args) => { let tasks = project.task_names(Some(Platform::current())); if tasks.is_empty() { eprintln!("No tasks found",); } else { - let mut formatted = String::new(); - for name in tasks { - formatted.push_str(&format!("* {}\n", console::style(name).bold(),)); - } - eprintln!("{}", formatted); + let formatted: String = tasks + .iter() + .map(|name| { + if args.summary { + format!("{} ", console::style(name)) + } else { + format!("* {}\n", console::style(name).bold()) + } + }) + .collect(); + + println!("{}", formatted); } } }; From ffc32132a14aa1a4bb2934f238f363276b3f8012 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Mon, 16 Oct 2023 20:50:21 +0200 Subject: [PATCH 2/9] wip: working version of the find replace --- src/cli/completions.rs | 22 ------ src/cli/mod.rs | 152 +++++++++++++++++++++++++++++++++-------- 2 files changed, 123 insertions(+), 51 deletions(-) delete mode 100644 src/cli/completions.rs diff --git a/src/cli/completions.rs b/src/cli/completions.rs deleted file mode 100644 index 5c43127b0..000000000 --- a/src/cli/completions.rs +++ /dev/null @@ -1,22 +0,0 @@ -pub(crate) const BASH_COMPLETION_REPLACEMENTS: (&str, &str) = ( - r#"(?s)pixi__run\) - opts="(.*?)" - if \[\[ \${cur} == -\* \|\| \${COMP_CWORD} -eq 2 \]\] ; then - COMPREPLY=\( \$(compgen -W "\${opts}" -- "\${cur}") \) - return 0 - fi"#, - r#"pixi__run) - opts="$1" - if [[ ${cur} == -* ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - elif [[ ${COMP_CWORD} -eq 2 ]]; then - - local tasks=$(pixi task list --summary 2> /dev/null) - - if [[ $? -eq 0 ]]; then - COMPREPLY=( $(compgen -W "${tasks}" -- "${cur}") ) - return 0 - fi - fi"#, -); diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 1084998c3..875606786 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -4,16 +4,13 @@ use clap::{CommandFactory, Parser}; use clap_complete; use clap_verbosity_flag::Verbosity; use miette::IntoDiagnostic; -use rattler_shell::shell::{Shell, ShellEnum}; use regex::Regex; use std::io::{IsTerminal, Write}; -use std::ops::{Deref, DerefMut}; -use std::str::FromStr; +use std::str::from_utf8_mut; use tracing_subscriber::{filter::LevelFilter, util::SubscriberInitExt, EnvFilter}; pub mod add; pub mod auth; -mod completions; pub mod global; pub mod info; pub mod init; @@ -78,7 +75,7 @@ fn completion(args: CompletionCommand) -> miette::Result<()> { .or(clap_complete::Shell::from_env()) .unwrap_or(clap_complete::Shell::Bash); - let mut script = b""; + let mut script = vec![]; clap_complete::generate( clap_shell, &mut Args::command(), @@ -86,42 +83,36 @@ fn completion(args: CompletionCommand) -> miette::Result<()> { &mut script, // &mut std::io::stdout(), ); - let pattern = r#"(?s)pixi__run\) - opts="(.*?)" - if \[\[ \$\{cur\} == -\\* \|\| \$\{COMP_CWORD\} -eq 2 \]\] ; then - COMPREPLY=\( \$(compgen -W "\$\{opts\}" -- "\$\{cur\}") \) - return 0 - fi"#; + let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; let replacement = r#"pixi__run) opts="$1" - if [[ ${cur} == -* ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - elif [[ ${COMP_CWORD} -eq 2 ]]; then - - local tasks=$(pixi task list --summary 2> /dev/null) - - if [[ $? -eq 0 ]]; then - COMPREPLY=( $(compgen -W "${tasks}" -- "${cur}") ) - return 0 - fi + if [[ $${cur} == -* ]] ; then + COMPREPLY=( $$(compgen -W "$${opts}" -- "$${cur}") ) + return 0 + elif [[ $${COMP_CWORD} -eq 2 ]]; then + local tasks=$$(pixi task list --summary 2> /dev/null) + if [[ $$? -eq 0 ]]; then + COMPREPLY=( $$(compgen -W "$${tasks}" -- "$${cur}") ) + return 0 + fi fi"#; match clap_shell { clap_complete::Shell::Bash => { // let (pattern, replacement) = completions::BASH_COMPLETION_REPLACEMENTS; let re = Regex::new(pattern).unwrap(); - script = re.replace(script.as_str(), replacement); + let script = re.replace(from_utf8_mut(&mut script).into_diagnostic()?, replacement); + // Just like the clap autocompletion code write directly to the stdout + std::io::stdout() + .write_all(script.as_ref().as_ref()) + .into_diagnostic()?; + } + _ => { + std::io::stdout().write_all(&script).into_diagnostic()?; } - _ => {} } - // Just like the clap autocompletion code write directly to the stdout - std::io::stdout() - .write_all(script.as_bytes()) - .into_diagnostic()?; - Ok(()) } @@ -218,3 +209,106 @@ fn use_color_output(args: &Args) -> bool { ColorOutput::Auto => std::env::var_os("NO_COLOR").is_none() && is_terminal(), } } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_completion() { + let clap_shell = clap_complete::Shell::Bash; + + let mut script = vec![]; + clap_complete::generate( + clap_shell, + &mut Args::command(), + "pixi", + &mut script, // &mut std::io::stdout(), + ); + let mut script = r#" + pixi__project__help__help) + opts="" + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pixi__run) + opts="-v -q -h --manifest-path --locked --frozen --verbose --quiet --color --help [TASK]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --manifest-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pixi__search) + opts="-c -l -v -q -h --channel --manifest-path --limit --verbose --quiet --color --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --channel) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + -c) + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + "#; + let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; + // let replacement = "pixi__run)\n opts=\"$1\"\n if [[ ${cur} == -* ]] ; then\n COMPREPLY=( dollar(compgen -W \"dollar{opts}\" -- \"dollar{cur}\") )\n return 0\n elif [[ dollar{COMP_CWORD} -eq 2 ]]; then\n local tasks=dollar(pixi task list --summary 2> /dev/null)\n if [[ dollar? -eq 0 ]]; then\n COMPREPLY=( dollar(compgen -W \"dollar{tasks}\" -- \"dollar{cur}\") )\n return 0\n fi\n fi"; + + let replacement = r#"pixi__run) + opts="$1" + if [[ $${cur} == -* ]] ; then + COMPREPLY=( $$(compgen -W "$${opts}" -- "$${cur}") ) + return 0 + elif [[ $${COMP_CWORD} -eq 2 ]]; then + local tasks=$$(pixi task list --summary 2> /dev/null) + if [[ $$? -eq 0 ]]; then + COMPREPLY=( $$(compgen -W "$${tasks}" -- "$${cur}") ) + return 0 + fi + fi"#; + // let replacement = r#"pixi__run) + // opts="$1" + // if [[ ${cur} == -* ]] ; then + // COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + // return 0 + // elif [[ ${COMP_CWORD} -eq 2 ]]; then + // + // local tasks=$(pixi task list --summary 2> /dev/null) + // + // if [[ $? -eq 0 ]]; then + // COMPREPLY=( $(compgen -W "${tasks}" -- "${cur}") ) + // return 0 + // fi + // fi"#; + + // let (pattern, replacement) = completions::BASH_COMPLETION_REPLACEMENTS; + let re = Regex::new(pattern).unwrap(); + let script = re.replace(&mut script, replacement); + println!("{}", script) + } +} From 9a4757de63da791f2e275738a1c062328e06e4db Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Mon, 16 Oct 2023 21:29:02 +0200 Subject: [PATCH 3/9] fix: cleanup and move to new file, including test --- src/cli/completion.rs | 124 ++++++++++++++ src/cli/mod.rs | 159 +----------------- ...i__cli__completion__tests__completion.snap | 47 ++++++ 3 files changed, 175 insertions(+), 155 deletions(-) create mode 100644 src/cli/completion.rs create mode 100644 src/cli/snapshots/pixi__cli__completion__tests__completion.snap diff --git a/src/cli/completion.rs b/src/cli/completion.rs new file mode 100644 index 000000000..3d8b03e70 --- /dev/null +++ b/src/cli/completion.rs @@ -0,0 +1,124 @@ +use crate::cli::{Args, CompletionCommand}; +use clap::CommandFactory; +use miette::IntoDiagnostic; +use regex::Regex; +use std::borrow::Cow; +use std::io::Write; +use std::str::from_utf8_mut; + +pub(crate) fn execute(args: CompletionCommand) -> miette::Result<()> { + let clap_shell = args + .shell + .or(clap_complete::Shell::from_env()) + .unwrap_or(clap_complete::Shell::Bash); + + let mut script = vec![]; + + // Generate the original completion script. + clap_complete::generate( + clap_shell, + &mut Args::command(), + "pixi", + &mut script, // &mut std::io::stdout(), + ); + + match clap_shell { + clap_complete::Shell::Bash => { + let script = replace_bash_completion(from_utf8_mut(&mut script).into_diagnostic()?); + std::io::stdout() + .write_all(script.as_ref().as_ref()) + .into_diagnostic()?; + } + _ => { + // If no replacements needed write original script to stdout + std::io::stdout().write_all(&script).into_diagnostic()?; + } + } + + Ok(()) +} + +fn replace_bash_completion(script: &str) -> Cow { + let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; + + let replacement = r#"pixi__run) + opts="$1" + if [[ $${cur} == -* ]] ; then + COMPREPLY=( $$(compgen -W "$${opts}" -- "$${cur}") ) + return 0 + elif [[ $${COMP_CWORD} -eq 2 ]]; then + local tasks=$$(pixi task list --summary 2> /dev/null) + if [[ $$? -eq 0 ]]; then + COMPREPLY=( $$(compgen -W "$${tasks}" -- "$${cur}") ) + return 0 + fi + fi"#; + let re = Regex::new(pattern).unwrap(); + re.replace(script, replacement) +} +#[cfg(test)] +mod tests { + use super::*; + + #[test] + pub fn test_completion() { + let mut script = r#" + pixi__project__help__help) + opts="" + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pixi__run) + opts="-v -q -h --manifest-path --locked --frozen --verbose --quiet --color --help [TASK]..." + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + fi + case "${prev}" in + --manifest-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pixi__search) + opts="-c -l -v -q -h --channel --color --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + fi + case "${prev}" in + --channel) + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + + ;; + "#; + let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; + + let replacement = r#"pixi__run) + opts="$1" + if [[ $${cur} == -* ]] ; then + COMPREPLY=( $$(compgen -W "$${opts}" -- "$${cur}") ) + return 0 + elif [[ $${COMP_CWORD} -eq 2 ]]; then + local tasks=$$(pixi task list --summary 2> /dev/null) + if [[ $$? -eq 0 ]]; then + COMPREPLY=( $$(compgen -W "$${tasks}" -- "$${cur}") ) + return 0 + fi + fi"#; + + let re = Regex::new(pattern).unwrap(); + let script = re.replace(&mut script, replacement); + insta::assert_snapshot!(script); + println!("{}", script) + } +} diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 875606786..4e9eb5bf3 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -1,16 +1,15 @@ use super::util::IndicatifWriter; use crate::progress; -use clap::{CommandFactory, Parser}; +use clap::Parser; use clap_complete; use clap_verbosity_flag::Verbosity; use miette::IntoDiagnostic; -use regex::Regex; -use std::io::{IsTerminal, Write}; -use std::str::from_utf8_mut; +use std::io::IsTerminal; use tracing_subscriber::{filter::LevelFilter, util::SubscriberInitExt, EnvFilter}; pub mod add; pub mod auth; +pub mod completion; pub mod global; pub mod info; pub mod init; @@ -69,53 +68,6 @@ pub enum Command { Project(project::Args), } -fn completion(args: CompletionCommand) -> miette::Result<()> { - let clap_shell = args - .shell - .or(clap_complete::Shell::from_env()) - .unwrap_or(clap_complete::Shell::Bash); - - let mut script = vec![]; - clap_complete::generate( - clap_shell, - &mut Args::command(), - "pixi", - &mut script, // &mut std::io::stdout(), - ); - - let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; - - let replacement = r#"pixi__run) - opts="$1" - if [[ $${cur} == -* ]] ; then - COMPREPLY=( $$(compgen -W "$${opts}" -- "$${cur}") ) - return 0 - elif [[ $${COMP_CWORD} -eq 2 ]]; then - local tasks=$$(pixi task list --summary 2> /dev/null) - if [[ $$? -eq 0 ]]; then - COMPREPLY=( $$(compgen -W "$${tasks}" -- "$${cur}") ) - return 0 - fi - fi"#; - - match clap_shell { - clap_complete::Shell::Bash => { - // let (pattern, replacement) = completions::BASH_COMPLETION_REPLACEMENTS; - let re = Regex::new(pattern).unwrap(); - let script = re.replace(from_utf8_mut(&mut script).into_diagnostic()?, replacement); - // Just like the clap autocompletion code write directly to the stdout - std::io::stdout() - .write_all(script.as_ref().as_ref()) - .into_diagnostic()?; - } - _ => { - std::io::stdout().write_all(&script).into_diagnostic()?; - } - } - - Ok(()) -} - pub async fn execute() -> miette::Result<()> { let args = Args::parse(); let use_colors = use_color_output(&args); @@ -168,7 +120,7 @@ pub async fn execute() -> miette::Result<()> { /// Execute the actual command pub async fn execute_command(command: Command) -> miette::Result<()> { match command { - Command::Completion(cmd) => completion(cmd), + Command::Completion(cmd) => completion::execute(cmd), Command::Init(cmd) => init::execute(cmd).await, Command::Add(cmd) => add::execute(cmd).await, Command::Run(cmd) => run::execute(cmd).await, @@ -209,106 +161,3 @@ fn use_color_output(args: &Args) -> bool { ColorOutput::Auto => std::env::var_os("NO_COLOR").is_none() && is_terminal(), } } -#[cfg(test)] -mod tests { - use super::*; - - #[test] - pub fn test_completion() { - let clap_shell = clap_complete::Shell::Bash; - - let mut script = vec![]; - clap_complete::generate( - clap_shell, - &mut Args::command(), - "pixi", - &mut script, // &mut std::io::stdout(), - ); - let mut script = r#" - pixi__project__help__help) - opts="" - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - pixi__run) - opts="-v -q -h --manifest-path --locked --frozen --verbose --quiet --color --help [TASK]..." - if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --manifest-path) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - --color) - COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - pixi__search) - opts="-c -l -v -q -h --channel --manifest-path --limit --verbose --quiet --color --help " - if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - fi - case "${prev}" in - --channel) - COMPREPLY=($(compgen -f "${cur}")) - return 0 - ;; - -c) - --color) - COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) - return 0 - ;; - *) - COMPREPLY=() - ;; - esac - COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - return 0 - ;; - "#; - let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; - // let replacement = "pixi__run)\n opts=\"$1\"\n if [[ ${cur} == -* ]] ; then\n COMPREPLY=( dollar(compgen -W \"dollar{opts}\" -- \"dollar{cur}\") )\n return 0\n elif [[ dollar{COMP_CWORD} -eq 2 ]]; then\n local tasks=dollar(pixi task list --summary 2> /dev/null)\n if [[ dollar? -eq 0 ]]; then\n COMPREPLY=( dollar(compgen -W \"dollar{tasks}\" -- \"dollar{cur}\") )\n return 0\n fi\n fi"; - - let replacement = r#"pixi__run) - opts="$1" - if [[ $${cur} == -* ]] ; then - COMPREPLY=( $$(compgen -W "$${opts}" -- "$${cur}") ) - return 0 - elif [[ $${COMP_CWORD} -eq 2 ]]; then - local tasks=$$(pixi task list --summary 2> /dev/null) - if [[ $$? -eq 0 ]]; then - COMPREPLY=( $$(compgen -W "$${tasks}" -- "$${cur}") ) - return 0 - fi - fi"#; - // let replacement = r#"pixi__run) - // opts="$1" - // if [[ ${cur} == -* ]] ; then - // COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) - // return 0 - // elif [[ ${COMP_CWORD} -eq 2 ]]; then - // - // local tasks=$(pixi task list --summary 2> /dev/null) - // - // if [[ $? -eq 0 ]]; then - // COMPREPLY=( $(compgen -W "${tasks}" -- "${cur}") ) - // return 0 - // fi - // fi"#; - - // let (pattern, replacement) = completions::BASH_COMPLETION_REPLACEMENTS; - let re = Regex::new(pattern).unwrap(); - let script = re.replace(&mut script, replacement); - println!("{}", script) - } -} diff --git a/src/cli/snapshots/pixi__cli__completion__tests__completion.snap b/src/cli/snapshots/pixi__cli__completion__tests__completion.snap new file mode 100644 index 000000000..59f1af8f1 --- /dev/null +++ b/src/cli/snapshots/pixi__cli__completion__tests__completion.snap @@ -0,0 +1,47 @@ +--- +source: src/cli/completion.rs +expression: script +--- + + pixi__project__help__help) + opts="" + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pixi__run) + opts="-v -q -h --manifest-path --locked --frozen --verbose --quiet --color --help [TASK]..." + if [[ ${cur} == -* ]] ; then + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + elif [[ ${COMP_CWORD} -eq 2 ]]; then + local tasks=$(pixi task list --summary 2> /dev/null) + if [[ $? -eq 0 ]]; then + COMPREPLY=( $(compgen -W "${tasks}" -- "${cur}") ) + return 0 + fi + fi + case "${prev}" in + --manifest-path) + COMPREPLY=($(compgen -f "${cur}")) + return 0 + ;; + --color) + COMPREPLY=($(compgen -W "always never auto" -- "${cur}")) + return 0 + ;; + *) + COMPREPLY=() + ;; + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + return 0 + ;; + pixi__search) + opts="-c -l -v -q -h --channel --color --help " + if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then + fi + case "${prev}" in + --channel) + esac + COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + ;; From 2719cfd0436faf2b59e49e31227db1f7ae85e64c Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Mon, 16 Oct 2023 21:36:22 +0200 Subject: [PATCH 4/9] fix test --- .pre-commit-config.yaml | 1 + src/cli/snapshots/pixi__cli__completion__tests__completion.snap | 2 ++ 2 files changed, 3 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 42ff9e4f1..445243468 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,3 +44,4 @@ repos: rev: v2.2.5 hooks: - id: codespell +exclude: ".snap" diff --git a/src/cli/snapshots/pixi__cli__completion__tests__completion.snap b/src/cli/snapshots/pixi__cli__completion__tests__completion.snap index 59f1af8f1..be64d4bcd 100644 --- a/src/cli/snapshots/pixi__cli__completion__tests__completion.snap +++ b/src/cli/snapshots/pixi__cli__completion__tests__completion.snap @@ -44,4 +44,6 @@ expression: script --channel) esac COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) + ;; + From 2d77349167224a41eff03d8af13b6983b0e04514 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Tue, 17 Oct 2023 13:08:50 +0200 Subject: [PATCH 5/9] wip: start zsh support --- src/cli/completion.rs | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/cli/completion.rs b/src/cli/completion.rs index 3d8b03e70..9db625a27 100644 --- a/src/cli/completion.rs +++ b/src/cli/completion.rs @@ -29,6 +29,12 @@ pub(crate) fn execute(args: CompletionCommand) -> miette::Result<()> { .write_all(script.as_ref().as_ref()) .into_diagnostic()?; } + clap_complete::Shell::Zsh => { + let script = replace_zsh_completion(from_utf8_mut(&mut script).into_diagnostic()?); + std::io::stdout() + .write_all(script.as_ref().as_ref()) + .into_diagnostic()?; + } _ => { // If no replacements needed write original script to stdout std::io::stdout().write_all(&script).into_diagnostic()?; @@ -56,6 +62,27 @@ fn replace_bash_completion(script: &str) -> Cow { let re = Regex::new(pattern).unwrap(); re.replace(script, replacement) } + +fn replace_zsh_completion(script: &str) -> Cow { + let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; + + let zsh_replacement = r#"pixi__run) + opts="$1" + if [[ $${CURRENT} -eq 2 ]]; then + local tasks=$$(pixi task list --summary 2> /dev/null) + if [[ $$? -eq 0 ]]; then + compadd "$${tasks}" + return 1 + fi + elif [[ $${cur} == -* ]]; then + compadd -- "$${opts}" + return 1 + fi"#; + + let re = Regex::new(pattern).unwrap(); + re.replace(script, zsh_replacement) +} + #[cfg(test)] mod tests { use super::*; From c15905c487c8e1c444b23dd9b9afbdda8053d46e Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Wed, 18 Oct 2023 16:23:14 +0200 Subject: [PATCH 6/9] fix and cleanup autocompletion scripts --- src/cli/completion.rs | 117 ++++++++++-------- ...__completion__tests__bash_completion.snap} | 2 +- ...li__completion__tests__zsh_completion.snap | 41 ++++++ 3 files changed, 105 insertions(+), 55 deletions(-) rename src/cli/snapshots/{pixi__cli__completion__tests__completion.snap => pixi__cli__completion__tests__bash_completion.snap} (98%) create mode 100644 src/cli/snapshots/pixi__cli__completion__tests__zsh_completion.snap diff --git a/src/cli/completion.rs b/src/cli/completion.rs index 9db625a27..f32102519 100644 --- a/src/cli/completion.rs +++ b/src/cli/completion.rs @@ -6,6 +6,7 @@ use std::borrow::Cow; use std::io::Write; use std::str::from_utf8_mut; +/// Generate completions for the pixi cli, and print those to the stdout pub(crate) fn execute(args: CompletionCommand) -> miette::Result<()> { let clap_shell = args .shell @@ -15,38 +16,28 @@ pub(crate) fn execute(args: CompletionCommand) -> miette::Result<()> { let mut script = vec![]; // Generate the original completion script. - clap_complete::generate( - clap_shell, - &mut Args::command(), - "pixi", - &mut script, // &mut std::io::stdout(), - ); + clap_complete::generate(clap_shell, &mut Args::command(), "pixi", &mut script); - match clap_shell { + let script = match clap_shell { clap_complete::Shell::Bash => { - let script = replace_bash_completion(from_utf8_mut(&mut script).into_diagnostic()?); - std::io::stdout() - .write_all(script.as_ref().as_ref()) - .into_diagnostic()?; + replace_bash_completion(from_utf8_mut(&mut script).into_diagnostic()?) } clap_complete::Shell::Zsh => { - let script = replace_zsh_completion(from_utf8_mut(&mut script).into_diagnostic()?); - std::io::stdout() - .write_all(script.as_ref().as_ref()) - .into_diagnostic()?; + replace_zsh_completion(from_utf8_mut(&mut script).into_diagnostic()?) } - _ => { - // If no replacements needed write original script to stdout - std::io::stdout().write_all(&script).into_diagnostic()?; - } - } + _ => Cow::Borrowed(from_utf8_mut(&mut script).into_diagnostic()?), + }; + + std::io::stdout() + .write_all(script.as_bytes()) + .into_diagnostic()?; Ok(()) } fn replace_bash_completion(script: &str) -> Cow { let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; - + // NOTE THIS IS FORMATTED BY HAND let replacement = r#"pixi__run) opts="$1" if [[ $${cur} == -* ]] ; then @@ -64,20 +55,12 @@ fn replace_bash_completion(script: &str) -> Cow { } fn replace_zsh_completion(script: &str) -> Cow { - let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; + let pattern = r#"(?ms)(\(run\))(?:.*?)(_arguments)"#; - let zsh_replacement = r#"pixi__run) - opts="$1" - if [[ $${CURRENT} -eq 2 ]]; then - local tasks=$$(pixi task list --summary 2> /dev/null) - if [[ $$? -eq 0 ]]; then - compadd "$${tasks}" - return 1 - fi - elif [[ $${cur} == -* ]]; then - compadd -- "$${opts}" - return 1 - fi"#; + // NOTE THIS IS FORMATTED BY HAND + let zsh_replacement = r#"$1 +_values 'task' $$( pixi task list --summary 2> /dev/null ) +$2"#; let re = Regex::new(pattern).unwrap(); re.replace(script, zsh_replacement) @@ -88,8 +71,51 @@ mod tests { use super::*; #[test] - pub fn test_completion() { + pub fn test_zsh_completion() { let mut script = r#" +(add) +_arguments "${_arguments_options[@]}" \ +'--manifest-path=[The path to '\''pixi.toml'\'']:MANIFEST_PATH:_files' \ +'*::specs -- Specify the dependencies you wish to add to the project:' \ +&& ret=0 +;; +(run) +_arguments "${_arguments_options[@]}" \ +'--manifest-path=[The path to '\''pixi.toml'\'']:MANIFEST_PATH:_files' \ +'--color=[Whether the log needs to be colored]:COLOR:(always never auto)' \ +'(--frozen)--locked[Require pixi.lock is up-to-date]' \ +'(--locked)--frozen[Don'\''t check if pixi.lock is up-to-date, install as lockfile states]' \ +'*-v[More output per occurrence]' \ +'*--verbose[More output per occurrence]' \ +'(-v --verbose)*-q[Less output per occurrence]' \ +'(-v --verbose)*--quiet[Less output per occurrence]' \ +'-h[Print help]' \ +'--help[Print help]' \ +'*::task -- The task you want to run in the projects environment:' \ +&& ret=0 +;; +(add) +_arguments "${_arguments_options[@]}" \ +&& ret=0 +;; +(run) +_arguments "${_arguments_options[@]}" \ +&& ret=0 +;; +(shell) +_arguments "${_arguments_options[@]}" \ +&& ret=0 +;; + + "#; + let result = replace_zsh_completion(&mut script); + insta::assert_snapshot!(result); + } + + #[test] + pub fn test_bash_completion() { + // NOTE THIS IS FORMATTED BY HAND! + let script = r#" pixi__project__help__help) opts="" COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") ) @@ -128,24 +154,7 @@ mod tests { ;; "#; - let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; - - let replacement = r#"pixi__run) - opts="$1" - if [[ $${cur} == -* ]] ; then - COMPREPLY=( $$(compgen -W "$${opts}" -- "$${cur}") ) - return 0 - elif [[ $${COMP_CWORD} -eq 2 ]]; then - local tasks=$$(pixi task list --summary 2> /dev/null) - if [[ $$? -eq 0 ]]; then - COMPREPLY=( $$(compgen -W "$${tasks}" -- "$${cur}") ) - return 0 - fi - fi"#; - - let re = Regex::new(pattern).unwrap(); - let script = re.replace(&mut script, replacement); - insta::assert_snapshot!(script); - println!("{}", script) + let result = replace_bash_completion(script); + insta::assert_snapshot!(result); } } diff --git a/src/cli/snapshots/pixi__cli__completion__tests__completion.snap b/src/cli/snapshots/pixi__cli__completion__tests__bash_completion.snap similarity index 98% rename from src/cli/snapshots/pixi__cli__completion__tests__completion.snap rename to src/cli/snapshots/pixi__cli__completion__tests__bash_completion.snap index be64d4bcd..8c5118032 100644 --- a/src/cli/snapshots/pixi__cli__completion__tests__completion.snap +++ b/src/cli/snapshots/pixi__cli__completion__tests__bash_completion.snap @@ -1,6 +1,6 @@ --- source: src/cli/completion.rs -expression: script +expression: result --- pixi__project__help__help) diff --git a/src/cli/snapshots/pixi__cli__completion__tests__zsh_completion.snap b/src/cli/snapshots/pixi__cli__completion__tests__zsh_completion.snap new file mode 100644 index 000000000..35930501b --- /dev/null +++ b/src/cli/snapshots/pixi__cli__completion__tests__zsh_completion.snap @@ -0,0 +1,41 @@ +--- +source: src/cli/completion.rs +expression: result +--- + +(add) +_arguments "${_arguments_options[@]}" \ +'--manifest-path=[The path to '\''pixi.toml'\'']:MANIFEST_PATH:_files' \ +'*::specs -- Specify the dependencies you wish to add to the project:' \ +&& ret=0 +;; +(run) +_values 'task' $( pixi task list --summary 2> /dev/null ) +_arguments "${_arguments_options[@]}" \ +'--manifest-path=[The path to '\''pixi.toml'\'']:MANIFEST_PATH:_files' \ +'--color=[Whether the log needs to be colored]:COLOR:(always never auto)' \ +'(--frozen)--locked[Require pixi.lock is up-to-date]' \ +'(--locked)--frozen[Don'\''t check if pixi.lock is up-to-date, install as lockfile states]' \ +'*-v[More output per occurrence]' \ +'*--verbose[More output per occurrence]' \ +'(-v --verbose)*-q[Less output per occurrence]' \ +'(-v --verbose)*--quiet[Less output per occurrence]' \ +'-h[Print help]' \ +'--help[Print help]' \ +'*::task -- The task you want to run in the projects environment:' \ +&& ret=0 +;; +(add) +_arguments "${_arguments_options[@]}" \ +&& ret=0 +;; +(run) +_arguments "${_arguments_options[@]}" \ +&& ret=0 +;; +(shell) +_arguments "${_arguments_options[@]}" \ +&& ret=0 +;; + + From a29869c961fc83bff0bcc7c345772ac917261180 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Wed, 18 Oct 2023 16:31:08 +0200 Subject: [PATCH 7/9] fix: autocomplete the tasks only once in zsh --- src/cli/completion.rs | 4 ++-- .../pixi__cli__completion__tests__zsh_completion.snap | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/cli/completion.rs b/src/cli/completion.rs index f32102519..a7d44bb73 100644 --- a/src/cli/completion.rs +++ b/src/cli/completion.rs @@ -55,12 +55,12 @@ fn replace_bash_completion(script: &str) -> Cow { } fn replace_zsh_completion(script: &str) -> Cow { - let pattern = r#"(?ms)(\(run\))(?:.*?)(_arguments)"#; + let pattern = r#"(?ms)(\(run\))(?:.*?)(_arguments.*?)(\*::task)"#; // NOTE THIS IS FORMATTED BY HAND let zsh_replacement = r#"$1 _values 'task' $$( pixi task list --summary 2> /dev/null ) -$2"#; +$2::task"#; let re = Regex::new(pattern).unwrap(); re.replace(script, zsh_replacement) diff --git a/src/cli/snapshots/pixi__cli__completion__tests__zsh_completion.snap b/src/cli/snapshots/pixi__cli__completion__tests__zsh_completion.snap index 35930501b..bdd87a054 100644 --- a/src/cli/snapshots/pixi__cli__completion__tests__zsh_completion.snap +++ b/src/cli/snapshots/pixi__cli__completion__tests__zsh_completion.snap @@ -22,7 +22,7 @@ _arguments "${_arguments_options[@]}" \ '(-v --verbose)*--quiet[Less output per occurrence]' \ '-h[Print help]' \ '--help[Print help]' \ -'*::task -- The task you want to run in the projects environment:' \ +'::task -- The task you want to run in the projects environment:' \ && ret=0 ;; (add) From 301678d32a6428fd8b356a048765b45ce3588b21 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Wed, 18 Oct 2023 17:50:27 +0200 Subject: [PATCH 8/9] misc: cleanup execute function and add test on the actual clap_complete code. --- src/cli/completion.rs | 51 ++++++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/src/cli/completion.rs b/src/cli/completion.rs index a7d44bb73..f3179669f 100644 --- a/src/cli/completion.rs +++ b/src/cli/completion.rs @@ -4,7 +4,6 @@ use miette::IntoDiagnostic; use regex::Regex; use std::borrow::Cow; use std::io::Write; -use std::str::from_utf8_mut; /// Generate completions for the pixi cli, and print those to the stdout pub(crate) fn execute(args: CompletionCommand) -> miette::Result<()> { @@ -13,21 +12,19 @@ pub(crate) fn execute(args: CompletionCommand) -> miette::Result<()> { .or(clap_complete::Shell::from_env()) .unwrap_or(clap_complete::Shell::Bash); - let mut script = vec![]; - // Generate the original completion script. - clap_complete::generate(clap_shell, &mut Args::command(), "pixi", &mut script); - + let script = { + let mut buf = vec![]; + clap_complete::generate(clap_shell, &mut Args::command(), "pixi", &mut buf); + String::from_utf8(buf).expect("clap_complete did not generate a valid UTF8 script") + }; + // For supported shells, modify the script to include more context sensitive completions. let script = match clap_shell { - clap_complete::Shell::Bash => { - replace_bash_completion(from_utf8_mut(&mut script).into_diagnostic()?) - } - clap_complete::Shell::Zsh => { - replace_zsh_completion(from_utf8_mut(&mut script).into_diagnostic()?) - } - _ => Cow::Borrowed(from_utf8_mut(&mut script).into_diagnostic()?), + clap_complete::Shell::Bash => replace_bash_completion(&script), + clap_complete::Shell::Zsh => replace_zsh_completion(&script), + _ => Cow::Owned(script), }; - + // Write the result to the standard output std::io::stdout() .write_all(script.as_bytes()) .into_diagnostic()?; @@ -157,4 +154,32 @@ _arguments "${_arguments_options[@]}" \ let result = replace_bash_completion(script); insta::assert_snapshot!(result); } + + #[test] + pub fn test_bash_completion_working_regex() { + let clap_shell = clap_complete::Shell::Bash; + + // Generate the original completion script. + let script = { + let mut buf = vec![]; + clap_complete::generate(clap_shell, &mut Args::command(), "pixi", &mut buf); + String::from_utf8(buf).expect("clap_complete did not generate a valid UTF8 script") + }; + // 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() { + let clap_shell = clap_complete::Shell::Zsh; + + // Generate the original completion script. + let script = { + let mut buf = vec![]; + clap_complete::generate(clap_shell, &mut Args::command(), "pixi", &mut buf); + String::from_utf8(buf).expect("clap_complete did not generate a valid UTF8 script") + }; + // Test if there was a replacement done on the clap generated completions + assert_ne!(replace_zsh_completion(&script), script); + } } From 611426f314f6b69e635e494a104e54b4659e3848 Mon Sep 17 00:00:00 2001 From: Ruben Arts Date: Thu, 19 Oct 2023 08:41:04 +0200 Subject: [PATCH 9/9] misc: avoid duplicate code, add more docstrings --- src/cli/completion.rs | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/src/cli/completion.rs b/src/cli/completion.rs index f3179669f..1f4f554eb 100644 --- a/src/cli/completion.rs +++ b/src/cli/completion.rs @@ -13,17 +13,15 @@ pub(crate) fn execute(args: CompletionCommand) -> miette::Result<()> { .unwrap_or(clap_complete::Shell::Bash); // Generate the original completion script. - let script = { - let mut buf = vec![]; - clap_complete::generate(clap_shell, &mut Args::command(), "pixi", &mut buf); - String::from_utf8(buf).expect("clap_complete did not generate a valid UTF8 script") - }; + let script = get_completion_script(clap_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), _ => Cow::Owned(script), }; + // Write the result to the standard output std::io::stdout() .write_all(script.as_bytes()) @@ -32,8 +30,17 @@ pub(crate) fn execute(args: CompletionCommand) -> miette::Result<()> { Ok(()) } +/// Generate the completion script using clap_complete for a specified shell. +fn get_completion_script(shell: clap_complete::Shell) -> String { + let mut buf = vec![]; + clap_complete::generate(shell, &mut Args::command(), "pixi", &mut buf); + String::from_utf8(buf).expect("clap_complete did not generate a valid UTF8 script") +} + +/// Replace the parts of the bash completion script that need different functionality. fn replace_bash_completion(script: &str) -> Cow { let pattern = r#"(?s)pixi__run\).*?opts="(.*?)".*?(if.*?fi)"#; + // Adds tab completion to the pixi run command. // NOTE THIS IS FORMATTED BY HAND let replacement = r#"pixi__run) opts="$1" @@ -51,9 +58,10 @@ fn replace_bash_completion(script: &str) -> Cow { re.replace(script, replacement) } +/// Replace the parts of the bash completion script that need different functionality. fn replace_zsh_completion(script: &str) -> Cow { let pattern = r#"(?ms)(\(run\))(?:.*?)(_arguments.*?)(\*::task)"#; - + // Adds tab completion to the pixi run command. // NOTE THIS IS FORMATTED BY HAND let zsh_replacement = r#"$1 _values 'task' $$( pixi task list --summary 2> /dev/null ) @@ -157,28 +165,16 @@ _arguments "${_arguments_options[@]}" \ #[test] pub fn test_bash_completion_working_regex() { - let clap_shell = clap_complete::Shell::Bash; - // Generate the original completion script. - let script = { - let mut buf = vec![]; - clap_complete::generate(clap_shell, &mut Args::command(), "pixi", &mut buf); - String::from_utf8(buf).expect("clap_complete did not generate a valid UTF8 script") - }; + let script = get_completion_script(clap_complete::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() { - let clap_shell = clap_complete::Shell::Zsh; - // Generate the original completion script. - let script = { - let mut buf = vec![]; - clap_complete::generate(clap_shell, &mut Args::command(), "pixi", &mut buf); - String::from_utf8(buf).expect("clap_complete did not generate a valid UTF8 script") - }; + let script = get_completion_script(clap_complete::Shell::Zsh); // Test if there was a replacement done on the clap generated completions assert_ne!(replace_zsh_completion(&script), script); }