@@ -77,6 +77,14 @@ pub fn complete(
7777 }
7878 } else if let Some ( ( flag, value) ) = arg. to_long ( ) {
7979 if let Ok ( flag) = flag {
80+ if value. is_none ( ) {
81+ if let Some ( subcmd) = find_long_flag_subcmd ( current_cmd, flag) {
82+ current_cmd = subcmd;
83+ pos_index = 1 ;
84+ continue ;
85+ }
86+ }
87+
8088 let opt = current_cmd. get_arguments ( ) . find ( |a| {
8189 let longs = a. get_long_and_visible_aliases ( ) ;
8290 let is_find = longs. map ( |v| {
@@ -96,15 +104,39 @@ pub fn complete(
96104 parse_positional ( current_cmd, pos_index, is_escaped, current_state) ;
97105 }
98106 }
99- } else if let Some ( short) = arg. to_short ( ) {
100- let ( _, takes_value_opt, mut short) = parse_shortflags ( current_cmd, short) ;
101- if let Some ( opt) = takes_value_opt {
102- if short. next_value_os ( ) . is_none ( ) {
103- next_state = ParseState :: Opt ( ( opt, 1 ) ) ;
107+ } else if let Some ( mut short) = arg. to_short ( ) {
108+ // Check if the first short flag is a subcommand flag.
109+ let mut found_subcmd = false ;
110+ let mut peek = short. clone ( ) ;
111+ if let Some ( Ok ( c) ) = peek. next_flag ( ) {
112+ if let Some ( subcmd) = find_short_flag_subcmd ( current_cmd, c) {
113+ current_cmd = subcmd;
114+ pos_index = 1 ;
115+ found_subcmd = true ;
116+ // Consume the subcommand flag from the real iterator.
117+ short. next_flag ( ) ;
118+ // If there are remaining flags, parse them as flags of the subcommand.
119+ if peek. next_flag ( ) . is_some ( ) {
120+ let ( _, takes_value_opt, remaining) = parse_shortflags ( current_cmd, short) ;
121+ short = remaining;
122+ if let Some ( opt) = takes_value_opt {
123+ if short. next_value_os ( ) . is_none ( ) {
124+ next_state = ParseState :: Opt ( ( opt, 1 ) ) ;
125+ }
126+ }
127+ }
128+ }
129+ }
130+ if !found_subcmd {
131+ let ( _, takes_value_opt, mut short) = parse_shortflags ( current_cmd, short) ;
132+ if let Some ( opt) = takes_value_opt {
133+ if short. next_value_os ( ) . is_none ( ) {
134+ next_state = ParseState :: Opt ( ( opt, 1 ) ) ;
135+ }
136+ } else if pos_allows_hyphen ( current_cmd, pos_index) {
137+ ( next_state, pos_index) =
138+ parse_positional ( current_cmd, pos_index, is_escaped, current_state) ;
104139 }
105- } else if pos_allows_hyphen ( current_cmd, pos_index) {
106- ( next_state, pos_index) =
107- parse_positional ( current_cmd, pos_index, is_escaped, current_state) ;
108140 }
109141 } else {
110142 match current_state {
@@ -285,10 +317,55 @@ fn complete_option(
285317 . into_iter ( )
286318 . filter ( |comp| comp. get_value ( ) . starts_with ( format ! ( "--{flag}" ) . as_str ( ) ) ) ,
287319 ) ;
320+ completions. extend (
321+ long_flag_subcommands ( cmd)
322+ . into_iter ( )
323+ . filter ( |comp| comp. get_value ( ) . starts_with ( format ! ( "--{flag}" ) . as_str ( ) ) ) ,
324+ ) ;
288325 }
289326 }
290327 } else if let Some ( short) = arg. to_short ( ) {
291328 if !short. is_negative_number ( ) {
329+ // Check if the first short flag is a subcommand flag.
330+ let mut peek = short. clone ( ) ;
331+ if let Some ( Ok ( c) ) = peek. next_flag ( ) {
332+ if let Some ( subcmd) = find_short_flag_subcmd ( cmd, c) {
333+ // First flag is a subcommand; complete remaining flags from the subcommand.
334+ let mut inner_short = short. clone ( ) ;
335+ inner_short. next_flag ( ) ; // consume the subcommand flag
336+ let subcmd_prefix = format ! ( "-{c}" ) ;
337+
338+ let ( leading_flags, takes_value_opt, mut remaining) =
339+ parse_shortflags ( subcmd, inner_short) ;
340+
341+ if let Some ( opt) = takes_value_opt {
342+ let mut peek_remaining = remaining. clone ( ) ;
343+ let has_equal = if let Some ( Ok ( '=' ) ) = peek_remaining. next_flag ( ) {
344+ remaining. next_flag ( ) ;
345+ true
346+ } else {
347+ false
348+ } ;
349+
350+ let value = remaining. next_value_os ( ) . unwrap_or ( OsStr :: new ( "" ) ) ;
351+ completions. extend (
352+ complete_arg_value ( value. to_str ( ) . ok_or ( value) , opt, current_dir)
353+ . into_iter ( )
354+ . map ( |comp| {
355+ let sep = if has_equal { "=" } else { "" } ;
356+ comp. add_prefix ( format ! ( "{subcmd_prefix}{leading_flags}{sep}" ) )
357+ } ) ,
358+ ) ;
359+ } else {
360+ completions. extend ( shorts_and_visible_aliases ( subcmd) . into_iter ( ) . map (
361+ |comp| comp. add_prefix ( format ! ( "{subcmd_prefix}{leading_flags}" ) ) ,
362+ ) ) ;
363+ }
364+
365+ return completions;
366+ }
367+ }
368+
292369 // Find the first takes_values option.
293370 let ( leading_flags, takes_value_opt, mut short) = parse_shortflags ( cmd, short) ;
294371
@@ -503,6 +580,30 @@ fn hidden_longs_aliases(p: &clap::Command) -> Vec<CompletionCandidate> {
503580 . collect ( )
504581}
505582
583+ /// Gets long flag subcommands as `--flag` style completion candidates.
584+ fn long_flag_subcommands ( cmd : & clap:: Command ) -> Vec < CompletionCandidate > {
585+ cmd. get_subcommands ( )
586+ . flat_map ( |sc| {
587+ let mut candidates = Vec :: new ( ) ;
588+ if let Some ( long_flag) = sc. get_long_flag ( ) {
589+ candidates. push ( populate_command_candidate (
590+ CompletionCandidate :: new ( format ! ( "--{long_flag}" ) ) ,
591+ cmd,
592+ sc,
593+ ) ) ;
594+ }
595+ for alias in sc. get_visible_long_flag_aliases ( ) {
596+ candidates. push ( populate_command_candidate (
597+ CompletionCandidate :: new ( format ! ( "--{alias}" ) ) ,
598+ cmd,
599+ sc,
600+ ) ) ;
601+ }
602+ candidates
603+ } )
604+ . collect ( )
605+ }
606+
506607/// Gets all the short options, their visible aliases and flags of a [`clap::Command`].
507608/// Includes `h` and `V` depending on the [`clap::Command`] settings.
508609fn shorts_and_visible_aliases ( p : & clap:: Command ) -> Vec < CompletionCandidate > {
@@ -705,3 +806,17 @@ fn opt_allows_hyphen(state: &ParseState<'_>, arg: &clap_lex::ParsedArg<'_>) -> b
705806
706807 false
707808}
809+
810+ /// Find a subcommand by short flag (including aliases).
811+ fn find_short_flag_subcmd ( cmd : & clap:: Command , flag : char ) -> Option < & clap:: Command > {
812+ cmd. get_subcommands ( ) . find ( |sc| {
813+ sc. get_short_flag ( ) == Some ( flag) || sc. get_all_short_flag_aliases ( ) . any ( |a| a == flag)
814+ } )
815+ }
816+
817+ /// Find a subcommand by long flag (including aliases).
818+ fn find_long_flag_subcmd < ' c > ( cmd : & ' c clap:: Command , flag : & str ) -> Option < & ' c clap:: Command > {
819+ cmd. get_subcommands ( ) . find ( |sc| {
820+ sc. get_long_flag ( ) == Some ( flag) || sc. get_all_long_flag_aliases ( ) . any ( |a| a == flag)
821+ } )
822+ }
0 commit comments