66from stdl .st import ansi_len , with_style
77
88from interfacy .appearance .layout import HelpLayout
9+ from interfacy .executable_flag import ExecutableFlag , executable_flag_to_argument
910from interfacy .schema .schema import Argument , ArgumentKind , Command , ParserSchema , ValueShape
1011from interfacy .util import get_terminal_width
1112
@@ -81,6 +82,7 @@ def render_parser_help(self, schema: ParserSchema, prog: str) -> str:
8182 prog ,
8283 parser_description = schema .description ,
8384 parser_epilog = schema .epilog ,
85+ parser_executable_flags = schema .executable_flags ,
8486 )
8587
8688 return self ._render_multi_command_help (schema , prog )
@@ -92,6 +94,7 @@ def render_command_help(
9294 * ,
9395 parser_description : str | None = None ,
9496 parser_epilog : str | None = None ,
97+ parser_executable_flags : list [ExecutableFlag ] | None = None ,
9598 ) -> str :
9699 """
97100 Render help text for one command schema.
@@ -101,23 +104,26 @@ def render_command_help(
101104 prog (str): Program name or invocation prefix.
102105 parser_description (str | None): Optional parser-level description override.
103106 parser_epilog (str | None): Optional parser-level epilog text.
107+ parser_executable_flags (list[ExecutableFlag] | None): Parser-level executable
108+ flags to merge into single-command help output.
104109 """
105110 layout = self .layout
106111 all_args = command .initializer + command .parameters
107112 positionals = [a for a in all_args if a .kind == ArgumentKind .POSITIONAL ]
108- options = [a for a in all_args if a .kind == ArgumentKind .OPTION ]
109- options = layout .order_option_arguments_for_help (
110- options ,
113+ options = self ._ordered_option_arguments (
114+ [a for a in all_args if a .kind == ArgumentKind .OPTION ],
115+ command .executable_flags ,
116+ parser_executable_flags = parser_executable_flags ,
111117 rules = command .help_option_sort_effective ,
112118 )
113119 help_arg = self ._get_help_argument ()
114120
115121 layout .prepare_default_field_width_for_arguments (
116- [* ([help_arg ] if help_arg is not None else []), * all_args ]
122+ [* ([help_arg ] if help_arg is not None else []), * positionals , * options ]
117123 )
118124
119125 sections : list [str ] = []
120- usage = self ._build_usage (command , prog )
126+ usage = self ._build_usage (command , prog , parser_executable_flags = parser_executable_flags )
121127 description = parser_description or command .description
122128 self ._append_usage_and_description (sections = sections , usage = usage , description = description )
123129
@@ -234,13 +240,22 @@ def _render_multi_command_help(self, schema: ParserSchema, prog: str) -> str:
234240 sections .append (schema .description )
235241
236242 help_arg = self ._get_help_argument ()
237- if help_arg is not None :
238- layout .prepare_default_field_width_for_arguments ([help_arg ])
239- heading = self ._style_section_heading ("options" )
240- help_line = self ._normalize_help_only_option_line (
241- layout .format_argument (help_arg ), help_arg
243+ root_options = self ._ordered_option_arguments (
244+ [],
245+ schema .executable_flags ,
246+ rules = schema .help_option_sort_effective ,
247+ )
248+ root_options_with_help = [* ([help_arg ] if help_arg is not None else []), * root_options ]
249+ if root_options_with_help :
250+ layout .prepare_default_field_width_for_arguments (root_options_with_help )
251+ sections .append (
252+ self ._render_argument_section (
253+ "options" ,
254+ root_options_with_help ,
255+ normalize_help_only = help_arg is not None and not root_options ,
256+ )
257+ or ""
242258 )
243- sections .append (f"{ heading } \n { self ._indent (help_line )} " )
244259
245260 if schema .commands_help :
246261 sections .append (schema .commands_help )
@@ -252,12 +267,19 @@ def _render_multi_command_help(self, schema: ParserSchema, prog: str) -> str:
252267
253268 return "\n \n " .join (sections ) + "\n "
254269
255- def _build_usage (self , command : Command , prog : str ) -> str :
270+ def _build_usage (
271+ self ,
272+ command : Command ,
273+ prog : str ,
274+ * ,
275+ parser_executable_flags : list [ExecutableFlag ] | None = None ,
276+ ) -> str :
256277 all_args = command .initializer + command .parameters
257278 positionals = [a for a in all_args if a .kind == ArgumentKind .POSITIONAL ]
258- options = [a for a in all_args if a .kind == ArgumentKind .OPTION ]
259- options = self .layout .order_option_arguments_for_help (
260- options ,
279+ options = self ._ordered_option_arguments (
280+ [a for a in all_args if a .kind == ArgumentKind .OPTION ],
281+ command .executable_flags ,
282+ parser_executable_flags = parser_executable_flags ,
261283 rules = command .help_option_sort_effective ,
262284 )
263285 compact_options_usage = self .layout .compact_options_usage
@@ -314,6 +336,23 @@ def _build_usage(self, command: Command, prog: str) -> str:
314336
315337 return f"{ usage_prefix } { usage_text } "
316338
339+ def _ordered_option_arguments (
340+ self ,
341+ options : list [Argument ],
342+ executable_flags : list [ExecutableFlag ],
343+ * ,
344+ parser_executable_flags : list [ExecutableFlag ] | None = None ,
345+ rules : list [object ] | None = None ,
346+ ) -> list [Argument ]:
347+ flag_arguments = [
348+ executable_flag_to_argument (flag )
349+ for flag in [* (parser_executable_flags or []), * executable_flags ]
350+ ]
351+ return self .layout .order_option_arguments_for_help (
352+ [* options , * flag_arguments ],
353+ rules = rules ,
354+ )
355+
317356 def _usage_token_for_subcommands (self , command : Command ) -> str :
318357 token = self .layout .get_subcommand_usage_token ()
319358 if "{command}" not in token or not command .subcommands :
0 commit comments