Skip to content

Commit 65b9c2b

Browse files
committed
test(complete): Helper for asserting dynamic completions
1 parent 5b10a9d commit 65b9c2b

File tree

1 file changed

+81
-69
lines changed

1 file changed

+81
-69
lines changed
Lines changed: 81 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,38 @@
11
#![cfg(feature = "unstable-dynamic")]
22

3+
use std::path::Path;
4+
5+
use clap::Command;
6+
7+
macro_rules! complete {
8+
($cmd:expr, $input:expr$(, current_dir = $current_dir:expr)? $(,)?) => {
9+
{
10+
#[allow(unused)]
11+
let current_dir = None;
12+
$(let current_dir = $current_dir;)?
13+
complete(&mut $cmd, $input, current_dir)
14+
}
15+
}
16+
}
17+
318
#[test]
419
fn suggest_subcommand_subset() {
5-
let name = "exhaustive";
6-
let mut cmd = clap::Command::new(name)
7-
.subcommand(clap::Command::new("hello-world"))
8-
.subcommand(clap::Command::new("hello-moon"))
9-
.subcommand(clap::Command::new("goodbye-world"));
10-
11-
let args = [name, "he"];
12-
let arg_index = 1;
13-
let args = IntoIterator::into_iter(args)
14-
.map(std::ffi::OsString::from)
15-
.collect::<Vec<_>>();
16-
let current_dir = None;
17-
18-
let completions =
19-
clap_complete::dynamic::complete(&mut cmd, args, arg_index, current_dir).unwrap();
20-
let completions = completions
21-
.into_iter()
22-
.map(|s| s.0.to_string_lossy().into_owned())
23-
.collect::<Vec<_>>();
24-
25-
assert_eq!(completions, ["hello-moon", "hello-world", "help"]);
20+
let mut cmd = Command::new("exhaustive")
21+
.subcommand(Command::new("hello-world"))
22+
.subcommand(Command::new("hello-moon"))
23+
.subcommand(Command::new("goodbye-world"));
24+
25+
snapbox::assert_eq(
26+
"hello-moon
27+
hello-world
28+
help\tPrint this message or the help of the given subcommand(s)",
29+
complete!(cmd, "he"),
30+
);
2631
}
2732

2833
#[test]
2934
fn suggest_long_flag_subset() {
30-
let name = "exhaustive";
31-
let mut cmd = clap::Command::new(name)
35+
let mut cmd = Command::new("exhaustive")
3236
.arg(
3337
clap::Arg::new("hello-world")
3438
.long("hello-world")
@@ -45,53 +49,33 @@ fn suggest_long_flag_subset() {
4549
.action(clap::ArgAction::Count),
4650
);
4751

48-
let args = [name, "--he"];
49-
let arg_index = 1;
50-
let args = IntoIterator::into_iter(args)
51-
.map(std::ffi::OsString::from)
52-
.collect::<Vec<_>>();
53-
let current_dir = None;
54-
55-
let completions =
56-
clap_complete::dynamic::complete(&mut cmd, args, arg_index, current_dir).unwrap();
57-
let completions = completions
58-
.into_iter()
59-
.map(|s| s.0.to_string_lossy().into_owned())
60-
.collect::<Vec<_>>();
61-
62-
assert_eq!(completions, ["--hello-world", "--hello-moon", "--help"]);
52+
snapbox::assert_eq(
53+
"--hello-world
54+
--hello-moon
55+
--help\tPrint help",
56+
complete!(cmd, "--he"),
57+
);
6358
}
6459

6560
#[test]
6661
fn suggest_possible_value_subset() {
6762
let name = "exhaustive";
68-
let mut cmd = clap::Command::new(name).arg(clap::Arg::new("hello-world").value_parser([
63+
let mut cmd = Command::new(name).arg(clap::Arg::new("hello-world").value_parser([
6964
"hello-world",
7065
"hello-moon",
7166
"goodbye-world",
7267
]));
7368

74-
let args = [name, "hello"];
75-
let arg_index = 1;
76-
let args = IntoIterator::into_iter(args)
77-
.map(std::ffi::OsString::from)
78-
.collect::<Vec<_>>();
79-
let current_dir = None;
80-
81-
let completions =
82-
clap_complete::dynamic::complete(&mut cmd, args, arg_index, current_dir).unwrap();
83-
let completions = completions
84-
.into_iter()
85-
.map(|s| s.0.to_string_lossy().into_owned())
86-
.collect::<Vec<_>>();
87-
88-
assert_eq!(completions, ["hello-world", "hello-moon"]);
69+
snapbox::assert_eq(
70+
"hello-world
71+
hello-moon",
72+
complete!(cmd, "hello"),
73+
);
8974
}
9075

9176
#[test]
9277
fn suggest_additional_short_flags() {
93-
let name = "exhaustive";
94-
let mut cmd = clap::Command::new(name)
78+
let mut cmd = Command::new("exhaustive")
9579
.arg(
9680
clap::Arg::new("a")
9781
.short('a')
@@ -108,19 +92,47 @@ fn suggest_additional_short_flags() {
10892
.action(clap::ArgAction::Count),
10993
);
11094

111-
let args = [name, "-a"];
112-
let arg_index = 1;
113-
let args = IntoIterator::into_iter(args)
114-
.map(std::ffi::OsString::from)
115-
.collect::<Vec<_>>();
116-
let current_dir = None;
95+
snapbox::assert_eq(
96+
"-aa
97+
-ab
98+
-ac
99+
-ah\tPrint help",
100+
complete!(cmd, "-a"),
101+
);
102+
}
117103

118-
let completions =
119-
clap_complete::dynamic::complete(&mut cmd, args, arg_index, current_dir).unwrap();
120-
let completions = completions
104+
fn complete(cmd: &mut Command, args: impl AsRef<str>, current_dir: Option<&Path>) -> String {
105+
let input = args.as_ref();
106+
let mut args = vec![std::ffi::OsString::from(cmd.get_name())];
107+
let arg_index;
108+
109+
if let Some((prior, after)) = input.split_once("[TAB]") {
110+
args.extend(prior.split_whitespace().map(From::from));
111+
if prior.ends_with(char::is_whitespace) {
112+
args.push(std::ffi::OsString::default())
113+
}
114+
arg_index = args.len() - 1;
115+
// HACK: this cannot handle in-word '[TAB]'
116+
args.extend(after.split_whitespace().map(From::from));
117+
} else {
118+
args.extend(input.split_whitespace().map(From::from));
119+
if input.ends_with(char::is_whitespace) {
120+
args.push(std::ffi::OsString::default())
121+
}
122+
arg_index = args.len() - 1;
123+
}
124+
125+
clap_complete::dynamic::complete(cmd, args, arg_index, current_dir)
126+
.unwrap()
121127
.into_iter()
122-
.map(|s| s.0.to_string_lossy().into_owned())
123-
.collect::<Vec<_>>();
124-
125-
assert_eq!(completions, ["-aa", "-ab", "-ac", "-ah"]);
128+
.map(|(compl, help)| {
129+
let compl = compl.to_str().unwrap();
130+
if let Some(help) = help {
131+
format!("{compl}\t{help}")
132+
} else {
133+
compl.to_owned()
134+
}
135+
})
136+
.collect::<Vec<_>>()
137+
.join("\n")
126138
}

0 commit comments

Comments
 (0)