1+ use core:: num;
12use std:: ffi:: OsStr ;
23use std:: ffi:: OsString ;
34
@@ -71,8 +72,7 @@ pub fn complete(
7172 }
7273
7374 if is_escaped {
74- pos_index += 1 ;
75- state = ParseState :: Pos ( pos_index) ;
75+ ( state, pos_index) = parse_positional ( current_cmd, pos_index, is_escaped, state) ;
7676 } else if arg. is_escape ( ) {
7777 is_escaped = true ;
7878 state = ParseState :: ValueDone ;
@@ -92,7 +92,7 @@ pub fn complete(
9292 if value. is_some ( ) {
9393 ParseState :: ValueDone
9494 } else {
95- ParseState :: Opt ( opt. unwrap ( ) )
95+ ParseState :: Opt ( ( opt. unwrap ( ) , 1 ) )
9696 }
9797 }
9898 Some ( clap:: ArgAction :: SetTrue ) | Some ( clap:: ArgAction :: SetFalse ) => {
@@ -115,7 +115,7 @@ pub fn complete(
115115 Some ( opt) => {
116116 state = match short. next_value_os ( ) {
117117 Some ( _) => ParseState :: ValueDone ,
118- None => ParseState :: Opt ( opt) ,
118+ None => ParseState :: Opt ( ( opt, 1 ) ) ,
119119 } ;
120120 }
121121 None => {
@@ -124,13 +124,24 @@ pub fn complete(
124124 }
125125 } else {
126126 match state {
127- ParseState :: ValueDone | ParseState :: Pos ( _) => {
128- pos_index += 1 ;
129- state = ParseState :: ValueDone ;
130- }
131- ParseState :: Opt ( _) => {
132- state = ParseState :: ValueDone ;
127+ ParseState :: ValueDone | ParseState :: Pos ( ..) => {
128+ ( state, pos_index) =
129+ parse_positional ( current_cmd, pos_index, is_escaped, state) ;
133130 }
131+
132+ ParseState :: Opt ( ( ref opt, count) ) => match opt. get_num_args ( ) {
133+ Some ( range) => {
134+ let max = range. max_values ( ) ;
135+ if count < max {
136+ state = ParseState :: Opt ( ( opt. clone ( ) , count + 1 ) ) ;
137+ } else {
138+ state = ParseState :: ValueDone ;
139+ }
140+ }
141+ None => {
142+ state = ParseState :: ValueDone ;
143+ }
144+ } ,
134145 }
135146 }
136147 }
@@ -146,11 +157,11 @@ enum ParseState<'a> {
146157 /// Parsing a value done, there is no state to record.
147158 ValueDone ,
148159
149- /// Parsing a positional argument after `--`
150- Pos ( usize ) ,
160+ /// Parsing a positional argument after `--`. Pos(pos_index, takes_num_args)
161+ Pos ( ( usize , usize ) ) ,
151162
152163 /// Parsing a optional flag argument
153- Opt ( & ' a clap:: Arg ) ,
164+ Opt ( ( & ' a clap:: Arg , usize ) ) ,
154165}
155166
156167fn complete_arg (
@@ -290,16 +301,27 @@ fn complete_arg(
290301 completions. extend ( complete_subcommand ( value, cmd) ) ;
291302 }
292303 }
293- ParseState :: Pos ( _ ) => {
304+ ParseState :: Pos ( .. ) => {
294305 if let Some ( positional) = cmd
295306 . get_positionals ( )
296307 . find ( |p| p. get_index ( ) == Some ( pos_index) )
297308 {
298309 completions. extend ( complete_arg_value ( arg. to_value ( ) , positional, current_dir) ) ;
299310 }
300311 }
301- ParseState :: Opt ( opt) => {
312+ ParseState :: Opt ( ( opt, count ) ) => {
302313 completions. extend ( complete_arg_value ( arg. to_value ( ) , opt, current_dir) ) ;
314+ let min = opt. get_num_args ( ) . map ( |r| r. min_values ( ) ) . unwrap_or ( 0 ) ;
315+ if count > min {
316+ // Also complete this raw_arg as a positional argument, flags, options and subcommand.
317+ completions. extend ( complete_arg (
318+ arg,
319+ cmd,
320+ current_dir,
321+ pos_index,
322+ ParseState :: ValueDone ,
323+ ) ?) ;
324+ }
303325 }
304326 }
305327 if completions. iter ( ) . any ( |a| a. is_visible ( ) ) {
@@ -582,6 +604,57 @@ fn parse_shortflags<'c, 's>(
582604 ( leading_flags, takes_value_opt, short)
583605}
584606
607+ /// Parse the positional arguments. Return the new state and the new positional index.
608+ fn parse_positional < ' a > (
609+ cmd : & clap:: Command ,
610+ pos_index : usize ,
611+ is_escaped : bool ,
612+ state : ParseState < ' a > ,
613+ ) -> ( ParseState < ' a > , usize ) {
614+ let pos_arg = cmd
615+ . get_positionals ( )
616+ . find ( |p| p. get_index ( ) == Some ( pos_index) ) ;
617+ let num_args = pos_arg
618+ . and_then ( |a| a. get_num_args ( ) . and_then ( |r| Some ( r. max_values ( ) ) ) )
619+ . unwrap_or ( 1 ) ;
620+
621+ let update_state_with_new_positional = |pos_index| -> ( ParseState < ' a > , usize ) {
622+ if num_args > 1 {
623+ ( ParseState :: Pos ( ( pos_index, 1 ) ) , pos_index)
624+ } else {
625+ if is_escaped {
626+ ( ParseState :: Pos ( ( pos_index, 1 ) ) , pos_index + 1 )
627+ } else {
628+ ( ParseState :: ValueDone , pos_index + 1 )
629+ }
630+ }
631+ } ;
632+ match state {
633+ ParseState :: ValueDone => {
634+ update_state_with_new_positional ( pos_index)
635+ } ,
636+ ParseState :: Pos ( ( prev_pos_index, num_arg) ) => {
637+ if prev_pos_index == pos_index {
638+ if num_arg + 1 < num_args {
639+ ( ParseState :: Pos ( ( pos_index, num_arg + 1 ) ) , pos_index)
640+ } else {
641+ if is_escaped {
642+ ( ParseState :: Pos ( ( pos_index, 1 ) ) , pos_index + 1 )
643+ } else {
644+ ( ParseState :: ValueDone , pos_index + 1 )
645+ }
646+ }
647+ } else {
648+ update_state_with_new_positional ( pos_index)
649+ }
650+ }
651+ ParseState :: Opt ( ..) => unreachable ! (
652+ "This branch won't be hit,
653+ because ParseState::Opt should not be seen as a positional argument and passed to this function."
654+ ) ,
655+ }
656+ }
657+
585658/// A completion candidate definition
586659///
587660/// This makes it easier to add more fields to completion candidate,
0 commit comments