From 62a5ace9f1e71d10d4e947f7c8a1656e9a34a778 Mon Sep 17 00:00:00 2001 From: sudotac Date: Sun, 10 Dec 2023 21:09:26 +0900 Subject: [PATCH 1/4] test(complete): Verify some variants of ValueHint --- clap_complete/tests/testsuite/bash.rs | 52 +++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index 2e960136704..23d9353324f 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -161,6 +161,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] From 3a222def2221ef379033eab22840745cb50b8a7f Mon Sep 17 00:00:00 2001 From: sudotac Date: Sun, 3 Dec 2023 17:13:57 +0900 Subject: [PATCH 2/4] fix(complete): Fix path completion in bash Fix #5239 --- clap_complete/src/shells/bash.rs | 51 +++++++++++++------ .../home/static/exhaustive/bash/.bashrc | 6 +++ clap_complete/tests/snapshots/value_hint.bash | 6 +++ clap_complete/tests/testsuite/bash.rs | 2 +- 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/clap_complete/src/shells/bash.rs b/clap_complete/src/shells/bash.rs index 726078ab366..8c2755f68d7 100644 --- a/clap_complete/src/shells/bash.rs +++ b/clap_complete/src/shells/bash.rs @@ -168,29 +168,48 @@ 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"), + _ => 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 ") })); } } diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc index 490fcbf3c1d..42621a2db84 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc +++ b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc @@ -554,10 +554,16 @@ _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) diff --git a/clap_complete/tests/snapshots/value_hint.bash b/clap_complete/tests/snapshots/value_hint.bash index 61e2f61e96e..ca042c541e4 100644 --- a/clap_complete/tests/snapshots/value_hint.bash +++ b/clap_complete/tests/snapshots/value_hint.bash @@ -47,10 +47,16 @@ _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) diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index 23d9353324f..66a6e4c54ce 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -164,7 +164,7 @@ fn complete() { // 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 expected = "exhaustive hint --file test % exhaustive hint --file tests/"; let actual = runtime.complete(input, &term).unwrap(); snapbox::assert_eq(expected, actual); From e25b1abddf7c32001a15caf0aa1e2630a6822ea5 Mon Sep 17 00:00:00 2001 From: sudotac Date: Sun, 3 Dec 2023 17:20:38 +0900 Subject: [PATCH 3/4] feat(complete): Add DirPath support in bash --- clap_complete/src/shells/bash.rs | 3 +++ .../snapshots/home/static/exhaustive/bash/.bashrc | 10 ++++++++-- clap_complete/tests/snapshots/value_hint.bash | 10 ++++++++-- clap_complete/tests/testsuite/bash.rs | 4 ++-- 4 files changed, 21 insertions(+), 6 deletions(-) diff --git a/clap_complete/src/shells/bash.rs b/clap_complete/src/shells/bash.rs index 8c2755f68d7..ed15b1e8eea 100644 --- a/clap_complete/src/shells/bash.rs +++ b/clap_complete/src/shells/bash.rs @@ -170,6 +170,7 @@ fn option_details_for_path(cmd: &Command, path: &str) -> String { 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"), _ => None, }; @@ -229,6 +230,8 @@ fn vals_for(o: &Arg) -> String { .collect::>() .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 { diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc index 42621a2db84..50f1b0ff0a1 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc +++ b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc @@ -567,11 +567,17 @@ _exhaustive() { 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) diff --git a/clap_complete/tests/snapshots/value_hint.bash b/clap_complete/tests/snapshots/value_hint.bash index ca042c541e4..19eba6b4bce 100644 --- a/clap_complete/tests/snapshots/value_hint.bash +++ b/clap_complete/tests/snapshots/value_hint.bash @@ -60,11 +60,17 @@ _my-app() { 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) diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index 66a6e4c54ce..a1f18a3e58f 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -200,8 +200,8 @@ fn complete() { ); let actual = runtime.complete(input.as_str(), &term).unwrap(); assert!( - actual.contains("a_file") - && actual.contains("b_file") + !actual.contains("a_file") + && !actual.contains("b_file") && actual.contains("c_dir") && actual.contains("d_dir"), "Actual output:\n{}", From 13a79804c958c5b58ac509f97edee9bf10aaad43 Mon Sep 17 00:00:00 2001 From: sudotac Date: Sun, 3 Dec 2023 17:27:00 +0900 Subject: [PATCH 4/4] fix(complete): Suppress a useless space completion --- clap_complete/src/shells/bash.rs | 1 + .../tests/snapshots/home/static/exhaustive/bash/.bashrc | 3 +++ clap_complete/tests/snapshots/value_hint.bash | 3 +++ clap_complete/tests/testsuite/bash.rs | 2 +- 4 files changed, 8 insertions(+), 1 deletion(-) diff --git a/clap_complete/src/shells/bash.rs b/clap_complete/src/shells/bash.rs index ed15b1e8eea..cc9ce2a874e 100644 --- a/clap_complete/src/shells/bash.rs +++ b/clap_complete/src/shells/bash.rs @@ -171,6 +171,7 @@ fn option_details_for_path(cmd: &Command, path: &str) -> String { 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, }; diff --git a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc index 50f1b0ff0a1..2f72ccf40b2 100644 --- a/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc +++ b/clap_complete/tests/snapshots/home/static/exhaustive/bash/.bashrc @@ -542,6 +542,9 @@ _exhaustive() { ;; --other) COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi return 0 ;; --path) diff --git a/clap_complete/tests/snapshots/value_hint.bash b/clap_complete/tests/snapshots/value_hint.bash index 19eba6b4bce..35f6b3d56b1 100644 --- a/clap_complete/tests/snapshots/value_hint.bash +++ b/clap_complete/tests/snapshots/value_hint.bash @@ -35,6 +35,9 @@ _my-app() { ;; --other) COMPREPLY=("${cur}") + if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then + compopt -o nospace + fi return 0 ;; --path) diff --git a/clap_complete/tests/testsuite/bash.rs b/clap_complete/tests/testsuite/bash.rs index a1f18a3e58f..7edb458cc9d 100644 --- a/clap_complete/tests/testsuite/bash.rs +++ b/clap_complete/tests/testsuite/bash.rs @@ -210,7 +210,7 @@ fn complete() { } let input = "exhaustive hint --other \t"; - let expected = "exhaustive hint --other % exhaustive hint --other "; + let expected = "exhaustive hint --other % exhaustive hint --other "; let actual = runtime.complete(input, &term).unwrap(); snapbox::assert_eq(expected, actual); }