@@ -5,6 +5,7 @@ package cmd
5
5
6
6
import (
7
7
"bufio"
8
+ "bytes"
8
9
"errors"
9
10
"fmt"
10
11
"io"
@@ -354,83 +355,35 @@ func (c *documentationCommand) linkForCommand(cmd string) string {
354
355
// whether the command name should be a title or not. This is particularly
355
356
// handy when splitting the commands in different files.
356
357
func (c * documentationCommand ) formatCommand (ref commandReference , title bool , commandSeq []string ) string {
357
- formatted := ""
358
+ var fmtedTitle string
358
359
if title {
359
- formatted = "# " + strings .ToUpper (strings .Join (commandSeq [1 :], " " )) + " \n "
360
+ fmtedTitle = strings .ToUpper (strings .Join (commandSeq [1 :], " " ))
360
361
}
361
362
362
- var info * Info
363
- if ref .name == "documentation" {
364
- info = c .Info ()
365
- } else {
366
- info = ref .command .Info ()
367
- }
368
-
369
- // See Also
370
- if len (info .SeeAlso ) > 0 {
371
- formatted += "> See also: "
372
- prefix := "#"
373
- if c .ids != nil {
374
- prefix = "/t/"
375
- }
376
- if c .url != "" {
377
- prefix = c .url + "t/"
378
- }
363
+ var buf bytes.Buffer
364
+ PrintMarkdown (& buf , ref .command , MarkdownOptions {
365
+ Title : fmtedTitle ,
366
+ UsagePrefix : strings .Join (commandSeq [:len (commandSeq )- 1 ], " " ) + " " ,
367
+ LinkForCommand : func (s string ) string {
368
+ prefix := "#"
369
+ if c .ids != nil {
370
+ prefix = "/t/"
371
+ }
372
+ if c .url != "" {
373
+ prefix = c .url + "t/"
374
+ }
379
375
380
- for i , s := range info .SeeAlso {
381
376
target , err := c .getTargetCmd (s )
382
377
if err != nil {
383
378
fmt .Println (err .Error ())
384
379
}
385
- formatted += fmt .Sprintf ("[%s](%s%s)" , s , prefix , target )
386
- if i < len (info .SeeAlso )- 1 {
387
- formatted += ", "
388
- }
389
- }
390
- formatted += "\n "
391
- }
392
-
393
- if ref .alias != "" {
394
- formatted += "**Alias:** " + ref .alias + "\n "
395
- }
396
- if ref .check != nil && ref .check .Obsolete () {
397
- formatted += "*This command is deprecated*\n "
398
- }
399
- formatted += "\n "
400
-
401
- // Summary
402
- formatted += "## Summary\n " + info .Purpose + "\n \n "
403
-
404
- // Usage
405
- if strings .TrimSpace (info .Args ) != "" {
406
- formatted += fmt .Sprintf (`## Usage
407
- ` + "```" + `%s [options] %s` + "```" + `
408
-
409
- ` , strings .Join (commandSeq , " " ), info .Args )
410
- }
411
-
412
- // Options
413
- formattedFlags := c .formatFlags (ref .command , info )
414
- if len (formattedFlags ) > 0 {
415
- formatted += "### Options\n " + formattedFlags + "\n "
416
- }
417
-
418
- // Examples
419
- examples := info .Examples
420
- if strings .TrimSpace (examples ) != "" {
421
- formatted += "## Examples\n " + examples + "\n \n "
422
- }
423
-
424
- // Details
425
- doc := EscapeMarkdown (info .Doc )
426
- if strings .TrimSpace (doc ) != "" {
427
- formatted += "## Details\n " + doc + "\n \n "
428
- }
429
-
430
- formatted += c .formatSubcommands (info .Subcommands , commandSeq )
431
- formatted += "---\n \n "
432
-
433
- return formatted
380
+ return fmt .Sprintf ("%s%s" , prefix , target )
381
+ },
382
+ LinkForSubcommand : func (s string ) string {
383
+ return c .linkForCommand (strings .Join (append (commandSeq [1 :], s ), "_" ))
384
+ },
385
+ })
386
+ return buf .String ()
434
387
}
435
388
436
389
// getTargetCmd is an auxiliary function that returns the target command or
@@ -456,177 +409,3 @@ func (d *documentationCommand) getTargetCmd(cmd string) (string, error) {
456
409
457
410
}
458
411
}
459
-
460
- // formatFlags is an internal formatting solution similar to
461
- // the gnuflag.PrintDefaults. The code is extended here
462
- // to permit additional formatting without modifying the
463
- // gnuflag package.
464
- func (d * documentationCommand ) formatFlags (c Command , info * Info ) string {
465
- flagsAlias := FlagAlias (c , "" )
466
- if flagsAlias == "" {
467
- // For backward compatibility, the default is 'flag'.
468
- flagsAlias = "flag"
469
- }
470
- f := gnuflag .NewFlagSetWithFlagKnownAs (info .Name , gnuflag .ContinueOnError , flagsAlias )
471
-
472
- // if we are working with the documentation command,
473
- // we have to set flags on a new instance, otherwise
474
- // we will overwrite the current flag values
475
- if info .Name != "documentation" {
476
- c .SetFlags (f )
477
- } else {
478
- c = newDocumentationCommand (d .super )
479
- c .SetFlags (f )
480
- }
481
-
482
- // group together all flags for a given value, meaning that flag which sets the same value are
483
- // grouped together and displayed with the same description, as below:
484
- //
485
- // -s, --short, --alternate-string | default value | some description.
486
- flags := make (map [interface {}]flagsByLength )
487
- f .VisitAll (func (f * gnuflag.Flag ) {
488
- flags [f .Value ] = append (flags [f .Value ], f )
489
- })
490
- if len (flags ) == 0 {
491
- return ""
492
- }
493
-
494
- // sort the output flags by shortest name for each group.
495
- // Caution: this mean that description/default value displayed in documentation will
496
- // be the one of the shortest alias. Other will be discarded. Be careful to have the same default
497
- // values between each alias, and put the description on the shortest alias.
498
- var byName flagsByName
499
- for _ , fl := range flags {
500
- sort .Sort (fl )
501
- byName = append (byName , fl )
502
- }
503
- sort .Sort (byName )
504
-
505
- formatted := "| Flag | Default | Usage |\n "
506
- formatted += "| --- | --- | --- |\n "
507
- for _ , fs := range byName {
508
- // Collect all flag aliases (usually a short one and a plain one, like -v / --verbose)
509
- formattedFlags := ""
510
- for i , f := range fs {
511
- if i > 0 {
512
- formattedFlags += ", "
513
- }
514
- if len (f .Name ) == 1 {
515
- formattedFlags += fmt .Sprintf ("`-%s`" , f .Name )
516
- } else {
517
- formattedFlags += fmt .Sprintf ("`--%s`" , f .Name )
518
- }
519
- }
520
- // display all the flags aliases and the default value and description of the shortest one.
521
- // Escape Markdown in description in order to display it cleanly in the final documentation.
522
- formatted += fmt .Sprintf ("| %s | %s | %s |\n " , formattedFlags ,
523
- EscapeMarkdown (fs [0 ].DefValue ),
524
- strings .ReplaceAll (EscapeMarkdown (fs [0 ].Usage ), "\n " , " " ),
525
- )
526
- }
527
- return formatted
528
- }
529
-
530
- // flagsByLength is a slice of flags implementing sort.Interface,
531
- // sorting primarily by the length of the flag, and secondarily
532
- // alphabetically.
533
- type flagsByLength []* gnuflag.Flag
534
-
535
- func (f flagsByLength ) Less (i , j int ) bool {
536
- s1 , s2 := f [i ].Name , f [j ].Name
537
- if len (s1 ) != len (s2 ) {
538
- return len (s1 ) < len (s2 )
539
- }
540
- return s1 < s2
541
- }
542
- func (f flagsByLength ) Swap (i , j int ) {
543
- f [i ], f [j ] = f [j ], f [i ]
544
- }
545
- func (f flagsByLength ) Len () int {
546
- return len (f )
547
- }
548
-
549
- // flagsByName is a slice of slices of flags implementing sort.Interface,
550
- // alphabetically sorting by the name of the first flag in each slice.
551
- type flagsByName [][]* gnuflag.Flag
552
-
553
- func (f flagsByName ) Less (i , j int ) bool {
554
- return f [i ][0 ].Name < f [j ][0 ].Name
555
- }
556
- func (f flagsByName ) Swap (i , j int ) {
557
- f [i ], f [j ] = f [j ], f [i ]
558
- }
559
- func (f flagsByName ) Len () int {
560
- return len (f )
561
- }
562
-
563
- // EscapeMarkdown returns a copy of the input string, in which any special
564
- // Markdown characters (e.g. < > |) are escaped.
565
- func EscapeMarkdown (raw string ) string {
566
- escapeSeqs := map [rune ]string {
567
- '<' : "<" ,
568
- '>' : ">" ,
569
- '&' : "&" ,
570
- '|' : "|" ,
571
- }
572
-
573
- var escaped strings.Builder
574
- escaped .Grow (len (raw ))
575
-
576
- lines := strings .Split (raw , "\n " )
577
- for i , line := range lines {
578
- if strings .HasPrefix (line , " " ) {
579
- // Literal code block - don't escape anything
580
- escaped .WriteString (line )
581
-
582
- } else {
583
- // Keep track of whether we are inside a code span `...`
584
- // If so, don't escape characters
585
- insideCodeSpan := false
586
-
587
- for _ , c := range line {
588
- if c == '`' {
589
- insideCodeSpan = ! insideCodeSpan
590
- }
591
-
592
- if ! insideCodeSpan {
593
- if escapeSeq , ok := escapeSeqs [c ]; ok {
594
- escaped .WriteString (escapeSeq )
595
- continue
596
- }
597
- }
598
- escaped .WriteRune (c )
599
- }
600
- }
601
-
602
- if i < len (lines )- 1 {
603
- escaped .WriteRune ('\n' )
604
- }
605
- }
606
-
607
- return escaped .String ()
608
- }
609
-
610
- func (c * documentationCommand ) formatSubcommands (subcommands map [string ]string , commandSeq []string ) string {
611
- var output string
612
-
613
- sorted := []string {}
614
- for name := range subcommands {
615
- if isDefaultCommand (name ) {
616
- continue
617
- }
618
- sorted = append (sorted , name )
619
- }
620
- sort .Strings (sorted )
621
-
622
- if len (sorted ) > 0 {
623
- output += "## Subcommands\n "
624
- for _ , name := range sorted {
625
- output += fmt .Sprintf ("- [%s](%s)\n " , name ,
626
- c .linkForCommand (strings .Join (append (commandSeq [1 :], name ), "_" )))
627
- }
628
- output += "\n "
629
- }
630
-
631
- return output
632
- }
0 commit comments