Skip to content

Commit b7cfbdc

Browse files
committed
feat(complete): Native support for hyphen values
1 parent 1d97c29 commit b7cfbdc

File tree

2 files changed

+59
-21
lines changed

2 files changed

+59
-21
lines changed

clap_complete/src/engine/complete.rs

+43-11
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,13 @@ pub fn complete(
5959
} else if arg.is_escape() {
6060
is_escaped = true;
6161
} else if let Some((flag, value)) = arg.to_long() {
62+
if let ParseState::Opt((opt, count)) = current_state {
63+
if opt.is_allow_hyphen_values_set() {
64+
next_state = parse_opt(opt, count);
65+
continue;
66+
}
67+
}
68+
6269
if let Ok(flag) = flag {
6370
let opt = current_cmd.get_arguments().find(|a| {
6471
let longs = a.get_long_and_visible_aliases();
@@ -69,33 +76,40 @@ pub fn complete(
6976
});
7077
is_find.unwrap_or(false)
7178
});
72-
if opt.map(|o| o.get_action().takes_values()).unwrap_or(false) {
73-
if value.is_none() {
74-
next_state = ParseState::Opt((opt.unwrap(), 1));
79+
80+
if let Some(opt) = opt {
81+
if opt.get_action().takes_values() && value.is_none() {
82+
next_state = ParseState::Opt((opt, 1));
7583
};
84+
} else if pos_allows_hyphen(current_cmd, pos_index) {
85+
(next_state, pos_index) =
86+
parse_positional(current_cmd, pos_index, is_escaped, current_state);
7687
}
7788
}
7889
} else if let Some(short) = arg.to_short() {
90+
if let ParseState::Opt((opt, count)) = current_state {
91+
if opt.is_allow_hyphen_values_set() {
92+
next_state = parse_opt(opt, count);
93+
continue;
94+
}
95+
}
96+
7997
let (_, takes_value_opt, mut short) = parse_shortflags(current_cmd, short);
8098
if let Some(opt) = takes_value_opt {
8199
if short.next_value_os().is_none() {
82100
next_state = ParseState::Opt((opt, 1));
83101
}
102+
} else if pos_allows_hyphen(current_cmd, pos_index) {
103+
(next_state, pos_index) =
104+
parse_positional(current_cmd, pos_index, is_escaped, current_state);
84105
}
85106
} else {
86107
match current_state {
87108
ParseState::ValueDone | ParseState::Pos(..) => {
88109
(next_state, pos_index) =
89110
parse_positional(current_cmd, pos_index, is_escaped, current_state);
90111
}
91-
92-
ParseState::Opt((opt, count)) => {
93-
let range = opt.get_num_args().expect("built");
94-
let max = range.max_values();
95-
if count < max {
96-
next_state = ParseState::Opt((opt, count + 1));
97-
}
98-
}
112+
ParseState::Opt((opt, count)) => next_state = parse_opt(opt, count),
99113
}
100114
}
101115
}
@@ -546,3 +560,21 @@ fn parse_positional<'a>(
546560
),
547561
}
548562
}
563+
564+
/// Parse optional flag argument. Return new state
565+
fn parse_opt(opt: &clap::Arg, count: usize) -> ParseState<'_> {
566+
let range = opt.get_num_args().expect("built");
567+
let max = range.max_values();
568+
if count < max {
569+
ParseState::Opt((opt, count + 1))
570+
} else {
571+
ParseState::ValueDone
572+
}
573+
}
574+
575+
fn pos_allows_hyphen(cmd: &clap::Command, pos_index: usize) -> bool {
576+
cmd.get_positionals()
577+
.find(|a| a.get_index() == Some(pos_index))
578+
.map(|p| p.is_allow_hyphen_values_set())
579+
.unwrap_or(false)
580+
}

clap_complete/tests/testsuite/engine.rs

+16-10
Original file line numberDiff line numberDiff line change
@@ -1018,10 +1018,13 @@ fn suggest_allow_hyhpen() {
10181018

10191019
assert_data_eq!(
10201020
complete!(cmd, "--format --json --j[TAB]"),
1021-
snapbox::str![""]
1021+
snapbox::str!["--json"]
10221022
);
10231023

1024-
assert_data_eq!(complete!(cmd, "-F --json --j[TAB]"), snapbox::str![""]);
1024+
assert_data_eq!(
1025+
complete!(cmd, "-F --json --j[TAB]"),
1026+
snapbox::str!["--json"]
1027+
);
10251028
}
10261029

10271030
#[test]
@@ -1062,7 +1065,7 @@ fn suggest_positional_long_allow_hyhpen() {
10621065
--help Print help
10631066
-F
10641067
-h Print help
1065-
--pos_a"
1068+
pos_b"
10661069
]
10671070
);
10681071
assert_data_eq!(
@@ -1072,17 +1075,17 @@ fn suggest_positional_long_allow_hyhpen() {
10721075
--help Print help
10731076
-F
10741077
-h Print help
1075-
--pos_a"
1078+
pos_b"
10761079
]
10771080
);
10781081

10791082
assert_data_eq!(
10801083
complete!(cmd, "--format --json --pos_a p[TAB]"),
1081-
snapbox::str![""]
1084+
snapbox::str!["pos_b"]
10821085
);
10831086
assert_data_eq!(
10841087
complete!(cmd, "-F --json --pos_a p[TAB]"),
1085-
snapbox::str![""]
1088+
snapbox::str!["pos_b"]
10861089
);
10871090
}
10881091

@@ -1115,7 +1118,7 @@ fn suggest_positional_short_allow_hyhpen() {
11151118
--help Print help
11161119
-F
11171120
-h Print help
1118-
-a"
1121+
pos_b"
11191122
]
11201123
);
11211124
assert_data_eq!(
@@ -1125,15 +1128,18 @@ fn suggest_positional_short_allow_hyhpen() {
11251128
--help Print help
11261129
-F
11271130
-h Print help
1128-
-a"
1131+
pos_b"
11291132
]
11301133
);
11311134

11321135
assert_data_eq!(
11331136
complete!(cmd, "--format --json -a p[TAB]"),
1134-
snapbox::str![""]
1137+
snapbox::str!["pos_b"]
1138+
);
1139+
assert_data_eq!(
1140+
complete!(cmd, "-F --json -a p[TAB]"),
1141+
snapbox::str!["pos_b"]
11351142
);
1136-
assert_data_eq!(complete!(cmd, "-F --json -a p[TAB]"), snapbox::str![""]);
11371143
}
11381144

11391145
fn complete(cmd: &mut Command, args: impl AsRef<str>, current_dir: Option<&Path>) -> String {

0 commit comments

Comments
 (0)