diff --git a/complete/src/bash.rs b/complete/src/bash.rs new file mode 100644 index 0000000..37b7711 --- /dev/null +++ b/complete/src/bash.rs @@ -0,0 +1,64 @@ +// For the full copyright and license information, please view the LICENSE +// file that was distributed with this source code. + +use crate::{Command, Flag}; + +/// Create completion script for `bash` +/// +/// Short and long options are combined into single `complete` calls, even if +/// they differ in whether they take arguments or not; just like in case of `fish`. +/// Also, pretend that files are fine in any position. ValueHints are ignored entirely. +pub fn render(c: &Command) -> String { + let mut out = String::new(); + let name = &c.name; + // Register _comp_uu_FOO as a bash function that computes completions: + out.push_str(&format!("complete -F _comp_uu_{name} {name};")); + out.push_str(&format!("_comp_uu_{name}()")); + // Unless the current argument starts with "-", pre-populate the completions list with all files and dirs: + out.push_str("{ local cur;_init_completion||return;COMPREPLY=();if [[ \"$cur\" != \"-*\" ]]; then _filedir;fi;COMPREPLY+=($(compgen -W \""); + for arg in &c.args { + for Flag { flag, .. } in &arg.short { + out.push_str(&format!("-{flag} ")); + } + for Flag { flag, .. } in &arg.long { + out.push_str(&format!("--{flag} ")); + } + } + out.push_str("\" -- \"$cur\"));}\n"); + out +} + +#[cfg(test)] +mod test { + use super::render; + use crate::{Arg, Command, Flag, Value}; + + #[test] + fn simple() { + let c = Command { + name: "foo", + args: vec![ + Arg { + short: vec![Flag { + flag: "a", + value: Value::No, + }], + long: vec![Flag { + flag: "all", + value: Value::No, + }], + ..Arg::default() + }, + Arg { + short: vec![Flag { + flag: "x", + value: Value::No, + }], + ..Arg::default() + }, + ], + ..Command::default() + }; + assert_eq!(render(&c), "complete -F _comp_uu_foo foo;_comp_uu_foo(){ local cur;_init_completion||return;COMPREPLY=();if [[ \"$cur\" != \"-*\" ]]; then _filedir;fi;COMPREPLY+=($(compgen -W \"-a --all -x \" -- \"$cur\"));}\n") + } +} diff --git a/complete/src/lib.rs b/complete/src/lib.rs index ee84b8e..39cbd62 100644 --- a/complete/src/lib.rs +++ b/complete/src/lib.rs @@ -13,6 +13,7 @@ //! - Some information is removed because it is irrelevant for completion and documentation //! - This struct is meant to exist at runtime of the program //! +mod bash; mod fish; mod man; mod md; @@ -75,7 +76,8 @@ pub fn render(c: &Command, shell: &str) -> String { "zsh" => zsh::render(c), "nu" | "nushell" => nu::render(c), "man" => man::render(c), - "sh" | "bash" | "csh" | "elvish" | "powershell" => panic!("shell '{shell}' completion is not implemented yet!"), + "bash" => bash::render(c), + "sh" | "csh" | "elvish" | "powershell" => panic!("shell '{shell}' completion is not implemented yet!"), _ => panic!("unknown option '{shell}'! Expected one of: \"md\", \"fish\", \"zsh\", \"nu[shell]\", \"man\", \"sh\", \"bash\", \"csh\", \"elvish\", \"powershell\""), } }