diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-26 22:11:15 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-26 22:11:15 +0200 |
| commit | c8108deb77f1872e6a55925d5abf9c0ae12813c6 (patch) | |
| tree | e6c0ae91926f0cf68c7dfb89ce46ab1d8facf933 | |
| parent | c9d96d4c1918881f771165e97e71de49917d4181 (diff) | |
ask: align list header columns for task da6d3d36
| -rw-r--r-- | internal/askcli/formatter.go | 92 | ||||
| -rw-r--r-- | internal/askcli/formatter_test.go | 44 |
2 files changed, 125 insertions, 11 deletions
diff --git a/internal/askcli/formatter.go b/internal/askcli/formatter.go index 886e819..828588b 100644 --- a/internal/askcli/formatter.go +++ b/internal/askcli/formatter.go @@ -7,23 +7,93 @@ import ( ) func FormatTaskList(tasks []TaskExport) string { + widths := taskListWidthsFor(tasks) var b strings.Builder - io.WriteString(&b, "Urgency | Priority | UUID | Status | Tags | Description\n") - io.WriteString(&b, strings.Repeat("-", 120)+"\n") + writeTaskListHeader(&b, widths) + writeTaskListSeparator(&b, widths) for _, t := range tasks { - tags := strings.Join(t.Tags, ",") - if tags == "" { - tags = "-" - } - desc := t.Description - if len(desc) > 50 { - desc = desc[:47] + "..." - } - fmt.Fprintf(&b, "%.1f | %s | %s | %s | %s | %s\n", t.Urgency, t.Priority, t.UUID, t.Status, tags, desc) + writeTaskListRow(&b, widths, t) } return b.String() } +type taskListWidths struct { + Urgency int + Priority int + UUID int + Status int + Tags int + Description int +} + +func taskListWidthsFor(tasks []TaskExport) taskListWidths { + widths := taskListWidths{ + Urgency: len("Urgency"), + Priority: len("Priority"), + UUID: len("UUID"), + Status: len("Status"), + Tags: len("Tags"), + Description: len("Description"), + } + for _, t := range tasks { + widths.Urgency = maxInt(widths.Urgency, len(fmt.Sprintf("%.1f", t.Urgency))) + widths.Priority = maxInt(widths.Priority, len(t.Priority)) + widths.UUID = maxInt(widths.UUID, len(t.UUID)) + widths.Status = maxInt(widths.Status, len(t.Status)) + widths.Tags = maxInt(widths.Tags, len(formatTaskTags(t.Tags))) + widths.Description = maxInt(widths.Description, len(formatTaskDescription(t.Description))) + } + return widths +} + +func writeTaskListHeader(b *strings.Builder, widths taskListWidths) { + fmt.Fprintf(b, "%-*s | %-*s | %-*s | %-*s | %-*s | %-*s\n", + widths.Urgency, "Urgency", + widths.Priority, "Priority", + widths.UUID, "UUID", + widths.Status, "Status", + widths.Tags, "Tags", + widths.Description, "Description", + ) +} + +func writeTaskListSeparator(b *strings.Builder, widths taskListWidths) { + total := widths.Urgency + widths.Priority + widths.UUID + widths.Status + widths.Tags + widths.Description + 15 + io.WriteString(b, strings.Repeat("-", total)+"\n") +} + +func writeTaskListRow(b *strings.Builder, widths taskListWidths, t TaskExport) { + fmt.Fprintf(b, "%-*s | %-*s | %-*s | %-*s | %-*s | %-*s\n", + widths.Urgency, fmt.Sprintf("%.1f", t.Urgency), + widths.Priority, t.Priority, + widths.UUID, t.UUID, + widths.Status, t.Status, + widths.Tags, formatTaskTags(t.Tags), + widths.Description, formatTaskDescription(t.Description), + ) +} + +func formatTaskTags(tags []string) string { + if len(tags) == 0 { + return "-" + } + return strings.Join(tags, ",") +} + +func formatTaskDescription(desc string) string { + if len(desc) > 50 { + return desc[:47] + "..." + } + return desc +} + +func maxInt(a, b int) int { + if a > b { + return a + } + return b +} + func FormatTaskInfo(t TaskExport) string { var b strings.Builder fmt.Fprintf(&b, "UUID: %s\n", t.UUID) diff --git a/internal/askcli/formatter_test.go b/internal/askcli/formatter_test.go index 50d6ae3..adb1e6e 100644 --- a/internal/askcli/formatter_test.go +++ b/internal/askcli/formatter_test.go @@ -1,6 +1,7 @@ package askcli import ( + "fmt" "strings" "testing" ) @@ -27,6 +28,49 @@ func TestFormatTaskList(t *testing.T) { } } +func TestFormatTaskList_AlignsHeaderAndSeparator(t *testing.T) { + tasks := []TaskExport{ + { + UUID: "uuid-short", + Description: "Short task", + Status: "pending", + Priority: "H", + Tags: []string{"cli"}, + Urgency: 1.0, + }, + { + UUID: "uuid-with-a-longer-value", + Description: strings.Repeat("x", 60), + Status: "completed", + Priority: "M", + Tags: []string{"agent", "cli"}, + Urgency: 12.3, + }, + } + + output := FormatTaskList(tasks) + lines := strings.Split(strings.TrimSuffix(output, "\n"), "\n") + if len(lines) != 4 { + t.Fatalf("FormatTaskList produced %d lines, want 4: %q", len(lines), output) + } + + widths := taskListWidthsFor(tasks) + wantHeader := fmt.Sprintf("%-*s | %-*s | %-*s | %-*s | %-*s | %-*s", + widths.Urgency, "Urgency", + widths.Priority, "Priority", + widths.UUID, "UUID", + widths.Status, "Status", + widths.Tags, "Tags", + widths.Description, "Description", + ) + if lines[0] != wantHeader { + t.Fatalf("header = %q, want %q", lines[0], wantHeader) + } + if len(lines[1]) != len(wantHeader) { + t.Fatalf("separator length = %d, want %d", len(lines[1]), len(wantHeader)) + } +} + func TestFormatTaskInfo(t *testing.T) { task := TaskExport{ UUID: "test-uuid", |
