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
12 changes: 8 additions & 4 deletions doc/md_docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string)
name := cmd.CommandPath()

buf.WriteString("## " + name + "\n\n")
buf.WriteString(cmd.Short + "\n\n")
buf.WriteString(escapeAngleBrackets(cmd.Short) + "\n\n")
if len(cmd.Long) > 0 {
buf.WriteString("### Synopsis\n\n")
buf.WriteString(cmd.Long + "\n\n")
buf.WriteString(escapeAngleBrackets(cmd.Long) + "\n\n")
}

if cmd.Runnable() {
Expand All @@ -87,7 +87,7 @@ func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string)
pname := parent.CommandPath()
link := pname + markdownExtension
link = strings.ReplaceAll(link, " ", "_")
fmt.Fprintf(buf, "* [%s](%s)\t - %s\n", pname, linkHandler(link), parent.Short)
fmt.Fprintf(buf, "* [%s](%s)\t - %s\n", pname, linkHandler(link), escapeAngleBrackets(parent.Short))
cmd.VisitParents(func(c *cobra.Command) {
if c.DisableAutoGenTag {
cmd.DisableAutoGenTag = c.DisableAutoGenTag
Expand All @@ -105,7 +105,7 @@ func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string)
cname := name + " " + child.Name()
link := cname + markdownExtension
link = strings.ReplaceAll(link, " ", "_")
fmt.Fprintf(buf, "* [%s](%s)\t - %s\n", cname, linkHandler(link), child.Short)
fmt.Fprintf(buf, "* [%s](%s)\t - %s\n", cname, linkHandler(link), escapeAngleBrackets(child.Short))
}
buf.WriteString("\n")
}
Expand All @@ -116,6 +116,10 @@ func GenMarkdownCustom(cmd *cobra.Command, w io.Writer, linkHandler func(string)
return err
}

func escapeAngleBrackets(s string) string {
return strings.NewReplacer("<", "\\<", ">", "\\>").Replace(s)
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

escapeAngleBrackets currently escapes both < and >. Escaping > is not needed to prevent MDX JSX parsing (escaping < is sufficient) and it will also change intended Markdown semantics, e.g. a description line starting with > will no longer render as a blockquote. Consider only escaping < (and update the new test expectations accordingly) to minimize formatting regressions.

Suggested change
return strings.NewReplacer("<", "\\<", ">", "\\>").Replace(s)
return strings.NewReplacer("<", "\\<").Replace(s)

Copilot uses AI. Check for mistakes.
}
Comment on lines +119 to +121
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

escapeAngleBrackets creates a new strings.Replacer on every call. Since GenMarkdownCustom may run across many commands (e.g., GenMarkdownTreeCustom), consider caching the replacer as a package-level var and reusing it to avoid repeated allocations/work.

Copilot uses AI. Check for mistakes.

// GenMarkdownTree will generate a markdown page for this command and all
// descendants in the directory given. The header may be nil.
// This function may not work correctly if your command names have `-` in them.
Expand Down
26 changes: 26 additions & 0 deletions doc/md_docs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,32 @@ func TestGenMdNoTag(t *testing.T) {
checkStringOmits(t, output, "Auto generated")
}

func TestGenMdEscapesAngleBrackets(t *testing.T) {
cmd := &cobra.Command{
Use: "export <output_path>",
Short: "Export to <output_path>",
Long: "Export the database to <output_path>.",
Run: emptyRun,
}
child := &cobra.Command{
Use: "status",
Short: "Inspect <target>",
Run: emptyRun,
}
cmd.AddCommand(child)

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

checkStringContains(t, output, "Export to \\<output_path\\>")
checkStringContains(t, output, "Export the database to \\<output_path\\>.")
checkStringContains(t, output, "Inspect \\<target\\>")
checkStringContains(t, output, "```\nexport <output_path> [flags]\n```")
}

func TestGenMdTree(t *testing.T) {
c := &cobra.Command{Use: "do [OPTIONS] arg1 arg2"}
tmpdir, err := os.MkdirTemp("", "test-gen-md-tree")
Expand Down
Loading