From 7f52074788c37dbed9490d4ffbb7593ceddb049f Mon Sep 17 00:00:00 2001 From: Stefan Haubold Date: Wed, 3 Jun 2026 13:35:24 +0200 Subject: [PATCH 1/3] make sure subcommands are properly aligned Entire-Checkpoint: 942f12e5f6c3 --- cmd/entire/cli/labs.go | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/cmd/entire/cli/labs.go b/cmd/entire/cli/labs.go index d4f86cd67d..3e22e21078 100644 --- a/cmd/entire/cli/labs.go +++ b/cmd/entire/cli/labs.go @@ -3,6 +3,7 @@ package cli import ( "fmt" "strings" + "unicode/utf8" "github.com/spf13/cobra" ) @@ -94,10 +95,17 @@ Try: } func renderExperimentalCommands(commands []experimentalCommandInfo) string { + width := 0 + for _, info := range commands { + if w := utf8.RuneCountInString(info.Invocation); w > width { + width = w + } + } + var out strings.Builder for _, info := range commands { out.WriteString(" ") - out.WriteString(padRight(info.Invocation, 16)) + out.WriteString(padRight(info.Invocation, width)) out.WriteByte(' ') out.WriteString(info.Summary) out.WriteByte('\n') @@ -106,8 +114,9 @@ func renderExperimentalCommands(commands []experimentalCommandInfo) string { } func padRight(value string, width int) string { - if len(value) >= width { + n := utf8.RuneCountInString(value) + if n >= width { return value } - return value + strings.Repeat(" ", width-len(value)) + return value + strings.Repeat(" ", width-n) } From f872ee4da61ec4db8a13afd6ae84c8e19a3dae39 Mon Sep 17 00:00:00 2001 From: Stefan Haubold Date: Wed, 3 Jun 2026 13:42:57 +0200 Subject: [PATCH 2/3] adds some tests too Entire-Checkpoint: 11c16f0c4d1f --- cmd/entire/cli/labs_test.go | 68 +++++++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) diff --git a/cmd/entire/cli/labs_test.go b/cmd/entire/cli/labs_test.go index eed525401d..48d380a8f2 100644 --- a/cmd/entire/cli/labs_test.go +++ b/cmd/entire/cli/labs_test.go @@ -4,6 +4,7 @@ import ( "bytes" "strings" "testing" + "unicode/utf8" ) func TestLabsCmd_PrintsExperimentalCommandList(t *testing.T) { @@ -97,6 +98,73 @@ func TestRootHelp_ShowsLabsButHidesReview(t *testing.T) { } } +// summaryColumns returns, for each non-empty rendered row, the rune offset at +// which the summary begins (i.e. the column after the padded invocation). +func summaryColumns(t *testing.T, commands []experimentalCommandInfo) []int { + t.Helper() + var cols []int + for _, line := range strings.Split(renderExperimentalCommands(commands), "\n") { + if line == "" { + continue + } + info := indexOfSummary(t, line, commands) + cols = append(cols, info) + } + return cols +} + +// indexOfSummary finds the rune offset of a row's summary text within the line. +func indexOfSummary(t *testing.T, line string, commands []experimentalCommandInfo) int { + t.Helper() + for _, info := range commands { + if idx := strings.Index(line, info.Summary); idx >= 0 { + return utf8.RuneCountInString(line[:idx]) + } + } + t.Fatalf("no known summary found in rendered line %q", line) + return -1 +} + +func TestRenderExperimentalCommands_SummariesAlign(t *testing.T) { + t.Parallel() + + cols := summaryColumns(t, experimentalCommands) + if len(cols) < 2 { + t.Fatalf("expected multiple experimental commands, got %d", len(cols)) + } + for i, col := range cols { + if col != cols[0] { + t.Fatalf("summary column %d (%d) does not match first column (%d); descriptions are misaligned", i, col, cols[0]) + } + } +} + +func TestRenderExperimentalCommands_ColumnWidthAdjustsToLongest(t *testing.T) { + t.Parallel() + + short := []experimentalCommandInfo{ + {Name: "a", Invocation: "entire a", Summary: "first"}, + {Name: "b", Invocation: "entire b", Summary: "second"}, + } + long := []experimentalCommandInfo{ + {Name: "a", Invocation: "entire a", Summary: "first"}, + {Name: "verylongcommand", Invocation: "entire verylongcommand", Summary: "second"}, + } + + shortCol := summaryColumns(t, short)[0] + longCol := summaryColumns(t, long)[0] + + if longCol <= shortCol { + t.Fatalf("column should widen for a longer invocation: short=%d long=%d", shortCol, longCol) + } + // All rows in the long set must still align despite differing invocation lengths. + for i, col := range summaryColumns(t, long) { + if col != longCol { + t.Fatalf("row %d column %d does not match %d", i, col, longCol) + } + } +} + func TestLabsRegistryCommandsExistAtCanonicalPaths(t *testing.T) { t.Parallel() From 8798c52a8077e2f55340298e6f4bc3809d0b09cf Mon Sep 17 00:00:00 2001 From: Stefan Haubold Date: Wed, 3 Jun 2026 13:57:12 +0200 Subject: [PATCH 3/3] added a proper utf8 test tooadded a proper utf8 test tooadded a proper utf8 test tooadded a proper utf8 test tooadded a proper utf8 test tooadded a proper utf8 test tooadded a proper utf8 test tooadded a proper utf8 test tooadded a proper utf8 test too Entire-Checkpoint: dd5b7466cf9c --- cmd/entire/cli/labs_test.go | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/cmd/entire/cli/labs_test.go b/cmd/entire/cli/labs_test.go index 48d380a8f2..7ec74b7f70 100644 --- a/cmd/entire/cli/labs_test.go +++ b/cmd/entire/cli/labs_test.go @@ -165,6 +165,28 @@ func TestRenderExperimentalCommands_ColumnWidthAdjustsToLongest(t *testing.T) { } } +func TestRenderExperimentalCommands_MultiByteInvocationAligns(t *testing.T) { + t.Parallel() + + // "entire ▶▶" is 9 runes but 13 bytes (each ▶ is 3 bytes). The longest + // invocation below is 12 runes, so the column width is 12. With byte-based + // padding, len("entire ▶▶") == 13 >= 12 would skip padding and misalign the + // row; rune-based padding correctly adds 3 spaces. + commands := []experimentalCommandInfo{ + {Name: "long", Invocation: "entire aaaaa", Summary: "first"}, + {Name: "multibyte", Invocation: "entire ▶▶", Summary: "second"}, + } + + if got := len("entire ▶▶"); got < 12 { + t.Fatalf("test precondition broken: byte length %d should exceed column width 12", got) + } + + cols := summaryColumns(t, commands) + if cols[0] != cols[1] { + t.Fatalf("multi-byte invocation summary misaligned: %v", cols) + } +} + func TestLabsRegistryCommandsExistAtCanonicalPaths(t *testing.T) { t.Parallel()