Skip to content

Commit

Permalink
feat(clap_complete): Support multi-values of positional argument with…
Browse files Browse the repository at this point in the history
… `num_arg(N)`
  • Loading branch information
shannmu committed Jul 26, 2024
1 parent 076a138 commit 788a1cc
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 20 deletions.
72 changes: 66 additions & 6 deletions clap_complete/src/dynamic/completer.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::num;
use std::ffi::OsStr;
use std::ffi::OsString;

Expand Down Expand Up @@ -71,8 +72,37 @@ pub fn complete(
}

if is_escaped {
pos_index += 1;
state = ParseState::Pos(pos_index);
match state {
ParseState::ValueDone => {
// Begin parsing a new positional argument after `--`.
let num_args = parse_positional(current_cmd, pos_index);
match num_args {
Some(num_args) => {
state = ParseState::Pos(1);
if num_args <= 1 {
pos_index += 1;
}
}
None => {
state = ParseState::ValueDone;
pos_index += 1;
}
}
}
ParseState::Pos(num_arg) => {
// HACK: Assuming `num_args` is fixed.
let num_args = parse_positional(current_cmd, pos_index).unwrap_or(1);
if num_arg + 1 < num_args {
state = ParseState::Pos(num_arg + 1);
} else {
state = ParseState::Pos(1);
pos_index += 1;
}
}
ParseState::Opt(..) => {
unreachable!("reason it won't be hit")
}
}
} else if arg.is_escape() {
is_escaped = true;
state = ParseState::ValueDone;
Expand Down Expand Up @@ -124,9 +154,32 @@ pub fn complete(
}
} else {
match state {
ParseState::ValueDone | ParseState::Pos(_) => {
pos_index += 1;
state = ParseState::ValueDone;
ParseState::ValueDone => {
let num_args = parse_positional(current_cmd, pos_index);
match num_args {
Some(num_args) => {
if num_args > 1 {
state = ParseState::Pos(1);
} else {
state = ParseState::ValueDone;
pos_index += 1;
}
}
None => {
state = ParseState::ValueDone;
pos_index += 1;
}
}
}
ParseState::Pos(num_arg) => {
// HACK: Assuming `num_args` is fixed.
let num_args = parse_positional(current_cmd, pos_index).unwrap_or(1);
if num_arg + 1 < num_args {
state = ParseState::Pos(num_arg + 1);
} else {
state = ParseState::ValueDone;
pos_index += 1;
}
}
ParseState::Opt((ref opt, count)) => match opt.get_num_args() {
Some(range) => {
Expand Down Expand Up @@ -156,7 +209,7 @@ enum ParseState {
/// Parsing a value done, there is no state to record.
ValueDone,

/// Parsing a positional argument after `--`
/// Parsing a positional argument after `--`. Pos(takes_num_args)
Pos(usize),

/// Parsing a optional flag argument
Expand Down Expand Up @@ -603,6 +656,13 @@ fn parse_shortflags<'s>(
(leading_flags, takes_value_opt.cloned(), short)
}

fn parse_positional(cmd: &clap::Command, pos_index: usize) -> Option<usize> {
let pos_arg = cmd
.get_positionals()
.find(|p| p.get_index() == Some(pos_index));
pos_arg.and_then(|a| a.get_num_args().and_then(|r| Some(r.max_values())))
}

/// A completion candidate definition
///
/// This makes it easier to add more fields to completion candidate,
Expand Down
33 changes: 19 additions & 14 deletions clap_complete/tests/testsuite/dynamic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -609,20 +609,18 @@ fn suggest_multi_positional() {
assert_data_eq!(
complete!(cmd, "pos_a [TAB]"),
snapbox::str![
"--format
--help\tPrint help
-F
-h\tPrint help"
"pos_a
pos_b
pos_c"
]
);

assert_data_eq!(
complete!(cmd, "pos_a pos_b [TAB]"),
snapbox::str![
"--format
--help\tPrint help
-F
-h\tPrint help"
"pos_a
pos_b
pos_c"
]
);

Expand All @@ -642,10 +640,9 @@ pos_c"
assert_data_eq!(
complete!(cmd, "--format json pos_a [TAB]"),
snapbox::str![
"--format
--help\tPrint help
-F
-h\tPrint help"
"pos_a
pos_b
pos_c"
]
);

Expand All @@ -661,12 +658,20 @@ pos_c"

assert_data_eq!(
complete!(cmd, "--format json -- pos_a [TAB]"),
snapbox::str![""]
snapbox::str![
"pos_a
pos_b
pos_c"
]
);

assert_data_eq!(
complete!(cmd, "--format json -- pos_a pos_b [TAB]"),
snapbox::str![""]
snapbox::str![
"pos_a
pos_b
pos_c"
]
);

assert_data_eq!(
Expand Down

0 comments on commit 788a1cc

Please sign in to comment.