mirror of
https://github.com/clap-rs/clap
synced 2024-12-14 14:52:33 +00:00
1190 lines
27 KiB
Rust
1190 lines
27 KiB
Rust
#![cfg(feature = "unstable-dynamic")]
|
|
|
|
use std::fs;
|
|
use std::path::Path;
|
|
|
|
use clap::{builder::PossibleValue, Command};
|
|
use clap_complete::engine::{
|
|
ArgValueCandidates, ArgValueCompleter, CompletionCandidate, PathCompleter,
|
|
};
|
|
use snapbox::assert_data_eq;
|
|
|
|
macro_rules! complete {
|
|
($cmd:expr, $input:expr$(, current_dir = $current_dir:expr)? $(,)?) => {
|
|
{
|
|
#[allow(unused)]
|
|
let current_dir: Option<&Path> = None;
|
|
$(let current_dir = $current_dir;)?
|
|
complete(&mut $cmd, $input, current_dir)
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_subcommand_subset() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.subcommand(Command::new("hello-world"))
|
|
.subcommand(Command::new("hello-moon"))
|
|
.subcommand(Command::new("goodbye-world"));
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "he"),
|
|
snapbox::str![[r#"
|
|
hello-moon
|
|
hello-world
|
|
help Print this message or the help of the given subcommand(s)
|
|
"#]],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_hidden_long_flags() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.arg(clap::Arg::new("hello-world-visible").long("hello-world-visible"))
|
|
.arg(
|
|
clap::Arg::new("hello-world-hidden")
|
|
.long("hello-world-hidden")
|
|
.hide(true),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--hello-world"),
|
|
snapbox::str!["--hello-world-visible"]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--hello-world-h"),
|
|
snapbox::str!["--hello-world-hidden"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_hidden_subcommand_and_aliases() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.subcommand(
|
|
Command::new("test_visible")
|
|
.visible_alias("test_visible-alias_visible")
|
|
.alias("test_visible-alias_hidden"),
|
|
)
|
|
.subcommand(
|
|
Command::new("test_hidden")
|
|
.visible_alias("test_hidden-alias_visible")
|
|
.alias("test_hidden-alias_hidden")
|
|
.hide(true),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "test"),
|
|
snapbox::str![[r#"
|
|
test_visible
|
|
test_visible-alias_visible
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "test_h"),
|
|
snapbox::str![[r#"
|
|
test_hidden
|
|
test_hidden-alias_hidden
|
|
test_hidden-alias_visible
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "test_hidden-alias_h"),
|
|
snapbox::str!["test_hidden-alias_hidden"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_subcommand_aliases() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.subcommand(
|
|
Command::new("hello-world")
|
|
.visible_alias("hello-world-foo")
|
|
.alias("hidden-world"),
|
|
)
|
|
.subcommand(
|
|
Command::new("hello-moon")
|
|
.visible_alias("hello-moon-foo")
|
|
.alias("hidden-moon"),
|
|
)
|
|
.subcommand(
|
|
Command::new("goodbye-world")
|
|
.visible_alias("goodbye-world-foo")
|
|
.alias("hidden-goodbye"),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "hello"),
|
|
snapbox::str![[r#"
|
|
hello-moon
|
|
hello-moon-foo
|
|
hello-world
|
|
hello-world-foo
|
|
"#]],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_hidden_possible_value() {
|
|
let mut cmd = Command::new("exhaustive").arg(
|
|
clap::Arg::new("possible_value").long("test").value_parser([
|
|
PossibleValue::new("test-visible").help("Say hello to the world"),
|
|
PossibleValue::new("test-hidden")
|
|
.help("Say hello to the moon")
|
|
.hide(true),
|
|
]),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--test=test"),
|
|
snapbox::str!["--test=test-visible Say hello to the world"]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--test=test-h"),
|
|
snapbox::str!["--test=test-hidden Say hello to the moon"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_hidden_long_flag_aliases() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.arg(
|
|
clap::Arg::new("test_visible")
|
|
.long("test_visible")
|
|
.visible_alias("test_visible-alias_visible")
|
|
.alias("test_visible-alias_hidden"),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("test_hidden")
|
|
.long("test_hidden")
|
|
.visible_alias("test_hidden-alias_visible")
|
|
.alias("test_hidden-alias_hidden")
|
|
.hide(true),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--test"),
|
|
snapbox::str![[r#"
|
|
--test_visible
|
|
--test_visible-alias_visible
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--test_h"),
|
|
snapbox::str![[r#"
|
|
--test_hidden
|
|
--test_hidden-alias_visible
|
|
--test_hidden-alias_hidden
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--test_visible-alias_h"),
|
|
snapbox::str!["--test_visible-alias_hidden"]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--test_hidden-alias_h"),
|
|
snapbox::str!["--test_hidden-alias_hidden"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_long_flag_subset() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.arg(
|
|
clap::Arg::new("hello-world")
|
|
.long("hello-world")
|
|
.action(clap::ArgAction::Count),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("hello-moon")
|
|
.long("hello-moon")
|
|
.action(clap::ArgAction::Count),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("goodbye-world")
|
|
.long("goodbye-world")
|
|
.action(clap::ArgAction::Count),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--he"),
|
|
snapbox::str![[r#"
|
|
--hello-world
|
|
--hello-moon
|
|
--help Print help
|
|
"#]],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_possible_value_subset() {
|
|
let name = "exhaustive";
|
|
let mut cmd = Command::new(name).arg(clap::Arg::new("hello-world").value_parser([
|
|
PossibleValue::new("hello-world").help("Say hello to the world"),
|
|
"hello-moon".into(),
|
|
"goodbye-world".into(),
|
|
]));
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "hello"),
|
|
snapbox::str![[r#"
|
|
hello-world Say hello to the world
|
|
hello-moon
|
|
"#]],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_additional_short_flags() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.arg(
|
|
clap::Arg::new("a")
|
|
.short('a')
|
|
.action(clap::ArgAction::Count),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("b")
|
|
.short('b')
|
|
.action(clap::ArgAction::Count),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("c")
|
|
.short('c')
|
|
.action(clap::ArgAction::Count),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-a"),
|
|
snapbox::str![[r#"
|
|
-aa
|
|
-ab
|
|
-ac
|
|
-ah Print help
|
|
"#]],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_subcommand_positional() {
|
|
let mut cmd = Command::new("exhaustive").subcommand(Command::new("hello-world").arg(
|
|
clap::Arg::new("hello-world").value_parser([
|
|
PossibleValue::new("hello-world").help("Say hello to the world"),
|
|
"hello-moon".into(),
|
|
"goodbye-world".into(),
|
|
]),
|
|
));
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "hello-world [TAB]"),
|
|
snapbox::str![[r#"
|
|
--help Print help (see more with '--help')
|
|
-h Print help (see more with '--help')
|
|
hello-world Say hello to the world
|
|
hello-moon
|
|
goodbye-world
|
|
"#]],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_argument_value() {
|
|
let mut cmd = Command::new("dynamic")
|
|
.arg(
|
|
clap::Arg::new("format")
|
|
.long("format")
|
|
.short('F')
|
|
.value_parser(["json", "yaml", "toml"]),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("stream")
|
|
.long("stream")
|
|
.short('S')
|
|
.value_parser(["stdout", "stderr"]),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("count")
|
|
.long("count")
|
|
.short('c')
|
|
.action(clap::ArgAction::Count),
|
|
)
|
|
.arg(clap::Arg::new("positional").value_parser(["pos_a", "pos_b", "pos_c"]))
|
|
.args_conflicts_with_subcommands(true);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format [TAB]"),
|
|
snapbox::str![[r#"
|
|
json
|
|
yaml
|
|
toml
|
|
"#]],
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-F [TAB]"),
|
|
snapbox::str![[r#"
|
|
json
|
|
yaml
|
|
toml
|
|
"#]],
|
|
);
|
|
|
|
assert_data_eq!(complete!(cmd, "--format j[TAB]"), snapbox::str!["json"],);
|
|
|
|
assert_data_eq!(complete!(cmd, "-F j[TAB]"), snapbox::str!["json"],);
|
|
|
|
assert_data_eq!(complete!(cmd, "--format t[TAB]"), snapbox::str!["toml"],);
|
|
|
|
assert_data_eq!(complete!(cmd, "-F t[TAB]"), snapbox::str!["toml"],);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-cccF [TAB]"),
|
|
snapbox::str![[r#"
|
|
json
|
|
yaml
|
|
toml
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format toml [TAB]"),
|
|
snapbox::str![[r#"
|
|
--format
|
|
--stream
|
|
--count
|
|
--help Print help
|
|
-F
|
|
-S
|
|
-c
|
|
-h Print help
|
|
pos_a
|
|
pos_b
|
|
pos_c
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-cS[TAB]"),
|
|
snapbox::str![[r#"
|
|
-cSstdout
|
|
-cSstderr
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-cS=[TAB]"),
|
|
snapbox::str![[r#"
|
|
-cS=stdout
|
|
-cS=stderr
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(complete!(cmd, "-cS=stdo[TAB]"), snapbox::str!["-cS=stdout"]);
|
|
|
|
assert_data_eq!(complete!(cmd, "-cSF[TAB]"), snapbox::str![]);
|
|
|
|
assert_data_eq!(complete!(cmd, "-cSF=[TAB]"), snapbox::str![]);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_argument_multi_values() {
|
|
let mut cmd = Command::new("dynamic")
|
|
.arg(
|
|
clap::Arg::new("certain-num")
|
|
.long("certain-num")
|
|
.short('Y')
|
|
.value_parser(["val1", "val2", "val3"])
|
|
.num_args(3),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("uncertain-num")
|
|
.long("uncertain-num")
|
|
.short('N')
|
|
.value_parser(["val1", "val2", "val3"])
|
|
.num_args(1..=3),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--certain-num [TAB]"),
|
|
snapbox::str![[r#"
|
|
val1
|
|
val2
|
|
val3
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--certain-num val1 [TAB]"),
|
|
snapbox::str![[r#"
|
|
val1
|
|
val2
|
|
val3
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--certain-num val1 val2 val3 [TAB]"),
|
|
snapbox::str![[r#"
|
|
--certain-num
|
|
--uncertain-num
|
|
--help Print help
|
|
-Y
|
|
-N
|
|
-h Print help
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--uncertain-num [TAB]"),
|
|
snapbox::str![[r#"
|
|
val1
|
|
val2
|
|
val3
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--uncertain-num val1 [TAB]"),
|
|
snapbox::str![[r#"
|
|
val1
|
|
val2
|
|
val3
|
|
--certain-num
|
|
--uncertain-num
|
|
--help Print help
|
|
-Y
|
|
-N
|
|
-h Print help
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--uncertain-num val1 val2 val3 [TAB]"),
|
|
snapbox::str![[r#"
|
|
--certain-num
|
|
--uncertain-num
|
|
--help Print help
|
|
-Y
|
|
-N
|
|
-h Print help
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-Y [TAB]"),
|
|
snapbox::str![[r#"
|
|
val1
|
|
val2
|
|
val3
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-Y val1 [TAB]"),
|
|
snapbox::str![[r#"
|
|
val1
|
|
val2
|
|
val3
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-Y val1 val2 val3 [TAB]"),
|
|
snapbox::str![[r#"
|
|
--certain-num
|
|
--uncertain-num
|
|
--help Print help
|
|
-Y
|
|
-N
|
|
-h Print help
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-N [TAB]"),
|
|
snapbox::str![[r#"
|
|
val1
|
|
val2
|
|
val3
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-N val1 [TAB]"),
|
|
snapbox::str![[r#"
|
|
val1
|
|
val2
|
|
val3
|
|
--certain-num
|
|
--uncertain-num
|
|
--help Print help
|
|
-Y
|
|
-N
|
|
-h Print help
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-N val1 val2 val3 [TAB]"),
|
|
snapbox::str![[r#"
|
|
--certain-num
|
|
--uncertain-num
|
|
--help Print help
|
|
-Y
|
|
-N
|
|
-h Print help
|
|
"#]]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_value_hint_file_path() {
|
|
let mut cmd = Command::new("dynamic")
|
|
.arg(
|
|
clap::Arg::new("input")
|
|
.long("input")
|
|
.short('i')
|
|
.value_hint(clap::ValueHint::FilePath),
|
|
)
|
|
.args_conflicts_with_subcommands(true);
|
|
|
|
let testdir = snapbox::dir::DirRoot::mutable_temp().unwrap();
|
|
let testdir_path = testdir.path().unwrap();
|
|
|
|
fs::write(testdir_path.join("a_file"), "").unwrap();
|
|
fs::write(testdir_path.join("b_file"), "").unwrap();
|
|
fs::create_dir_all(testdir_path.join("c_dir")).unwrap();
|
|
fs::create_dir_all(testdir_path.join("d_dir")).unwrap();
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--input [TAB]", current_dir = Some(testdir_path)),
|
|
snapbox::str![[r#"
|
|
a_file
|
|
b_file
|
|
c_dir/
|
|
d_dir/
|
|
"#]],
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--input a[TAB]", current_dir = Some(testdir_path)),
|
|
snapbox::str!["a_file"],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_value_path_file() {
|
|
let testdir = snapbox::dir::DirRoot::mutable_temp().unwrap();
|
|
let testdir_path = testdir.path().unwrap();
|
|
fs::write(testdir_path.join("a_file"), "").unwrap();
|
|
fs::write(testdir_path.join("b_file"), "").unwrap();
|
|
fs::create_dir_all(testdir_path.join("c_dir")).unwrap();
|
|
fs::create_dir_all(testdir_path.join("d_dir")).unwrap();
|
|
|
|
let mut cmd = Command::new("dynamic")
|
|
.arg(
|
|
clap::Arg::new("input")
|
|
.long("input")
|
|
.short('i')
|
|
.add(ArgValueCompleter::new(
|
|
PathCompleter::file()
|
|
.stdio()
|
|
.current_dir(testdir_path.to_owned()),
|
|
)),
|
|
)
|
|
.args_conflicts_with_subcommands(true);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--input [TAB]", current_dir = Some(testdir_path)),
|
|
snapbox::str![[r#"
|
|
a_file
|
|
b_file
|
|
c_dir/
|
|
d_dir/
|
|
- stdio
|
|
"#]],
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--input a[TAB]", current_dir = Some(testdir_path)),
|
|
snapbox::str!["a_file"],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_value_path_dir() {
|
|
let testdir = snapbox::dir::DirRoot::mutable_temp().unwrap();
|
|
let testdir_path = testdir.path().unwrap();
|
|
fs::write(testdir_path.join("a_file"), "").unwrap();
|
|
fs::write(testdir_path.join("b_file"), "").unwrap();
|
|
fs::create_dir_all(testdir_path.join("c_dir")).unwrap();
|
|
fs::create_dir_all(testdir_path.join("d_dir")).unwrap();
|
|
|
|
let mut cmd = Command::new("dynamic")
|
|
.arg(
|
|
clap::Arg::new("input")
|
|
.long("input")
|
|
.short('i')
|
|
.add(ArgValueCompleter::new(
|
|
PathCompleter::dir().current_dir(testdir_path.to_owned()),
|
|
)),
|
|
)
|
|
.args_conflicts_with_subcommands(true);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--input [TAB]", current_dir = Some(testdir_path)),
|
|
snapbox::str![[r#"
|
|
.
|
|
c_dir/
|
|
d_dir/
|
|
"#]],
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--input c[TAB]", current_dir = Some(testdir_path)),
|
|
snapbox::str!["c_dir/"],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_custom_arg_value() {
|
|
fn custom_completer() -> Vec<CompletionCandidate> {
|
|
vec![
|
|
CompletionCandidate::new("foo"),
|
|
CompletionCandidate::new("bar"),
|
|
CompletionCandidate::new("baz"),
|
|
]
|
|
}
|
|
|
|
let mut cmd = Command::new("dynamic").arg(
|
|
clap::Arg::new("custom")
|
|
.long("custom")
|
|
.add(ArgValueCandidates::new(custom_completer)),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--custom [TAB]"),
|
|
snapbox::str![[r#"
|
|
foo
|
|
bar
|
|
baz
|
|
"#]],
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--custom b[TAB]"),
|
|
snapbox::str![[r#"
|
|
bar
|
|
baz
|
|
"#]],
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_custom_arg_completer() {
|
|
fn custom_completer(current: &std::ffi::OsStr) -> Vec<CompletionCandidate> {
|
|
let mut completions = vec![];
|
|
let Some(current) = current.to_str() else {
|
|
return completions;
|
|
};
|
|
|
|
if "foo".starts_with(current) {
|
|
completions.push(CompletionCandidate::new("foo"));
|
|
}
|
|
if "bar".starts_with(current) {
|
|
completions.push(CompletionCandidate::new("bar"));
|
|
}
|
|
if "baz".starts_with(current) {
|
|
completions.push(CompletionCandidate::new("baz"));
|
|
}
|
|
completions
|
|
}
|
|
|
|
let mut cmd = Command::new("dynamic").arg(
|
|
clap::Arg::new("custom")
|
|
.long("custom")
|
|
.add(ArgValueCompleter::new(custom_completer)),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--custom [TAB]"),
|
|
snapbox::str![[r#"
|
|
foo
|
|
bar
|
|
baz
|
|
"#]]
|
|
);
|
|
assert_data_eq!(
|
|
complete!(cmd, "--custom b[TAB]"),
|
|
snapbox::str![[r#"
|
|
bar
|
|
baz
|
|
"#]]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_multi_positional() {
|
|
let mut cmd = Command::new("dynamic")
|
|
.arg(
|
|
clap::Arg::new("positional")
|
|
.value_parser(["pos_1, pos_2, pos_3"])
|
|
.index(1),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("positional-2")
|
|
.value_parser(["pos_a", "pos_b", "pos_c"])
|
|
.index(2)
|
|
.num_args(3),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("--format")
|
|
.long("format")
|
|
.short('F')
|
|
.value_parser(["json", "yaml", "toml"]),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "pos_1 pos_a [TAB]"),
|
|
snapbox::str![[r#"
|
|
pos_a
|
|
pos_b
|
|
pos_c
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "pos_1 pos_a pos_b [TAB]"),
|
|
snapbox::str![[r#"
|
|
pos_a
|
|
pos_b
|
|
pos_c
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format json pos_1 [TAB]"),
|
|
snapbox::str![[r#"
|
|
--format
|
|
--help Print help
|
|
-F
|
|
-h Print help
|
|
pos_a
|
|
pos_b
|
|
pos_c
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format json pos_1 pos_a [TAB]"),
|
|
snapbox::str![[r#"
|
|
pos_a
|
|
pos_b
|
|
pos_c
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format json pos_1 pos_a pos_b pos_c [TAB]"),
|
|
snapbox::str![[r#"
|
|
--format
|
|
--help Print help
|
|
-F
|
|
-h Print help
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format json -- pos_1 pos_a [TAB]"),
|
|
snapbox::str![[r#"
|
|
pos_a
|
|
pos_b
|
|
pos_c
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format json -- pos_1 pos_a pos_b [TAB]"),
|
|
snapbox::str![[r#"
|
|
pos_a
|
|
pos_b
|
|
pos_c
|
|
"#]]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format json -- pos_1 pos_a pos_b pos_c [TAB]"),
|
|
snapbox::str![]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_delimiter_values() {
|
|
let mut cmd = Command::new("delimiter")
|
|
.arg(
|
|
clap::Arg::new("delimiter")
|
|
.long("delimiter")
|
|
.short('D')
|
|
.value_parser([
|
|
PossibleValue::new("comma"),
|
|
PossibleValue::new("space"),
|
|
PossibleValue::new("tab"),
|
|
])
|
|
.value_delimiter(','),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("pos")
|
|
.index(1)
|
|
.value_parser(["a_pos", "b_pos", "c_pos"])
|
|
.value_delimiter(','),
|
|
);
|
|
|
|
assert_data_eq!(complete!(cmd, "--delimiter [TAB]"), snapbox::str![[r#"
|
|
comma
|
|
space
|
|
tab
|
|
"#]]);
|
|
|
|
assert_data_eq!(complete!(cmd, "--delimiter=[TAB]"), snapbox::str![[r#"
|
|
--delimiter=comma
|
|
--delimiter=space
|
|
--delimiter=tab
|
|
"#]]);
|
|
|
|
assert_data_eq!(complete!(cmd, "--delimiter c[TAB]"), snapbox::str!["comma"]);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--delimiter=c[TAB]"),
|
|
snapbox::str!["--delimiter=comma"]
|
|
);
|
|
|
|
assert_data_eq!(complete!(cmd, "--delimiter comma,[TAB]"), snapbox::str![[r#"
|
|
comma,comma
|
|
comma,space
|
|
comma,tab
|
|
"#]]);
|
|
|
|
assert_data_eq!(complete!(cmd, "--delimiter=comma,[TAB]"), snapbox::str![[r#"
|
|
--delimiter=comma,comma
|
|
--delimiter=comma,space
|
|
--delimiter=comma,tab
|
|
--delimiter=comma,a_pos
|
|
--delimiter=comma,b_pos
|
|
--delimiter=comma,c_pos
|
|
"#]]);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--delimiter comma,s[TAB]"),
|
|
snapbox::str!["comma,space"]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--delimiter=comma,s[TAB]"),
|
|
snapbox::str!["--delimiter=comma,space"]
|
|
);
|
|
|
|
assert_data_eq!(complete!(cmd, "-D [TAB]"), snapbox::str![[r#"
|
|
comma
|
|
space
|
|
tab
|
|
"#]]);
|
|
|
|
assert_data_eq!(complete!(cmd, "-D=[TAB]"), snapbox::str![[r#"
|
|
-D=comma
|
|
-D=space
|
|
-D=tab
|
|
"#]]);
|
|
|
|
assert_data_eq!(complete!(cmd, "-D c[TAB]"), snapbox::str!["comma"]);
|
|
|
|
assert_data_eq!(complete!(cmd, "-D=c[TAB]"), snapbox::str!["-D=comma"]);
|
|
|
|
assert_data_eq!(complete!(cmd, "-D comma,[TAB]"), snapbox::str![[r#"
|
|
comma,comma
|
|
comma,space
|
|
comma,tab
|
|
"#]]);
|
|
|
|
assert_data_eq!(complete!(cmd, "-D=comma,[TAB]"), snapbox::str![[r#"
|
|
-D=comma,comma
|
|
-D=comma,space
|
|
-D=comma,tab
|
|
-D=comma,a_pos
|
|
-D=comma,b_pos
|
|
-D=comma,c_pos
|
|
"#]]);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-D comma,s[TAB]"),
|
|
snapbox::str!["comma,space"]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-D=comma,s[TAB]"),
|
|
snapbox::str!["-D=comma,space"]
|
|
);
|
|
|
|
assert_data_eq!(complete!(cmd, "-- [TAB]"), snapbox::str![[r#"
|
|
--delimiter
|
|
--help Print help
|
|
-D
|
|
-h Print help
|
|
a_pos
|
|
b_pos
|
|
c_pos
|
|
"#]]);
|
|
|
|
assert_data_eq!(complete!(cmd, " -- a_pos,[TAB]"), snapbox::str![[r#"
|
|
a_pos,a_pos
|
|
a_pos,b_pos
|
|
a_pos,c_pos
|
|
"#]]);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-- a_pos,b[TAB]"),
|
|
snapbox::str!["a_pos,b_pos"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_allow_hyphen() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.arg(
|
|
clap::Arg::new("format")
|
|
.long("format")
|
|
.short('F')
|
|
.allow_hyphen_values(true)
|
|
.value_parser(["--json", "--toml", "--yaml"]),
|
|
)
|
|
.arg(clap::Arg::new("json").long("json"));
|
|
|
|
assert_data_eq!(complete!(cmd, "--format --j[TAB]"), snapbox::str!["--json"]);
|
|
assert_data_eq!(complete!(cmd, "-F --j[TAB]"), snapbox::str!["--json"]);
|
|
assert_data_eq!(complete!(cmd, "--format --t[TAB]"), snapbox::str!["--toml"]);
|
|
assert_data_eq!(complete!(cmd, "-F --t[TAB]"), snapbox::str!["--toml"]);
|
|
|
|
assert_data_eq!(complete!(cmd, "--format --[TAB]"), snapbox::str![[r#"
|
|
--json
|
|
--toml
|
|
--yaml
|
|
"#]]);
|
|
|
|
assert_data_eq!(complete!(cmd, "-F --[TAB]"), snapbox::str![[r#"
|
|
--json
|
|
--toml
|
|
--yaml
|
|
"#]]);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format --json --j[TAB]"),
|
|
snapbox::str!["--json"]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "-F --json --j[TAB]"),
|
|
snapbox::str!["--json"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_positional_long_allow_hyphen() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.arg(
|
|
clap::Arg::new("format")
|
|
.long("format")
|
|
.short('F')
|
|
.allow_hyphen_values(true)
|
|
.value_parser(["--json", "--toml", "--yaml"]),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("positional_a")
|
|
.value_parser(["--pos_a"])
|
|
.index(1)
|
|
.allow_hyphen_values(true),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("positional_b")
|
|
.index(2)
|
|
.value_parser(["pos_b"]),
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format --json --pos[TAB]"),
|
|
snapbox::str!["--pos_a"]
|
|
);
|
|
assert_data_eq!(
|
|
complete!(cmd, "-F --json --pos[TAB]"),
|
|
snapbox::str!["--pos_a"]
|
|
);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format --json --pos_a [TAB]"),
|
|
snapbox::str![[r#"
|
|
--format
|
|
--help Print help
|
|
-F
|
|
-h Print help
|
|
pos_b
|
|
"#]]
|
|
);
|
|
assert_data_eq!(complete!(cmd, "-F --json --pos_a [TAB]"), snapbox::str![[r#"
|
|
--format
|
|
--help Print help
|
|
-F
|
|
-h Print help
|
|
pos_b
|
|
"#]]);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format --json --pos_a p[TAB]"),
|
|
snapbox::str!["pos_b"]
|
|
);
|
|
assert_data_eq!(
|
|
complete!(cmd, "-F --json --pos_a p[TAB]"),
|
|
snapbox::str!["pos_b"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn suggest_positional_short_allow_hyphen() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.arg(
|
|
clap::Arg::new("format")
|
|
.long("format")
|
|
.short('F')
|
|
.allow_hyphen_values(true)
|
|
.value_parser(["--json", "--toml", "--yaml"]),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("positional_a")
|
|
.value_parser(["-a"])
|
|
.index(1)
|
|
.allow_hyphen_values(true),
|
|
)
|
|
.arg(
|
|
clap::Arg::new("positional_b")
|
|
.index(2)
|
|
.value_parser(["pos_b"]),
|
|
);
|
|
|
|
assert_data_eq!(complete!(cmd, "--format --json -a [TAB]"), snapbox::str![[r#"
|
|
--format
|
|
--help Print help
|
|
-F
|
|
-h Print help
|
|
pos_b
|
|
"#]]);
|
|
assert_data_eq!(complete!(cmd, "-F --json -a [TAB]"), snapbox::str![[r#"
|
|
--format
|
|
--help Print help
|
|
-F
|
|
-h Print help
|
|
pos_b
|
|
"#]]);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, "--format --json -a p[TAB]"),
|
|
snapbox::str!["pos_b"]
|
|
);
|
|
assert_data_eq!(
|
|
complete!(cmd, "-F --json -a p[TAB]"),
|
|
snapbox::str!["pos_b"]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn sort_and_filter() {
|
|
let mut cmd = Command::new("exhaustive")
|
|
.args([
|
|
clap::Arg::new("required-flag")
|
|
.long("required-flag")
|
|
.visible_alias("required-flag2")
|
|
.short('r')
|
|
.required(true),
|
|
clap::Arg::new("optional-flag")
|
|
.long("optional-flag")
|
|
.visible_alias("2optional-flag")
|
|
.short('o'),
|
|
clap::Arg::new("long-flag").long("long-flag"),
|
|
clap::Arg::new("short-flag").short('s'),
|
|
clap::Arg::new("positional").value_parser(["pos-a", "pos-b", "pos-c"]),
|
|
])
|
|
.subcommands([Command::new("sub")]);
|
|
|
|
assert_data_eq!(
|
|
complete!(cmd, " [TAB]"),
|
|
snapbox::str![[r#"
|
|
--required-flag
|
|
--required-flag2
|
|
--optional-flag
|
|
--2optional-flag
|
|
--long-flag
|
|
--help Print help
|
|
-r
|
|
-o
|
|
-s
|
|
-h Print help
|
|
pos-a
|
|
pos-b
|
|
pos-c
|
|
help Print this message or the help of the given subcommand(s)
|
|
sub
|
|
"#]]
|
|
);
|
|
assert_data_eq!(
|
|
complete!(cmd, "-[TAB]"),
|
|
snapbox::str![[r#"
|
|
--required-flag
|
|
--required-flag2
|
|
--optional-flag
|
|
--2optional-flag
|
|
--long-flag
|
|
--help Print help
|
|
-r
|
|
-o
|
|
-s
|
|
-h Print help
|
|
"#]]
|
|
);
|
|
}
|
|
|
|
fn complete(cmd: &mut Command, args: impl AsRef<str>, current_dir: Option<&Path>) -> String {
|
|
let input = args.as_ref();
|
|
let mut args = vec![std::ffi::OsString::from(cmd.get_name())];
|
|
let arg_index;
|
|
|
|
if let Some((prior, after)) = input.split_once("[TAB]") {
|
|
args.extend(prior.split_whitespace().map(From::from));
|
|
if prior.ends_with(char::is_whitespace) {
|
|
args.push(std::ffi::OsString::default());
|
|
}
|
|
arg_index = args.len() - 1;
|
|
// HACK: this cannot handle in-word '[TAB]'
|
|
args.extend(after.split_whitespace().map(From::from));
|
|
} else {
|
|
args.extend(input.split_whitespace().map(From::from));
|
|
if input.ends_with(char::is_whitespace) {
|
|
args.push(std::ffi::OsString::default());
|
|
}
|
|
arg_index = args.len() - 1;
|
|
}
|
|
|
|
clap_complete::engine::complete(cmd, args, arg_index, current_dir)
|
|
.unwrap()
|
|
.into_iter()
|
|
.map(|candidate| {
|
|
let compl = candidate.get_value().to_str().unwrap();
|
|
if let Some(help) = candidate.get_help() {
|
|
format!("{compl}\t{help}")
|
|
} else {
|
|
compl.to_owned()
|
|
}
|
|
})
|
|
.collect::<Vec<_>>()
|
|
.join("\n")
|
|
}
|