11use crate :: args:: Args ;
2+ use clap:: builder:: PossibleValue ;
23use clap:: { Arg , Command , CommandFactory } ;
34use itertools:: Itertools ;
45use pipe_trait:: Pipe ;
@@ -86,63 +87,12 @@ fn render_option(arg: &Arg, out: &mut String) {
8687 let primary_long = arg. get_long ( ) . expect ( "option must have a long flag" ) ;
8788 let primary_name = format ! ( "--{primary_long}" ) ;
8889
89- let short = arg. get_short ( ) ;
90- let visible_long_aliases: Vec < & str > = arg. get_visible_aliases ( ) . unwrap_or_default ( ) ;
91- let visible_short_aliases: Vec < char > = arg. get_visible_short_aliases ( ) . unwrap_or_default ( ) ;
92-
93- // Invisible anchors: short first, then primary long, then long aliases
94- let mut anchor_ids: Vec < String > = Vec :: new ( ) ;
95- if let Some ( short) = short {
96- anchor_ids. push ( format ! ( "option-{short}" ) ) ;
97- }
98- anchor_ids. push ( primary_long. to_string ( ) ) ;
99- for & alias in & visible_long_aliases {
100- anchor_ids. push ( alias. to_string ( ) ) ;
101- }
102- for char in & visible_short_aliases {
103- anchor_ids. push ( format ! ( "option-{char}" ) ) ;
104- }
105- for id in & anchor_ids {
106- out. push_str ( & format ! ( r#"<a id="{id}" name="{id}"></a>"# ) ) ;
107- }
108- out. push ( '\n' ) ;
109-
110- // Heading
90+ write_option_anchors ( arg, primary_long, out) ;
11191 out. push_str ( & format ! ( "### `{primary_name}`\n \n " ) ) ;
11292
113- // Aliases for display in metadata
114- let mut aliases: Vec < String > = Vec :: new ( ) ;
115- if let Some ( short) = short {
116- aliases. push ( format ! ( "-{short}" ) ) ;
117- }
118- for & alias in & visible_long_aliases {
119- aliases. push ( format ! ( "--{alias}" ) ) ;
120- }
121- for alias in & visible_short_aliases {
122- aliases. push ( format ! ( "-{alias}" ) ) ;
123- }
124-
125- // Default values – skip "false" (clap's implicit default for boolean flags)
126- let default_values: Vec < String > = if arg. is_hide_default_value_set ( ) {
127- Vec :: new ( )
128- } else {
129- arg. get_default_values ( )
130- . iter ( )
131- . map ( |value| value. to_string_lossy ( ) )
132- . filter ( |value| value != "false" )
133- . map ( |value| value. to_string ( ) )
134- . collect ( )
135- } ;
136-
137- // Possible values (choices)
138- let possible_values: Vec < _ > = if arg. is_hide_possible_values_set ( ) {
139- Vec :: new ( )
140- } else {
141- arg. get_possible_values ( )
142- . into_iter ( )
143- . filter ( |value| !value. is_hide_set ( ) )
144- . collect ( )
145- } ;
93+ let aliases = collect_option_display_aliases ( arg) ;
94+ let default_values = collect_option_default_values ( arg) ;
95+ let possible_values = collect_option_possible_values ( arg) ;
14696
14797 let has_metadata =
14898 !aliases. is_empty ( ) || !default_values. is_empty ( ) || !possible_values. is_empty ( ) ;
@@ -152,14 +102,14 @@ fn render_option(arg: &Arg, out: &mut String) {
152102 out. push_str ( & format ! ( "* _Aliases:_ {aliases_str}.\n " ) ) ;
153103 }
154104 if !default_values. is_empty ( ) {
155- let default_values = default_values. join ( ", " ) ;
156- out. push_str ( & format ! ( "* _Default:_ `{default_values }`.\n " ) ) ;
105+ let default_values_str = default_values. join ( ", " ) ;
106+ out. push_str ( & format ! ( "* _Default:_ `{default_values_str }`.\n " ) ) ;
157107 }
158108 if !possible_values. is_empty ( ) {
159109 out. push_str ( "* _Choices:_\n " ) ;
160- for value in & possible_values {
161- let name = value . get_name ( ) ;
162- if let Some ( help) = value . get_help ( ) {
110+ for possible_value in & possible_values {
111+ let name = possible_value . get_name ( ) ;
112+ if let Some ( help) = possible_value . get_help ( ) {
163113 out. push_str ( & format ! ( " - `{name}`: {help}\n " ) ) ;
164114 } else {
165115 out. push_str ( & format ! ( " - `{name}`\n " ) ) ;
@@ -171,7 +121,72 @@ fn render_option(arg: &Arg, out: &mut String) {
171121 out. push ( '\n' ) ;
172122 }
173123
174- // Description: short help, with long help appended if also set
124+ write_option_description ( arg, out) ;
125+ }
126+
127+ fn write_option_anchors ( arg : & Arg , primary_long : & str , out : & mut String ) {
128+ let short = arg. get_short ( ) ;
129+ let visible_long_aliases: Vec < & str > = arg. get_visible_aliases ( ) . unwrap_or_default ( ) ;
130+ let visible_short_aliases: Vec < char > = arg. get_visible_short_aliases ( ) . unwrap_or_default ( ) ;
131+
132+ let mut anchor_ids: Vec < String > = Vec :: new ( ) ;
133+ if let Some ( short_char) = short {
134+ anchor_ids. push ( format ! ( "option-{short_char}" ) ) ;
135+ }
136+ anchor_ids. push ( primary_long. to_string ( ) ) ;
137+ for & long_alias in & visible_long_aliases {
138+ anchor_ids. push ( long_alias. to_string ( ) ) ;
139+ }
140+ for & short_alias in & visible_short_aliases {
141+ anchor_ids. push ( format ! ( "option-{short_alias}" ) ) ;
142+ }
143+ for id in & anchor_ids {
144+ out. push_str ( & format ! ( r#"<a id="{id}" name="{id}"></a>"# ) ) ;
145+ }
146+ out. push ( '\n' ) ;
147+ }
148+
149+ fn collect_option_display_aliases ( arg : & Arg ) -> Vec < String > {
150+ let short = arg. get_short ( ) ;
151+ let visible_long_aliases: Vec < & str > = arg. get_visible_aliases ( ) . unwrap_or_default ( ) ;
152+ let visible_short_aliases: Vec < char > = arg. get_visible_short_aliases ( ) . unwrap_or_default ( ) ;
153+
154+ let mut aliases: Vec < String > = Vec :: new ( ) ;
155+ if let Some ( short_char) = short {
156+ aliases. push ( format ! ( "-{short_char}" ) ) ;
157+ }
158+ for & long_alias in & visible_long_aliases {
159+ aliases. push ( format ! ( "--{long_alias}" ) ) ;
160+ }
161+ for & short_alias in & visible_short_aliases {
162+ aliases. push ( format ! ( "-{short_alias}" ) ) ;
163+ }
164+ aliases
165+ }
166+
167+ fn collect_option_default_values ( arg : & Arg ) -> Vec < String > {
168+ if arg. is_hide_default_value_set ( ) {
169+ return Vec :: new ( ) ;
170+ }
171+ arg. get_default_values ( )
172+ . iter ( )
173+ . map ( |value| value. to_string_lossy ( ) )
174+ . filter ( |value| value != "false" )
175+ . map ( |value| value. to_string ( ) )
176+ . collect ( )
177+ }
178+
179+ fn collect_option_possible_values ( arg : & Arg ) -> Vec < PossibleValue > {
180+ if arg. is_hide_possible_values_set ( ) {
181+ return Vec :: new ( ) ;
182+ }
183+ arg. get_possible_values ( )
184+ . into_iter ( )
185+ . filter ( |possible_value| !possible_value. is_hide_set ( ) )
186+ . collect ( )
187+ }
188+
189+ fn write_option_description ( arg : & Arg , out : & mut String ) {
175190 let description = get_help_text ( arg) ;
176191 if !description. is_empty ( ) {
177192 let description = ensure_ends_with_punctuation ( & description) ;
@@ -190,20 +205,24 @@ fn get_help_text(arg: &Arg) -> Cow<'static, str> {
190205}
191206
192207fn render_examples_section < ' a > ( lines : impl Iterator < Item = & ' a str > , out : & mut String ) {
208+ let mut current_title: Option < & ' a str > = None ;
193209 for line in lines {
194210 let line = line. trim ( ) ;
195211
196212 if line. is_empty ( ) {
197213 continue ;
198214 }
199215
200- if let Some ( command) = line. strip_prefix ( '$' ) {
201- let command = command. trim ( ) ;
202- out. push_str ( & format ! ( "```sh\n {command}\n ```\n \n " ) ) ;
203- continue ;
216+ if let Some ( cmd) = line. strip_prefix ( '$' ) . map ( str:: trim) {
217+ let heading = if let Some ( title) = current_title. take ( ) {
218+ title. to_string ( )
219+ } else {
220+ format ! ( "`{cmd}`" )
221+ } ;
222+ out. push_str ( & format ! ( "### {heading}\n \n ```sh\n {cmd}\n ```\n \n " ) ) ;
223+ } else {
224+ current_title = Some ( line) ;
204225 }
205-
206- out. push_str ( & format ! ( "### {line}\n \n " ) ) ;
207226 }
208227}
209228
0 commit comments