@@ -10,18 +10,31 @@ import {
1010} from './option'
1111
1212export const GenerateTableCommandConfig = z . object ( {
13+ /** Length of each row in the table */
1314 lineLength : z . number ( ) . optional ( ) ,
15+ /** When `false`, each row is separated by a blank line */
1416 compact : z . boolean ( ) . optional ( ) ,
17+ /** Style of the command/option name */
1518 nameStyle : StringStyle . optional ( ) ,
19+ /** Style of the command/option description */
1620 descriptionStyle : StringStyle . optional ( ) ,
21+ /** Prefix for the command/option name (default is the command's prefix) */
1722 namePrefix : z . string ( ) . optional ( ) ,
23+ /** Prefix for the command/option aliases (default is the command's prefix) */
1824 aliasPrefix : z . string ( ) . optional ( ) ,
19- negatePrefix : z . string ( ) . optional ( ) ,
20- negateAliasPrefix : z . string ( ) . optional ( ) ,
2125} )
2226export type GenerateTableCommandConfig = z . infer < typeof GenerateTableCommandConfig >
2327
24- export const GenerateTableOptionConfig = GenerateTableCommandConfig
28+ export const GenerateTableOptionConfig = GenerateTableCommandConfig . merge (
29+ z . object ( {
30+ /** Prefix for the command/option negations (default is the command's prefix) */
31+ negatePrefix : z . string ( ) . optional ( ) ,
32+ /** Prefix for the command/option negation aliases (default is the command's prefix) */
33+ negateAliasPrefix : z . string ( ) . optional ( ) ,
34+ /** Whether to display negations with each option name */
35+ displayNegations : z . boolean ( ) . optional ( ) ,
36+ } ) ,
37+ )
2538export type GenerateTableOptionConfig = z . infer < typeof GenerateTableOptionConfig >
2639
2740export const HelpConfig = z . object ( {
@@ -38,6 +51,9 @@ export const HelpConfig = z.object({
3851 */
3952 bindOption : z . boolean ( ) . optional ( ) ,
4053
54+ /** Whether to align all tables to the column widths, or have each table be independent. Default is `true` */
55+ useGlobalTableColumns : z . boolean ( ) . default ( true ) . optional ( ) ,
56+
4157 /** Options for generating the table of commands */
4258 commandOptions : GenerateTableCommandConfig . omit ( { lineLength : true } ) . optional ( ) ,
4359 /** Options for generating the table of options */
@@ -83,6 +99,7 @@ export type HelpConfig = z.infer<typeof HelpConfig>
8399
84100export const defaultHelpConfig : DeepRequired < HelpConfig > = {
85101 lineLength : 80 ,
102+ useGlobalTableColumns : true ,
86103 commandOptions : {
87104 nameStyle : {
88105 color : 'yellow' ,
@@ -96,6 +113,7 @@ export const defaultHelpConfig: DeepRequired<HelpConfig> = {
96113 aliasPrefix : OPT_SHORT_PREFIX ,
97114 negatePrefix : NEGATE_FULL_PREFIX ,
98115 negateAliasPrefix : NEGATE_SHORT_PREFIX ,
116+ displayNegations : false ,
99117 nameStyle : {
100118 color : 'yellow' ,
101119 } ,
@@ -160,14 +178,23 @@ export class HelpGenerator {
160178 const entry = this . entry
161179 const CMD_OPT_INDENT = 4
162180 const _wrap = ( text : string , indent = 0 ) => wrap ( text , this . config . lineLength - indent )
163- const options = generateHelpTable ( entry . options , {
181+ const optionOptions = {
164182 ...this . config . optionOptions ,
165183 lineLength : this . config . lineLength - CMD_OPT_INDENT ,
166- } ) . trimEnd ( )
167- const commands = generateHelpTable ( entry . commands , {
184+ }
185+ const commandOptions = {
168186 ...this . config . commandOptions ,
187+ displayNegations : false ,
169188 lineLength : this . config . lineLength - CMD_OPT_INDENT ,
170- } ) . trimEnd ( )
189+ }
190+ const maxNameLength = this . config . useGlobalTableColumns
191+ ? Math . max (
192+ getMaxNameLength ( entry . options . map ( ( e ) => getItemDetails ( e , optionOptions ) ) ) ,
193+ getMaxNameLength ( entry . commands . map ( ( e ) => getItemDetails ( e , commandOptions ) ) ) ,
194+ )
195+ : undefined
196+ const options = generateHelpTable ( entry . options , optionOptions , maxNameLength ) . trimEnd ( )
197+ const commands = generateHelpTable ( entry . commands , commandOptions , maxNameLength ) . trimEnd ( )
171198 const examples = entry . examples
172199 . map ( ( example ) => {
173200 const { description, input, output } = example
@@ -266,46 +293,80 @@ function wrap(text: string, lineLength: number): string {
266293 return subRows . join ( '\n' )
267294}
268295
269- function generateHelpTable < T extends Partial < GenerateTableCommandConfig > > (
270- items : HelpItem [ ] ,
271- {
272- lineLength : lineLength = 80 ,
296+ type ParsedHelpItem = {
297+ name : string
298+ description : string
299+ hidden : boolean
300+ }
301+
302+ const getMaxNameLength = ( items : ParsedHelpItem [ ] ) : number =>
303+ Math . max ( ...items . map ( ( o ) => o . name . length ) )
304+
305+ function getItemDetails (
306+ o : HelpItem ,
307+ options ?: Pick <
308+ GenerateTableOptionConfig & GenerateTableOptionConfig ,
309+ 'displayNegations' | 'namePrefix' | 'aliasPrefix' | 'negatePrefix' | 'negateAliasPrefix'
310+ > ,
311+ ) : ParsedHelpItem {
312+ const {
313+ displayNegations = false ,
273314 namePrefix = '' ,
274315 negatePrefix = '' ,
275316 aliasPrefix = '' ,
276- negateAliasPrefix : aliasNegatePrefix = '' ,
317+ negateAliasPrefix = '' ,
318+ } = options ?? { }
319+ const cmdNames = {
320+ full : `${ namePrefix } ${ o . name } ` ,
321+ fullNegated : negatePrefix ? `${ negatePrefix } ${ o . name } ` : undefined ,
322+ aliases : o . aliases . map ( ( a ) => `${ aliasPrefix } ${ a } ` ) . join ( ' | ' ) ,
323+ aliasesNegated : negatePrefix
324+ ? o . aliases . map ( ( a ) => `${ negateAliasPrefix } ${ a } ` ) . join ( ' | ' )
325+ : undefined ,
326+ }
327+ const name = [
328+ cmdNames . full ,
329+ cmdNames . aliases ,
330+ displayNegations && cmdNames . fullNegated ,
331+ displayNegations && cmdNames . aliasesNegated ,
332+ ]
333+ . filter ( Boolean )
334+ . join ( ' | ' )
335+ const description = o . description
336+ const hidden = o . hidden || false
337+ return { name, description, hidden }
338+ }
339+
340+ function generateHelpTable < T extends GenerateTableCommandConfig | GenerateTableOptionConfig > (
341+ items : HelpItem [ ] ,
342+ fullConfig : Partial < T > = { } ,
343+ maxNameLength ?: number ,
344+ ) : string {
345+ const {
346+ lineLength = 80 ,
347+ namePrefix = '' ,
348+ aliasPrefix = '' ,
349+ negatePrefix = '' ,
350+ negateAliasPrefix = '' ,
351+ displayNegations = false ,
277352 compact = false ,
278353 ...config
279- } : Partial < T > = { } ,
280- ) : string {
354+ } = fullConfig as GenerateTableOptionConfig
281355 const rows = items
282- . map ( ( o ) => {
283- const cmdNames = {
284- full : `${ namePrefix } ${ o . name } ` ,
285- fullNegated : negatePrefix ? `${ negatePrefix } ${ o . name } ` : undefined ,
286- aliases : o . aliases . map ( ( a ) => `${ aliasPrefix } ${ a } ` ) . join ( ' | ' ) ,
287- aliasesNegated : negatePrefix
288- ? o . aliases . map ( ( a ) => `${ aliasNegatePrefix } ${ a } ` ) . join ( ' | ' )
289- : undefined ,
290- }
291- const name = [
292- cmdNames . full ,
293- cmdNames . aliases ,
294- // cmdNames.fullNegated,
295- // cmdNames.aliasesNegated,
296- ]
297- . filter ( Boolean )
298- . join ( ' | ' )
299- const description = o . description
300- const hidden = o . hidden || false
301- return { name, description, hidden }
302- } )
356+ . map ( ( o ) =>
357+ getItemDetails ( o , {
358+ namePrefix,
359+ aliasPrefix,
360+ negatePrefix,
361+ negateAliasPrefix,
362+ } ) ,
363+ )
303364 . filter ( ( r ) => ! r . hidden )
304- const maxNameLength = Math . max ( ... rows . map ( ( o ) => o . name . length ) )
365+ maxNameLength ??= getMaxNameLength ( rows )
305366 const nameStyle = ( name : string ) => format ( name , config . nameStyle )
306367 const descStyle = ( desc : string ) => format ( desc , config . descriptionStyle )
307368 const table = rows . map ( ( row ) => {
308- const name = nameStyle ( row . name . padEnd ( maxNameLength + 2 ) )
369+ const name = nameStyle ( row . name . padEnd ( maxNameLength ! + 2 ) )
309370 const description = descStyle ( row . description )
310371 const length = stripStyle ( name ) . length + stripStyle ( description ) . length
311372 if ( length <= lineLength ) {
@@ -322,7 +383,7 @@ function generateHelpTable<T extends Partial<GenerateTableCommandConfig>>(
322383 for ( const word of words ) {
323384 if ( stripStyle ( currentRow ) . length + stripStyle ( word ) . length + 1 > lineLength ) {
324385 subRows . push ( currentRow )
325- currentRow = ' ' . repeat ( maxNameLength + 2 )
386+ currentRow = ' ' . repeat ( maxNameLength ! + 2 )
326387 }
327388 currentRow += `${ word } `
328389 }
0 commit comments