Skip to content

Commit c74afa7

Browse files
committed
markdown: format generated tables to have equal-width columns
This makes the tables more readable when reading the source code. Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent ca8c2e2 commit c74afa7

File tree

4 files changed

+83
-50
lines changed

4 files changed

+83
-50
lines changed

clidocstool_md.go

Lines changed: 49 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222
"path/filepath"
2323
"regexp"
2424
"strings"
25+
"text/tabwriter"
2526
"text/template"
2627

2728
"github.com/docker/cli-docs-tool/annotation"
@@ -30,7 +31,8 @@ import (
3031
)
3132

3233
var (
33-
nlRegexp = regexp.MustCompile(`\r?\n`)
34+
nlRegexp = regexp.MustCompile(`\r?\n`)
35+
adjustSep = regexp.MustCompile(`\|:---(\s+)`)
3436
)
3537

3638
// GenMarkdownTree will generate a markdown page for this command and all
@@ -149,6 +151,42 @@ func mdMakeLink(txt, link string, f *pflag.Flag, isAnchor bool) string {
149151
return "[" + txt + "](" + link + ")"
150152
}
151153

154+
type mdTable struct {
155+
out *strings.Builder
156+
tabWriter *tabwriter.Writer
157+
}
158+
159+
func newMdTable(headers ...string) *mdTable {
160+
w := &strings.Builder{}
161+
t := &mdTable{
162+
out: w,
163+
// Using tabwriter.Debug, which uses "|" as separator instead of tabs,
164+
// which is what we want. It's a bit of a hack, but does the job :)
165+
tabWriter: tabwriter.NewWriter(w, 5, 5, 1, ' ', tabwriter.Debug),
166+
}
167+
t.addHeader(headers...)
168+
return t
169+
}
170+
171+
func (t *mdTable) addHeader(cols ...string) {
172+
t.AddRow(cols...)
173+
_, _ = t.tabWriter.Write([]byte("|" + strings.Repeat(":---\t", len(cols)) + "\n"))
174+
}
175+
176+
func (t *mdTable) AddRow(cols ...string) {
177+
for i, _ := range cols {
178+
cols[i] = mdEscapePipe(cols[i])
179+
}
180+
_, _ = t.tabWriter.Write([]byte("| " + strings.Join(cols, "\t ") + "\t\n"))
181+
}
182+
183+
func (t *mdTable) String() string {
184+
_ = t.tabWriter.Flush()
185+
return adjustSep.ReplaceAllStringFunc(t.out.String()+"\n", func(in string) string {
186+
return strings.ReplaceAll(in, " ", "-")
187+
})
188+
}
189+
152190
func mdCmdOutput(cmd *cobra.Command, old string) (string, error) {
153191
b := &strings.Builder{}
154192

@@ -168,35 +206,30 @@ func mdCmdOutput(cmd *cobra.Command, old string) (string, error) {
168206

169207
if len(cmd.Commands()) != 0 {
170208
b.WriteString("### Subcommands\n\n")
171-
b.WriteString("| Name | Description |\n")
172-
b.WriteString("| --- | --- |\n")
209+
table := newMdTable("Name", "Description")
173210
for _, c := range cmd.Commands() {
174-
b.WriteString(fmt.Sprintf("| [`%s`](%s) | %s |\n", c.Name(), mdFilename(c), c.Short))
211+
table.AddRow(fmt.Sprintf("[`%s`](%s)", c.Name(), mdFilename(c)), c.Short)
175212
}
176-
b.WriteString("\n\n")
213+
b.WriteString(table.String() + "\n")
177214
}
178215

179216
// add inherited flags before checking for flags availability
180217
cmd.Flags().AddFlagSet(cmd.InheritedFlags())
181218

182219
if cmd.Flags().HasAvailableFlags() {
183220
b.WriteString("### Options\n\n")
184-
b.WriteString("| Name | Type | Default | Description |\n")
185-
b.WriteString("| --- | --- | --- | --- |\n")
186-
221+
table := newMdTable("Name", "Type", "Default", "Description")
187222
cmd.Flags().VisitAll(func(f *pflag.Flag) {
188223
if f.Hidden {
189224
return
190225
}
191226
isLink := strings.Contains(old, "<a name=\""+f.Name+"\"></a>")
192-
b.WriteString("| ")
227+
var name string
193228
if f.Shorthand != "" {
194-
name := "`-" + f.Shorthand + "`"
195-
name = mdMakeLink(name, f.Name, f, isLink)
196-
b.WriteString(name + ", ")
229+
name = mdMakeLink("`-"+f.Shorthand+"`", f.Name, f, isLink)
230+
name += ", "
197231
}
198-
name := "`--" + f.Name + "`"
199-
name = mdMakeLink(name, f.Name, f, isLink)
232+
name += mdMakeLink("`--"+f.Name+"`", f.Name, f, isLink)
200233

201234
var ftype string
202235
if f.Value.Type() != "bool" {
@@ -221,9 +254,9 @@ func mdCmdOutput(cmd *cobra.Command, old string) (string, error) {
221254
} else if cd, ok := cmd.Annotations[annotation.CodeDelimiter]; ok {
222255
usage = strings.ReplaceAll(usage, cd, "`")
223256
}
224-
b.WriteString(fmt.Sprintf("%s | %s | %s | %s |\n", mdEscapePipe(name), mdEscapePipe(ftype), mdEscapePipe(defval), mdReplaceNewline(mdEscapePipe(usage))))
257+
table.AddRow(name, ftype, defval, mdReplaceNewline(usage))
225258
})
226-
b.WriteString("\n")
259+
b.WriteString(table.String())
227260
}
228261

229262
return b.String(), nil

fixtures/buildx.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ Extended build capabilities with BuildKit
55

66
### Subcommands
77

8-
| Name | Description |
9-
| --- | --- |
10-
| [`build`](buildx_build.md) | Start a build |
11-
| [`stop`](buildx_stop.md) | Stop builder instance |
8+
| Name | Description |
9+
|:---------------------------|:----------------------|
10+
| [`build`](buildx_build.md) | Start a build |
11+
| [`stop`](buildx_stop.md) | Stop builder instance |
1212

1313

1414
### Options
1515

16-
| Name | Type | Default | Description |
17-
| --- | --- | --- | --- |
18-
| `--builder` | `string` | | Override the configured builder instance |
16+
| Name | Type | Default | Description |
17+
|:------------|:---------|:--------|:-----------------------------------------|
18+
| `--builder` | `string` | | Override the configured builder instance |
1919

2020

2121
<!---MARKER_GEN_END-->

fixtures/buildx_build.md

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,30 +9,30 @@ Start a build
99

1010
### Options
1111

12-
| Name | Type | Default | Description |
13-
| --- | --- | --- | --- |
14-
| [`--add-host`](https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host) | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
15-
| `--allow` | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
16-
| [`--build-arg`](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg) | `stringArray` | | Set build-time variables |
17-
| `--builder` | `string` | | Override the configured builder instance |
18-
| `--cache-from` | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
19-
| `--cache-to` | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
20-
| [`--cgroup-parent`](https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent) | `string` | | Optional parent cgroup for the container |
21-
| [`-f`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f), [`--file`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
22-
| `--iidfile` | `string` | | Write the image ID to the file |
23-
| `--label` | `stringArray` | | Set metadata for an image |
24-
| `--load` | | | Shorthand for `--output=type=docker` |
25-
| `--network` | `string` | `default` | Set the networking mode for the `RUN` instructions during build |
26-
| `-o`, `--output` | `stringArray` | | Output destination (format: `type=local,dest=path`) |
27-
| `--platform` | `stringArray` | local | Set target platform for build |
28-
| `--push` | | | Shorthand for `--output=type=registry` |
29-
| `-q`, `--quiet` | | | Suppress the build output and print image ID on success |
30-
| `--secret` | `stringArray` | | Secret file to expose to the build (format: `id=mysecret,src=/local/secret`) |
31-
| `--shm-size` | `string` | | Size of `/dev/shm` |
32-
| `--ssh` | `stringArray` | | SSH agent socket or keys to expose to the build<br>format: `default\|<id>[=<socket>\|<key>[,<key>]]` |
33-
| [`-t`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t), [`--tag`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t) | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
34-
| [`--target`](https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target) | `string` | | Set the target build stage to build. |
35-
| `--ulimit` | `string` | | Ulimit options |
12+
| Name | Type | Default | Description |
13+
|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:--------------|:----------|:-----------------------------------------------------------------------------------------------------|
14+
| [`--add-host`](https://docs.docker.com/engine/reference/commandline/build/#add-entries-to-container-hosts-file---add-host) | `stringSlice` | | Add a custom host-to-IP mapping (format: `host:ip`) |
15+
| `--allow` | `stringSlice` | | Allow extra privileged entitlement (e.g., `network.host`, `security.insecure`) |
16+
| [`--build-arg`](https://docs.docker.com/engine/reference/commandline/build/#set-build-time-variables---build-arg) | `stringArray` | | Set build-time variables |
17+
| `--builder` | `string` | | Override the configured builder instance |
18+
| `--cache-from` | `stringArray` | | External cache sources (e.g., `user/app:cache`, `type=local,src=path/to/dir`) |
19+
| `--cache-to` | `stringArray` | | Cache export destinations (e.g., `user/app:cache`, `type=local,dest=path/to/dir`) |
20+
| [`--cgroup-parent`](https://docs.docker.com/engine/reference/commandline/build/#use-a-custom-parent-cgroup---cgroup-parent) | `string` | | Optional parent cgroup for the container |
21+
| [`-f`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f), [`--file`](https://docs.docker.com/engine/reference/commandline/build/#specify-a-dockerfile--f) | `string` | | Name of the Dockerfile (default: `PATH/Dockerfile`) |
22+
| `--iidfile` | `string` | | Write the image ID to the file |
23+
| `--label` | `stringArray` | | Set metadata for an image |
24+
| `--load` | | | Shorthand for `--output=type=docker` |
25+
| `--network` | `string` | `default` | Set the networking mode for the `RUN` instructions during build |
26+
| `-o`, `--output` | `stringArray` | | Output destination (format: `type=local,dest=path`) |
27+
| `--platform` | `stringArray` | local | Set target platform for build |
28+
| `--push` | | | Shorthand for `--output=type=registry` |
29+
| `-q`, `--quiet` | | | Suppress the build output and print image ID on success |
30+
| `--secret` | `stringArray` | | Secret file to expose to the build (format: `id=mysecret,src=/local/secret`) |
31+
| `--shm-size` | `string` | | Size of `/dev/shm` |
32+
| `--ssh` | `stringArray` | | SSH agent socket or keys to expose to the build<br>format: `default\|<id>[=<socket>\|<key>[,<key>]]` |
33+
| [`-t`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t), [`--tag`](https://docs.docker.com/engine/reference/commandline/build/#tag-an-image--t) | `stringArray` | | Name and optionally a tag (format: `name:tag`) |
34+
| [`--target`](https://docs.docker.com/engine/reference/commandline/build/#specifying-target-build-stage---target) | `string` | | Set the target build stage to build. |
35+
| `--ulimit` | `string` | | Ulimit options |
3636

3737

3838
<!---MARKER_GEN_END-->

fixtures/buildx_stop.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ Stop builder instance
55

66
### Options
77

8-
| Name | Type | Default | Description |
9-
| --- | --- | --- | --- |
10-
| `--builder` | `string` | | Override the configured builder instance |
8+
| Name | Type | Default | Description |
9+
|:------------|:---------|:--------|:-----------------------------------------|
10+
| `--builder` | `string` | | Override the configured builder instance |
1111

1212

1313
<!---MARKER_GEN_END-->

0 commit comments

Comments
 (0)