From b8574837e116031c572879115e155f7964aa63ec Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 14:59:31 +0400 Subject: [PATCH 01/28] =?UTF-8?q?=F0=9F=9A=80=20feat(docs.go):=20add=20sup?= =?UTF-8?q?port=20for=20generating=20tabular=20markdown=20documentation=20?= =?UTF-8?q?=E2=9C=85=20test(docs=5Ftest.go):=20add=20tests=20for=20generat?= =?UTF-8?q?ing=20tabular=20markdown=20documentation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ feat(template.go): add MarkdownTabularDocTemplate to generate a markdown table for command flags and environment variables --- docs.go | 258 +++++++++++++++++++++ docs_test.go | 12 + template.go | 68 ++++++ testdata/expected-tabular-markdown-full.md | 110 +++++++++ 4 files changed, 448 insertions(+) create mode 100644 testdata/expected-tabular-markdown-full.md diff --git a/docs.go b/docs.go index 45a0bc314d..dd338150cd 100644 --- a/docs.go +++ b/docs.go @@ -7,13 +7,70 @@ import ( "bytes" "fmt" "io" + "regexp" "sort" + "strconv" "strings" "text/template" + "unicode/utf8" "github.com/cpuguy83/go-md2man/v2/md2man" ) +type ( + tabularOptions struct { + appPath string + } + + TabularOption func(*tabularOptions) +) + +// WithTabularAppPath allows to override the default app path. +func WithTabularAppPath(path string) TabularOption { + return func(o *tabularOptions) { o.appPath = path } +} + +// ToTabularMarkdown creates a tabular markdown documentation for the `*App`. +// The function errors if either parsing or writing of the string fails. +func (a *App) ToTabularMarkdown(opts ...TabularOption) (string, error) { + var o = tabularOptions{ + appPath: "app", + } + + for _, opt := range opts { + opt(&o) + } + + const name = "cli" + + t, err := template.New(name).Funcs(template.FuncMap{ + "join": strings.Join, + }).Parse(MarkdownTabularDocTemplate) + if err != nil { + return "", err + } + + var ( + w bytes.Buffer + tt tabularTemplate + ) + + if err = t.ExecuteTemplate(&w, name, cliTabularAppTemplate{ + AppPath: o.appPath, + Name: a.Name, + Description: tt.PrepareMultilineString(a.Description), + Usage: tt.PrepareMultilineString(a.Usage), + UsageText: tt.PrepareMultilineString(a.UsageText), + ArgsUsage: tt.PrepareMultilineString(a.ArgsUsage), + GlobalFlags: tt.PrepareFlags(a.VisibleFlags()), + Commands: tt.PrepareCommands(a.VisibleCommands(), o.appPath, "", 0), + }); err != nil { + return "", err + } + + return tt.Prettify(w.String()), nil +} + // ToMarkdown creates a markdown string for the `*App` // The function errors if either parsing or writing of the string fails. func (a *App) ToMarkdown() (string, error) { @@ -196,3 +253,204 @@ func prepareUsage(command *Command, usageText string) string { return usage } + +type ( + cliTabularAppTemplate struct { + AppPath string + Name string + Usage, UsageText, ArgsUsage string + Description string + GlobalFlags []cliTabularFlagTemplate + Commands []cliTabularCommandTemplate + } + + cliTabularCommandTemplate struct { + AppPath string + Name string + Aliases []string + Usage, UsageText, ArgsUsage string + Description string + Category string + Flags []cliTabularFlagTemplate + SubCommands []cliTabularCommandTemplate + Level uint + } + + cliTabularFlagTemplate struct { + Name string + Aliases []string + Usage string + TakesValue bool + Default string + EnvVars []string + } +) + +// tabularTemplate is a struct for the tabular template preparation. +type tabularTemplate struct{} + +// PrepareCommands converts CLI commands into a structs for the rendering. +func (tt tabularTemplate) PrepareCommands(commands []*Command, appPath, parentCommandName string, level uint) []cliTabularCommandTemplate { + var result = make([]cliTabularCommandTemplate, 0, len(commands)) + + for _, cmd := range commands { + var command = cliTabularCommandTemplate{ + AppPath: appPath, + Name: strings.TrimSpace(strings.Join([]string{parentCommandName, cmd.Name}, " ")), + Aliases: cmd.Aliases, + Usage: tt.PrepareMultilineString(cmd.Usage), + UsageText: tt.PrepareMultilineString(cmd.UsageText), + ArgsUsage: tt.PrepareMultilineString(cmd.ArgsUsage), + Description: tt.PrepareMultilineString(cmd.Description), + Category: cmd.Category, + Flags: tt.PrepareFlags(cmd.VisibleFlags()), + SubCommands: tt.PrepareCommands( // note: recursive call + cmd.Commands, + appPath, + strings.Join([]string{parentCommandName, cmd.Name}, " "), + level+1, + ), + Level: level, + } + + result = append(result, command) + } + + return result +} + +// PrepareFlags converts CLI flags into a structs for the rendering. +func (tt tabularTemplate) PrepareFlags(flags []Flag) []cliTabularFlagTemplate { + var result = make([]cliTabularFlagTemplate, 0, len(flags)) + + for _, appFlag := range flags { + flag, ok := appFlag.(DocGenerationFlag) + if !ok { + continue + } + + var f = cliTabularFlagTemplate{ + Usage: tt.PrepareMultilineString(flag.GetUsage()), + EnvVars: flag.GetEnvVars(), + TakesValue: flag.TakesValue(), + Default: flag.GetValue(), + } + + if boolFlag, isBool := appFlag.(*BoolFlag); isBool { + f.Default = strconv.FormatBool(boolFlag.Value) + } + + for i, name := range flag.Names() { + name = strings.TrimSpace(name) + + if i == 0 { + f.Name = "--" + name + + continue + } + + if len(name) > 1 { + name = "--" + name + } else { + name = "-" + name + } + + f.Aliases = append(f.Aliases, name) + } + + result = append(result, f) + } + + return result +} + +// PrepareMultilineString prepares a string (removes line breaks). +func (tabularTemplate) PrepareMultilineString(s string) string { + return strings.TrimRight( + strings.TrimSpace( + strings.ReplaceAll(s, "\n", " "), + ), + ".\r\n\t", + ) +} + +func (tabularTemplate) Prettify(s string) string { + s = regexp.MustCompile(`\n{2,}`).ReplaceAllString(s, "\n\n") // normalize newlines + s = strings.Trim(s, " \n") // trim spaces and newlines + + // 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' }) + + if len(lines) < 3 { // header, separator, body + continue + } + + // parse table into the matrix + var matrix = make([][]string, 0, len(lines)) + for _, line := range lines { + items := strings.FieldsFunc(strings.Trim(line, "| "), func(r rune) bool { return r == '|' }) + + for i := range items { + items[i] = strings.TrimSpace(items[i]) // trim spaces in cells + } + + matrix = append(matrix, items) + } + + // determine centered columns + var 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])) + const padding = 2 // 2 spaces for padding + for _, row := range matrix { + for i, cell := range row { + if len(cell) > lengths[i]-padding { + lengths[i] = utf8.RuneCountInString(cell) + padding + } + } + } + + // format cells + for i, row := range matrix { + for j, cell := range row { + if i == 1 { // is separator + if centered[j] { + cell = ":" + strings.Repeat("-", lengths[j]-2) + ":" + } else { + cell = strings.Repeat("-", lengths[j]+1) + } + } + + var ( + padLeft, padRight = 1, 1 + cellWidth = utf8.RuneCountInString(cell) + ) + + if centered[j] { // is centered + padLeft = (lengths[j] - cellWidth) / 2 + padRight = lengths[j] - cellWidth - padLeft + } else if i == 1 { // is header + padLeft, padRight = 0, 0 + } else { // align to the left + padRight = lengths[j] - cellWidth + } + + row[j] = strings.Repeat(" ", padLeft) + cell + strings.Repeat(" ", padRight) + } + } + + var newTable string + for _, row := range matrix { // build new table + newTable += "|" + strings.Join(row, "|") + "|\n" + } + + s = strings.Replace(s, rawTable, newTable, 1) + } + + return s + "\n" // add an extra newline +} diff --git a/docs_test.go b/docs_test.go index e4ae2fe7b4..26162463ca 100644 --- a/docs_test.go +++ b/docs_test.go @@ -20,6 +20,18 @@ func TestToMarkdownFull(t *testing.T) { expectFileContent(t, "testdata/expected-doc-full.md", res) } +func TestToTabularMarkdownFull(t *testing.T) { + // Given + app := testApp() + + // When + res, err := app.ToTabularMarkdown() + + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res) +} + func TestToMarkdownNoFlags(t *testing.T) { // Given app := testApp() diff --git a/template.go b/template.go index 85e066c833..4d07a3c8e4 100644 --- a/template.go +++ b/template.go @@ -128,6 +128,74 @@ var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionN {{ range $v := .Commands }} {{ $v }}{{ end }}{{ end }}` +var MarkdownTabularDocTemplate = `{{ define "flags" }} +| Name | Description | Default value | Environment variables | +|------|-------------|:-------------:|:---------------------:| +{{ range $flag := . -}} +{{- /**/ -}} | ` + "`" + `{{ $flag.Name }}{{ if $flag.TakesValue }}="…"{{ end }}` + "`" + ` {{ if $flag.Aliases }}(` + "`" + `{{ join $flag.Aliases "` + "`, `" + `" }}` + "`" + `) {{ end }} +{{- /**/ -}} | {{ $flag.Usage }} +{{- /**/ -}} | {{ if $flag.Default }}` + "`" + `{{ $flag.Default }}` + "`" + `{{ end }} +{{- /**/ -}} | {{ if $flag.EnvVars }}` + "`" + `{{ join $flag.EnvVars "` + "`, `" + `" }}` + "`" + `{{ else }}*none*{{ end }} +{{- /**/ -}} | +{{ end }} +{{ end }} + +{{ define "command" }} +### ` + "`" + `{{ .Name }}` + "`" + ` {{ if gt .Level 0 }}sub{{ end }}command{{ if .Aliases }} (aliases: ` + "`" + `{{ join .Aliases "` + "`, `" + `" }}` + "`" + `){{ end }} +{{ if .Usage }} +{{ .Usage }}. +{{ end }} +{{ if .UsageText }} +> {{ .UsageText }}. +{{ end }} +{{ if .Description }} +{{ .Description }}. +{{ end }} +Usage: + +` + "```" + `bash +$ {{ .AppPath }} [GLOBAL FLAGS] {{ .Name }}{{ if .Flags }} [COMMAND FLAGS]{{ end }} {{ if .ArgsUsage }}{{ .ArgsUsage }}{{ else }}[ARGUMENTS...]{{ end }} +` + "```" + ` + +{{ if .Flags -}} +The following flags are supported: +{{ template "flags" .Flags }} +{{ end -}} + +{{ if .SubCommands -}} +{{ range $subCmd := .SubCommands -}} +{{ template "command" $subCmd }} +{{ end -}} +{{ end -}} +{{ end }} + +## CLI interface{{ if .Name }} - {{ .Name }}{{ end }} + +{{ if .Description }}{{ .Description }}. +{{ end }} +{{ if .Usage }}{{ .Usage }}. +{{ end }} +{{ if .UsageText }} +> {{ .UsageText }}. +{{ end }} +Usage: + +` + "```" + `bash +$ {{ .AppPath }}{{ if .GlobalFlags }} [GLOBAL FLAGS]{{ end }} [COMMAND] [COMMAND FLAGS] {{ if .ArgsUsage }}{{ .ArgsUsage }}{{ else }}[ARGUMENTS...]{{ end }} +` + "```" + ` + +{{ if .GlobalFlags }} +Global flags: + +{{ template "flags" .GlobalFlags }} + +{{ end -}} +{{ if .Commands -}} +{{ range $cmd := .Commands -}} +{{ template "command" $cmd }} +{{ end }} +{{- end }}` + var FishCompletionTemplate = `# {{ .App.Name }} fish shell completion function __fish_{{ .App.Name }}_no_subcommand --description 'Test if there has been any subcommand yet' diff --git a/testdata/expected-tabular-markdown-full.md b/testdata/expected-tabular-markdown-full.md new file mode 100644 index 0000000000..7a16da55f2 --- /dev/null +++ b/testdata/expected-tabular-markdown-full.md @@ -0,0 +1,110 @@ +## CLI interface - greet + +Description of the application. + +Some app. + +> app [first_arg] [second_arg]. + +Usage: + +```bash +$ app [GLOBAL FLAGS] [COMMAND] [COMMAND FLAGS] [ARGUMENTS...] +``` + +Global flags: + +| Name | Description | Default value | Environment variables | +|------------------------------|---------------------|:---------------:|:-----------------------:| +| `--socket="…"` (`-s`) | some 'usage' text | `value` | *none* | +| `--flag="…"` (`--fl`, `-f`) | | | *none* | +| `--another-flag` (`-b`) | another usage text | `false` | *none* | + +### `config` command (aliases: `c`) + +another usage test. + +Usage: + +```bash +$ app [GLOBAL FLAGS] config [COMMAND FLAGS] [ARGUMENTS...] +``` + +The following flags are supported: + +| Name | Description | Default value | Environment variables | +|-----------------------------|--------------------|:-------------:|:---------------------:| +| `--flag="…"` (`--fl`, `-f`) | | | *none* | +| `--another-flag` (`-b`) | another usage text | `false` | *none* | + +### `config sub-config` subcommand (aliases: `s`, `ss`) + +another usage test. + +Usage: + +```bash +$ app [GLOBAL FLAGS] config sub-config [COMMAND FLAGS] [ARGUMENTS...] +``` + +The following flags are supported: + +| Name | Description | Default value | Environment variables | +|-------------------------------------|-----------------|:-------------:|:---------------------:| +| `--sub-flag="…"` (`--sub-fl`, `-s`) | | | *none* | +| `--sub-command-flag` (`-s`) | some usage text | `false` | *none* | + +### `info` command (aliases: `i`, `in`) + +retrieve generic information. + +Usage: + +```bash +$ app [GLOBAL FLAGS] info [ARGUMENTS...] +``` + +### `some-command` command + +Usage: + +```bash +$ app [GLOBAL FLAGS] some-command [ARGUMENTS...] +``` + +### `usage` command (aliases: `u`) + +standard usage text. + +> Usage for the usage text - formatted: Based on the specified ConfigMap and summon secrets.yml - list: Inspect the environment for a specific process running on a Pod - for_effect: Compare 'namespace' environment with 'local' ``` func() { ... } ``` Should be a part of the same code block. + +Usage: + +```bash +$ app [GLOBAL FLAGS] usage [COMMAND FLAGS] [ARGUMENTS...] +``` + +The following flags are supported: + +| Name | Description | Default value | Environment variables | +|-----------------------------|--------------------|:-------------:|:---------------------:| +| `--flag="…"` (`--fl`, `-f`) | | | *none* | +| `--another-flag` (`-b`) | another usage text | `false` | *none* | + +### `usage sub-usage` subcommand (aliases: `su`) + +standard usage text. + +> Single line of UsageText. + +Usage: + +```bash +$ app [GLOBAL FLAGS] usage sub-usage [COMMAND FLAGS] [ARGUMENTS...] +``` + +The following flags are supported: + +| Name | Description | Default value | Environment variables | +|-----------------------------|-----------------|:-------------:|:---------------------:| +| `--sub-command-flag` (`-s`) | some usage text | `false` | *none* | From 763d715fc0f91e83be52627cdc98f526a2714af1 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:29:32 +0400 Subject: [PATCH 02/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs.go):=20split=20Us?= =?UTF-8?q?ageText=20into=20an=20array=20of=20strings=20=F0=9F=90=9B=20fix?= =?UTF-8?q?(fish=5Ftest.go):=20add=20EXAMPLE=5FVARIABLE=5FNAME=20to=20anot?= =?UTF-8?q?her-flag's=20EnvVars=20=E2=9C=A8=20feat(template.go):=20add=20s?= =?UTF-8?q?upport=20for=20multiline=20UsageText=20in=20MarkdownTabularDocT?= =?UTF-8?q?emplate?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs.go | 103 ++++++++++++++------- fish_test.go | 1 + template.go | 8 +- testdata/expected-tabular-markdown-full.md | 23 +++-- 4 files changed, 89 insertions(+), 46 deletions(-) diff --git a/docs.go b/docs.go index dd338150cd..16e91225b1 100644 --- a/docs.go +++ b/docs.go @@ -60,7 +60,7 @@ func (a *App) ToTabularMarkdown(opts ...TabularOption) (string, error) { Name: a.Name, Description: tt.PrepareMultilineString(a.Description), Usage: tt.PrepareMultilineString(a.Usage), - UsageText: tt.PrepareMultilineString(a.UsageText), + UsageText: strings.FieldsFunc(a.UsageText, func(r rune) bool { return r == '\n' }), ArgsUsage: tt.PrepareMultilineString(a.ArgsUsage), GlobalFlags: tt.PrepareFlags(a.VisibleFlags()), Commands: tt.PrepareCommands(a.VisibleCommands(), o.appPath, "", 0), @@ -256,24 +256,26 @@ func prepareUsage(command *Command, usageText string) string { type ( cliTabularAppTemplate struct { - AppPath string - Name string - Usage, UsageText, ArgsUsage string - Description string - GlobalFlags []cliTabularFlagTemplate - Commands []cliTabularCommandTemplate + AppPath string + Name string + Usage, ArgsUsage string + UsageText []string + Description string + GlobalFlags []cliTabularFlagTemplate + Commands []cliTabularCommandTemplate } cliTabularCommandTemplate struct { - AppPath string - Name string - Aliases []string - Usage, UsageText, ArgsUsage string - Description string - Category string - Flags []cliTabularFlagTemplate - SubCommands []cliTabularCommandTemplate - Level uint + AppPath string + Name string + Aliases []string + Usage, ArgsUsage string + UsageText []string + Description string + Category string + Flags []cliTabularFlagTemplate + SubCommands []cliTabularCommandTemplate + Level uint } cliTabularFlagTemplate struct { @@ -299,7 +301,7 @@ func (tt tabularTemplate) PrepareCommands(commands []*Command, appPath, parentCo Name: strings.TrimSpace(strings.Join([]string{parentCommandName, cmd.Name}, " ")), Aliases: cmd.Aliases, Usage: tt.PrepareMultilineString(cmd.Usage), - UsageText: tt.PrepareMultilineString(cmd.UsageText), + UsageText: strings.FieldsFunc(cmd.UsageText, func(r rune) bool { return r == '\n' }), ArgsUsage: tt.PrepareMultilineString(cmd.ArgsUsage), Description: tt.PrepareMultilineString(cmd.Description), Category: cmd.Category, @@ -375,8 +377,14 @@ func (tabularTemplate) PrepareMultilineString(s string) string { } func (tabularTemplate) Prettify(s string) string { - s = regexp.MustCompile(`\n{2,}`).ReplaceAllString(s, "\n\n") // normalize newlines - s = strings.Trim(s, " \n") // trim spaces and newlines + var max = func(x, y int) int { + if x > y { + return x + } + return y + } + + var b strings.Builder // search for tables for _, rawTable := range regexp.MustCompile(`(?m)^(\|[^\n]+\|\r?\n)((?:\|:?-+:?)+\|)(\n(?:\|[^\n]+\|\r?\n?)*)?$`).FindAllString(s, -1) { @@ -406,11 +414,14 @@ func (tabularTemplate) Prettify(s string) string { // calculate max lengths var lengths = make([]int, len(matrix[0])) - const padding = 2 // 2 spaces for padding - for _, row := range matrix { + for n, row := range matrix { for i, cell := range row { - if len(cell) > lengths[i]-padding { - lengths[i] = utf8.RuneCountInString(cell) + padding + if n == 1 { + continue // skip separator + } + + if l := utf8.RuneCountInString(cell); l > lengths[i] { + lengths[i] = l } } } @@ -420,37 +431,57 @@ func (tabularTemplate) Prettify(s string) string { for j, cell := range row { if i == 1 { // is separator if centered[j] { - cell = ":" + strings.Repeat("-", lengths[j]-2) + ":" + b.Reset() + b.WriteRune(':') + b.WriteString(strings.Repeat("-", max(0, lengths[j]))) + b.WriteRune(':') + + row[j] = b.String() } else { - cell = strings.Repeat("-", lengths[j]+1) + row[j] = strings.Repeat("-", max(0, lengths[j]+2)) } + + continue } var ( - padLeft, padRight = 1, 1 cellWidth = utf8.RuneCountInString(cell) + padLeft, padRight = 1, max(1, lengths[j]-cellWidth+1) // align to the left ) if centered[j] { // is centered - padLeft = (lengths[j] - cellWidth) / 2 - padRight = lengths[j] - cellWidth - padLeft - } else if i == 1 { // is header - padLeft, padRight = 0, 0 - } else { // align to the left - padRight = lengths[j] - cellWidth + padLeft = max(1, (lengths[j]-cellWidth)/2) + padRight = max(1, lengths[j]-cellWidth-(padLeft-1)) } - row[j] = strings.Repeat(" ", padLeft) + cell + strings.Repeat(" ", padRight) + b.Reset() + b.WriteString(strings.Repeat(" ", padLeft)) + + if padLeft+cellWidth+padRight <= lengths[j]+1 { + b.WriteRune(' ') // add an extra space if the cell is not full + } + + b.WriteString(cell) + b.WriteString(strings.Repeat(" ", padRight)) + + row[j] = b.String() } } - var newTable string + b.Reset() + for _, row := range matrix { // build new table - newTable += "|" + strings.Join(row, "|") + "|\n" + b.WriteRune('|') + b.WriteString(strings.Join(row, "|")) + b.WriteRune('|') + b.WriteRune('\n') } - s = strings.Replace(s, rawTable, newTable, 1) + s = strings.Replace(s, rawTable, b.String(), 1) } + s = regexp.MustCompile(`\n{2,}`).ReplaceAllString(s, "\n\n") // normalize newlines + s = strings.Trim(s, " \n") // trim spaces and newlines + return s + "\n" // add an extra newline } diff --git a/fish_test.go b/fish_test.go index de75939614..2826ba8d3e 100644 --- a/fish_test.go +++ b/fish_test.go @@ -39,6 +39,7 @@ func testApp() *App { Name: "another-flag", Aliases: []string{"b"}, Usage: "another usage text", + EnvVars: []string{"EXAMPLE_VARIABLE_NAME"}, }, &BoolFlag{ Name: "hidden-flag", diff --git a/template.go b/template.go index 4d07a3c8e4..87695aab52 100644 --- a/template.go +++ b/template.go @@ -146,7 +146,9 @@ var MarkdownTabularDocTemplate = `{{ define "flags" }} {{ .Usage }}. {{ end }} {{ if .UsageText }} -> {{ .UsageText }}. +{{ range $line := .UsageText -}} +> {{ $line }} +{{ end -}} {{ end }} {{ if .Description }} {{ .Description }}. @@ -176,7 +178,9 @@ The following flags are supported: {{ if .Usage }}{{ .Usage }}. {{ end }} {{ if .UsageText }} -> {{ .UsageText }}. +{{ range $line := .UsageText -}} +> {{ $line }} +{{ end -}} {{ end }} Usage: diff --git a/testdata/expected-tabular-markdown-full.md b/testdata/expected-tabular-markdown-full.md index 7a16da55f2..2828e8462a 100644 --- a/testdata/expected-tabular-markdown-full.md +++ b/testdata/expected-tabular-markdown-full.md @@ -4,7 +4,7 @@ Description of the application. Some app. -> app [first_arg] [second_arg]. +> app [first_arg] [second_arg] Usage: @@ -14,11 +14,11 @@ $ app [GLOBAL FLAGS] [COMMAND] [COMMAND FLAGS] [ARGUMENTS...] Global flags: -| Name | Description | Default value | Environment variables | -|------------------------------|---------------------|:---------------:|:-----------------------:| -| `--socket="…"` (`-s`) | some 'usage' text | `value` | *none* | -| `--flag="…"` (`--fl`, `-f`) | | | *none* | -| `--another-flag` (`-b`) | another usage text | `false` | *none* | +| Name | Description | Default value | Environment variables | +|-----------------------------|--------------------|:-------------:|:-----------------------:| +| `--socket="…"` (`-s`) | some 'usage' text | `value` | *none* | +| `--flag="…"` (`--fl`, `-f`) | | | *none* | +| `--another-flag` (`-b`) | another usage text | `false` | `EXAMPLE_VARIABLE_NAME` | ### `config` command (aliases: `c`) @@ -76,7 +76,14 @@ $ app [GLOBAL FLAGS] some-command [ARGUMENTS...] standard usage text. -> Usage for the usage text - formatted: Based on the specified ConfigMap and summon secrets.yml - list: Inspect the environment for a specific process running on a Pod - for_effect: Compare 'namespace' environment with 'local' ``` func() { ... } ``` Should be a part of the same code block. +> Usage for the usage text +> - formatted: Based on the specified ConfigMap and summon secrets.yml +> - list: Inspect the environment for a specific process running on a Pod +> - for_effect: Compare 'namespace' environment with 'local' +> ``` +> func() { ... } +> ``` +> Should be a part of the same code block Usage: @@ -95,7 +102,7 @@ The following flags are supported: standard usage text. -> Single line of UsageText. +> Single line of UsageText Usage: From bfe40463ffebe2ea066df93e09cbc5eb9c3f4829 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:41:41 +0400 Subject: [PATCH 03/28] =?UTF-8?q?=F0=9F=93=9D=20docs(godoc-current.txt):?= =?UTF-8?q?=20add=20ToTabularMarkdown=20method=20to=20App=20struct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- godoc-current.txt | 80 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/godoc-current.txt b/godoc-current.txt index 8a8158c487..722daaab7c 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -131,6 +131,77 @@ var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionN # COMMANDS {{ range $v := .Commands }} {{ $v }}{{ end }}{{ end }}` +var MarkdownTabularDocTemplate = `{{ define "flags" }} +| Name | Description | Default value | Environment variables | +|------|-------------|:-------------:|:---------------------:| +{{ range $flag := . -}} +{{- /**/ -}} | ` + "`" + `{{ $flag.Name }}{{ if $flag.TakesValue }}="…"{{ end }}` + "`" + ` {{ if $flag.Aliases }}(` + "`" + `{{ join $flag.Aliases "` + "`, `" + `" }}` + "`" + `) {{ end }} +{{- /**/ -}} | {{ $flag.Usage }} +{{- /**/ -}} | {{ if $flag.Default }}` + "`" + `{{ $flag.Default }}` + "`" + `{{ end }} +{{- /**/ -}} | {{ if $flag.EnvVars }}` + "`" + `{{ join $flag.EnvVars "` + "`, `" + `" }}` + "`" + `{{ else }}*none*{{ end }} +{{- /**/ -}} | +{{ end }} +{{ end }} + +{{ define "command" }} +### ` + "`" + `{{ .Name }}` + "`" + ` {{ if gt .Level 0 }}sub{{ end }}command{{ if .Aliases }} (aliases: ` + "`" + `{{ join .Aliases "` + "`, `" + `" }}` + "`" + `){{ end }} +{{ if .Usage }} +{{ .Usage }}. +{{ end }} +{{ if .UsageText }} +{{ range $line := .UsageText -}} +> {{ $line }} +{{ end -}} +{{ end }} +{{ if .Description }} +{{ .Description }}. +{{ end }} +Usage: + +` + "```" + `bash +$ {{ .AppPath }} [GLOBAL FLAGS] {{ .Name }}{{ if .Flags }} [COMMAND FLAGS]{{ end }} {{ if .ArgsUsage }}{{ .ArgsUsage }}{{ else }}[ARGUMENTS...]{{ end }} +` + "```" + ` + +{{ if .Flags -}} +The following flags are supported: +{{ template "flags" .Flags }} +{{ end -}} + +{{ if .SubCommands -}} +{{ range $subCmd := .SubCommands -}} +{{ template "command" $subCmd }} +{{ end -}} +{{ end -}} +{{ end }} + +## CLI interface{{ if .Name }} - {{ .Name }}{{ end }} + +{{ if .Description }}{{ .Description }}. +{{ end }} +{{ if .Usage }}{{ .Usage }}. +{{ end }} +{{ if .UsageText }} +{{ range $line := .UsageText -}} +> {{ $line }} +{{ end -}} +{{ end }} +Usage: + +` + "```" + `bash +$ {{ .AppPath }}{{ if .GlobalFlags }} [GLOBAL FLAGS]{{ end }} [COMMAND] [COMMAND FLAGS] {{ if .ArgsUsage }}{{ .ArgsUsage }}{{ else }}[ARGUMENTS...]{{ end }} +` + "```" + ` + +{{ if .GlobalFlags }} +Global flags: + +{{ template "flags" .GlobalFlags }} + +{{ end -}} +{{ if .Commands -}} +{{ range $cmd := .Commands -}} +{{ template "command" $cmd }} +{{ end }} +{{- end }}` var NewFloat64Slice = NewSliceBase[float64, NoConfig, float64Value] var NewInt64Slice = NewSliceBase[int64, IntegerConfig, int64Value] var NewIntSlice = NewSliceBase[int, IntegerConfig, intValue] @@ -378,6 +449,10 @@ func (a *App) ToMarkdown() (string, error) ToMarkdown creates a markdown string for the `*App` The function errors if either parsing or writing of the string fails. +func (a *App) ToTabularMarkdown(opts ...TabularOption) (string, error) + ToTabularMarkdown creates a tabular markdown documentation for the `*App`. + The function errors if either parsing or writing of the string fails. + func (a *App) VisibleCategories() []CommandCategory VisibleCategories returns a slice of categories and commands that are Hidden=false @@ -1065,6 +1140,11 @@ type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string +type TabularOption func(*tabularOptions) + +func WithTabularAppPath(path string) TabularOption + WithTabularAppPath allows to override the default app path. + type TimestampConfig struct { Timezone *time.Location Layout string From 02295d9bdb134ce566b6ed94fff44d1d7c9da036 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 16:43:56 +0400 Subject: [PATCH 04/28] =?UTF-8?q?=F0=9F=93=9D=20docs(godoc-v3.x.txt):=20ad?= =?UTF-8?q?d=20ToTabularMarkdown=20method=20to=20App=20struct?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- testdata/godoc-v3.x.txt | 80 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/testdata/godoc-v3.x.txt b/testdata/godoc-v3.x.txt index 8a8158c487..722daaab7c 100644 --- a/testdata/godoc-v3.x.txt +++ b/testdata/godoc-v3.x.txt @@ -131,6 +131,77 @@ var MarkdownDocTemplate = `{{if gt .SectionNum 0}}% {{ .App.Name }} {{ .SectionN # COMMANDS {{ range $v := .Commands }} {{ $v }}{{ end }}{{ end }}` +var MarkdownTabularDocTemplate = `{{ define "flags" }} +| Name | Description | Default value | Environment variables | +|------|-------------|:-------------:|:---------------------:| +{{ range $flag := . -}} +{{- /**/ -}} | ` + "`" + `{{ $flag.Name }}{{ if $flag.TakesValue }}="…"{{ end }}` + "`" + ` {{ if $flag.Aliases }}(` + "`" + `{{ join $flag.Aliases "` + "`, `" + `" }}` + "`" + `) {{ end }} +{{- /**/ -}} | {{ $flag.Usage }} +{{- /**/ -}} | {{ if $flag.Default }}` + "`" + `{{ $flag.Default }}` + "`" + `{{ end }} +{{- /**/ -}} | {{ if $flag.EnvVars }}` + "`" + `{{ join $flag.EnvVars "` + "`, `" + `" }}` + "`" + `{{ else }}*none*{{ end }} +{{- /**/ -}} | +{{ end }} +{{ end }} + +{{ define "command" }} +### ` + "`" + `{{ .Name }}` + "`" + ` {{ if gt .Level 0 }}sub{{ end }}command{{ if .Aliases }} (aliases: ` + "`" + `{{ join .Aliases "` + "`, `" + `" }}` + "`" + `){{ end }} +{{ if .Usage }} +{{ .Usage }}. +{{ end }} +{{ if .UsageText }} +{{ range $line := .UsageText -}} +> {{ $line }} +{{ end -}} +{{ end }} +{{ if .Description }} +{{ .Description }}. +{{ end }} +Usage: + +` + "```" + `bash +$ {{ .AppPath }} [GLOBAL FLAGS] {{ .Name }}{{ if .Flags }} [COMMAND FLAGS]{{ end }} {{ if .ArgsUsage }}{{ .ArgsUsage }}{{ else }}[ARGUMENTS...]{{ end }} +` + "```" + ` + +{{ if .Flags -}} +The following flags are supported: +{{ template "flags" .Flags }} +{{ end -}} + +{{ if .SubCommands -}} +{{ range $subCmd := .SubCommands -}} +{{ template "command" $subCmd }} +{{ end -}} +{{ end -}} +{{ end }} + +## CLI interface{{ if .Name }} - {{ .Name }}{{ end }} + +{{ if .Description }}{{ .Description }}. +{{ end }} +{{ if .Usage }}{{ .Usage }}. +{{ end }} +{{ if .UsageText }} +{{ range $line := .UsageText -}} +> {{ $line }} +{{ end -}} +{{ end }} +Usage: + +` + "```" + `bash +$ {{ .AppPath }}{{ if .GlobalFlags }} [GLOBAL FLAGS]{{ end }} [COMMAND] [COMMAND FLAGS] {{ if .ArgsUsage }}{{ .ArgsUsage }}{{ else }}[ARGUMENTS...]{{ end }} +` + "```" + ` + +{{ if .GlobalFlags }} +Global flags: + +{{ template "flags" .GlobalFlags }} + +{{ end -}} +{{ if .Commands -}} +{{ range $cmd := .Commands -}} +{{ template "command" $cmd }} +{{ end }} +{{- end }}` var NewFloat64Slice = NewSliceBase[float64, NoConfig, float64Value] var NewInt64Slice = NewSliceBase[int64, IntegerConfig, int64Value] var NewIntSlice = NewSliceBase[int, IntegerConfig, intValue] @@ -378,6 +449,10 @@ func (a *App) ToMarkdown() (string, error) ToMarkdown creates a markdown string for the `*App` The function errors if either parsing or writing of the string fails. +func (a *App) ToTabularMarkdown(opts ...TabularOption) (string, error) + ToTabularMarkdown creates a tabular markdown documentation for the `*App`. + The function errors if either parsing or writing of the string fails. + func (a *App) VisibleCategories() []CommandCategory VisibleCategories returns a slice of categories and commands that are Hidden=false @@ -1065,6 +1140,11 @@ type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string +type TabularOption func(*tabularOptions) + +func WithTabularAppPath(path string) TabularOption + WithTabularAppPath allows to override the default app path. + type TimestampConfig struct { Timezone *time.Location Layout string From 216710797651c174b2118b2fc2c679420f6c64ba Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 17:48:00 +0400 Subject: [PATCH 05/28] =?UTF-8?q?=F0=9F=94=A8=20refactor(docs.go):=20remov?= =?UTF-8?q?e=20unused=20TabularOption=20type=20and=20WithTabularAppPath=20?= =?UTF-8?q?function=20=E2=9C=A8=20feat(docs.go):=20add=20ToTabularToFileBe?= =?UTF-8?q?tweenTags=20function=20to=20generate=20tabular=20markdown=20doc?= =?UTF-8?q?umentation=20and=20update=20file=20between=20tags=20?= =?UTF-8?q?=F0=9F=94=A8=20refactor(docs=5Ftest.go):=20update=20tests=20to?= =?UTF-8?q?=20use=20ToTabularMarkdown=20with=20appPath=20parameter=20inste?= =?UTF-8?q?ad=20of=20TabularOption=20=E2=9C=85=20test(docs=5Ftest.go):=20a?= =?UTF-8?q?dd=20tests=20for=20ToTabularToFileBetweenTags=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs.go | 68 +++++++++++++++++++++---------- docs_test.go | 90 ++++++++++++++++++++++++++++++++++++++++- godoc-current.txt | 12 +++--- testdata/godoc-v3.x.txt | 12 +++--- 4 files changed, 147 insertions(+), 35 deletions(-) diff --git a/docs.go b/docs.go index 16e91225b1..a99fe1bac9 100644 --- a/docs.go +++ b/docs.go @@ -7,6 +7,7 @@ import ( "bytes" "fmt" "io" + "os" "regexp" "sort" "strconv" @@ -17,28 +18,11 @@ import ( "github.com/cpuguy83/go-md2man/v2/md2man" ) -type ( - tabularOptions struct { - appPath string - } - - TabularOption func(*tabularOptions) -) - -// WithTabularAppPath allows to override the default app path. -func WithTabularAppPath(path string) TabularOption { - return func(o *tabularOptions) { o.appPath = path } -} - // ToTabularMarkdown creates a tabular markdown documentation for the `*App`. // The function errors if either parsing or writing of the string fails. -func (a *App) ToTabularMarkdown(opts ...TabularOption) (string, error) { - var o = tabularOptions{ - appPath: "app", - } - - for _, opt := range opts { - opt(&o) +func (a *App) ToTabularMarkdown(appPath string) (string, error) { + if appPath == "" { + appPath = "app" } const name = "cli" @@ -56,14 +40,14 @@ func (a *App) ToTabularMarkdown(opts ...TabularOption) (string, error) { ) if err = t.ExecuteTemplate(&w, name, cliTabularAppTemplate{ - AppPath: o.appPath, + AppPath: appPath, Name: a.Name, Description: tt.PrepareMultilineString(a.Description), Usage: tt.PrepareMultilineString(a.Usage), UsageText: strings.FieldsFunc(a.UsageText, func(r rune) bool { return r == '\n' }), ArgsUsage: tt.PrepareMultilineString(a.ArgsUsage), GlobalFlags: tt.PrepareFlags(a.VisibleFlags()), - Commands: tt.PrepareCommands(a.VisibleCommands(), o.appPath, "", 0), + Commands: tt.PrepareCommands(a.VisibleCommands(), appPath, "", 0), }); err != nil { return "", err } @@ -71,6 +55,46 @@ func (a *App) ToTabularMarkdown(opts ...TabularOption) (string, error) { return tt.Prettify(w.String()), nil } +// 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 (a *App) ToTabularToFileBetweenTags(appPath, filePath string, startEndTags ...string) error { + var start, end = "", "" // default tags + + if len(startEndTags) == 2 { + start, end = startEndTags[0], startEndTags[1] + } + + // read original file content + content, err := os.ReadFile(filePath) + if err != nil { + return err + } + + // generate markdown + md, err := a.ToTabularMarkdown(appPath) + if err != nil { + return err + } + + // prepare regexp to replace content between start and end tags + re, err := regexp.Compile("(?s)" + regexp.QuoteMeta(start) + "(.*?)" + regexp.QuoteMeta(end)) + if err != nil { + return err + } + + const comment = "" + + // replace content between start and end tags + 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 { + return err + } + + return nil +} + // ToMarkdown creates a markdown string for the `*App` // The function errors if either parsing or writing of the string fails. func (a *App) ToMarkdown() (string, error) { diff --git a/docs_test.go b/docs_test.go index 26162463ca..ad34bdefad 100644 --- a/docs_test.go +++ b/docs_test.go @@ -5,6 +5,8 @@ package cli import ( "errors" + "io/fs" + "os" "testing" ) @@ -25,13 +27,99 @@ func TestToTabularMarkdownFull(t *testing.T) { app := testApp() // When - res, err := app.ToTabularMarkdown() + res, err := app.ToTabularMarkdown("app") // Then expect(t, err, nil) expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res) } +func TestToTabularToFileBetweenTags(t *testing.T) { + expectedDocs, fErr := os.ReadFile("testdata/expected-tabular-markdown-full.md") + expect(t, fErr, nil) // read without error + + t.Run("default tags", func(t *testing.T) { + tmpFile, err := os.CreateTemp("", "") + expect(t, err, nil) // created without error + + defer func() { expect(t, os.Remove(tmpFile.Name()), nil) }() // cleanup + + _, err = tmpFile.WriteString(`# App readme file + +Some description + + + + +Some other text`) + expect(t, err, nil) // wrote without error + + expect(t, testApp().ToTabularToFileBetweenTags("app", tmpFile.Name()), nil) // replaced without error + + content, err := os.ReadFile(tmpFile.Name()) // read the file content + expect(t, err, nil) + + expected := `# App readme file + +Some description + + + +` + string(expectedDocs) + ` + + +Some other text` + + expect(t, string(content), expected) // content matches + }) + + t.Run("custom tags", func(t *testing.T) { + tmpFile, err := os.CreateTemp("", "") + expect(t, err, nil) // created without error + + defer func() { expect(t, os.Remove(tmpFile.Name()), nil) }() // cleanup + + _, err = tmpFile.WriteString(`# App readme file + +Some description + +foo_BAR|baz +lorem+ipsum + +Some other text`) + expect(t, err, nil) // wrote without error + + expect(t, testApp().ToTabularToFileBetweenTags("app", tmpFile.Name(), "foo_BAR|baz", "lorem+ipsum"), nil) + + content, err := os.ReadFile(tmpFile.Name()) // read the file content + expect(t, err, nil) + + expected := `# App readme file + +Some description + +foo_BAR|baz + +` + string(expectedDocs) + ` +lorem+ipsum + +Some other text` + + expect(t, string(content), expected) // content matches + }) + + t.Run("missing file", func(t *testing.T) { + tmpFile, err := os.CreateTemp("", "") + expect(t, err, nil) // created without error + + expect(t, os.Remove(tmpFile.Name()), nil) // and remove immediately + + err = testApp().ToTabularToFileBetweenTags("app", tmpFile.Name()) + + expect(t, errors.Is(err, fs.ErrNotExist), true) + }) +} + func TestToMarkdownNoFlags(t *testing.T) { // Given app := testApp() diff --git a/godoc-current.txt b/godoc-current.txt index 722daaab7c..6b7a4ee1a3 100644 --- a/godoc-current.txt +++ b/godoc-current.txt @@ -449,10 +449,15 @@ func (a *App) ToMarkdown() (string, error) ToMarkdown creates a markdown string for the `*App` The function errors if either parsing or writing of the string fails. -func (a *App) ToTabularMarkdown(opts ...TabularOption) (string, error) +func (a *App) ToTabularMarkdown(appPath string) (string, error) ToTabularMarkdown creates a tabular markdown documentation for the `*App`. The function errors if either parsing or writing of the string fails. +func (a *App) ToTabularToFileBetweenTags(appPath, filePath string, startEndTags ...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 (a *App) VisibleCategories() []CommandCategory VisibleCategories returns a slice of categories and commands that are Hidden=false @@ -1140,11 +1145,6 @@ type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string -type TabularOption func(*tabularOptions) - -func WithTabularAppPath(path string) TabularOption - WithTabularAppPath allows to override the default app path. - type TimestampConfig struct { Timezone *time.Location Layout string diff --git a/testdata/godoc-v3.x.txt b/testdata/godoc-v3.x.txt index 722daaab7c..6b7a4ee1a3 100644 --- a/testdata/godoc-v3.x.txt +++ b/testdata/godoc-v3.x.txt @@ -449,10 +449,15 @@ func (a *App) ToMarkdown() (string, error) ToMarkdown creates a markdown string for the `*App` The function errors if either parsing or writing of the string fails. -func (a *App) ToTabularMarkdown(opts ...TabularOption) (string, error) +func (a *App) ToTabularMarkdown(appPath string) (string, error) ToTabularMarkdown creates a tabular markdown documentation for the `*App`. The function errors if either parsing or writing of the string fails. +func (a *App) ToTabularToFileBetweenTags(appPath, filePath string, startEndTags ...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 (a *App) VisibleCategories() []CommandCategory VisibleCategories returns a slice of categories and commands that are Hidden=false @@ -1140,11 +1145,6 @@ type SuggestCommandFunc func(commands []*Command, provided string) string type SuggestFlagFunc func(flags []Flag, provided string, hideHelp bool) string -type TabularOption func(*tabularOptions) - -func WithTabularAppPath(path string) TabularOption - WithTabularAppPath allows to override the default app path. - type TimestampConfig struct { Timezone *time.Location Layout string From c45d216308c27a32b8b61fabbeb10614e08bacfa Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:12:30 +0400 Subject: [PATCH 06/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20add?= =?UTF-8?q?=20error=20handling=20for=20file=20close=20operation?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs_test.go b/docs_test.go index ad34bdefad..ffcb2639df 100644 --- a/docs_test.go +++ b/docs_test.go @@ -53,6 +53,7 @@ Some description Some other text`) expect(t, err, nil) // wrote without error + _ = tmpFile.Close() expect(t, testApp().ToTabularToFileBetweenTags("app", tmpFile.Name()), nil) // replaced without error @@ -88,6 +89,7 @@ lorem+ipsum Some other text`) expect(t, err, nil) // wrote without error + _ = tmpFile.Close() expect(t, testApp().ToTabularToFileBetweenTags("app", tmpFile.Name(), "foo_BAR|baz", "lorem+ipsum"), nil) @@ -111,6 +113,7 @@ Some other text` t.Run("missing file", func(t *testing.T) { tmpFile, err := os.CreateTemp("", "") expect(t, err, nil) // created without error + _ = tmpFile.Close() expect(t, os.Remove(tmpFile.Name()), nil) // and remove immediately From b0466796ab992d2e46dcfa5df00327f3f3ff6960 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:24:24 +0400 Subject: [PATCH 07/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20ign?= =?UTF-8?q?ore=20windows=20line=20endings=20in=20file=20content?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs_test.go b/docs_test.go index ffcb2639df..2c0f61ace7 100644 --- a/docs_test.go +++ b/docs_test.go @@ -4,6 +4,7 @@ package cli import ( + "bytes" "errors" "io/fs" "os" @@ -60,6 +61,8 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) + content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) // Ignore windows line endings + expected := `# App readme file Some description @@ -96,6 +99,8 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) + content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) // Ignore windows line endings + expected := `# App readme file Some description From 015e17603292d697b333d1a210728d4dac15be6c Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:28:11 +0400 Subject: [PATCH 08/28] =?UTF-8?q?=F0=9F=94=A7=20chore(docs=5Ftest.go):=20c?= =?UTF-8?q?hange=20comment=20to=20lowercase=20'ignore'=20and=20remove=20ex?= =?UTF-8?q?tra=20space?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs_test.go b/docs_test.go index 2c0f61ace7..fe1b390bd8 100644 --- a/docs_test.go +++ b/docs_test.go @@ -61,7 +61,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) // Ignore windows line endings + content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) // ignore windows line endings expected := `# App readme file @@ -99,7 +99,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) // Ignore windows line endings + content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) // ignore windows line endings expected := `# App readme file From c2ec6333b9f6a7d7fb366eabd9cf4820befd1838 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:33:14 +0400 Subject: [PATCH 09/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20fix?= =?UTF-8?q?=20typo=20in=20comment,=20add=20space=20before=20'line=20ending?= =?UTF-8?q?s'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs_test.go b/docs_test.go index fe1b390bd8..ee074bbe58 100644 --- a/docs_test.go +++ b/docs_test.go @@ -61,7 +61,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) // ignore windows line endings + content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) // ignore windows line endings expected := `# App readme file From f46c8f5e2ffaa17bb1d6855cee57953d8b9ff3f9 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:37:43 +0400 Subject: [PATCH 10/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20use?= =?UTF-8?q?=20bytes.Replace=20instead=20of=20bytes.ReplaceAll=20to=20suppo?= =?UTF-8?q?rt=20older=20Go=20versions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs_test.go b/docs_test.go index ee074bbe58..6768bc6644 100644 --- a/docs_test.go +++ b/docs_test.go @@ -61,7 +61,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) // ignore windows line endings + content = bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1) // ignore windows line endings expected := `# App readme file @@ -99,7 +99,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.ReplaceAll(content, []byte("\r\n"), []byte("\n")) // ignore windows line endings + content = bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1) // ignore windows line endings expected := `# App readme file From 7396688cac7aa63f27b03438106e0656c76bb665 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:46:38 +0400 Subject: [PATCH 11/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20tri?= =?UTF-8?q?m=20whitespaces=20and=20line=20endings=20after=20replacing=20wi?= =?UTF-8?q?ndows=20line=20endings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs_test.go b/docs_test.go index 6768bc6644..2c2badd29a 100644 --- a/docs_test.go +++ b/docs_test.go @@ -61,7 +61,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1) // ignore windows line endings + content = bytes.Trim(bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1), "\r\n ") // ignore windows line endings expected := `# App readme file @@ -99,7 +99,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1) // ignore windows line endings + content = bytes.Trim(bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1), "\r\n ") // ignore windows line endings expected := `# App readme file From d42ae6d37f679993b6aad09ce4e307cc5eec3672 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:49:55 +0400 Subject: [PATCH 12/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20rem?= =?UTF-8?q?ove=20unnecessary=20trimming=20of=20bytes=20in=20bytes.Replace?= =?UTF-8?q?=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs_test.go b/docs_test.go index 2c2badd29a..1b3105dec3 100644 --- a/docs_test.go +++ b/docs_test.go @@ -61,7 +61,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.Trim(bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1), "\r\n ") // ignore windows line endings + content = bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1) // ignore windows line endings expected := `# App readme file @@ -99,7 +99,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.Trim(bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1), "\r\n ") // ignore windows line endings + content = bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1) // ignore windows line endings expected := `# App readme file @@ -112,6 +112,9 @@ lorem+ipsum Some other text` + t.Log(content) + t.Log([]byte(expected)) + expect(t, string(content), expected) // content matches }) From daf2ae3b66eb065bc90623964d3536b965b74b5d Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 18:56:32 +0400 Subject: [PATCH 13/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20rem?= =?UTF-8?q?ove=20unnecessary=20second=20argument=20in=20bytes.Replace=20fu?= =?UTF-8?q?nction=20call=20to=20ignore=20windows=20line=20endings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs_test.go b/docs_test.go index 1b3105dec3..05214ff51d 100644 --- a/docs_test.go +++ b/docs_test.go @@ -61,7 +61,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1) // ignore windows line endings + content = bytes.Replace(content, []byte("\r\n"), []byte{}, -1) // ignore windows line endings expected := `# App readme file @@ -99,7 +99,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.Replace(content, []byte("\r\n"), []byte("\n"), -1) // ignore windows line endings + content = bytes.Replace(content, []byte("\r\n"), []byte{}, -1) // ignore windows line endings expected := `# App readme file From 260031b39e2497e8d6c118fbb7566f832a477c6e Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:01:56 +0400 Subject: [PATCH 14/28] =?UTF-8?q?=F0=9F=94=A5=20refactor(docs=5Ftest.go):?= =?UTF-8?q?=20remove=20unused=20bytes=20package=20import=20=F0=9F=94=A5=20?= =?UTF-8?q?refactor(docs=5Ftest.go):=20comment=20out=20unused=20windows=20?= =?UTF-8?q?line=20endings=20replacement?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs_test.go b/docs_test.go index 05214ff51d..1845df8482 100644 --- a/docs_test.go +++ b/docs_test.go @@ -4,7 +4,6 @@ package cli import ( - "bytes" "errors" "io/fs" "os" @@ -61,7 +60,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.Replace(content, []byte("\r\n"), []byte{}, -1) // ignore windows line endings + // content = bytes.Replace(content, []byte("\r\n"), []byte{}, -1) // ignore windows line endings expected := `# App readme file @@ -99,7 +98,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - content = bytes.Replace(content, []byte("\r\n"), []byte{}, -1) // ignore windows line endings + // content = bytes.Replace(content, []byte("\r\n"), []byte{}, -1) // ignore windows line endings expected := `# App readme file From 8940782af22c14f12b98fc883932302b8350096f Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:07:30 +0400 Subject: [PATCH 15/28] =?UTF-8?q?=F0=9F=94=A8=20refactor(docs=5Ftest.go):?= =?UTF-8?q?=20remove=20commented=20out=20code=20=F0=9F=90=9B=20fix(docs=5F?= =?UTF-8?q?test.go):=20fix=20expected=20content=20type=20to=20byte=20array?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/docs_test.go b/docs_test.go index 1845df8482..629fd9307e 100644 --- a/docs_test.go +++ b/docs_test.go @@ -4,6 +4,7 @@ package cli import ( + "bytes" "errors" "io/fs" "os" @@ -60,9 +61,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - // content = bytes.Replace(content, []byte("\r\n"), []byte{}, -1) // ignore windows line endings - - expected := `# App readme file + expected := []byte(`# App readme file Some description @@ -71,9 +70,11 @@ Some description ` + string(expectedDocs) + ` -Some other text` +Some other text`) + + expected = bytes.Replace(expected, []byte("\r\n"), []byte{}, -1) // ignore windows line endings - expect(t, string(content), expected) // content matches + expect(t, string(content), string(expected)) // content matches }) t.Run("custom tags", func(t *testing.T) { @@ -98,9 +99,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - // content = bytes.Replace(content, []byte("\r\n"), []byte{}, -1) // ignore windows line endings - - expected := `# App readme file + expected := []byte(`# App readme file Some description @@ -109,12 +108,14 @@ foo_BAR|baz ` + string(expectedDocs) + ` lorem+ipsum -Some other text` +Some other text`) + + expected = bytes.Replace(expected, []byte("\r\n"), []byte{}, -1) // ignore windows line endings t.Log(content) - t.Log([]byte(expected)) + t.Log(expected) - expect(t, string(content), expected) // content matches + expect(t, string(content), string(expected)) // content matches }) t.Run("missing file", func(t *testing.T) { From 012763e7d59da8d1ae84404055027d55ec9edff8 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:11:15 +0400 Subject: [PATCH 16/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20rep?= =?UTF-8?q?lace=20windows=20line=20endings=20with=20unix=20line=20endings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs_test.go b/docs_test.go index 629fd9307e..2d16ecd27f 100644 --- a/docs_test.go +++ b/docs_test.go @@ -72,7 +72,7 @@ Some description Some other text`) - expected = bytes.Replace(expected, []byte("\r\n"), []byte{}, -1) // ignore windows line endings + expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{10}, -1) // ignore windows line endings expect(t, string(content), string(expected)) // content matches }) @@ -110,7 +110,7 @@ lorem+ipsum Some other text`) - expected = bytes.Replace(expected, []byte("\r\n"), []byte{}, -1) // ignore windows line endings + expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{10}, -1) // ignore windows line endings t.Log(content) t.Log(expected) From a0c1d801d239923a9a6b79d6c6a28f6bc9cfc472 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:14:08 +0400 Subject: [PATCH 17/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20rep?= =?UTF-8?q?lace=20line=20ending=20byte=20sequence=20to=20fix=20test=20fail?= =?UTF-8?q?ure=20on=20Windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs_test.go b/docs_test.go index 2d16ecd27f..48e0fb412b 100644 --- a/docs_test.go +++ b/docs_test.go @@ -72,7 +72,7 @@ Some description Some other text`) - expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{10}, -1) // ignore windows line endings + expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{13}, -1) // ignore windows line endings expect(t, string(content), string(expected)) // content matches }) @@ -110,7 +110,7 @@ lorem+ipsum Some other text`) - expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{10}, -1) // ignore windows line endings + expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{13}, -1) // ignore windows line endings t.Log(content) t.Log(expected) From ebf4e518a923135acf2577fb0519e8ca7670de8f Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:19:20 +0400 Subject: [PATCH 18/28] =?UTF-8?q?=F0=9F=94=A5=20refactor(docs=5Ftest.go):?= =?UTF-8?q?=20remove=20unused=20bytes=20package=20import=20=F0=9F=94=A5=20?= =?UTF-8?q?refactor(docs=5Ftest.go):=20comment=20out=20unused=20code?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/docs_test.go b/docs_test.go index 48e0fb412b..1cdbf901c8 100644 --- a/docs_test.go +++ b/docs_test.go @@ -4,7 +4,6 @@ package cli import ( - "bytes" "errors" "io/fs" "os" @@ -72,7 +71,7 @@ Some description Some other text`) - expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{13}, -1) // ignore windows line endings + // expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{13}, -1) // ignore windows line endings expect(t, string(content), string(expected)) // content matches }) @@ -110,7 +109,7 @@ lorem+ipsum Some other text`) - expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{13}, -1) // ignore windows line endings + // expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{13}, -1) // ignore windows line endings t.Log(content) t.Log(expected) From 1865722135f89bff70f3a7828b651d79f1391370 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:22:36 +0400 Subject: [PATCH 19/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20nor?= =?UTF-8?q?malize=20newlines=20in=20file=20content=20before=20comparing=20?= =?UTF-8?q?=E2=9C=A8=20feat(docs=5Ftest.go):=20add=20normalizeNewlines=20f?= =?UTF-8?q?unction=20to=20normalize=20windows=20and=20mac=20newlines=20int?= =?UTF-8?q?o=20unix=20newlines?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/docs_test.go b/docs_test.go index 1cdbf901c8..9136a7d181 100644 --- a/docs_test.go +++ b/docs_test.go @@ -4,6 +4,7 @@ package cli import ( + "bytes" "errors" "io/fs" "os" @@ -38,6 +39,15 @@ func TestToTabularToFileBetweenTags(t *testing.T) { expectedDocs, fErr := os.ReadFile("testdata/expected-tabular-markdown-full.md") expect(t, fErr, nil) // read without error + // normalizes \r\n (windows) and \r (mac) into \n (unix) + var normalizeNewlines = func(d []byte) []byte { + // replace CR LF \r\n (windows) with LF \n (unix) + d = bytes.Replace(d, []byte{13, 10}, []byte{10}, -1) + // replace CF \r (mac) with LF \n (unix) + d = bytes.Replace(d, []byte{13}, []byte{10}, -1) + return d + } + t.Run("default tags", func(t *testing.T) { tmpFile, err := os.CreateTemp("", "") expect(t, err, nil) // created without error @@ -60,7 +70,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - expected := []byte(`# App readme file + expected := normalizeNewlines([]byte(`# App readme file Some description @@ -69,9 +79,7 @@ Some description ` + string(expectedDocs) + ` -Some other text`) - - // expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{13}, -1) // ignore windows line endings +Some other text`)) expect(t, string(content), string(expected)) // content matches }) @@ -98,7 +106,7 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) - expected := []byte(`# App readme file + expected := normalizeNewlines([]byte(`# App readme file Some description @@ -107,9 +115,7 @@ foo_BAR|baz ` + string(expectedDocs) + ` lorem+ipsum -Some other text`) - - // expected = bytes.Replace(expected, []byte{10, 13, 10}, []byte{13}, -1) // ignore windows line endings +Some other text`)) t.Log(content) t.Log(expected) From 33ea5a2e68c6de4765bfb2b4818794b3d124cc81 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:23:50 +0400 Subject: [PATCH 20/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20ref?= =?UTF-8?q?actor=20normalizeNewlines=20function=20to=20remove=20duplicated?= =?UTF-8?q?=20code=20and=20improve=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/docs_test.go b/docs_test.go index 9136a7d181..b24e56cbb7 100644 --- a/docs_test.go +++ b/docs_test.go @@ -41,10 +41,9 @@ func TestToTabularToFileBetweenTags(t *testing.T) { // normalizes \r\n (windows) and \r (mac) into \n (unix) var normalizeNewlines = func(d []byte) []byte { - // replace CR LF \r\n (windows) with LF \n (unix) - d = bytes.Replace(d, []byte{13, 10}, []byte{10}, -1) - // replace CF \r (mac) with LF \n (unix) - d = bytes.Replace(d, []byte{13}, []byte{10}, -1) + d = bytes.Replace(d, []byte{13, 10}, []byte{10}, -1) // replace CR LF \r\n (windows) with LF \n (unix) + d = bytes.Replace(d, []byte{13}, []byte{10}, -1) // replace CF \r (mac) with LF \n (unix) + return d } @@ -70,6 +69,8 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) + content = normalizeNewlines(content) + expected := normalizeNewlines([]byte(`# App readme file Some description @@ -106,6 +107,8 @@ Some other text`) content, err := os.ReadFile(tmpFile.Name()) // read the file content expect(t, err, nil) + content = normalizeNewlines(content) + expected := normalizeNewlines([]byte(`# App readme file Some description @@ -117,9 +120,6 @@ lorem+ipsum Some other text`)) - t.Log(content) - t.Log(expected) - expect(t, string(content), string(expected)) // content matches }) From e2824a8430d1a6a4fef85ecb723771ddd53ec34b Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:25:46 +0400 Subject: [PATCH 21/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20add?= =?UTF-8?q?=20comment=20to=20clarify=20the=20purpose=20of=20normalizing=20?= =?UTF-8?q?newlines=20in=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs_test.go b/docs_test.go index b24e56cbb7..13533d7bf2 100644 --- a/docs_test.go +++ b/docs_test.go @@ -39,7 +39,7 @@ func TestToTabularToFileBetweenTags(t *testing.T) { expectedDocs, fErr := os.ReadFile("testdata/expected-tabular-markdown-full.md") expect(t, fErr, nil) // read without error - // normalizes \r\n (windows) and \r (mac) into \n (unix) + // normalizes \r\n (windows) and \r (mac) into \n (unix) (required for tests to pass on windows) var normalizeNewlines = func(d []byte) []byte { d = bytes.Replace(d, []byte{13, 10}, []byte{10}, -1) // replace CR LF \r\n (windows) with LF \n (unix) d = bytes.Replace(d, []byte{13}, []byte{10}, -1) // replace CF \r (mac) with LF \n (unix) From aa63bac3da4c8dcf78254d1f70563584edcb09f5 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:28:25 +0400 Subject: [PATCH 22/28] =?UTF-8?q?=F0=9F=94=A7=20chore(docs=5Ftest.go):=20u?= =?UTF-8?q?pdate=20comment=20to=20clarify=20the=20purpose=20of=20the=20fun?= =?UTF-8?q?ction?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs_test.go b/docs_test.go index 13533d7bf2..b24e56cbb7 100644 --- a/docs_test.go +++ b/docs_test.go @@ -39,7 +39,7 @@ func TestToTabularToFileBetweenTags(t *testing.T) { expectedDocs, fErr := os.ReadFile("testdata/expected-tabular-markdown-full.md") expect(t, fErr, nil) // read without error - // normalizes \r\n (windows) and \r (mac) into \n (unix) (required for tests to pass on windows) + // normalizes \r\n (windows) and \r (mac) into \n (unix) var normalizeNewlines = func(d []byte) []byte { d = bytes.Replace(d, []byte{13, 10}, []byte{10}, -1) // replace CR LF \r\n (windows) with LF \n (unix) d = bytes.Replace(d, []byte{13}, []byte{10}, -1) // replace CF \r (mac) with LF \n (unix) From d36393b2a514e6358a9ee700520aa23300c9e058 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Fri, 14 Apr 2023 19:31:21 +0400 Subject: [PATCH 23/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20add?= =?UTF-8?q?=20comment=20to=20clarify=20the=20purpose=20of=20normalizing=20?= =?UTF-8?q?newlines=20in=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs_test.go b/docs_test.go index b24e56cbb7..13533d7bf2 100644 --- a/docs_test.go +++ b/docs_test.go @@ -39,7 +39,7 @@ func TestToTabularToFileBetweenTags(t *testing.T) { expectedDocs, fErr := os.ReadFile("testdata/expected-tabular-markdown-full.md") expect(t, fErr, nil) // read without error - // normalizes \r\n (windows) and \r (mac) into \n (unix) + // normalizes \r\n (windows) and \r (mac) into \n (unix) (required for tests to pass on windows) var normalizeNewlines = func(d []byte) []byte { d = bytes.Replace(d, []byte{13, 10}, []byte{10}, -1) // replace CR LF \r\n (windows) with LF \n (unix) d = bytes.Replace(d, []byte{13}, []byte{10}, -1) // replace CF \r (mac) with LF \n (unix) From 05cf3862adae33760cbe3ef7a9551abeacbc07f8 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Mon, 1 May 2023 10:08:56 +0400 Subject: [PATCH 24/28] =?UTF-8?q?=F0=9F=A7=AA=20test(docs=5Ftest.go):=20ad?= =?UTF-8?q?d=20test=20for=20ToTabularMarkdown=20with=20custom=20app=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 12 ++ ...pected-tabular-markdown-custom-app-path.md | 117 ++++++++++++++++++ 2 files changed, 129 insertions(+) create mode 100644 testdata/expected-tabular-markdown-custom-app-path.md diff --git a/docs_test.go b/docs_test.go index 13533d7bf2..e85d5344f8 100644 --- a/docs_test.go +++ b/docs_test.go @@ -35,6 +35,18 @@ func TestToTabularMarkdownFull(t *testing.T) { expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res) } +func TestToTabularMarkdownWithCustomAppPath(t *testing.T) { + // Given + app := testApp() + + // When + res, err := app.ToTabularMarkdown("/usr/local/bin") + + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-tabular-markdown-custom-app-path.md", res) +} + func TestToTabularToFileBetweenTags(t *testing.T) { expectedDocs, fErr := os.ReadFile("testdata/expected-tabular-markdown-full.md") expect(t, fErr, nil) // read without error diff --git a/testdata/expected-tabular-markdown-custom-app-path.md b/testdata/expected-tabular-markdown-custom-app-path.md new file mode 100644 index 0000000000..5aad2dfb98 --- /dev/null +++ b/testdata/expected-tabular-markdown-custom-app-path.md @@ -0,0 +1,117 @@ +## CLI interface - greet + +Description of the application. + +Some app. + +> app [first_arg] [second_arg] + +Usage: + +```bash +$ /usr/local/bin [GLOBAL FLAGS] [COMMAND] [COMMAND FLAGS] [ARGUMENTS...] +``` + +Global flags: + +| Name | Description | Default value | Environment variables | +|-----------------------------|--------------------|:-------------:|:-----------------------:| +| `--socket="…"` (`-s`) | some 'usage' text | `value` | *none* | +| `--flag="…"` (`--fl`, `-f`) | | | *none* | +| `--another-flag` (`-b`) | another usage text | `false` | `EXAMPLE_VARIABLE_NAME` | + +### `config` command (aliases: `c`) + +another usage test. + +Usage: + +```bash +$ /usr/local/bin [GLOBAL FLAGS] config [COMMAND FLAGS] [ARGUMENTS...] +``` + +The following flags are supported: + +| Name | Description | Default value | Environment variables | +|-----------------------------|--------------------|:-------------:|:---------------------:| +| `--flag="…"` (`--fl`, `-f`) | | | *none* | +| `--another-flag` (`-b`) | another usage text | `false` | *none* | + +### `config sub-config` subcommand (aliases: `s`, `ss`) + +another usage test. + +Usage: + +```bash +$ /usr/local/bin [GLOBAL FLAGS] config sub-config [COMMAND FLAGS] [ARGUMENTS...] +``` + +The following flags are supported: + +| Name | Description | Default value | Environment variables | +|-------------------------------------|-----------------|:-------------:|:---------------------:| +| `--sub-flag="…"` (`--sub-fl`, `-s`) | | | *none* | +| `--sub-command-flag` (`-s`) | some usage text | `false` | *none* | + +### `info` command (aliases: `i`, `in`) + +retrieve generic information. + +Usage: + +```bash +$ /usr/local/bin [GLOBAL FLAGS] info [ARGUMENTS...] +``` + +### `some-command` command + +Usage: + +```bash +$ /usr/local/bin [GLOBAL FLAGS] some-command [ARGUMENTS...] +``` + +### `usage` command (aliases: `u`) + +standard usage text. + +> Usage for the usage text +> - formatted: Based on the specified ConfigMap and summon secrets.yml +> - list: Inspect the environment for a specific process running on a Pod +> - for_effect: Compare 'namespace' environment with 'local' +> ``` +> func() { ... } +> ``` +> Should be a part of the same code block + +Usage: + +```bash +$ /usr/local/bin [GLOBAL FLAGS] usage [COMMAND FLAGS] [ARGUMENTS...] +``` + +The following flags are supported: + +| Name | Description | Default value | Environment variables | +|-----------------------------|--------------------|:-------------:|:---------------------:| +| `--flag="…"` (`--fl`, `-f`) | | | *none* | +| `--another-flag` (`-b`) | another usage text | `false` | *none* | + +### `usage sub-usage` subcommand (aliases: `su`) + +standard usage text. + +> Single line of UsageText + +Usage: + +```bash +$ /usr/local/bin [GLOBAL FLAGS] usage sub-usage [COMMAND FLAGS] [ARGUMENTS...] +``` + +The following flags are supported: + +| Name | Description | Default value | Environment variables | +|-----------------------------|-----------------|:-------------:|:---------------------:| +| `--sub-command-flag` (`-s`) | some usage text | `false` | *none* | From 949f45e950f427d93f41286f38707e352480699e Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Mon, 1 May 2023 10:10:17 +0400 Subject: [PATCH 25/28] =?UTF-8?q?=F0=9F=A7=AA=20test(docs=5Ftest.go):=20ad?= =?UTF-8?q?d=20test=20case=20for=20ToTabularMarkdown=20with=20empty=20app?= =?UTF-8?q?=20path?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs_test.go b/docs_test.go index e85d5344f8..edf91b64ad 100644 --- a/docs_test.go +++ b/docs_test.go @@ -35,6 +35,18 @@ func TestToTabularMarkdownFull(t *testing.T) { expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res) } +func TestToTabularMarkdownWithEmptyAppPath(t *testing.T) { + // Given + app := testApp() + + // When + res, err := app.ToTabularMarkdown("") + + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res) +} + func TestToTabularMarkdownWithCustomAppPath(t *testing.T) { // Given app := testApp() From d945e8d754cbecdc07c4a496a6911b0f41522f63 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Mon, 1 May 2023 10:26:38 +0400 Subject: [PATCH 26/28] =?UTF-8?q?=F0=9F=9A=A8=20test(docs=5Ftest.go):=20ad?= =?UTF-8?q?d=20test=20case=20for=20ToTabularMarkdownFailed=20function=20wi?= =?UTF-8?q?th=20invalid=20template.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/docs_test.go b/docs_test.go index edf91b64ad..6bb982c1ec 100644 --- a/docs_test.go +++ b/docs_test.go @@ -59,6 +59,21 @@ func TestToTabularMarkdownWithCustomAppPath(t *testing.T) { expectFileContent(t, "testdata/expected-tabular-markdown-custom-app-path.md", res) } +func TestToTabularMarkdownFailed(t *testing.T) { + // Given + app := testApp() + MarkdownTabularDocTemplate = "{{ .Foo }}" // invalid template + + // When + res, err := app.ToTabularMarkdown("") + + // Then + if err == nil { + t.Fatal("Expected error but got nil") + } + expect(t, res, "") +} + func TestToTabularToFileBetweenTags(t *testing.T) { expectedDocs, fErr := os.ReadFile("testdata/expected-tabular-markdown-full.md") expect(t, fErr, nil) // read without error From d8caf8b6054565043aa06fffe534d8e522e7ced2 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Mon, 1 May 2023 10:28:08 +0400 Subject: [PATCH 27/28] ArgsUsage in next line --- docs.go | 36 +++++++++++++++++++----------------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/docs.go b/docs.go index a99fe1bac9..af15575c97 100644 --- a/docs.go +++ b/docs.go @@ -280,26 +280,28 @@ func prepareUsage(command *Command, usageText string) string { type ( cliTabularAppTemplate struct { - AppPath string - Name string - Usage, ArgsUsage string - UsageText []string - Description string - GlobalFlags []cliTabularFlagTemplate - Commands []cliTabularCommandTemplate + AppPath string + Name string + Usage string + ArgsUsage string + UsageText []string + Description string + GlobalFlags []cliTabularFlagTemplate + Commands []cliTabularCommandTemplate } cliTabularCommandTemplate struct { - AppPath string - Name string - Aliases []string - Usage, ArgsUsage string - UsageText []string - Description string - Category string - Flags []cliTabularFlagTemplate - SubCommands []cliTabularCommandTemplate - Level uint + AppPath string + Name string + Aliases []string + Usage string + ArgsUsage string + UsageText []string + Description string + Category string + Flags []cliTabularFlagTemplate + SubCommands []cliTabularCommandTemplate + Level uint } cliTabularFlagTemplate struct { From 9640f324bb8df837b26019992c78e0319eca4c14 Mon Sep 17 00:00:00 2001 From: Paramtamtam <7326800+tarampampam@users.noreply.github.com> Date: Mon, 1 May 2023 10:39:37 +0400 Subject: [PATCH 28/28] =?UTF-8?q?=F0=9F=90=9B=20fix(docs=5Ftest.go):=20ren?= =?UTF-8?q?ame=20TestToTabularMarkdownFull=20to=20TestToTabularMarkdown=20?= =?UTF-8?q?and=20remove=20TestToTabularMarkdownWithEmptyAppPath=20?= =?UTF-8?q?=E2=9C=A8=20feat(docs=5Ftest.go):=20add=20subtests=20to=20TestT?= =?UTF-8?q?oTabularMarkdown=20to=20test=20different=20scenarios=20?= =?UTF-8?q?=F0=9F=94=A5=20chore(docs=5Ftest.go):=20remove=20unused=20varia?= =?UTF-8?q?ble=20and=20restore=20MarkdownTabularDocTemplate=20after=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs_test.go | 55 ++++++++++++++++++++++++++-------------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/docs_test.go b/docs_test.go index 6bb982c1ec..f6ac4bead3 100644 --- a/docs_test.go +++ b/docs_test.go @@ -23,46 +23,45 @@ func TestToMarkdownFull(t *testing.T) { expectFileContent(t, "testdata/expected-doc-full.md", res) } -func TestToTabularMarkdownFull(t *testing.T) { - // Given +func TestToTabularMarkdown(t *testing.T) { app := testApp() - // When - res, err := app.ToTabularMarkdown("app") - - // Then - expect(t, err, nil) - expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res) -} - -func TestToTabularMarkdownWithEmptyAppPath(t *testing.T) { - // Given - app := testApp() + t.Run("full", func(t *testing.T) { + // When + res, err := app.ToTabularMarkdown("app") - // When - res, err := app.ToTabularMarkdown("") + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res) + }) - // Then - expect(t, err, nil) - expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res) -} + t.Run("with empty path", func(t *testing.T) { + // When + res, err := app.ToTabularMarkdown("") -func TestToTabularMarkdownWithCustomAppPath(t *testing.T) { - // Given - app := testApp() + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-tabular-markdown-full.md", res) + }) - // When - res, err := app.ToTabularMarkdown("/usr/local/bin") + t.Run("with custom app path", func(t *testing.T) { + // When + res, err := app.ToTabularMarkdown("/usr/local/bin") - // Then - expect(t, err, nil) - expectFileContent(t, "testdata/expected-tabular-markdown-custom-app-path.md", res) + // Then + expect(t, err, nil) + expectFileContent(t, "testdata/expected-tabular-markdown-custom-app-path.md", res) + }) } func TestToTabularMarkdownFailed(t *testing.T) { + tpl := MarkdownTabularDocTemplate + defer func() { MarkdownTabularDocTemplate = tpl }() // restore + + MarkdownTabularDocTemplate = "{{ .Foo }}" // invalid template + // Given app := testApp() - MarkdownTabularDocTemplate = "{{ .Foo }}" // invalid template // When res, err := app.ToTabularMarkdown("")