From 1f43bd47c87fb1496176db9c40a53ad11785be43 Mon Sep 17 00:00:00 2001 From: ysthakur <45539777+ysthakur@users.noreply.github.com> Date: Thu, 17 Aug 2023 17:52:58 -0400 Subject: [PATCH] feat: Untested Bash impl --- src/gen/bash.rs | 75 +++++++++++++++++++++++----- tests/integration_tests.rs | 5 ++ tests/resources/expected/_test1.bash | 17 +++++-- 3 files changed, 79 insertions(+), 18 deletions(-) diff --git a/src/gen/bash.rs b/src/gen/bash.rs index f25a4c4..f178ca6 100644 --- a/src/gen/bash.rs +++ b/src/gen/bash.rs @@ -7,22 +7,71 @@ use crate::{gen::util::Output, parse::CommandInfo}; /// Generate a completion file for Bash pub fn generate( cmd_name: &str, - _cmd_info: &CommandInfo, + cmd_info: &CommandInfo, out_dir: &Path, ) -> Result<()> { - // TODO make option to not overwrite file let comp_name = format!("_comp_cmd_{cmd_name}"); - let mut res = Output::new(String::from("\t")); - res.writeln("#!/usr/bin/env bash\n"); - res.writeln(&format!("function {comp_name} {{")); - res.writeln("COMPREPLY=()"); - res.writeln("case ${COMP_CWORD} in"); - // generate_fn(&cmd_name, cmd_info, &mut res, 0, &comp_name); - res.writeln("esac"); - res.writeln("return 0"); - res.writeln("}"); - - fs::write(out_dir.join(format!("_{cmd_name}.bash")), res.text())?; + let mut out = Output::new(String::from("\t")); + out.writeln("#!/usr/bin/env bash\n"); + out.writeln(format!("function {comp_name} {{")); + out.indent(); + out.writeln("COMPREPLY=()"); + + generate_cmd(cmd_info, 1, &mut out); + + out.writeln("return 0"); + out.dedent(); + out.writeln("}\n"); + + out.writeln(format!("complete -F _comp_cmd_{cmd_name} {cmd_name}")); + + fs::write(out_dir.join(format!("_{cmd_name}.bash")), out.text())?; Ok(()) } + +fn generate_cmd(cmd_info: &CommandInfo, pos: usize, out: &mut Output) { + out.writeln("case $COMP_CWORD in"); + out.indent(); + + let flags = cmd_info + .flags + .iter() + .map(|f| f.forms.join(" ")) + .collect::>() + .join(" "); + let subcmds = cmd_info + .subcommands + .keys() + .map(String::from) + .collect::>() + .join(" "); + let completions = format!("{flags} {subcmds}"); + // This case is for when the subcommand we're processing is the one to + // complete + out.writeln(format!( + "{pos}) COMPREPLY=($(compgen -W '{completions}' -- $2)) ;;" + )); + + // This case is in case we need to go further to a deeper subcommand + if !cmd_info.subcommands.is_empty() { + out.writeln("*)"); + out.indent(); + out.writeln(format!("case ${{COMP_WORDS[{pos}]}} in")); + out.indent(); + for (cmd_name, cmd_info) in &cmd_info.subcommands { + out.writeln(format!("{cmd_name})")); + out.indent(); + generate_cmd(cmd_info, pos + 1, out); + out.writeln(";;"); + out.dedent(); + } + out.dedent(); + out.writeln("esac"); + out.writeln(";;"); + out.dedent(); + } + + out.dedent(); + out.writeln("esac"); +} diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 045cffe..987291f 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -106,6 +106,11 @@ fn test1_zsh() { run_test("zsh", &["test1"], &["--cmds", "^test1"]); } +#[test] +fn test1_bash() { + run_test("bash", &["test1"], &["--cmds", "^test1"]); +} + #[test] fn test1_nu() { run_test("nu", &["test1"], &["--cmds", "^test1"]); diff --git a/tests/resources/expected/_test1.bash b/tests/resources/expected/_test1.bash index f7c60b0..7c7a319 100644 --- a/tests/resources/expected/_test1.bash +++ b/tests/resources/expected/_test1.bash @@ -3,11 +3,18 @@ function _comp_cmd_test1 { COMPREPLY=() case $COMP_CWORD in - 1) COMPREPLY=($(compgen -W "-h --help -v --verbose --loud sub1" -- $2)) ;; + 1) COMPREPLY=($(compgen -W '-h --h -v --verbose --loud sub1' -- $2)) ;; *) - case $3 in - sub1) COMPREPLY=($(compgen -W "--foobar" -- $2)) ;; - esac - ;; + case ${COMP_WORDS[1]} in + sub1) + case $COMP_CWORD in + 2) COMPREPLY=($(compgen -W '--foobar ' -- $2)) ;; + esac + ;; + esac + ;; esac + return 0 } + +complete -F _comp_cmd_test1 test1