1 // Std 2 use std::io::Write; 3 4 // Internal 5 use app::parser::Parser; 6 use args::OptBuilder; 7 use completions; 8 9 pub struct BashGen<'a, 'b> 10 where 11 'a: 'b, 12 { 13 p: &'b Parser<'a, 'b>, 14 } 15 16 impl<'a, 'b> BashGen<'a, 'b> { new(p: &'b Parser<'a, 'b>) -> Self17 pub fn new(p: &'b Parser<'a, 'b>) -> Self { 18 BashGen { p: p } 19 } 20 generate_to<W: Write>(&self, buf: &mut W)21 pub fn generate_to<W: Write>(&self, buf: &mut W) { 22 w!( 23 buf, 24 format!( 25 r#"_{name}() {{ 26 local i cur prev opts cmds 27 COMPREPLY=() 28 cur="${{COMP_WORDS[COMP_CWORD]}}" 29 prev="${{COMP_WORDS[COMP_CWORD-1]}}" 30 cmd="" 31 opts="" 32 33 for i in ${{COMP_WORDS[@]}} 34 do 35 case "${{i}}" in 36 {name}) 37 cmd="{name}" 38 ;; 39 {subcmds} 40 *) 41 ;; 42 esac 43 done 44 45 case "${{cmd}}" in 46 {name}) 47 opts="{name_opts}" 48 if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq 1 ]] ; then 49 COMPREPLY=( $(compgen -W "${{opts}}" -- "${{cur}}") ) 50 return 0 51 fi 52 case "${{prev}}" in 53 {name_opts_details} 54 *) 55 COMPREPLY=() 56 ;; 57 esac 58 COMPREPLY=( $(compgen -W "${{opts}}" -- "${{cur}}") ) 59 return 0 60 ;; 61 {subcmd_details} 62 esac 63 }} 64 65 complete -F _{name} -o bashdefault -o default {name} 66 "#, 67 name = self.p.meta.bin_name.as_ref().unwrap(), 68 name_opts = self.all_options_for_path(self.p.meta.bin_name.as_ref().unwrap()), 69 name_opts_details = 70 self.option_details_for_path(self.p.meta.bin_name.as_ref().unwrap()), 71 subcmds = self.all_subcommands(), 72 subcmd_details = self.subcommand_details() 73 ) 74 .as_bytes() 75 ); 76 } 77 all_subcommands(&self) -> String78 fn all_subcommands(&self) -> String { 79 debugln!("BashGen::all_subcommands;"); 80 let mut subcmds = String::new(); 81 let scs = completions::all_subcommand_names(self.p); 82 83 for sc in &scs { 84 subcmds = format!( 85 r#"{} 86 {name}) 87 cmd+="__{fn_name}" 88 ;;"#, 89 subcmds, 90 name = sc, 91 fn_name = sc.replace("-", "__") 92 ); 93 } 94 95 subcmds 96 } 97 subcommand_details(&self) -> String98 fn subcommand_details(&self) -> String { 99 debugln!("BashGen::subcommand_details;"); 100 let mut subcmd_dets = String::new(); 101 let mut scs = completions::get_all_subcommand_paths(self.p, true); 102 scs.sort(); 103 scs.dedup(); 104 105 for sc in &scs { 106 subcmd_dets = format!( 107 r#"{} 108 {subcmd}) 109 opts="{sc_opts}" 110 if [[ ${{cur}} == -* || ${{COMP_CWORD}} -eq {level} ]] ; then 111 COMPREPLY=( $(compgen -W "${{opts}}" -- "${{cur}}") ) 112 return 0 113 fi 114 case "${{prev}}" in 115 {opts_details} 116 *) 117 COMPREPLY=() 118 ;; 119 esac 120 COMPREPLY=( $(compgen -W "${{opts}}" -- "${{cur}}") ) 121 return 0 122 ;;"#, 123 subcmd_dets, 124 subcmd = sc.replace("-", "__"), 125 sc_opts = self.all_options_for_path(&*sc), 126 level = sc.split("__").map(|_| 1).fold(0, |acc, n| acc + n), 127 opts_details = self.option_details_for_path(&*sc) 128 ); 129 } 130 131 subcmd_dets 132 } 133 option_details_for_path(&self, path: &str) -> String134 fn option_details_for_path(&self, path: &str) -> String { 135 debugln!("BashGen::option_details_for_path: path={}", path); 136 let mut p = self.p; 137 for sc in path.split("__").skip(1) { 138 debugln!("BashGen::option_details_for_path:iter: sc={}", sc); 139 p = &find_subcmd!(p, sc).unwrap().p; 140 } 141 let mut opts = String::new(); 142 for o in p.opts() { 143 if let Some(l) = o.s.long { 144 opts = format!( 145 "{} 146 --{}) 147 COMPREPLY=({}) 148 return 0 149 ;;", 150 opts, 151 l, 152 self.vals_for(o) 153 ); 154 } 155 if let Some(s) = o.s.short { 156 opts = format!( 157 "{} 158 -{}) 159 COMPREPLY=({}) 160 return 0 161 ;;", 162 opts, 163 s, 164 self.vals_for(o) 165 ); 166 } 167 } 168 opts 169 } 170 vals_for(&self, o: &OptBuilder) -> String171 fn vals_for(&self, o: &OptBuilder) -> String { 172 debugln!("BashGen::vals_for: o={}", o.b.name); 173 use args::AnyArg; 174 if let Some(vals) = o.possible_vals() { 175 format!(r#"$(compgen -W "{}" -- "${{cur}}")"#, vals.join(" ")) 176 } else { 177 String::from(r#"$(compgen -f "${cur}")"#) 178 } 179 } 180 all_options_for_path(&self, path: &str) -> String181 fn all_options_for_path(&self, path: &str) -> String { 182 debugln!("BashGen::all_options_for_path: path={}", path); 183 let mut p = self.p; 184 for sc in path.split("__").skip(1) { 185 debugln!("BashGen::all_options_for_path:iter: sc={}", sc); 186 p = &find_subcmd!(p, sc).unwrap().p; 187 } 188 let mut opts = shorts!(p).fold(String::new(), |acc, s| format!("{} -{}", acc, s)); 189 opts = format!( 190 "{} {}", 191 opts, 192 longs!(p).fold(String::new(), |acc, l| format!("{} --{}", acc, l)) 193 ); 194 opts = format!( 195 "{} {}", 196 opts, 197 p.positionals 198 .values() 199 .fold(String::new(), |acc, p| format!("{} {}", acc, p)) 200 ); 201 opts = format!( 202 "{} {}", 203 opts, 204 p.subcommands 205 .iter() 206 .fold(String::new(), |acc, s| format!("{} {}", acc, s.p.meta.name)) 207 ); 208 for sc in &p.subcommands { 209 if let Some(ref aliases) = sc.p.meta.aliases { 210 opts = format!( 211 "{} {}", 212 opts, 213 aliases 214 .iter() 215 .map(|&(n, _)| n) 216 .fold(String::new(), |acc, a| format!("{} {}", acc, a)) 217 ); 218 } 219 } 220 opts 221 } 222 } 223