From 6cd728394d2e78428b0c2f831887154c3232b2fc Mon Sep 17 00:00:00 2001 From: blenderfreaky Date: Thu, 24 Apr 2025 08:42:29 +0200 Subject: [PATCH] fix(site): escape flags with backticks (#4479) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(site): escape flags with backticks In the generated markdown docs, flags like `--foo` inside help texts currently get pretty-printed as `–foo` with an em-dash. This change applies backticks via a regex replacement, so that they appear as `--foo` in the docs but remain as --foo in the CLI output. --------- Co-authored-by: Julio Lopez <1953782+julio-lopez@users.noreply.github.com> --- tools/cli2md/cli2md.go | 17 ++++++-- tools/cli2md/cli2md_test.go | 83 +++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 tools/cli2md/cli2md_test.go diff --git a/tools/cli2md/cli2md.go b/tools/cli2md/cli2md.go index ab7237bd3..eda32e7aa 100644 --- a/tools/cli2md/cli2md.go +++ b/tools/cli2md/cli2md.go @@ -8,6 +8,7 @@ "log" "os" "path/filepath" + "regexp" "sort" "strings" @@ -31,6 +32,14 @@ dirMode = 0o750 ) +// The group at the start is to avoid matching things like "in-place". +var escapeFlagsRe = regexp.MustCompile(`(^|\s)(--?\w+[-\w]*)`) + +// escapeFlags escapes command-line flag references in help text by wrapping them in backticks. +func escapeFlags(text string) string { + return escapeFlagsRe.ReplaceAllString(text, "$1`$2`") +} + //nolint:gochecknoglobals var overrideDefault = map[string]string{ "config-file": "repository.config", @@ -75,9 +84,9 @@ func emitFlags(w io.Writer, flags []*kingpin.FlagModel) { defaultValue = "`false`" } - fmt.Fprintf(w, "| `--[no-]%v` | %v | %v | %v%v |\n", f.Name, shortFlag, defaultValue, maybeAdvanced, f.Help) //nolint:errcheck + fmt.Fprintf(w, "| `--[no-]%v` | %v | %v | %v%v |\n", f.Name, shortFlag, defaultValue, maybeAdvanced, escapeFlags(f.Help)) //nolint:errcheck } else { - fmt.Fprintf(w, "| `--%v` | %v | %v | %v%v |\n", f.Name, shortFlag, defaultValue, maybeAdvanced, f.Help) //nolint:errcheck + fmt.Fprintf(w, "| `--%v` | %v | %v | %v%v |\n", f.Name, shortFlag, defaultValue, maybeAdvanced, escapeFlags(f.Help)) //nolint:errcheck } } @@ -122,7 +131,7 @@ func emitArgs(w io.Writer, args []*kingpin.ArgModel) { }) for _, f := range args2 { - fmt.Fprintf(w, "| `%v` | %v |\n", f.Name, f.Help) //nolint:errcheck + fmt.Fprintf(w, "| `%v` | %v |\n", f.Name, escapeFlags(f.Help)) //nolint:errcheck } fmt.Fprintf(w, "\n") //nolint:errcheck @@ -297,7 +306,7 @@ func generateSubcommandPage(fname string, cmd *kingpin.CmdModel) { } fmt.Fprintf(f, "```shell\n$ kopia %v%v%v\n```\n\n", cmd.FullCommand, flagSummary, argSummary) //nolint:errcheck - fmt.Fprintf(f, "%v\n\n", cmd.Help) //nolint:errcheck + fmt.Fprintf(f, "%v\n\n", escapeFlags(cmd.Help)) //nolint:errcheck emitFlags(f, cmd.Flags) emitArgs(f, cmd.Args) diff --git a/tools/cli2md/cli2md_test.go b/tools/cli2md/cli2md_test.go new file mode 100644 index 000000000..ced62f5b6 --- /dev/null +++ b/tools/cli2md/cli2md_test.go @@ -0,0 +1,83 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestEscapeFlags(t *testing.T) { + cases := []struct { + name string + input string + expected string + }{ + { + name: "empty string", + input: "", + expected: "", + }, + { + name: "basic single flag", + input: "use -flag to enable", + expected: "use `-flag` to enable", + }, + { + name: "double dash flag", + input: "use --long-flag-name to configure", + expected: "use `--long-flag-name` to configure", + }, + { + name: "multiple flags", + input: "use -a or --bee flags", + expected: "use `-a` or `--bee` flags", + }, + { + name: "should not match in-place", + input: "performs in-place modification", + expected: "performs in-place modification", + }, + { + name: "flags with numbers and hyphens", + input: "use --http2-max-streams or -h2-timeout", + expected: "use `--http2-max-streams` or `-h2-timeout`", + }, + { + name: "flag at start of string", + input: "-flag at start", + expected: "`-flag` at start", + }, + { + name: "existing backticks", + input: "use `--existing` and -new flags", + expected: "use `--existing` and `-new` flags", + }, + { + name: "multiple spaces before flag", + input: "test -flag with spaces", + expected: "test `-flag` with spaces", + }, + { + name: "mixed valid and invalid patterns", + input: "test in-place and -valid --flags", + expected: "test in-place and `-valid` `--flags`", + }, + { + name: "separator line", + input: "---------------", + expected: "---------------", + }, + { + name: "too many dashes", + input: "none ---of these --- dashes ----should match", + expected: "none ---of these --- dashes ----should match", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + got := escapeFlags(tc.input) + require.Equal(t, tc.expected, got) + }) + } +}