Skip to content

Commit 51a319c

Browse files
committed
feat(complete): Added an map api for pathCompleter
1 parent fdbbf66 commit 51a319c

File tree

2 files changed

+70
-0
lines changed

2 files changed

+70
-0
lines changed

clap_complete/src/engine/custom.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,8 @@ pub struct PathCompleter {
216216
#[allow(clippy::type_complexity)]
217217
filter: Option<Box<dyn Fn(&std::path::Path) -> bool + Send + Sync>>,
218218
stdio: bool,
219+
#[allow(clippy::type_complexity)]
220+
mapper: Option<Box<dyn Fn(CompletionCandidate) -> CompletionCandidate + Send + Sync>>,
219221
}
220222

221223
impl PathCompleter {
@@ -225,6 +227,7 @@ impl PathCompleter {
225227
filter: None,
226228
current_dir: None,
227229
stdio: false,
230+
mapper: None,
228231
}
229232
}
230233

@@ -258,6 +261,15 @@ impl PathCompleter {
258261
self.current_dir = Some(path.into());
259262
self
260263
}
264+
265+
/// Transform completion candidates after filtering
266+
pub fn map(
267+
mut self,
268+
mapper: impl Fn(CompletionCandidate) -> CompletionCandidate + Send + Sync + 'static,
269+
) -> Self {
270+
self.mapper = Some(Box::new(mapper));
271+
self
272+
}
261273
}
262274

263275
impl Default for PathCompleter {
@@ -275,6 +287,12 @@ impl ValueCompleter for PathCompleter {
275287
current_dir_actual.as_deref()
276288
});
277289
let mut candidates = complete_path(current, current_dir, filter);
290+
if let Some(mapper) = &self.mapper {
291+
candidates = candidates
292+
.into_iter()
293+
.map(|candidate| mapper(candidate))
294+
.collect();
295+
}
278296
if self.stdio && current.is_empty() {
279297
candidates.push(CompletionCandidate::new("-").help(Some("stdio".into())));
280298
}

clap_complete/tests/testsuite/engine.rs

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1191,3 +1191,55 @@ fn complete(cmd: &mut Command, args: impl AsRef<str>, current_dir: Option<&Path>
11911191
.collect::<Vec<_>>()
11921192
.join("\n")
11931193
}
1194+
1195+
#[test]
1196+
fn suggest_value_path_with_mapper() {
1197+
let testdir = snapbox::dir::DirRoot::mutable_temp().unwrap();
1198+
let testdir_path = testdir.path().unwrap();
1199+
fs::write(testdir_path.join("a_file"), "").unwrap();
1200+
fs::write(testdir_path.join("b_file"), "").unwrap();
1201+
fs::create_dir_all(testdir_path.join("c_dir")).unwrap();
1202+
fs::create_dir_all(testdir_path.join("d_dir")).unwrap();
1203+
1204+
fs::write(testdir_path.join("c_dir").join("Cargo.toml"), "").unwrap();
1205+
1206+
let mut cmd = Command::new("dynamic")
1207+
.arg(
1208+
clap::Arg::new("input")
1209+
.long("input")
1210+
.short('i')
1211+
.add(ArgValueCompleter::new(
1212+
PathCompleter::dir()
1213+
.map(|candidate| {
1214+
let path = Path::new(candidate.get_value());
1215+
let cargo_toml_path = path.join("Cargo.toml");
1216+
let has_cargo_toml = cargo_toml_path.exists();
1217+
1218+
if has_cargo_toml {
1219+
candidate
1220+
.help(Some("Addable cargo package".into()))
1221+
.display_order(Some(0))
1222+
} else {
1223+
candidate.display_order(Some(1))
1224+
}
1225+
}),
1226+
)),
1227+
)
1228+
.args_conflicts_with_subcommands(true);
1229+
1230+
let original_dir = std::env::current_dir().unwrap();
1231+
std::env::set_current_dir(testdir_path).unwrap();
1232+
1233+
let result = complete!(cmd, "--input [TAB]", current_dir = None);
1234+
1235+
std::env::set_current_dir(original_dir).unwrap();
1236+
1237+
assert_data_eq!(
1238+
result,
1239+
snapbox::str![[r#"
1240+
c_dir/ Addable cargo package
1241+
.
1242+
d_dir/
1243+
"#]],
1244+
);
1245+
}

0 commit comments

Comments
 (0)