Mangen: allow callers to specify header information

We previously had this weak argument called projectName which let you
set a single part of a man page header. Instead do the best we can if
the caller doesn't pass us anything, but let the caller specify anything
they want.
This commit is contained in:
Eric Paris 2015-09-08 16:02:02 -04:00
parent cfecf1379c
commit edcf765d9f
5 changed files with 110 additions and 21 deletions

34
examples_test.go Normal file
View file

@ -0,0 +1,34 @@
package cobra_test
import (
"bytes"
"fmt"
"github.com/spf13/cobra"
)
func ExampleCommand_GenManTree() {
cmd := &cobra.Command{
Use: "test",
Short: "my test program",
}
header := &cobra.GenManHeader{
Title: "MINE",
Section: "3",
}
cmd.GenManTree(header, "/tmp")
}
func ExampleCommand_GenMan() {
cmd := &cobra.Command{
Use: "test",
Short: "my test program",
}
header := &cobra.GenManHeader{
Title: "MINE",
Section: "3",
}
out := new(bytes.Buffer)
cmd.GenMan(header, out)
fmt.Print(out.String())
}

View file

@ -25,20 +25,29 @@ import (
"github.com/spf13/pflag" "github.com/spf13/pflag"
) )
func GenManTree(cmd *Command, projectName, dir string) { // GenManTree will call cmd.GenManTree(header, dir)
cmd.GenManTree(projectName, dir) func GenManTree(cmd *Command, header *GenManHeader, dir string) {
cmd.GenManTree(header, dir)
} }
func (cmd *Command) GenManTree(projectName, dir string) { // GenManTree will generate a man page for this command and all decendants
// in the directory given. The header may be nil. This function may not work
// correctly if your command names have - in them. If you have `cmd` with two
// subcmds, `sub` and `sub-third`. And `sub` has a subcommand called `third`
// it is undefined which help output will be in the file `cmd-sub-third.1`.
func (cmd *Command) GenManTree(header *GenManHeader, dir string) {
if header == nil {
header = &GenManHeader{}
}
for _, c := range cmd.Commands() { for _, c := range cmd.Commands() {
if len(c.Deprecated) != 0 || c == cmd.helpCommand { if len(c.Deprecated) != 0 || c == cmd.helpCommand {
continue continue
} }
GenManTree(c, projectName, dir) GenManTree(c, header, dir)
} }
out := new(bytes.Buffer) out := new(bytes.Buffer)
cmd.GenMan(projectName, out) cmd.GenMan(header, out)
filename := cmd.CommandPath() filename := cmd.CommandPath()
filename = dir + strings.Replace(filename, " ", "-", -1) + ".1" filename = dir + strings.Replace(filename, " ", "-", -1) + ".1"
@ -55,21 +64,58 @@ func (cmd *Command) GenManTree(projectName, dir string) {
} }
} }
func GenMan(cmd *Command, projectName string, out *bytes.Buffer) { // GenManHeader is a lot like the .TH header at the start of man pages. These
cmd.GenMan(projectName, out) // include the title, section, date, source, and manual. We will use the
// current time if Date if unset and will use "Auto generated by spf13/cobra"
// if the Source is unset.
type GenManHeader struct {
Title string
Section string
Date *time.Time
date string
Source string
Manual string
} }
func (cmd *Command) GenMan(projectName string, out *bytes.Buffer) { // GenMan will call cmd.GenMan(header, out)
func GenMan(cmd *Command, header *GenManHeader, out *bytes.Buffer) {
cmd.GenMan(header, out)
}
buf := genMarkdown(cmd, projectName) // GenMan will generate a man page for the given command in the out buffer.
// The header argument may be nil, however obviously out may not.
func (cmd *Command) GenMan(header *GenManHeader, out *bytes.Buffer) {
if header == nil {
header = &GenManHeader{}
}
buf := genMarkdown(cmd, header)
final := mangen.Render(buf) final := mangen.Render(buf)
out.Write(final) out.Write(final)
} }
func manPreamble(out *bytes.Buffer, projectName, name, short, long string) { func fillHeader(header *GenManHeader, name string) {
fmt.Fprintf(out, `%% %s(1) if header.Title == "" {
header.Title = name
}
if header.Section == "" {
header.Section = "1"
}
if header.Date == nil {
now := time.Now()
header.Date = &now
}
header.date = (*header.Date).Format("Jan 2006")
if header.Source == "" {
header.Source = "Auto generated by spf13/cobra"
}
}
func manPreamble(out *bytes.Buffer, header *GenManHeader, name, short, long string) {
fmt.Fprintf(out, `%% %s(%s)%s
%% %s
%% %s
# NAME # NAME
`, projectName) `, header.Title, header.Section, header.date, header.Source, header.Manual)
fmt.Fprintf(out, "%s \\- %s\n\n", name, short) fmt.Fprintf(out, "%s \\- %s\n\n", name, short)
fmt.Fprintf(out, "# SYNOPSIS\n") fmt.Fprintf(out, "# SYNOPSIS\n")
fmt.Fprintf(out, "**%s** [OPTIONS]\n\n", name) fmt.Fprintf(out, "**%s** [OPTIONS]\n\n", name)
@ -120,7 +166,8 @@ func manPrintOptions(out *bytes.Buffer, command *Command) {
} }
} }
func genMarkdown(cmd *Command, projectName string) []byte { func genMarkdown(cmd *Command, header *GenManHeader) []byte {
fillHeader(header, cmd.Name())
// something like `rootcmd subcmd1 subcmd2` // something like `rootcmd subcmd1 subcmd2`
commandName := cmd.CommandPath() commandName := cmd.CommandPath()
// something like `rootcmd-subcmd1-subcmd2` // something like `rootcmd-subcmd1-subcmd2`
@ -134,7 +181,7 @@ func genMarkdown(cmd *Command, projectName string) []byte {
long = short long = short
} }
manPreamble(buf, projectName, commandName, short, long) manPreamble(buf, header, commandName, short, long)
manPrintOptions(buf, cmd) manPrintOptions(buf, cmd)
if len(cmd.Example) > 0 { if len(cmd.Example) > 0 {
@ -147,7 +194,7 @@ func genMarkdown(cmd *Command, projectName string) []byte {
if cmd.HasParent() { if cmd.HasParent() {
parentPath := cmd.Parent().CommandPath() parentPath := cmd.Parent().CommandPath()
dashParentPath := strings.Replace(parentPath, " ", "-", -1) dashParentPath := strings.Replace(parentPath, " ", "-", -1)
fmt.Fprintf(buf, "**%s(1)**, ", dashParentPath) fmt.Fprintf(buf, "**%s(%s)**, ", dashParentPath, header.Section)
} }
children := cmd.Commands() children := cmd.Commands()
@ -156,11 +203,11 @@ func genMarkdown(cmd *Command, projectName string) []byte {
if len(c.Deprecated) != 0 || c == cmd.helpCommand { if len(c.Deprecated) != 0 || c == cmd.helpCommand {
continue continue
} }
fmt.Fprintf(buf, "**%s-%s(1)**, ", dashCommandName, c.Name()) fmt.Fprintf(buf, "**%s-%s(%s)**, ", dashCommandName, c.Name(), header.Section)
} }
fmt.Fprintf(buf, "\n") fmt.Fprintf(buf, "\n")
} }
fmt.Fprintf(buf, "# HISTORY\n%s Auto generated by spf13/cobra\n", time.Now().UTC()) fmt.Fprintf(buf, "# HISTORY\n%s Auto generated by spf13/cobra\n", header.Date.Format("2-Jan-2006"))
return buf.Bytes() return buf.Bytes()
} }

View file

@ -14,7 +14,11 @@ func main() {
Use: "test", Use: "test",
Short: "my test program", Short: "my test program",
} }
cmd.GenManTree("/tmp") header := &cobra.GenManHeader{
Title: "MINE",
Section: "3",
}
cmd.GenManTree(header, "/tmp")
} }
``` ```

View file

@ -24,15 +24,19 @@ func TestGenManDoc(t *testing.T) {
out := new(bytes.Buffer) out := new(bytes.Buffer)
header := &GenManHeader{
Title: "Project",
Section: "2",
}
// We generate on a subcommand so we have both subcommands and parents // We generate on a subcommand so we have both subcommands and parents
cmdEcho.GenMan("PROJECT", out) cmdEcho.GenMan(header, out)
found := out.String() found := out.String()
// Make sure parent has - in CommandPath() in SEE ALSO: // Make sure parent has - in CommandPath() in SEE ALSO:
parentPath := cmdEcho.Parent().CommandPath() parentPath := cmdEcho.Parent().CommandPath()
dashParentPath := strings.Replace(parentPath, " ", "-", -1) dashParentPath := strings.Replace(parentPath, " ", "-", -1)
expected := translate(dashParentPath) expected := translate(dashParentPath)
expected = expected + "(1)" expected = expected + "(" + header.Section + ")"
checkStringContains(t, found, expected) checkStringContains(t, found, expected)
// Our description // Our description

View file

@ -108,7 +108,7 @@ func (cmd *Command) GenMarkdownCustom(out *bytes.Buffer, linkHandler func(string
fmt.Fprintf(out, "\n") fmt.Fprintf(out, "\n")
} }
fmt.Fprintf(out, "###### Auto generated by spf13/cobra at %s\n", time.Now().UTC()) fmt.Fprintf(out, "###### Auto generated by spf13/cobra on %s\n", time.Now().Format("2-Jan-2006"))
} }
func GenMarkdownTree(cmd *Command, dir string) { func GenMarkdownTree(cmd *Command, dir string) {