Skip to content

Commit 0ab9f84

Browse files
committed
imp(Completions): completions now include aliases to subcommands, including all subcommand options
Aliases to subcommands can now be completed just like the original subcommand they alias. Imagine a subcommand `update` with alias `install`, the `update` subcommand has an option `--pkg` which accepts a package to update/install. The following completion would happen: ``` $ prog <tab><tab> -h --help -V --version install update $ prog install <tab><tab> -h --help -V --version --pkg $ prog install --pkg <tab><tab> $ prog install --pkg <PACKAGE> $ prog update <tab><tab> -h --help -V --version --pkg $ prog update --pkg <tab><tab> $ prog update --pkg <PACKAGE> ``` Closes #556
1 parent 9b359bf commit 0ab9f84

File tree

1 file changed

+49
-15
lines changed

1 file changed

+49
-15
lines changed

src/completions.rs

+49-15
Original file line numberDiff line numberDiff line change
@@ -97,9 +97,7 @@ complete -F _{name} {name}
9797

9898
fn all_subcommands(&self) -> String {
9999
let mut subcmds = String::new();
100-
let mut scs = get_all_subcommands(self.p);
101-
scs.sort();
102-
scs.dedup();
100+
let scs = get_all_subcommands(self.p);
103101

104102
for sc in &scs {
105103
subcmds = format!(
@@ -153,7 +151,14 @@ complete -F _{name} {name}
153151
let mut p = self.p;
154152
for sc in path.split("_").skip(1) {
155153
debugln!("iter;sc={}", sc);
156-
p = &p.subcommands.iter().filter(|s| s.p.meta.name == sc).next().unwrap().p;
154+
p = &p.subcommands.iter()
155+
.filter(|s| s.p.meta.name == sc
156+
|| (s.p.meta.aliases.is_some() && s.p.meta.aliases.as_ref()
157+
.unwrap()
158+
.iter()
159+
.any(|&(n,_)| n==sc )))
160+
.next()
161+
.unwrap().p;
157162
}
158163
let mut opts = p.short_list.iter().fold(String::new(), |acc, s| format!("{} -{}", acc, s));
159164
opts = format!("{} {}", opts, p.long_list.iter()
@@ -162,14 +167,19 @@ complete -F _{name} {name}
162167
.fold(String::new(), |acc, p| format!("{} {}", acc, p)));
163168
opts = format!("{} {}", opts, p.subcommands.iter()
164169
.fold(String::new(), |acc, s| format!("{} {}", acc, s.p.meta.name)));
170+
for sc in &p.subcommands {
171+
if let Some(ref aliases) = sc.p.meta.aliases {
172+
opts = format!("{} {}", opts, aliases.iter().map(|&(n,_)| n).fold(String::new(), |acc, a| format!("{} {}", acc, a)));
173+
}
174+
}
165175
opts
166176
}
167177

168178
fn option_details_for_path(&self, path: &str) -> String {
169179
let mut p = self.p;
170180
for sc in path.split("_").skip(1) {
171181
debugln!("iter;sc={}", sc);
172-
p = &p.subcommands.iter().filter(|s| s.p.meta.name == sc).next().unwrap().p;
182+
p = &p.subcommands.iter().filter(|s| s.p.meta.name == sc || (s.p.meta.aliases.is_some() && s.p.meta.aliases.as_ref().unwrap().iter().any(|&(n,_)| n==sc ))).next().unwrap().p;
173183
}
174184
let mut opts = String::new();
175185
for o in &p.opts {
@@ -195,31 +205,55 @@ complete -F _{name} {name}
195205
pub fn get_all_subcommands(p: &Parser) -> Vec<String> {
196206
let mut subcmds = vec![];
197207
if !p.has_subcommands() {
198-
return vec![p.meta.name.clone()]
208+
let mut ret = vec![p.meta.name.clone()];
209+
if let Some(ref aliases) = p.meta.aliases {
210+
for &(n, _) in aliases {
211+
ret.push(n.to_owned());
212+
}
213+
}
214+
return ret;
199215
}
200-
for sc in p.subcommands.iter().map(|ref s| s.p.meta.name.clone()) {
201-
subcmds.push(sc);
216+
for sc in &p.subcommands {
217+
if let Some(ref aliases) = sc.p.meta.aliases {
218+
for &(n, _) in aliases {
219+
subcmds.push(n.to_owned());
220+
}
221+
}
222+
subcmds.push(sc.p.meta.name.clone());
202223
}
203224
for sc_v in p.subcommands.iter().map(|ref s| get_all_subcommands(&s.p)) {
204225
subcmds.extend(sc_v);
205226
}
227+
subcmds.sort();
228+
subcmds.dedup();
206229
subcmds
207230
}
208231

209232
pub fn get_all_subcommand_paths(p: &Parser, first: bool) -> Vec<String> {
210233
let mut subcmds = vec![];
211234
if !p.has_subcommands() {
212235
if !first {
213-
return vec![p.meta.bin_name.as_ref().unwrap().clone().replace(" ", "_")]
236+
let name = &*p.meta.name;
237+
let path = p.meta.bin_name.as_ref().unwrap().clone().replace(" ", "_");
238+
let mut ret = vec![path.clone()];
239+
if let Some(ref aliases) = p.meta.aliases {
240+
for &(n, _) in aliases {
241+
ret.push(path.replace(name, n));
242+
}
243+
}
244+
return ret;
214245
}
215246
return vec![];
216247
}
217-
for sc in p.subcommands.iter()
218-
.map(|ref s| s.p.meta.bin_name.as_ref()
219-
.unwrap()
220-
.clone()
221-
.replace(" ", "_")) {
222-
subcmds.push(sc);
248+
for sc in &p.subcommands {
249+
let name = &*sc.p.meta.name;
250+
let path = sc.p.meta.bin_name.as_ref().unwrap().clone().replace(" ", "_");
251+
subcmds.push(path.clone());
252+
if let Some(ref aliases) = sc.p.meta.aliases {
253+
for &(n, _) in aliases {
254+
subcmds.push(path.replace(name, n));
255+
}
256+
}
223257
}
224258
for sc_v in p.subcommands.iter().map(|ref s| get_all_subcommand_paths(&s.p, false)) {
225259
subcmds.extend(sc_v);

0 commit comments

Comments
 (0)