Skip to content

Commit

Permalink
Add a COMMANDS section to generated man pages
Browse files Browse the repository at this point in the history
When a command has available sub-commands, the COMMANDS
man section is generated with the list of sub-commands, with
their name, short description, and the name of the dedicated
man page.
  • Loading branch information
orobardet committed Aug 30, 2019
1 parent b80588d commit 65614da
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 0 deletions.
13 changes: 13 additions & 0 deletions doc/cmd_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package doc

import (
"regexp"
"strings"
"testing"

Expand Down Expand Up @@ -84,3 +85,15 @@ func checkStringOmits(t *testing.T, got, expected string) {
t.Errorf("Expected to not contain: \n %v\nGot: %v", expected, got)
}
}

func checkStringMatch(t *testing.T, got, pattern string) {
if ok, _ := regexp.MatchString(pattern, got) ; !ok {
t.Errorf("Expected to match: \n%v\nGot:\n %v\n", pattern, got)
}
}

func checkStringDontMatch(t *testing.T, got, pattern string) {
if ok, _ := regexp.MatchString(pattern, got) ; ok {
t.Errorf("Expected not to match: \n%v\nGot:\n %v\n", pattern, got)
}
}
26 changes: 26 additions & 0 deletions doc/man_docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,31 @@ func manPreamble(buf *bytes.Buffer, header *GenManHeader, cmd *cobra.Command, da
buf.WriteString(description + "\n\n")
}

func manPrintCommands(buf *bytes.Buffer, header *GenManHeader, cmd *cobra.Command) {
// Find sub-commands that need to be documented
subCommands := []*cobra.Command{}
for _, c := range cmd.Commands() {
if !c.IsAvailableCommand() || c.IsAdditionalHelpTopicCommand() {
continue
}
subCommands = append(subCommands, c)
}

// No need to go further if there is no sub-commands to document
if len(subCommands) <= 0 {
return
}

// Add a 'COMMANDS' section in the generated documentation
buf.WriteString("# COMMANDS\n")
// For each sub-commands, and an entry with the command name and it's Short description and reference to dedicated
// man page
for _, c := range subCommands {
dashedPath := strings.Replace(c.CommandPath(), " ", "-", -1)
buf.WriteString(fmt.Sprintf("**%s**\n\t%s \n\tSee **%s(%s)**.\n\n", c.Name(), c.Short, dashedPath, header.Section))
}
}

func manPrintFlags(buf *bytes.Buffer, flags *pflag.FlagSet) {
flags.VisitAll(func(flag *pflag.Flag) {
if len(flag.Deprecated) > 0 || flag.Hidden {
Expand Down Expand Up @@ -210,6 +235,7 @@ func genMan(cmd *cobra.Command, header *GenManHeader) []byte {
buf := new(bytes.Buffer)

manPreamble(buf, header, cmd, dashCommandName)
manPrintCommands(buf, header, cmd)
manPrintOptions(buf, cmd)
if len(cmd.Example) > 0 {
buf.WriteString("# EXAMPLE\n")
Expand Down
39 changes: 39 additions & 0 deletions doc/man_docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,45 @@ func TestManPrintFlagsHidesShortDeperecated(t *testing.T) {
}
}

func TestGenManCommands(t *testing.T) {
header := &GenManHeader{
Title: "Project",
Section: "2",
}

// Root command
buf := new(bytes.Buffer)
if err := GenMan(rootCmd, header, buf); err != nil {
t.Fatal(err)
}
output := buf.String()

checkStringContains(t, output, ".SH COMMANDS")
checkStringMatch(t, output, "\\\\fBecho\\\\fP\n[ \t]+Echo anything to the screen\n[ \t]+See \\\\fBroot\\\\-echo\\(2\\)\\\\fP\\\\&\\.")
checkStringOmits(t, output, ".PP\n\\fBprint\\fP\n")

// Echo command
buf = new(bytes.Buffer)
if err := GenMan(echoCmd, header, buf); err != nil {
t.Fatal(err)
}
output = buf.String()

checkStringContains(t, output, ".SH COMMANDS")
checkStringMatch(t, output, "\\\\fBtimes\\\\fP\n[ \t]+Echo anything to the screen more times\n[ \t]+See \\\\fBroot\\\\-echo\\\\-times\\(2\\)\\\\fP\\\\&\\.")
checkStringMatch(t, output, "\\\\fBechosub\\\\fP\n[ \t]+second sub command for echo\n[ \t]+See \\\\fBroot\\\\-echo\\\\-echosub\\(2\\)\\\\fP\\\\&\\.")
checkStringOmits(t, output, ".PP\n\\fBdeprecated\\fP\n")

// Time command as echo's subcommand
buf = new(bytes.Buffer)
if err := GenMan(timesCmd, header, buf); err != nil {
t.Fatal(err)
}
output = buf.String()

checkStringOmits(t, output, ".SH COMMANDS")
}

func TestGenManTree(t *testing.T) {
c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"}
header := &GenManHeader{Section: "2"}
Expand Down

0 comments on commit 65614da

Please sign in to comment.