Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 13 additions & 4 deletions doc/man_docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,15 @@ func fillHeader(header *GenManHeader, name string, disableAutoGen bool) error {
return nil
}

// escapeAngleBrackets escapes angle brackets so they are not stripped by md2man
// which interprets them as HTML tags. This is necessary for command usage strings
// that contain placeholders like [<remote>:]<instance>.
func escapeAngleBrackets(s string) string {
s = strings.ReplaceAll(s, "<", `\<`)
s = strings.ReplaceAll(s, ">", `\>`)
return s
}

func manPreamble(buf io.StringWriter, header *GenManHeader, cmd *cobra.Command, dashedName string) {
description := cmd.Long
if len(description) == 0 {
Expand All @@ -149,11 +158,11 @@ func manPreamble(buf io.StringWriter, header *GenManHeader, cmd *cobra.Command,
cobra.WriteStringAndCheck(buf, fmt.Sprintf(`%% "%s" "%s" "%s" "%s" "%s"
# NAME
`, header.Title, header.Section, header.date, header.Source, header.Manual))
cobra.WriteStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, cmd.Short))
cobra.WriteStringAndCheck(buf, fmt.Sprintf("%s \\- %s\n\n", dashedName, escapeAngleBrackets(cmd.Short)))
cobra.WriteStringAndCheck(buf, "# SYNOPSIS\n")
cobra.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", cmd.UseLine()))
cobra.WriteStringAndCheck(buf, fmt.Sprintf("**%s**\n\n", escapeAngleBrackets(cmd.UseLine())))
cobra.WriteStringAndCheck(buf, "# DESCRIPTION\n")
cobra.WriteStringAndCheck(buf, description+"\n\n")
cobra.WriteStringAndCheck(buf, escapeAngleBrackets(description)+"\n\n")
}

func manPrintFlags(buf io.StringWriter, flags *pflag.FlagSet) {
Expand All @@ -180,7 +189,7 @@ func manPrintFlags(buf io.StringWriter, flags *pflag.FlagSet) {
format += "]"
}
format += "\n\t%s\n\n"
cobra.WriteStringAndCheck(buf, fmt.Sprintf(format, flag.DefValue, flag.Usage))
cobra.WriteStringAndCheck(buf, fmt.Sprintf(format, flag.DefValue, escapeAngleBrackets(flag.Usage)))
})
}

Expand Down
25 changes: 25 additions & 0 deletions doc/man_docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,31 @@ func assertNextLineEquals(scanner *bufio.Scanner, expectedLine string) error {
return fmt.Errorf("hit EOF before finding %v", expectedLine)
}

func TestGenManAngleBracketsPreserved(t *testing.T) {
// Regression test for https://github.com/spf13/cobra/issues/2330
// Angle brackets in Usage strings were stripped by md2man because
// it interpreted them as HTML tags.
cmd := &cobra.Command{
Use: "transfer [<remote>:]<source> [<remote>:]<dest>",
Short: "Transfer <files> between remotes",
Long: "Transfer <files> from <source> to <dest> across remotes.",
}

buf := new(bytes.Buffer)
if err := GenMan(cmd, nil, buf); err != nil {
t.Fatal(err)
}
output := buf.String()

// The rendered man page must contain the angle-bracketed placeholders.
// Before the fix, md2man stripped everything between < and > (treating
// them as HTML tags), leaving only "transfer [:] [:]" in the output.
checkStringContains(t, output, "<remote>")
checkStringContains(t, output, "<source>")
checkStringContains(t, output, "<dest>")
checkStringContains(t, output, "<files>")
}

func BenchmarkGenManToFile(b *testing.B) {
file, err := os.CreateTemp("", "")
if err != nil {
Expand Down