From 77c93135f0a4e3a56d3257ee60eca7d1baca91a4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 19 Oct 2025 13:36:41 +0200 Subject: [PATCH 01/12] test driven development ;) --- docs_test.go | 33 +++++++++++-------- testdata/expected-doc-full.man | 4 +++ testdata/expected-doc-full.md | 3 ++ testdata/expected-doc-no-authors.md | 3 ++ testdata/expected-doc-no-commands.md | 3 ++ testdata/expected-doc-no-usagetext.md | 3 ++ ...pected-tabular-markdown-custom-app-path.md | 11 ++++--- testdata/expected-tabular-markdown-full.md | 11 ++++--- 8 files changed, 47 insertions(+), 24 deletions(-) diff --git a/docs_test.go b/docs_test.go index 66986d13..358bbfe9 100644 --- a/docs_test.go +++ b/docs_test.go @@ -39,7 +39,7 @@ func normalizeNewlines(d []byte) []byte { ) } -func buildExtendedTestCommand() *cli.Command { +func buildExtendedTestCommand(t *testing.T) *cli.Command { return &cli.Command{ Writer: io.Discard, Name: "greet", @@ -62,6 +62,11 @@ func buildExtendedTestCommand() *cli.Command { Name: "hidden-flag", Hidden: true, }, + &cli.StringFlag{ + Name: "temp-dir", + Value: t.TempDir(), + DefaultText: "test temp dir", + }, }, Commands: []*cli.Command{{ Aliases: []string{"c"}, @@ -154,14 +159,14 @@ Should be a part of the same code block } func TestToMarkdownFull(t *testing.T) { - cmd := buildExtendedTestCommand() + cmd := buildExtendedTestCommand(t) res, err := ToMarkdown(cmd) require.NoError(t, err) expectFileContent(t, "testdata/expected-doc-full.md", res) } func TestToTabularMarkdown(t *testing.T) { - app := buildExtendedTestCommand() + app := buildExtendedTestCommand(t) t.Run("full", func(t *testing.T) { res, err := ToTabularMarkdown(app, "app") @@ -188,7 +193,7 @@ func TestToTabularMarkdownFailed(t *testing.T) { MarkdownTabularDocTemplate = "{{ .Foo }}" - app := buildExtendedTestCommand() + app := buildExtendedTestCommand(t) res, err := ToTabularMarkdown(app, "") @@ -223,7 +228,7 @@ Some other text`) r.NoError(err) _ = tmpFile.Close() - r.NoError(ToTabularToFileBetweenTags(buildExtendedTestCommand(), "app", tmpFile.Name())) + r.NoError(ToTabularToFileBetweenTags(buildExtendedTestCommand(t), "app", tmpFile.Name())) content, err := os.ReadFile(tmpFile.Name()) r.NoError(err) @@ -263,7 +268,7 @@ Some other text`) r.NoError(err) _ = tmpFile.Close() - r.NoError(ToTabularToFileBetweenTags(buildExtendedTestCommand(), "app", tmpFile.Name(), "foo_BAR|baz", "lorem+ipsum")) + r.NoError(ToTabularToFileBetweenTags(buildExtendedTestCommand(t), "app", tmpFile.Name(), "foo_BAR|baz", "lorem+ipsum")) content, err := os.ReadFile(tmpFile.Name()) r.NoError(err) @@ -293,7 +298,7 @@ Some other text`)) r.NoError(os.Remove(tmpFile.Name())) - err = ToTabularToFileBetweenTags(buildExtendedTestCommand(), "app", tmpFile.Name()) + err = ToTabularToFileBetweenTags(buildExtendedTestCommand(t), "app", tmpFile.Name()) r.ErrorIs(err, fs.ErrNotExist) }) @@ -301,7 +306,7 @@ Some other text`)) func TestToMarkdown(t *testing.T) { t.Run("no flags", func(t *testing.T) { - app := buildExtendedTestCommand() + app := buildExtendedTestCommand(t) app.Flags = nil res, err := ToMarkdown(app) @@ -311,7 +316,7 @@ func TestToMarkdown(t *testing.T) { }) t.Run("no commands", func(t *testing.T) { - app := buildExtendedTestCommand() + app := buildExtendedTestCommand(t) app.Commands = nil res, err := ToMarkdown(app) @@ -321,7 +326,7 @@ func TestToMarkdown(t *testing.T) { }) t.Run("no authors", func(t *testing.T) { - app := buildExtendedTestCommand() + app := buildExtendedTestCommand(t) app.Authors = []any{} res, err := ToMarkdown(app) @@ -331,7 +336,7 @@ func TestToMarkdown(t *testing.T) { }) t.Run("no usage text", func(t *testing.T) { - app := buildExtendedTestCommand() + app := buildExtendedTestCommand(t) app.UsageText = "" res, err := ToMarkdown(app) @@ -342,7 +347,7 @@ func TestToMarkdown(t *testing.T) { } func TestToMan(t *testing.T) { - app := buildExtendedTestCommand() + app := buildExtendedTestCommand(t) res, err := ToMan(app) @@ -351,7 +356,7 @@ func TestToMan(t *testing.T) { } func TestToManParseError(t *testing.T) { - app := buildExtendedTestCommand() + app := buildExtendedTestCommand(t) tmp := MarkdownDocTemplate t.Cleanup(func() { MarkdownDocTemplate = tmp }) @@ -363,7 +368,7 @@ func TestToManParseError(t *testing.T) { } func TestToManWithSection(t *testing.T) { - cmd := buildExtendedTestCommand() + cmd := buildExtendedTestCommand(t) res, err := ToManWithSection(cmd, 8) diff --git a/testdata/expected-doc-full.man b/testdata/expected-doc-full.man index a2131f0f..13abfa2f 100644 --- a/testdata/expected-doc-full.man +++ b/testdata/expected-doc-full.man @@ -17,6 +17,7 @@ greet [--another-flag|-b] [--flag|--fl|-f]=[value] [--socket|-s]=[value] +[--temp-dir]=[value] .fi .RE @@ -49,6 +50,9 @@ app [first_arg] [second_arg] .PP \fB--socket, -s\fP="": some 'usage' text (default: value) +.PP +\fB--temp-dir\fP="": (default: test temp dir) + .SH COMMANDS .SH config, c diff --git a/testdata/expected-doc-full.md b/testdata/expected-doc-full.md index 80ff6a7a..722c96e1 100644 --- a/testdata/expected-doc-full.md +++ b/testdata/expected-doc-full.md @@ -10,6 +10,7 @@ greet [--another-flag|-b] [--flag|--fl|-f]=[value] [--socket|-s]=[value] +[--temp-dir]=[value] ``` # DESCRIPTION @@ -30,6 +31,8 @@ app [first_arg] [second_arg] **--socket, -s**="": some 'usage' text (default: value) +**--temp-dir**="": (default: test temp dir) + # COMMANDS diff --git a/testdata/expected-doc-no-authors.md b/testdata/expected-doc-no-authors.md index 80ff6a7a..722c96e1 100644 --- a/testdata/expected-doc-no-authors.md +++ b/testdata/expected-doc-no-authors.md @@ -10,6 +10,7 @@ greet [--another-flag|-b] [--flag|--fl|-f]=[value] [--socket|-s]=[value] +[--temp-dir]=[value] ``` # DESCRIPTION @@ -30,6 +31,8 @@ app [first_arg] [second_arg] **--socket, -s**="": some 'usage' text (default: value) +**--temp-dir**="": (default: test temp dir) + # COMMANDS diff --git a/testdata/expected-doc-no-commands.md b/testdata/expected-doc-no-commands.md index 4db2a375..f4381dbd 100644 --- a/testdata/expected-doc-no-commands.md +++ b/testdata/expected-doc-no-commands.md @@ -10,6 +10,7 @@ greet [--another-flag|-b] [--flag|--fl|-f]=[value] [--socket|-s]=[value] +[--temp-dir]=[value] ``` # DESCRIPTION @@ -30,3 +31,5 @@ app [first_arg] [second_arg] **--socket, -s**="": some 'usage' text (default: value) +**--temp-dir**="": (default: test temp dir) + diff --git a/testdata/expected-doc-no-usagetext.md b/testdata/expected-doc-no-usagetext.md index fab4ba11..0d603b1a 100644 --- a/testdata/expected-doc-no-usagetext.md +++ b/testdata/expected-doc-no-usagetext.md @@ -10,6 +10,7 @@ greet [--another-flag|-b] [--flag|--fl|-f]=[value] [--socket|-s]=[value] +[--temp-dir]=[value] ``` # DESCRIPTION @@ -30,6 +31,8 @@ greet [GLOBAL OPTIONS] [command [COMMAND OPTIONS]] [ARGUMENTS...] **--socket, -s**="": some 'usage' text (default: value) +**--temp-dir**="": (default: test temp dir) + # COMMANDS diff --git a/testdata/expected-tabular-markdown-custom-app-path.md b/testdata/expected-tabular-markdown-custom-app-path.md index 70242cf6..69cd059f 100644 --- a/testdata/expected-tabular-markdown-custom-app-path.md +++ b/testdata/expected-tabular-markdown-custom-app-path.md @@ -14,11 +14,12 @@ $ /usr/local/bin [GLOBAL FLAGS] [COMMAND] [COMMAND FLAGS] [ARGUMENTS...] Global flags: -| Name | Description | Type | Default value | Environment variables | -|-----------------------------|--------------------|--------|:-------------:|:-----------------------:| -| `--socket="…"` (`-s`) | some 'usage' text | string | `value` | *none* | -| `--flag="…"` (`--fl`, `-f`) | | string | | *none* | -| `--another-flag` (`-b`) | another usage text | bool | `false` | `EXAMPLE_VARIABLE_NAME` | +| Name | Description | Type | Default value | Environment variables | +|-----------------------------|--------------------|--------|:---------------:|:-----------------------:| +| `--socket="…"` (`-s`) | some 'usage' text | string | `value` | *none* | +| `--flag="…"` (`--fl`, `-f`) | | string | | *none* | +| `--another-flag` (`-b`) | another usage text | bool | `false` | `EXAMPLE_VARIABLE_NAME` | +| `--temp-dir="…"` | | string | `test temp dir` | *none* | ### `config` command (aliases: `c`) diff --git a/testdata/expected-tabular-markdown-full.md b/testdata/expected-tabular-markdown-full.md index 5b210ec2..d0bb8daf 100644 --- a/testdata/expected-tabular-markdown-full.md +++ b/testdata/expected-tabular-markdown-full.md @@ -14,11 +14,12 @@ $ app [GLOBAL FLAGS] [COMMAND] [COMMAND FLAGS] [ARGUMENTS...] Global flags: -| Name | Description | Type | Default value | Environment variables | -|-----------------------------|--------------------|--------|:-------------:|:-----------------------:| -| `--socket="…"` (`-s`) | some 'usage' text | string | `value` | *none* | -| `--flag="…"` (`--fl`, `-f`) | | string | | *none* | -| `--another-flag` (`-b`) | another usage text | bool | `false` | `EXAMPLE_VARIABLE_NAME` | +| Name | Description | Type | Default value | Environment variables | +|-----------------------------|--------------------|--------|:---------------:|:-----------------------:| +| `--socket="…"` (`-s`) | some 'usage' text | string | `value` | *none* | +| `--flag="…"` (`--fl`, `-f`) | | string | | *none* | +| `--another-flag` (`-b`) | another usage text | bool | `false` | `EXAMPLE_VARIABLE_NAME` | +| `--temp-dir="…"` | | string | `test temp dir` | *none* | ### `config` command (aliases: `c`) From 76d7576f3dc052978250600ee775dc756d9dc075 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Sun, 19 Oct 2025 14:31:42 +0200 Subject: [PATCH 02/12] WIP first try --- docs.go | 46 ++++++++++++++----- markdown_tabular.md.gotmpl | 2 +- ...pected-tabular-markdown-custom-app-path.md | 12 ++--- testdata/expected-tabular-markdown-full.md | 12 ++--- 4 files changed, 48 insertions(+), 24 deletions(-) diff --git a/docs.go b/docs.go index 2b560a39..121453a9 100644 --- a/docs.go +++ b/docs.go @@ -278,8 +278,10 @@ func prepareFlags( // flagDetails returns a string containing the flags metadata func flagDetails(flag cli.DocGenerationFlag) string { description := flag.GetUsage() - value := getFlagDefaultValue(flag) - if value != "" { + value, defaultText := getFlagDefaultValue(flag) + if defaultText != "" { + description += " (default: " + value + ")" + } else if value != "" { description += " (default: " + value + ")" } return ": " + description @@ -402,11 +404,19 @@ func (tt tabularTemplate) PrepareFlags(flags []cli.Flag) []cliTabularFlagTemplat continue } - var f = cliTabularFlagTemplate{ + value, defaultText := getFlagDefaultValue(flag) + defaultValue := "" + if defaultText != "" { + defaultValue = defaultText + } else if value != "" { + defaultValue = fmt.Sprintf("`%s`", value) + } + + f := cliTabularFlagTemplate{ Usage: tt.PrepareMultilineString(flag.GetUsage()), EnvVars: flag.GetEnvVars(), TakesValue: flag.TakesValue(), - Default: getFlagDefaultValue(flag), + Default: defaultValue, Type: flag.TypeName(), } @@ -562,29 +572,43 @@ func (tabularTemplate) Prettify(s string) string { // a GetValue string method, but it was removed in https://github.com/urfave/cli/pull/1988. // This function serves as a workaround, attempting to retrieve the value using the removed method; if that fails, it // tries to obtain it via reflection (the [cli.FlagBase] still has a Value field). -func getFlagDefaultValue(f cli.DocGenerationFlag) string { +// It also checks if there is a DefaultText and if it differs from value and is set we will return that one. +func getFlagDefaultValue(f cli.DocGenerationFlag) (value, text string) { if !f.TakesValue() { - return "" + return "", "" + } + + var defaultText string + if v, ok := f.(interface{ GetDefaultText() string }); ok { + defaultText = v.GetDefaultText() } if v, ok := f.(interface{ GetValue() string }); ok { - return v.GetValue() + value = v.GetValue() + + // GetDefaultText also returns GetValue if default text not set + // but quotes it + if strings.Trim(defaultText, "\"") == value { + return value, "" + } else if defaultText != "" { + return "", defaultText + } } var ref = reflect.ValueOf(f) if ref.Kind() != reflect.Ptr { - return "" + return "", "" } else { ref = ref.Elem() } if ref.Kind() != reflect.Struct { - return "" + return "", "" } if val := ref.FieldByName("Value"); val.IsValid() && val.Type().Kind() != reflect.Bool { - return fmt.Sprintf("%v", val.Interface()) + return fmt.Sprintf("%v", val.Interface()), "" } - return "" + return "", "" } diff --git a/markdown_tabular.md.gotmpl b/markdown_tabular.md.gotmpl index f11bf924..e3bcb79f 100644 --- a/markdown_tabular.md.gotmpl +++ b/markdown_tabular.md.gotmpl @@ -5,7 +5,7 @@ {{- /**/ -}} | `{{ $flag.Name }}{{ if $flag.TakesValue }}="…"{{ end }}` {{ if $flag.Aliases }}(`{{ join $flag.Aliases "`, `" }}`) {{ end }} {{- /**/ -}} | {{ $flag.Usage }} {{- /**/ -}} | {{ $flag.Type }} -{{- /**/ -}} | {{ if $flag.Default }}`{{ $flag.Default }}`{{ end }} +{{- /**/ -}} | {{ if $flag.Default }}{{ $flag.Default }}{{ end }} {{- /**/ -}} | {{ if $flag.EnvVars }}`{{ join $flag.EnvVars "`, `" }}`{{ else }}*none*{{ end }} {{- /**/ -}} | {{ end }} diff --git a/testdata/expected-tabular-markdown-custom-app-path.md b/testdata/expected-tabular-markdown-custom-app-path.md index 69cd059f..4664e0d2 100644 --- a/testdata/expected-tabular-markdown-custom-app-path.md +++ b/testdata/expected-tabular-markdown-custom-app-path.md @@ -14,12 +14,12 @@ $ /usr/local/bin [GLOBAL FLAGS] [COMMAND] [COMMAND FLAGS] [ARGUMENTS...] Global flags: -| Name | Description | Type | Default value | Environment variables | -|-----------------------------|--------------------|--------|:---------------:|:-----------------------:| -| `--socket="…"` (`-s`) | some 'usage' text | string | `value` | *none* | -| `--flag="…"` (`--fl`, `-f`) | | string | | *none* | -| `--another-flag` (`-b`) | another usage text | bool | `false` | `EXAMPLE_VARIABLE_NAME` | -| `--temp-dir="…"` | | string | `test temp dir` | *none* | +| Name | Description | Type | Default value | Environment variables | +|-----------------------------|--------------------|--------|:-------------:|:-----------------------:| +| `--socket="…"` (`-s`) | some 'usage' text | string | `value` | *none* | +| `--flag="…"` (`--fl`, `-f`) | | string | | *none* | +| `--another-flag` (`-b`) | another usage text | bool | `false` | `EXAMPLE_VARIABLE_NAME` | +| `--temp-dir="…"` | | string | test temp dir | *none* | ### `config` command (aliases: `c`) diff --git a/testdata/expected-tabular-markdown-full.md b/testdata/expected-tabular-markdown-full.md index d0bb8daf..ebba4f5d 100644 --- a/testdata/expected-tabular-markdown-full.md +++ b/testdata/expected-tabular-markdown-full.md @@ -14,12 +14,12 @@ $ app [GLOBAL FLAGS] [COMMAND] [COMMAND FLAGS] [ARGUMENTS...] Global flags: -| Name | Description | Type | Default value | Environment variables | -|-----------------------------|--------------------|--------|:---------------:|:-----------------------:| -| `--socket="…"` (`-s`) | some 'usage' text | string | `value` | *none* | -| `--flag="…"` (`--fl`, `-f`) | | string | | *none* | -| `--another-flag` (`-b`) | another usage text | bool | `false` | `EXAMPLE_VARIABLE_NAME` | -| `--temp-dir="…"` | | string | `test temp dir` | *none* | +| Name | Description | Type | Default value | Environment variables | +|-----------------------------|--------------------|--------|:-------------:|:-----------------------:| +| `--socket="…"` (`-s`) | some 'usage' text | string | `value` | *none* | +| `--flag="…"` (`--fl`, `-f`) | | string | | *none* | +| `--another-flag` (`-b`) | another usage text | bool | `false` | `EXAMPLE_VARIABLE_NAME` | +| `--temp-dir="…"` | | string | test temp dir | *none* | ### `config` command (aliases: `c`) From b2ca258ccebafb863795aa0444937b86e5b0dd86 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 20 Oct 2025 11:28:31 +0200 Subject: [PATCH 03/12] fix test helper func expectFileContent --- docs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs_test.go b/docs_test.go index 358bbfe9..e3086f75 100644 --- a/docs_test.go +++ b/docs_test.go @@ -24,8 +24,8 @@ func expectFileContent(t *testing.T, file, got string) { r := require.New(t) r.NoError(err) r.Equal( - string(normalizeNewlines([]byte(got))), string(normalizeNewlines(data)), + string(normalizeNewlines([]byte(got))), ) } From e3304876feec643a363db32acc07ad5df441a3cc Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 20 Oct 2025 11:29:01 +0200 Subject: [PATCH 04/12] Use reflections for GetDefaultText --- docs.go | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/docs.go b/docs.go index 121453a9..8746c9fb 100644 --- a/docs.go +++ b/docs.go @@ -578,21 +578,19 @@ func getFlagDefaultValue(f cli.DocGenerationFlag) (value, text string) { return "", "" } - var defaultText string - if v, ok := f.(interface{ GetDefaultText() string }); ok { - defaultText = v.GetDefaultText() + if _, ok := f.(interface{ GetDefaultText() string }); ok { + // GetDefaultText also returns GetValue so we have to use reflection + if ref := reflect.ValueOf(f); ref.Kind() == reflect.Ptr && ref.Elem().Kind() == reflect.Struct { + if val := ref.Elem().FieldByName("DefaultText"); val.IsValid() && val.Type().Kind() == reflect.String { + if defaultText := val.Interface().(string); defaultText != "" { + return "", defaultText + } + } + } } if v, ok := f.(interface{ GetValue() string }); ok { - value = v.GetValue() - - // GetDefaultText also returns GetValue if default text not set - // but quotes it - if strings.Trim(defaultText, "\"") == value { - return value, "" - } else if defaultText != "" { - return "", defaultText - } + return v.GetValue(), "" } var ref = reflect.ValueOf(f) From ba08ebe7bfcecca4c5cab499078478a4b768cabf Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 20 Oct 2025 11:36:33 +0200 Subject: [PATCH 05/12] BoolFlag should respect DefaultText too --- docs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs.go b/docs.go index 8746c9fb..08805294 100644 --- a/docs.go +++ b/docs.go @@ -420,8 +420,8 @@ func (tt tabularTemplate) PrepareFlags(flags []cli.Flag) []cliTabularFlagTemplat Type: flag.TypeName(), } - if boolFlag, isBool := appFlag.(*cli.BoolFlag); isBool { - f.Default = strconv.FormatBool(boolFlag.Value) + if boolFlag, isBool := appFlag.(*cli.BoolFlag); isBool && defaultText == "" { + f.Default = fmt.Sprintf("`%s`", strconv.FormatBool(boolFlag.Value)) } for i, name := range appFlag.Names() { From 71fedf3befc7c1d7f5274dbabfe432ac86d83daa Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 20 Oct 2025 11:38:43 +0200 Subject: [PATCH 06/12] Never mid thats a simple copyPasta issue --- docs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs.go b/docs.go index 08805294..a361325f 100644 --- a/docs.go +++ b/docs.go @@ -280,7 +280,7 @@ func flagDetails(flag cli.DocGenerationFlag) string { description := flag.GetUsage() value, defaultText := getFlagDefaultValue(flag) if defaultText != "" { - description += " (default: " + value + ")" + description += " (default: " + defaultText + ")" } else if value != "" { description += " (default: " + value + ")" } From 8cff8e6fa9c5592006b573e93b8ebd76561a1672 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 20 Oct 2025 11:47:59 +0200 Subject: [PATCH 07/12] BoolFlag code should be next to other --- docs.go | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/docs.go b/docs.go index a361325f..afdbc8a4 100644 --- a/docs.go +++ b/docs.go @@ -411,6 +411,9 @@ func (tt tabularTemplate) PrepareFlags(flags []cli.Flag) []cliTabularFlagTemplat } else if value != "" { defaultValue = fmt.Sprintf("`%s`", value) } + if boolFlag, isBool := appFlag.(*cli.BoolFlag); isBool && defaultText == "" { + defaultValue = fmt.Sprintf("`%s`", strconv.FormatBool(boolFlag.Value)) + } f := cliTabularFlagTemplate{ Usage: tt.PrepareMultilineString(flag.GetUsage()), @@ -420,10 +423,6 @@ func (tt tabularTemplate) PrepareFlags(flags []cli.Flag) []cliTabularFlagTemplat Type: flag.TypeName(), } - if boolFlag, isBool := appFlag.(*cli.BoolFlag); isBool && defaultText == "" { - f.Default = fmt.Sprintf("`%s`", strconv.FormatBool(boolFlag.Value)) - } - for i, name := range appFlag.Names() { name = strings.TrimSpace(name) From 13ba9aa76be65c221b06cdb7e2d65606ed8eeea4 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 20 Oct 2025 11:50:59 +0200 Subject: [PATCH 08/12] Update docs.go --- docs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs.go b/docs.go index afdbc8a4..8e1106aa 100644 --- a/docs.go +++ b/docs.go @@ -571,7 +571,7 @@ func (tabularTemplate) Prettify(s string) string { // a GetValue string method, but it was removed in https://github.com/urfave/cli/pull/1988. // This function serves as a workaround, attempting to retrieve the value using the removed method; if that fails, it // tries to obtain it via reflection (the [cli.FlagBase] still has a Value field). -// It also checks if there is a DefaultText and if it differs from value and is set we will return that one. +// It also checks if there is a DefaultText and if set returns it. func getFlagDefaultValue(f cli.DocGenerationFlag) (value, text string) { if !f.TakesValue() { return "", "" From 434bec673296c96f204e532a5a307ec116793087 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 20 Oct 2025 12:33:32 +0200 Subject: [PATCH 09/12] gofumpt --- docs.go | 22 +++++++++++----------- docs_test.go | 6 ++---- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/docs.go b/docs.go index 8e1106aa..4a9d7630 100644 --- a/docs.go +++ b/docs.go @@ -98,7 +98,7 @@ func ToTabularMarkdown(cmd *cli.Command, appPath string) (string, error) { // ToTabularToFileBetweenTags creates a tabular markdown documentation for the `*App` and updates the file between // the tags in the file. The function errors if either parsing or writing of the string fails. func ToTabularToFileBetweenTags(cmd *cli.Command, appPath, filePath string, startEndTags ...string) error { - var start, end = "", "" // default tags + start, end := "", "" // default tags if len(startEndTags) == 2 { start, end = startEndTags[0], startEndTags[1] @@ -128,7 +128,7 @@ func ToTabularToFileBetweenTags(cmd *cli.Command, appPath, filePath string, star updated := re.ReplaceAll(content, []byte(strings.Join([]string{start, comment, md, end}, "\n"))) // write updated content to file - if err = os.WriteFile(filePath, updated, 0664); err != nil { + if err = os.WriteFile(filePath, updated, 0o664); err != nil { return err } @@ -366,10 +366,10 @@ type tabularTemplate struct{} // PrepareCommands converts CLI commands into a structs for the rendering. func (tt tabularTemplate) PrepareCommands(commands []*cli.Command, appPath, parentCommandName string, level uint) []cliTabularCommandTemplate { - var result = make([]cliTabularCommandTemplate, 0, len(commands)) + result := make([]cliTabularCommandTemplate, 0, len(commands)) for _, cmd := range commands { - var command = cliTabularCommandTemplate{ + command := cliTabularCommandTemplate{ AppPath: appPath, Name: strings.TrimSpace(strings.Join([]string{parentCommandName, cmd.Name}, " ")), Aliases: cmd.Aliases, @@ -396,7 +396,7 @@ func (tt tabularTemplate) PrepareCommands(commands []*cli.Command, appPath, pare // PrepareFlags converts CLI flags into a structs for the rendering. func (tt tabularTemplate) PrepareFlags(flags []cli.Flag) []cliTabularFlagTemplate { - var result = make([]cliTabularFlagTemplate, 0, len(flags)) + result := make([]cliTabularFlagTemplate, 0, len(flags)) for _, appFlag := range flags { flag, ok := appFlag.(cli.DocGenerationFlag) @@ -458,7 +458,7 @@ func (tabularTemplate) PrepareMultilineString(s string) string { } func (tabularTemplate) Prettify(s string) string { - var max = func(x, y int) int { + max := func(x, y int) int { if x > y { return x } @@ -469,14 +469,14 @@ func (tabularTemplate) Prettify(s string) string { // search for tables for _, rawTable := range regexp.MustCompile(`(?m)^(\|[^\n]+\|\r?\n)((?:\|:?-+:?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$`).FindAllString(s, -1) { - var lines = strings.FieldsFunc(rawTable, func(r rune) bool { return r == '\n' }) + lines := strings.FieldsFunc(rawTable, func(r rune) bool { return r == '\n' }) if len(lines) < 3 { // header, separator, body continue } // parse table into the matrix - var matrix = make([][]string, 0, len(lines)) + matrix := make([][]string, 0, len(lines)) for _, line := range lines { items := strings.FieldsFunc(strings.Trim(line, "| "), func(r rune) bool { return r == '|' }) @@ -488,13 +488,13 @@ func (tabularTemplate) Prettify(s string) string { } // determine centered columns - var centered = make([]bool, 0, len(matrix[1])) + centered := make([]bool, 0, len(matrix[1])) for _, cell := range matrix[1] { centered = append(centered, strings.HasPrefix(cell, ":") && strings.HasSuffix(cell, ":")) } // calculate max lengths - var lengths = make([]int, len(matrix[0])) + lengths := make([]int, len(matrix[0])) for n, row := range matrix { for i, cell := range row { if n == 1 { @@ -592,7 +592,7 @@ func getFlagDefaultValue(f cli.DocGenerationFlag) (value, text string) { return v.GetValue(), "" } - var ref = reflect.ValueOf(f) + ref := reflect.ValueOf(f) if ref.Kind() != reflect.Ptr { return "", "" } else { diff --git a/docs_test.go b/docs_test.go index e3086f75..e489751d 100644 --- a/docs_test.go +++ b/docs_test.go @@ -13,10 +13,8 @@ import ( "github.com/urfave/cli/v3" ) -var ( - //go:embed testdata - testdata embed.FS -) +//go:embed testdata +var testdata embed.FS func expectFileContent(t *testing.T, file, got string) { data, err := testdata.ReadFile(file) From c12711c33d8c4f5d8e034b7bdce76ce173e899ff Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 20 Oct 2025 12:41:57 +0200 Subject: [PATCH 10/12] unify and simplify --- docs.go | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/docs.go b/docs.go index 4a9d7630..d85f03d6 100644 --- a/docs.go +++ b/docs.go @@ -411,9 +411,6 @@ func (tt tabularTemplate) PrepareFlags(flags []cli.Flag) []cliTabularFlagTemplat } else if value != "" { defaultValue = fmt.Sprintf("`%s`", value) } - if boolFlag, isBool := appFlag.(*cli.BoolFlag); isBool && defaultText == "" { - defaultValue = fmt.Sprintf("`%s`", strconv.FormatBool(boolFlag.Value)) - } f := cliTabularFlagTemplate{ Usage: tt.PrepareMultilineString(flag.GetUsage()), @@ -577,35 +574,18 @@ func getFlagDefaultValue(f cli.DocGenerationFlag) (value, text string) { return "", "" } - if _, ok := f.(interface{ GetDefaultText() string }); ok { - // GetDefaultText also returns GetValue so we have to use reflection - if ref := reflect.ValueOf(f); ref.Kind() == reflect.Ptr && ref.Elem().Kind() == reflect.Struct { - if val := ref.Elem().FieldByName("DefaultText"); val.IsValid() && val.Type().Kind() == reflect.String { - if defaultText := val.Interface().(string); defaultText != "" { - return "", defaultText - } + // GetDefaultText also returns GetValue so we have to use reflection + if ref := reflect.ValueOf(f); ref.Kind() == reflect.Ptr && ref.Elem().Kind() == reflect.Struct { + if val := ref.Elem().FieldByName("DefaultText"); val.IsValid() && val.Type().Kind() == reflect.String { + if defaultText := val.Interface().(string); defaultText != "" { + return "", defaultText } } } - if v, ok := f.(interface{ GetValue() string }); ok { - return v.GetValue(), "" - } - - ref := reflect.ValueOf(f) - if ref.Kind() != reflect.Ptr { - return "", "" - } else { - ref = ref.Elem() - } - - if ref.Kind() != reflect.Struct { - return "", "" - } - - if val := ref.FieldByName("Value"); val.IsValid() && val.Type().Kind() != reflect.Bool { - return fmt.Sprintf("%v", val.Interface()), "" + if boolFlag, isBool := f.(*cli.BoolFlag); isBool { + return strconv.FormatBool(boolFlag.Value), "" } - return "", "" + return f.GetValue(), "" } From 565f23bb948604515ed08cdfa9ab60b559c21f7b Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 20 Oct 2025 12:54:01 +0200 Subject: [PATCH 11/12] get default value of bool flags always too --- docs.go | 11 +++++------ testdata/expected-doc-full.man | 10 +++++----- testdata/expected-doc-full.md | 10 +++++----- testdata/expected-doc-no-authors.md | 10 +++++----- testdata/expected-doc-no-commands.md | 2 +- testdata/expected-doc-no-flags.md | 8 ++++---- testdata/expected-doc-no-usagetext.md | 10 +++++----- 7 files changed, 30 insertions(+), 31 deletions(-) diff --git a/docs.go b/docs.go index d85f03d6..df34294b 100644 --- a/docs.go +++ b/docs.go @@ -570,10 +570,6 @@ func (tabularTemplate) Prettify(s string) string { // tries to obtain it via reflection (the [cli.FlagBase] still has a Value field). // It also checks if there is a DefaultText and if set returns it. func getFlagDefaultValue(f cli.DocGenerationFlag) (value, text string) { - if !f.TakesValue() { - return "", "" - } - // GetDefaultText also returns GetValue so we have to use reflection if ref := reflect.ValueOf(f); ref.Kind() == reflect.Ptr && ref.Elem().Kind() == reflect.Struct { if val := ref.Elem().FieldByName("DefaultText"); val.IsValid() && val.Type().Kind() == reflect.String { @@ -583,8 +579,11 @@ func getFlagDefaultValue(f cli.DocGenerationFlag) (value, text string) { } } - if boolFlag, isBool := f.(*cli.BoolFlag); isBool { - return strconv.FormatBool(boolFlag.Value), "" + if !f.TakesValue() { + if boolFlag, isBool := f.(*cli.BoolFlag); isBool { + return strconv.FormatBool(boolFlag.Value), "" + } + return "", "" } return f.GetValue(), "" diff --git a/testdata/expected-doc-full.man b/testdata/expected-doc-full.man index 13abfa2f..573893c8 100644 --- a/testdata/expected-doc-full.man +++ b/testdata/expected-doc-full.man @@ -42,7 +42,7 @@ app [first_arg] [second_arg] .SH GLOBAL OPTIONS .PP -\fB--another-flag, -b\fP: another usage text +\fB--another-flag, -b\fP: another usage text (default: false) .PP \fB--flag, --fl, -f\fP="": @@ -60,7 +60,7 @@ app [first_arg] [second_arg] another usage test .PP -\fB--another-flag, -b\fP: another usage text +\fB--another-flag, -b\fP: another usage text (default: false) .PP \fB--flag, --fl, -f\fP="": @@ -70,7 +70,7 @@ another usage test another usage test .PP -\fB--sub-command-flag, -s\fP: some usage text +\fB--sub-command-flag, -s\fP: some usage text (default: false) .PP \fB--sub-flag, --sub-fl, -s\fP="": @@ -103,7 +103,7 @@ Should be a part of the same code block .RE .PP -\fB--another-flag, -b\fP: another usage text +\fB--another-flag, -b\fP: another usage text (default: false) .PP \fB--flag, --fl, -f\fP="": @@ -121,4 +121,4 @@ Single line of UsageText .RE .PP -\fB--sub-command-flag, -s\fP: some usage text +\fB--sub-command-flag, -s\fP: some usage text (default: false) diff --git a/testdata/expected-doc-full.md b/testdata/expected-doc-full.md index 722c96e1..d74511b0 100644 --- a/testdata/expected-doc-full.md +++ b/testdata/expected-doc-full.md @@ -25,7 +25,7 @@ app [first_arg] [second_arg] # GLOBAL OPTIONS -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -40,7 +40,7 @@ app [first_arg] [second_arg] another usage test -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -48,7 +48,7 @@ another usage test another usage test -**--sub-command-flag, -s**: some usage text +**--sub-command-flag, -s**: some usage text (default: false) **--sub-flag, --sub-fl, -s**="": @@ -74,7 +74,7 @@ standard usage text Should be a part of the same code block -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -84,4 +84,4 @@ standard usage text >Single line of UsageText -**--sub-command-flag, -s**: some usage text +**--sub-command-flag, -s**: some usage text (default: false) diff --git a/testdata/expected-doc-no-authors.md b/testdata/expected-doc-no-authors.md index 722c96e1..d74511b0 100644 --- a/testdata/expected-doc-no-authors.md +++ b/testdata/expected-doc-no-authors.md @@ -25,7 +25,7 @@ app [first_arg] [second_arg] # GLOBAL OPTIONS -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -40,7 +40,7 @@ app [first_arg] [second_arg] another usage test -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -48,7 +48,7 @@ another usage test another usage test -**--sub-command-flag, -s**: some usage text +**--sub-command-flag, -s**: some usage text (default: false) **--sub-flag, --sub-fl, -s**="": @@ -74,7 +74,7 @@ standard usage text Should be a part of the same code block -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -84,4 +84,4 @@ standard usage text >Single line of UsageText -**--sub-command-flag, -s**: some usage text +**--sub-command-flag, -s**: some usage text (default: false) diff --git a/testdata/expected-doc-no-commands.md b/testdata/expected-doc-no-commands.md index f4381dbd..a22b02f0 100644 --- a/testdata/expected-doc-no-commands.md +++ b/testdata/expected-doc-no-commands.md @@ -25,7 +25,7 @@ app [first_arg] [second_arg] # GLOBAL OPTIONS -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": diff --git a/testdata/expected-doc-no-flags.md b/testdata/expected-doc-no-flags.md index 33d1275a..e8b64aab 100644 --- a/testdata/expected-doc-no-flags.md +++ b/testdata/expected-doc-no-flags.md @@ -22,7 +22,7 @@ app [first_arg] [second_arg] another usage test -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -30,7 +30,7 @@ another usage test another usage test -**--sub-command-flag, -s**: some usage text +**--sub-command-flag, -s**: some usage text (default: false) **--sub-flag, --sub-fl, -s**="": @@ -56,7 +56,7 @@ standard usage text Should be a part of the same code block -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -66,4 +66,4 @@ standard usage text >Single line of UsageText -**--sub-command-flag, -s**: some usage text +**--sub-command-flag, -s**: some usage text (default: false) diff --git a/testdata/expected-doc-no-usagetext.md b/testdata/expected-doc-no-usagetext.md index 0d603b1a..fb0c6799 100644 --- a/testdata/expected-doc-no-usagetext.md +++ b/testdata/expected-doc-no-usagetext.md @@ -25,7 +25,7 @@ greet [GLOBAL OPTIONS] [command [COMMAND OPTIONS]] [ARGUMENTS...] # GLOBAL OPTIONS -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -40,7 +40,7 @@ greet [GLOBAL OPTIONS] [command [COMMAND OPTIONS]] [ARGUMENTS...] another usage test -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -48,7 +48,7 @@ another usage test another usage test -**--sub-command-flag, -s**: some usage text +**--sub-command-flag, -s**: some usage text (default: false) **--sub-flag, --sub-fl, -s**="": @@ -74,7 +74,7 @@ standard usage text Should be a part of the same code block -**--another-flag, -b**: another usage text +**--another-flag, -b**: another usage text (default: false) **--flag, --fl, -f**="": @@ -84,4 +84,4 @@ standard usage text >Single line of UsageText -**--sub-command-flag, -s**: some usage text +**--sub-command-flag, -s**: some usage text (default: false) From f25ae49a4ddefb4473da7160f619934347f9d690 Mon Sep 17 00:00:00 2001 From: 6543 <6543@obermui.de> Date: Mon, 20 Oct 2025 13:48:33 +0200 Subject: [PATCH 12/12] update func desc --- docs.go | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs.go b/docs.go index df34294b..0c352b01 100644 --- a/docs.go +++ b/docs.go @@ -564,11 +564,8 @@ func (tabularTemplate) Prettify(s string) string { return s + "\n" // add an extra newline } -// getFlagDefaultValue returns the default value of a flag. Previously, the [cli.DocGenerationFlag] interface included -// a GetValue string method, but it was removed in https://github.com/urfave/cli/pull/1988. -// This function serves as a workaround, attempting to retrieve the value using the removed method; if that fails, it -// tries to obtain it via reflection (the [cli.FlagBase] still has a Value field). -// It also checks if there is a DefaultText and if set returns it. +// getFlagDefaultValue returns the default text or default value of a flag. +// cli.BoolFlag will always return an default. func getFlagDefaultValue(f cli.DocGenerationFlag) (value, text string) { // GetDefaultText also returns GetValue so we have to use reflection if ref := reflect.ValueOf(f); ref.Kind() == reflect.Ptr && ref.Elem().Kind() == reflect.Struct {