@@ -5,8 +5,6 @@ Synopsis: Implements the --help flag and help subcommand
55// TODO(cgay): Automatically display option default values. It's too easy to
66// forget to add %default% to the help string.
77
8- // TODO(cgay): Wrap the descriptions nicely
9-
108define function program-name () => (name :: <string> )
119 locator-base(as (<file-locator>, application-name()))
1210end function ;
@@ -157,27 +155,22 @@ define function print-help
157155 print-options(cmd, stream);
158156 if (cmd.has-subcommands?)
159157 format(stream, "\n Subcommands:\n " );
160- let (names, docs) = subcommand-columns(cmd);
161- if (~empty? (names))
162- let name-width = reduce1 (max , map (size , names));
163- for (name in names, doc in docs)
164- if (empty? (doc))
165- format(stream, "%s\n " , name);
166- else
167- format(stream, "%s %s\n " , pad-right(name, name-width), doc);
168- end ;
169- end ;
158+ let rows = subcommand-rows(cmd);
159+ if (~empty? (rows))
160+ columnize(stream, $subcommand-columns, rows);
161+ new-line(stream);
170162 end ;
171- format(stream, "\n " );
172163 let help-subcommand = find-subcommand(cmd, <help-subcommand>);
173164 if (help-subcommand)
165+ new-line(stream);
174166 format(stream, "Use '%s %s <cmd> [<cmd> ...]' to see subcommand options.\n " ,
175167 program-name(), subcommand-name(help-subcommand));
176168 end ;
177169 end ;
178170 if (~instance? (cmd, <command-line-parser>))
179171 let help-option = find-option(cmd, <help-option>);
180172 if (help-option)
173+ new-line(stream);
181174 format(stream, "Use '%s %s' to see global options.\n " ,
182175 program-name(), help-option.canonical-name.visible-option-name);
183176 end ;
@@ -186,85 +179,90 @@ end function;
186179
187180define function print-options
188181 (cmd :: <command>, stream :: <stream>) => ()
189- let (names, docs) = option-columns(cmd);
190- if (~empty? (names))
182+ // Column widths are chosen to have a max table width of 79 until columnist can
183+ // determine the screen width.
184+ let o-rows = option-rows(cmd);
185+ if (~empty? (o-rows))
191186 format(stream, "\n Options:\n " );
192- let name-width = reduce1 (max , map (size , names));
193- for (name in names, doc in docs)
194- format(stream, " %s %s\n " , pad-right(name, name-width), doc);
195- end ;
187+ columnize(stream, $optional-options-columns, o-rows);
188+ new-line(stream);
196189 end ;
197- let (names, docs) = positional-columns (cmd);
198- if (~empty? (names ))
190+ let p-rows = positional-option-rows (cmd);
191+ if (~empty? (p-rows ))
199192 format(stream, "\n Positional arguments:\n " );
200- let name-width = reduce1 (max , map (size , names));
201- for (name in names, doc in docs)
202- format(stream, " %s %s\n " , pad-right(name, name-width), doc);
203- end ;
193+ columnize(stream, $positional-option-columns, p-rows);
194+ new-line(stream);
204195 end ;
205196end function ;
206197
207- define function positional-columns
208- (cmd :: <command>) => (names :: <sequence> , docs :: <sequence> )
209- let names = make (<stretchy-vector> );
210- let docs = make (<stretchy-vector> );
198+ define constant $positional-option-columns
199+ = list (make (<column>),
200+ make (<column>, maximum-width: 25 ),
201+ make (<column>, maximum-width: 50 , pad?: #f ));
202+
203+ define function positional-option-rows
204+ (cmd :: <command>) => (rows :: <sequence> )
205+ let rows = make (<stretchy-vector> );
211206 for (opt in cmd.positional-options)
212207 let name = opt.option-variable;
213208 if (opt.option-repeated?)
214209 name := concatenate (name, "..." );
215210 end ;
216- add! (names, name);
217- add! (docs, opt.option-help);
211+ add! (rows, list ("" , name, opt.option-help));
218212 end ;
219- values (names, docs)
213+ rows
220214end function ;
221215
222- define function option-columns
223- (parser :: <command>)
224- => (names :: <sequence> , docs :: <sequence> )
225- let names = make (<stretchy-vector> );
226- let docs = make (<stretchy-vector> );
227- let any-shorts? = any? (method (opt) ~empty? (opt.short-names) end ,
228- parser.command-options);
216+ define constant $optional-options-columns
217+ = list (make (<column>), // empty string, creates column border
218+ make (<column>), // short option names
219+ make (<column>, maximum-width: 25 ), // long option names
220+ make (<column>, maximum-width: 50 , pad?: #f )); // docs
221+
222+ // Return rows of #[short-names, long-names, documentation]
223+ define function option-rows
224+ (parser :: <command>) => (rows :: <sequence> )
225+ let rows = make (<stretchy-vector> );
229226 for (option in parser.pass-by-name-options)
230- let longs = map (visible-option-name, option.long-names);
231- let shorts = map (visible-option-name, option.short-names);
232- let name = concatenate (join(concatenate (shorts, longs), ", " ),
233- " " ,
234- if (instance? (option, <flag-option>))
235- ""
236- else
237- option.option-variable | canonical-name(option);
238- end );
239- let indent = if (empty? (shorts) & any-shorts?)
240- " " // Makes long options align (usually).
241- else
242- ""
243- end ;
244- add! (names, concatenate (indent, name));
245- add! (docs, option.option-help);
227+ let flag? = instance? (option, <flag-option>);
228+ add! (rows,
229+ vector ("" , // causes a two space indent
230+ join(map (visible-option-name, option.short-names), ", " ),
231+ join(map (method (name)
232+ concatenate (visible-option-name(name),
233+ flag? & "" | "=" ,
234+ flag? & "" | (option.option-variable
235+ | canonical-name(option)))
236+ end ,
237+ option.long-names),
238+ " " ),
239+ option.option-help));
246240 end for ;
247- values (names, docs)
241+ rows
248242end function ;
249243
250- define function subcommand-columns
251- (cmd :: <command>)
252- => (names :: <sequence> , docs :: <sequence> )
253- let names = make (<stretchy-vector> );
254- let docs = make (<stretchy-vector> );
255- iterate loop (subs = as (<list> , cmd.command-subcommands), indent = " " )
244+ define constant $subcommand-columns
245+ = list (make (<column>), // empty string, creates column border
246+ make (<column>), // subcommand name
247+ make (<column>, // subcommand doc
248+ maximum-width: 50 , pad?: #f ));
249+
250+ define function subcommand-rows
251+ (cmd :: <command>) => (rows :: <sequence> )
252+ let rows = make (<stretchy-vector> );
253+ iterate loop (subs = as (<list> , cmd.command-subcommands), indent = "" )
256254 if (~empty? (subs))
257255 let subcmd = subs[0 ];
258- add! (names, concatenate (indent, subcmd.subcommand-name));
259- // TODO(cgay): Wrap doc text.
260- add! (docs, subcmd.command-help);
256+ add! (rows, list ( "" ,
257+ concatenate (indent, subcmd.subcommand-name),
258+ subcmd.command-help) );
261259 if (subcmd.has-subcommands?)
262260 loop(subcmd.command-subcommands, concatenate (indent, " " ));
263261 end ;
264262 loop(tail (subs), indent)
265263 end ;
266264 end iterate;
267- values (names, docs)
265+ rows
268266end function ;
269267
270268// Generate a one-line usage message showing the order of options and arguments.
0 commit comments