trail list: show shown/total counts when --limit truncates · Entire
trail list: show shown/total counts when --limit truncates
adfe5a0→main·
matthiaswenz·1mo ago·2 files·+125 added/-7 removed
A capped page rendered "Recent trails · 10", reading as the total number of matches rather than the page size. Header, per-status group counts, and the Other bucket now render "shown/total" when --limit dropped rows, and the header noun pluralizes by the total. Also fix the --json flag help, which claimed only --status is respected.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Sessions
72bbfd94df45View transcript
[?
Align Trail Status with Server and Refactor ListClaude Code·Fable 5·7 steps](/content/gh/entireio/cli/session/e489d20f-3db8-41fd-9446-9ab6aa550405#timeline-72bbfd94df45/index.html)
Changes
2
cmd/entire/cli
Mtrail_cmd.go+47/-7
Mtrail_cmd_test.go+78
159 unmodified lines
160
161
162
163
163
164
165
166
60 unmodified lines
227
228
229
230
231
232
233
234
14 unmodified lines
249
250
251
252
253
254
255
256
113 unmodified lines
370
371
372
373
374
375
376
377
378
379
380
381
382
17 unmodified lines
400
401
402
392
403
404
405
406
10 unmodified lines
417
418
419
409
420
421
422
423
424
425
426
427
2 unmodified lines
430
431
432
433
434
435
436
437
438
439
440
441
420
442
443
444
423
445
446
447
448
5 unmodified lines
454
455
456
435
457
458
459
438
460
461
462
463
75 unmodified lines
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
159 unmodified lines
"Filter by author login (case-insensitive); use '"+trailListAuthorMe+"' for yourself (requires gh CLI); omit for any author")
cmd.Flags().StringVar(&opts.Status, "status", defaultTrailListStatus,
"Filter by comma-separated status(es): "+formatValidStatuses()+"; use '"+trailListStatusAny+"' for all statuses")
cmd.Flags().BoolVar(&opts.JSON, "json", false, "Output as JSON (respects --status filter)")
cmd.Flags().BoolVar(&opts.JSON, "json", false, "Output as JSON (respects --author, --status, and --limit)")
cmd.Flags().IntVarP(&opts.Limit, "limit", "n", defaultTrailListLimit, "Maximum number of trails to show")
return cmd
60 unmodified lines
sort.Slice(trails, func(i, j int) bool {
return trails[i].UpdatedAt.After(trails[j].UpdatedAt)
})
totalMatched := len(trails)
statusTotals := trailStatusCounts(trails)
trails = limitTrails(trails, opts.Limit)
if opts.JSON {
14 unmodified lines
RequestedAuthor: authorFilter,
CurrentUser: currentUserLogin,
StatusFilters: statusFilters,
TotalMatched: totalMatched,
StatusTotals: statusTotals,
})
return nil
113 unmodified lines
RequestedAuthor string
CurrentUser string
StatusFilters []trail.Status
// TotalMatched is the number of trails matching the filters before
// --limit truncation. Counts render as "shown/total" when they differ so
// a full page doesn't read as the total number of matches.
TotalMatched int
// StatusTotals are the pre-truncation per-status counts backing the
// group headers in the grouped view.
StatusTotals map[trail.Status]int
}
func printTrailList(w io.Writer, trails []*trail.Metadata, opts trailListDisplayOptions) {
17 unmodified lines
for _, t := range group {
rendered[t] = true
}
fmt.Fprintf(w, " %s · %d\n", trailStatusTitle(status), len(group))
fmt.Fprintf(w, " %s · %s\n", trailStatusTitle(status), trailCountDisplay(len(group), opts.StatusTotals[status]))
fmt.Fprintln(w)
printTrailRows(w, group, showAuthor)
fmt.Fprintln(w)
10 unmodified lines
}
}
if len(other) > 0 {
fmt.Fprintf(w, " Other · %d\n", len(other))
otherTotal := opts.TotalMatched
for _, status := range trailListStatusOrder(nil) {
otherTotal -= opts.StatusTotals[status]
}
fmt.Fprintf(w, " Other · %s\n", trailCountDisplay(len(other), otherTotal))
fmt.Fprintln(w)
printTrailRows(w, other, showAuthor)
fmt.Fprintln(w)
2 unmodified lines
}
func printTrailListHeader(w io.Writer, opts trailListDisplayOptions, count int) {
countStr := trailCountDisplay(count, opts.TotalMatched)
// The noun refers to the full match set, so pluralize by the total when
// the page is truncated ("1/2 trails", not "1/2 trail").
nounCount := count
if opts.TotalMatched > count {
nounCount = opts.TotalMatched
}
if opts.RequestedAuthor == "" {
if len(opts.StatusFilters) == 0 {
fmt.Fprintf(w, " Recent %s · %d\n", pluralize("trail", count), count)
fmt.Fprintf(w, " Recent %s · %s\n", pluralize("trail", nounCount), countStr)
return
}
fmt.Fprintf(w, " %s · %d %s\n", trailStatusListTitle(opts.StatusFilters), count, pluralize("trail", count))
fmt.Fprintf(w, " %s · %s %s\n", trailStatusListTitle(opts.StatusFilters), countStr, pluralize("trail", nounCount))
return
}
5 unmodified lines
label = fmt.Sprintf("Your trails (%s)", opts.CurrentUser)
}
if len(opts.StatusFilters) == 0 {
fmt.Fprintf(w, " %s · %d\n", label, count)
fmt.Fprintf(w, " %s · %s\n", label, countStr)
return
}
fmt.Fprintf(w, " %s · %d %s\n", label, count, trailStatusListDisplay(opts.StatusFilters))
fmt.Fprintf(w, " %s · %s %s\n", label, countStr, trailStatusListDisplay(opts.StatusFilters))
}
func printTrailRows(w io.Writer, trails []*trail.Metadata, showAuthor bool) {
75 unmodified lines
return strings.ToUpper(display[:1]) + display[1:]
}
// trailCountDisplay renders a count as "shown/total" when --limit truncated
// the list, so a capped page doesn't read as the total number of matches.
func trailCountDisplay(shown, total int) string {
if total > shown {
return fmt.Sprintf("%d/%d", shown, total)
}
return strconv.Itoa(shown)
}
// trailStatusCounts tallies trails per status before --limit truncation.
func trailStatusCounts(trails []*trail.Metadata) map[trail.Status]int {
counts := make(map[trail.Status]int, len(trails))
for _, t := range trails {
counts[t.Status]++
}
return counts
}
func pluralize(s string, count int) string {
if count == 1 {
return s