@@ -36,7 +36,7 @@ private string GenerateCommandsCompletions(string[] parentCommandNames, CliComma
3636 // generate the words for options and subcommands
3737 var visibleSubcommands = command . Subcommands . Where ( c => ! c . Hidden ) . ToArray ( ) ;
3838 // notably, do not generate completions for all option aliases - since a user is tab-completing we can use the longest forms
39- var completionOptions = AllOptionsForCommand ( command ) . Where ( o => ! o . Hidden ) . Select ( o => o . Name ) . ToArray ( ) ;
39+ var completionOptions = command . HeirarchicalOptions ( ) . Where ( o => ! o . Hidden ) . Select ( o => o . Name ) . ToArray ( ) ;
4040 var completionSubcommands = visibleSubcommands . Select ( x => x . Name ) . ToArray ( ) ;
4141 string [ ] completionWords = [ .. completionSubcommands , .. completionOptions ] ;
4242
@@ -122,35 +122,7 @@ private string GenerateCommandsCompletions(string[] parentCommandNames, CliComma
122122 return textWriter . ToString ( ) + string . Join ( '\n ' , visibleSubcommands . Select ( c => GenerateCommandsCompletions ( parentCommandNamesForSubcommands , c , isNestedCommand : true ) ) ) ;
123123 }
124124
125- private static IEnumerable < CliOption > AllOptionsForCommand ( CliCommand c )
126- {
127- if ( c . Parents . Count ( ) == 0 )
128- {
129- return c . Options ;
130- }
131- else
132- {
133- return c . Options . Concat ( c . Parents . OfType < CliCommand > ( ) . SelectMany ( OptionsForParent ) ) ;
134- }
135-
136- }
137-
138- private static IEnumerable < CliOption > OptionsForParent ( CliCommand c )
139- {
140- foreach ( var o in c . Options )
141- {
142- if ( o . Recursive ) yield return o ;
143- }
144- foreach ( var p in c . Parents . OfType < CliCommand > ( ) )
145- {
146- foreach ( var o in OptionsForParent ( p ) )
147- {
148- yield return o ;
149- }
150- }
151- }
152-
153- private static string [ ] PositionalArgumentTerms ( CliArgument [ ] arguments )
125+ internal static string [ ] PositionalArgumentTerms ( CliArgument [ ] arguments )
154126 {
155127 var completions = new List < string > ( ) ;
156128 foreach ( var argument in arguments )
@@ -176,12 +148,12 @@ private static string[] PositionalArgumentTerms(CliArgument[] arguments)
176148 /// Generates a call to `dotnet complete <string> --position <int>` for dynamic completions where necessary, but in a more generic way
177149 /// </summary>
178150 /// <returns></returns>
179- private static string GenerateDynamicCall ( )
151+ internal static string GenerateDynamicCall ( )
180152 {
181153 return $$ """ ${COMP_WORDS[0]} complete --position ${COMP_POINT} ${COMP_LINE} 2>/dev/null | tr '\n' ' '""" ;
182154 }
183155
184- private static string ? GenerateOptionHandlers ( CliCommand command )
156+ internal static string ? GenerateOptionHandlers ( CliCommand command )
185157 {
186158 var optionHandlers = command . Options . Where ( o => ! o . Hidden ) . Select ( GenerateOptionHandler ) . Where ( handler => handler is not null ) . ToArray ( ) ;
187159 if ( optionHandlers . Length == 0 )
@@ -199,7 +171,7 @@ private static string GenerateDynamicCall()
199171 /// * a concrete set of choices in a bash array already ($opts), or
200172 /// * a subprocess that will return such an array (aka '(dotnet complete --position 10 'dotnet ad')') </param>
201173 /// <returns></returns>
202- private static string GenerateChoicesPrompt ( string choicesInvocation ) => $$ """ COMPREPLY=( $(compgen -W "{{ choicesInvocation }} " -- "$cur") )""" ;
174+ internal static string GenerateChoicesPrompt ( string choicesInvocation ) => $$ """ COMPREPLY=( $(compgen -W "{{ choicesInvocation }} " -- "$cur") )""" ;
203175
204176 /// <summary>
205177 /// Generates a concrete set of bash completion selection for a given option.
@@ -208,7 +180,7 @@ private static string GenerateDynamicCall()
208180 /// </summary>
209181 /// <param name="option"></param>
210182 /// <returns>a bash switch case expression for providing completions for this option</returns>
211- private static string ? GenerateOptionHandler ( CliOption option )
183+ internal static string ? GenerateOptionHandler ( CliOption option )
212184 {
213185 // unlike the completion-options generation, for actually implementing suggestions we should be able to handle all of the options' aliases.
214186 // this ensures if the user manually enters an alias we can support that usage.
@@ -250,7 +222,6 @@ private static string GenerateDynamicCall()
250222 }
251223}
252224
253-
254225public static class HelpExtensions
255226{
256227 /// <summary>
@@ -313,4 +284,39 @@ public static string[] Names(this CliCommand command)
313284 }
314285 }
315286
287+ public static IEnumerable < CliOption > HeirarchicalOptions ( this CliCommand c )
288+ {
289+ var myOptions = c . Options . Where ( o => ! o . Hidden ) ;
290+ if ( c . Parents . Count ( ) == 0 )
291+ {
292+ return myOptions ;
293+ }
294+ else
295+ {
296+ return c . Parents . OfType < CliCommand > ( ) . SelectMany ( OptionsForParent ) . Concat ( myOptions ) ;
297+ }
298+
299+ }
300+
301+ private static IEnumerable < CliOption > OptionsForParent ( CliCommand c )
302+ {
303+ foreach ( var o in c . Options )
304+ {
305+ if ( o . Recursive )
306+ {
307+ if ( ! o . Hidden )
308+ {
309+ yield return o ;
310+ }
311+ }
312+ }
313+ foreach ( var p in c . Parents . OfType < CliCommand > ( ) )
314+ {
315+ foreach ( var o in OptionsForParent ( p ) )
316+ {
317+ yield return o ;
318+ }
319+ }
320+ }
321+
316322}
0 commit comments