Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 646134a

Browse files
authored
Merge pull request #5240 from sudotac/improve-bash-completion-with-compopt
Improve bash completion with compopt
2 parents d18c327 + 13a7980 commit 646134a

File tree

4 files changed

+125
-20
lines changed

4 files changed

+125
-20
lines changed

clap_complete/src/shells/bash.rs

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -169,29 +169,50 @@ fn option_details_for_path(cmd: &Command, path: &str) -> String {
169169
let mut opts = vec![String::new()];
170170

171171
for o in p.get_opts() {
172+
let compopt = match o.get_value_hint() {
173+
ValueHint::FilePath => Some("compopt -o filenames"),
174+
ValueHint::DirPath => Some("compopt -o plusdirs"),
175+
ValueHint::Other => Some("compopt -o nospace"),
176+
_ => None,
177+
};
178+
172179
if let Some(longs) = o.get_long_and_visible_aliases() {
173180
opts.extend(longs.iter().map(|long| {
174-
format!(
175-
"--{})
176-
COMPREPLY=({})
177-
return 0
178-
;;",
179-
long,
180-
vals_for(o)
181-
)
181+
let mut v = vec![
182+
format!("--{})", long),
183+
format!("COMPREPLY=({})", vals_for(o)),
184+
];
185+
186+
if let Some(copt) = compopt {
187+
v.extend([
188+
r#"if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then"#.to_string(),
189+
format!(" {}", copt),
190+
"fi".to_string(),
191+
]);
192+
}
193+
194+
v.extend(["return 0", ";;"].iter().map(|s| s.to_string()));
195+
v.join("\n ")
182196
}));
183197
}
184198

185199
if let Some(shorts) = o.get_short_and_visible_aliases() {
186200
opts.extend(shorts.iter().map(|short| {
187-
format!(
188-
"-{})
189-
COMPREPLY=({})
190-
return 0
191-
;;",
192-
short,
193-
vals_for(o)
194-
)
201+
let mut v = vec![
202+
format!("-{})", short),
203+
format!("COMPREPLY=({})", vals_for(o)),
204+
];
205+
206+
if let Some(copt) = compopt {
207+
v.extend([
208+
r#"if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then"#.to_string(),
209+
format!(" {}", copt),
210+
"fi".to_string(),
211+
]);
212+
}
213+
214+
v.extend(["return 0", ";;"].iter().map(|s| s.to_string()));
215+
v.join("\n ")
195216
}));
196217
}
197218
}
@@ -211,6 +232,8 @@ fn vals_for(o: &Arg) -> String {
211232
.collect::<Vec<_>>()
212233
.join(" ")
213234
)
235+
} else if o.get_value_hint() == ValueHint::DirPath {
236+
String::from("") // should be empty to avoid duplicate candidates
214237
} else if o.get_value_hint() == ValueHint::Other {
215238
String::from("\"${cur}\"")
216239
} else {

clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -542,6 +542,9 @@ _exhaustive() {
542542
;;
543543
--other)
544544
COMPREPLY=("${cur}")
545+
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
546+
compopt -o nospace
547+
fi
545548
return 0
546549
;;
547550
--path)
@@ -554,18 +557,30 @@ _exhaustive() {
554557
;;
555558
--file)
556559
COMPREPLY=($(compgen -f "${cur}"))
560+
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
561+
compopt -o filenames
562+
fi
557563
return 0
558564
;;
559565
-f)
560566
COMPREPLY=($(compgen -f "${cur}"))
567+
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
568+
compopt -o filenames
569+
fi
561570
return 0
562571
;;
563572
--dir)
564-
COMPREPLY=($(compgen -f "${cur}"))
573+
COMPREPLY=()
574+
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
575+
compopt -o plusdirs
576+
fi
565577
return 0
566578
;;
567579
-d)
568-
COMPREPLY=($(compgen -f "${cur}"))
580+
COMPREPLY=()
581+
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
582+
compopt -o plusdirs
583+
fi
569584
return 0
570585
;;
571586
--exe)

clap_complete/tests/snapshots/value_hint.bash

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ _my-app() {
3535
;;
3636
--other)
3737
COMPREPLY=("${cur}")
38+
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
39+
compopt -o nospace
40+
fi
3841
return 0
3942
;;
4043
--path)
@@ -47,18 +50,30 @@ _my-app() {
4750
;;
4851
--file)
4952
COMPREPLY=($(compgen -f "${cur}"))
53+
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
54+
compopt -o filenames
55+
fi
5056
return 0
5157
;;
5258
-f)
5359
COMPREPLY=($(compgen -f "${cur}"))
60+
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
61+
compopt -o filenames
62+
fi
5463
return 0
5564
;;
5665
--dir)
57-
COMPREPLY=($(compgen -f "${cur}"))
66+
COMPREPLY=()
67+
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
68+
compopt -o plusdirs
69+
fi
5870
return 0
5971
;;
6072
-d)
61-
COMPREPLY=($(compgen -f "${cur}"))
73+
COMPREPLY=()
74+
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
75+
compopt -o plusdirs
76+
fi
6277
return 0
6378
;;
6479
--exe)

clap_complete/tests/testsuite/bash.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,58 @@ fn complete() {
174174
-V --generate --version quote pacman alias complete "#;
175175
let actual = runtime.complete(input, &term).unwrap();
176176
snapbox::assert_eq(expected, actual);
177+
178+
// Issue 5239 (https://github.com/clap-rs/clap/issues/5239)
179+
let input = "exhaustive hint --file test\t";
180+
let expected = "exhaustive hint --file test % exhaustive hint --file tests/";
181+
let actual = runtime.complete(input, &term).unwrap();
182+
snapbox::assert_eq(expected, actual);
183+
184+
{
185+
use std::fs::File;
186+
use std::path::Path;
187+
188+
let testdir = snapbox::path::PathFixture::mutable_temp().unwrap();
189+
let testdir_path = testdir.path().unwrap();
190+
191+
File::create(Path::new(testdir_path).join("a_file")).unwrap();
192+
File::create(Path::new(testdir_path).join("b_file")).unwrap();
193+
std::fs::create_dir(Path::new(testdir_path).join("c_dir")).unwrap();
194+
std::fs::create_dir(Path::new(testdir_path).join("d_dir")).unwrap();
195+
196+
let input = format!(
197+
"exhaustive hint --file {}/\t\t",
198+
testdir_path.to_string_lossy()
199+
);
200+
let actual = runtime.complete(input.as_str(), &term).unwrap();
201+
assert!(
202+
actual.contains("a_file")
203+
&& actual.contains("b_file")
204+
&& actual.contains("c_dir")
205+
&& actual.contains("d_dir"),
206+
"Actual output:\n{}",
207+
actual
208+
);
209+
210+
let input = format!(
211+
"exhaustive hint --dir {}/\t\t",
212+
testdir_path.to_string_lossy()
213+
);
214+
let actual = runtime.complete(input.as_str(), &term).unwrap();
215+
assert!(
216+
!actual.contains("a_file")
217+
&& !actual.contains("b_file")
218+
&& actual.contains("c_dir")
219+
&& actual.contains("d_dir"),
220+
"Actual output:\n{}",
221+
actual
222+
);
223+
}
224+
225+
let input = "exhaustive hint --other \t";
226+
let expected = "exhaustive hint --other % exhaustive hint --other ";
227+
let actual = runtime.complete(input, &term).unwrap();
228+
snapbox::assert_eq(expected, actual);
177229
}
178230

179231
#[test]

0 commit comments

Comments
 (0)