Skip to content

Commit

Permalink
Merge pull request #5240 from sudotac/improve-bash-completion-with-co…
Browse files Browse the repository at this point in the history
…mpopt

Improve bash completion with compopt
  • Loading branch information
epage authored Jan 22, 2024
2 parents d18c327 + 13a7980 commit 646134a
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 20 deletions.
55 changes: 39 additions & 16 deletions clap_complete/src/shells/bash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,29 +169,50 @@ fn option_details_for_path(cmd: &Command, path: &str) -> String {
let mut opts = vec![String::new()];

for o in p.get_opts() {
let compopt = match o.get_value_hint() {
ValueHint::FilePath => Some("compopt -o filenames"),
ValueHint::DirPath => Some("compopt -o plusdirs"),
ValueHint::Other => Some("compopt -o nospace"),
_ => None,
};

if let Some(longs) = o.get_long_and_visible_aliases() {
opts.extend(longs.iter().map(|long| {
format!(
"--{})
COMPREPLY=({})
return 0
;;",
long,
vals_for(o)
)
let mut v = vec![
format!("--{})", long),
format!("COMPREPLY=({})", vals_for(o)),
];

if let Some(copt) = compopt {
v.extend([
r#"if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then"#.to_string(),
format!(" {}", copt),
"fi".to_string(),
]);
}

v.extend(["return 0", ";;"].iter().map(|s| s.to_string()));
v.join("\n ")
}));
}

if let Some(shorts) = o.get_short_and_visible_aliases() {
opts.extend(shorts.iter().map(|short| {
format!(
"-{})
COMPREPLY=({})
return 0
;;",
short,
vals_for(o)
)
let mut v = vec![
format!("-{})", short),
format!("COMPREPLY=({})", vals_for(o)),
];

if let Some(copt) = compopt {
v.extend([
r#"if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then"#.to_string(),
format!(" {}", copt),
"fi".to_string(),
]);
}

v.extend(["return 0", ";;"].iter().map(|s| s.to_string()));
v.join("\n ")
}));
}
}
Expand All @@ -211,6 +232,8 @@ fn vals_for(o: &Arg) -> String {
.collect::<Vec<_>>()
.join(" ")
)
} else if o.get_value_hint() == ValueHint::DirPath {
String::from("") // should be empty to avoid duplicate candidates
} else if o.get_value_hint() == ValueHint::Other {
String::from("\"${cur}\"")
} else {
Expand Down
19 changes: 17 additions & 2 deletions clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@ _exhaustive() {
;;
--other)
COMPREPLY=("${cur}")
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o nospace
fi
return 0
;;
--path)
Expand All @@ -554,18 +557,30 @@ _exhaustive() {
;;
--file)
COMPREPLY=($(compgen -f "${cur}"))
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o filenames
fi
return 0
;;
-f)
COMPREPLY=($(compgen -f "${cur}"))
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o filenames
fi
return 0
;;
--dir)
COMPREPLY=($(compgen -f "${cur}"))
COMPREPLY=()
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o plusdirs
fi
return 0
;;
-d)
COMPREPLY=($(compgen -f "${cur}"))
COMPREPLY=()
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o plusdirs
fi
return 0
;;
--exe)
Expand Down
19 changes: 17 additions & 2 deletions clap_complete/tests/snapshots/value_hint.bash
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ _my-app() {
;;
--other)
COMPREPLY=("${cur}")
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o nospace
fi
return 0
;;
--path)
Expand All @@ -47,18 +50,30 @@ _my-app() {
;;
--file)
COMPREPLY=($(compgen -f "${cur}"))
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o filenames
fi
return 0
;;
-f)
COMPREPLY=($(compgen -f "${cur}"))
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o filenames
fi
return 0
;;
--dir)
COMPREPLY=($(compgen -f "${cur}"))
COMPREPLY=()
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o plusdirs
fi
return 0
;;
-d)
COMPREPLY=($(compgen -f "${cur}"))
COMPREPLY=()
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
compopt -o plusdirs
fi
return 0
;;
--exe)
Expand Down
52 changes: 52 additions & 0 deletions clap_complete/tests/testsuite/bash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,58 @@ fn complete() {
-V --generate --version quote pacman alias complete "#;
let actual = runtime.complete(input, &term).unwrap();
snapbox::assert_eq(expected, actual);

// Issue 5239 (https://github.com/clap-rs/clap/issues/5239)
let input = "exhaustive hint --file test\t";
let expected = "exhaustive hint --file test % exhaustive hint --file tests/";
let actual = runtime.complete(input, &term).unwrap();
snapbox::assert_eq(expected, actual);

{
use std::fs::File;
use std::path::Path;

let testdir = snapbox::path::PathFixture::mutable_temp().unwrap();
let testdir_path = testdir.path().unwrap();

File::create(Path::new(testdir_path).join("a_file")).unwrap();
File::create(Path::new(testdir_path).join("b_file")).unwrap();
std::fs::create_dir(Path::new(testdir_path).join("c_dir")).unwrap();
std::fs::create_dir(Path::new(testdir_path).join("d_dir")).unwrap();

let input = format!(
"exhaustive hint --file {}/\t\t",
testdir_path.to_string_lossy()
);
let actual = runtime.complete(input.as_str(), &term).unwrap();
assert!(
actual.contains("a_file")
&& actual.contains("b_file")
&& actual.contains("c_dir")
&& actual.contains("d_dir"),
"Actual output:\n{}",
actual
);

let input = format!(
"exhaustive hint --dir {}/\t\t",
testdir_path.to_string_lossy()
);
let actual = runtime.complete(input.as_str(), &term).unwrap();
assert!(
!actual.contains("a_file")
&& !actual.contains("b_file")
&& actual.contains("c_dir")
&& actual.contains("d_dir"),
"Actual output:\n{}",
actual
);
}

let input = "exhaustive hint --other \t";
let expected = "exhaustive hint --other % exhaustive hint --other ";
let actual = runtime.complete(input, &term).unwrap();
snapbox::assert_eq(expected, actual);
}

#[test]
Expand Down

0 comments on commit 646134a

Please sign in to comment.