implement rudimentary bash completion

This commit is contained in:
Ben Wiederhake
2025-04-01 10:39:13 +02:00
committed by Daniel Hofstetter
parent 6d2b4f423b
commit 1b5405cb0d
2 changed files with 67 additions and 1 deletions
+64
View File
@@ -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")
}
}
+3 -1
View File
@@ -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\""),
}
}