Skip to content

Commit 6a64a15

Browse files
refactor: deduplicate column order helpers and fix review findings
- Extract resolveColumnOrder/serializeColumnOrder/lookupDisplayName as shared generic functions; task and queue variants now delegate to them - Restore allHeaders to static array instead of per-render map allocation - Split down/up into separate cases instead of combined case with re-check - Use isColumn() guard instead of fragile lastColumnIdx = len - 2 - Move tasks-view no-op guard before the toggle to avoid toggle-then-revert - Clarify help footer: up/dn navigate vs j/k reorder - Pre-allocate slices in visibleTaskColumns and syncColumnOrderFromOptions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f5e281a commit 6a64a15

File tree

3 files changed

+54
-77
lines changed

3 files changed

+54
-77
lines changed

cmd/roborev/tui/handlers_queue.go

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -192,28 +192,29 @@ func (m model) handleColumnOptionsKey() (tea.Model, tea.Cmd) {
192192
}
193193

194194
func (m model) handleColumnOptionsInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
195-
lastColumnIdx := len(m.colOptionsList) - 2 // last column item (before borders)
195+
isColumn := func(idx int) bool {
196+
return idx >= 0 && idx < len(m.colOptionsList) && m.colOptionsList[idx].id != colOptionBorders
197+
}
196198

197199
switch msg.String() {
198200
case "esc":
199201
m.currentView = m.colOptionsReturnView
200202
return m, nil
201203
case "ctrl+c":
202204
return m, tea.Quit
203-
case "down", "up":
204-
if msg.String() == "down" {
205-
if m.colOptionsIdx < len(m.colOptionsList)-1 {
206-
m.colOptionsIdx++
207-
}
208-
} else {
209-
if m.colOptionsIdx > 0 {
210-
m.colOptionsIdx--
211-
}
205+
case "down":
206+
if m.colOptionsIdx < len(m.colOptionsList)-1 {
207+
m.colOptionsIdx++
208+
}
209+
return m, nil
210+
case "up":
211+
if m.colOptionsIdx > 0 {
212+
m.colOptionsIdx--
212213
}
213214
return m, nil
214215
case "j":
215216
// Move current column down in order
216-
if m.colOptionsIdx >= 0 && m.colOptionsIdx < lastColumnIdx {
217+
if isColumn(m.colOptionsIdx) && isColumn(m.colOptionsIdx+1) {
217218
m.colOptionsList[m.colOptionsIdx], m.colOptionsList[m.colOptionsIdx+1] =
218219
m.colOptionsList[m.colOptionsIdx+1], m.colOptionsList[m.colOptionsIdx]
219220
m.colOptionsIdx++
@@ -223,7 +224,7 @@ func (m model) handleColumnOptionsInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
223224
return m, nil
224225
case "k":
225226
// Move current column up in order
226-
if m.colOptionsIdx > 0 && m.colOptionsIdx <= lastColumnIdx {
227+
if isColumn(m.colOptionsIdx) && isColumn(m.colOptionsIdx-1) {
227228
m.colOptionsList[m.colOptionsIdx], m.colOptionsList[m.colOptionsIdx-1] =
228229
m.colOptionsList[m.colOptionsIdx-1], m.colOptionsList[m.colOptionsIdx]
229230
m.colOptionsIdx--
@@ -234,16 +235,14 @@ func (m model) handleColumnOptionsInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
234235
case " ", "enter":
235236
if m.colOptionsIdx >= 0 && m.colOptionsIdx < len(m.colOptionsList) {
236237
opt := &m.colOptionsList[m.colOptionsIdx]
237-
opt.enabled = !opt.enabled
238238
if opt.id == colOptionBorders {
239-
// Borders toggle
239+
opt.enabled = !opt.enabled
240240
m.colBordersOn = opt.enabled
241+
} else if m.colOptionsReturnView == viewTasks {
242+
// Tasks view: no visibility toggle (all columns always shown)
243+
return m, nil
241244
} else {
242-
if m.colOptionsReturnView == viewTasks {
243-
// Tasks view: no visibility toggle (all columns always shown)
244-
opt.enabled = true
245-
return m, nil
246-
}
245+
opt.enabled = !opt.enabled
247246
if opt.enabled {
248247
delete(m.hiddenColumns, opt.id)
249248
} else {
@@ -263,7 +262,7 @@ func (m model) handleColumnOptionsInput(msg tea.KeyMsg) (tea.Model, tea.Cmd) {
263262
// syncColumnOrderFromOptions updates m.columnOrder or m.taskColumnOrder
264263
// from the current colOptionsList (excluding the borders toggle).
265264
func (m *model) syncColumnOrderFromOptions() {
266-
var order []int
265+
order := make([]int, 0, len(m.colOptionsList))
267266
for _, opt := range m.colOptionsList {
268267
if opt.id != colOptionBorders {
269268
order = append(order, opt.id)

cmd/roborev/tui/render_queue.go

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -667,14 +667,19 @@ var columnConfigNames = map[int]string{
667667
colHandled: "handled",
668668
}
669669

670-
// columnDisplayName returns the display name for a column constant.
671-
func columnDisplayName(col int) string {
672-
if name, ok := columnNames[col]; ok {
670+
// lookupDisplayName returns the display name for a column constant from the given map.
671+
func lookupDisplayName(col int, displayNames map[int]string) string {
672+
if name, ok := displayNames[col]; ok {
673673
return name
674674
}
675675
return "?"
676676
}
677677

678+
// columnDisplayName returns the display name for a queue column constant.
679+
func columnDisplayName(col int) string {
680+
return lookupDisplayName(col, columnNames)
681+
}
682+
678683
// parseHiddenColumns converts config hidden_columns strings to column ID set.
679684
func parseHiddenColumns(names []string) map[int]bool {
680685
result := map[int]bool{}
@@ -702,16 +707,16 @@ func hiddenColumnsToNames(hidden map[int]bool) []string {
702707
return names
703708
}
704709

705-
// parseColumnOrder converts config names to ordered queue column IDs.
706-
// Any toggleable columns not in the config list are appended at the end in default order.
707-
func parseColumnOrder(names []string) []int {
710+
// resolveColumnOrder converts config names to ordered column IDs using the given
711+
// configNames map. Any columns from defaults not in names are appended at the end.
712+
func resolveColumnOrder(names []string, configNames map[int]string, defaults []int) []int {
708713
if len(names) == 0 {
709-
result := make([]int, len(toggleableColumns))
710-
copy(result, toggleableColumns)
714+
result := make([]int, len(defaults))
715+
copy(result, defaults)
711716
return result
712717
}
713718
lookup := map[string]int{}
714-
for id, name := range columnConfigNames {
719+
for id, name := range configNames {
715720
lookup[name] = id
716721
}
717722
seen := map[int]bool{}
@@ -722,26 +727,35 @@ func parseColumnOrder(names []string) []int {
722727
seen[id] = true
723728
}
724729
}
725-
// Append any missing toggleable columns
726-
for _, col := range toggleableColumns {
730+
for _, col := range defaults {
727731
if !seen[col] {
728732
order = append(order, col)
729733
}
730734
}
731735
return order
732736
}
733737

734-
// columnOrderToNames converts ordered queue column IDs to config names.
735-
func columnOrderToNames(order []int) []string {
738+
// serializeColumnOrder converts ordered column IDs to config names.
739+
func serializeColumnOrder(order []int, configNames map[int]string) []string {
736740
names := make([]string, 0, len(order))
737741
for _, col := range order {
738-
if name, ok := columnConfigNames[col]; ok {
742+
if name, ok := configNames[col]; ok {
739743
names = append(names, name)
740744
}
741745
}
742746
return names
743747
}
744748

749+
// parseColumnOrder converts config names to ordered queue column IDs.
750+
func parseColumnOrder(names []string) []int {
751+
return resolveColumnOrder(names, columnConfigNames, toggleableColumns)
752+
}
753+
754+
// columnOrderToNames converts ordered queue column IDs to config names.
755+
func columnOrderToNames(order []int) []string {
756+
return serializeColumnOrder(order, columnConfigNames)
757+
}
758+
745759
// visibleColumns returns the ordered list of column indices to display,
746760
// always including colSel and colJobID, plus any non-hidden toggleable columns.
747761
func (m model) visibleColumns() []int {
@@ -803,7 +817,7 @@ func (m model) renderColumnOptionsView() string {
803817

804818
b.WriteString("\n")
805819
helpRows := [][]helpItem{
806-
{{"j/k", "move"}, {"space", "toggle"}, {"esc", "close"}},
820+
{{"↑/↓", "navigate"}, {"j/k", "reorder"}, {"space", "toggle"}, {"esc", "close"}},
807821
}
808822
b.WriteString(renderHelpTable(helpRows, m.width))
809823

cmd/roborev/tui/render_tasks.go

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -55,55 +55,23 @@ var taskColumnConfigNames = map[int]string{
5555

5656
// taskColumnDisplayName returns the display name for a task column constant.
5757
func taskColumnDisplayName(col int) string {
58-
if name, ok := taskColumnNames[col]; ok {
59-
return name
60-
}
61-
return "?"
58+
return lookupDisplayName(col, taskColumnNames)
6259
}
6360

6461
// parseTaskColumnOrder converts config names to ordered task column IDs.
65-
// Any columns not in the config list are appended at the end in default order.
6662
func parseTaskColumnOrder(names []string) []int {
67-
if len(names) == 0 {
68-
result := make([]int, len(taskToggleableColumns))
69-
copy(result, taskToggleableColumns)
70-
return result
71-
}
72-
lookup := map[string]int{}
73-
for id, name := range taskColumnConfigNames {
74-
lookup[name] = id
75-
}
76-
seen := map[int]bool{}
77-
var order []int
78-
for _, n := range names {
79-
if id, ok := lookup[strings.ToLower(n)]; ok && !seen[id] {
80-
order = append(order, id)
81-
seen[id] = true
82-
}
83-
}
84-
// Append any missing toggleable columns
85-
for _, col := range taskToggleableColumns {
86-
if !seen[col] {
87-
order = append(order, col)
88-
}
89-
}
90-
return order
63+
return resolveColumnOrder(names, taskColumnConfigNames, taskToggleableColumns)
9164
}
9265

9366
// taskColumnOrderToNames converts ordered task column IDs to config names.
9467
func taskColumnOrderToNames(order []int) []string {
95-
names := make([]string, 0, len(order))
96-
for _, col := range order {
97-
if name, ok := taskColumnConfigNames[col]; ok {
98-
names = append(names, name)
99-
}
100-
}
101-
return names
68+
return serializeColumnOrder(order, taskColumnConfigNames)
10269
}
10370

10471
// visibleTaskColumns returns the ordered list of task column indices to display.
10572
func (m model) visibleTaskColumns() []int {
106-
cols := []int{tcolSel}
73+
cols := make([]int, 0, 1+len(m.taskColumnOrder))
74+
cols = append(cols, tcolSel)
10775
cols = append(cols, m.taskColumnOrder...)
10876
return cols
10977
}
@@ -206,11 +174,7 @@ func (m model) renderTasksView() string {
206174
visCols := m.visibleTaskColumns()
207175

208176
// Build full row data for ALL fixJobs (stable widths across scroll).
209-
allHeaders := make(map[int]string, tcolCount)
210-
allHeaders[tcolSel] = ""
211-
for col, name := range taskColumnNames {
212-
allHeaders[col] = name
213-
}
177+
allHeaders := [tcolCount]string{tcolSel: "", tcolStatus: "Status", tcolJobID: "Job", tcolParent: "Parent", tcolQueued: "Queued", tcolElapsed: "Elapsed", tcolBranch: "Branch", tcolRepo: "Repo", tcolRefSubject: "Ref/Subject"}
214178
allFullRows := make([][]string, len(m.fixJobs))
215179
for i, job := range m.fixJobs {
216180
cells := m.taskCells(job)

0 commit comments

Comments
 (0)