blob: 0f1f625aa7cad98ddb4d3a5374eecf5004a288ec [file]
// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"fmt"
"strings"
)
// labelWidth leaves one space after the longest label. Increase this if labels
// change and the tables get out of alignment.
const labelWidth = 47
// section writes a section heading and the tasks that move the figures in it.
// A tag long enough to push the heading past the line budget goes on its own
// line rather than trailing off the screen.
func section(w *bytes.Buffer, title, tasks string) {
head := "## " + title
if tag := taskTag(tasks); tag != "" {
if len(head)+len(tag)+1 <= 88 {
head += " " + tag
} else {
head += "\n " + strings.TrimSpace(tag)
}
}
fmt.Fprintf(w, "\n\n%s\n\n", head)
}
// note writes a block of explanatory prose, followed by a blank line.
func note(w *bytes.Buffer, text string) {
fmt.Fprintf(w, "%s\n\n", text)
}
// frac writes a figure heading for 100%.
func frac(w *bytes.Buffer, label string, r result, tasks string) {
pct := " --"
if r.d > 0 {
pct = fmt.Sprintf("%3.0f%%", 100*float64(r.n)/float64(r.d))
}
line(w, fmt.Sprintf("%-*s %5d / %-5d %s%s", labelWidth, label, r.n, r.d, pct, taskTag(tasks)))
}
// count writes a figure heading for zero.
func count(w *bytes.Buffer, label string, n int, tasks string) {
line(w, fmt.Sprintf("%-*s %5d %-9s%s", labelWidth, label, n, "-> 0", taskTag(tasks)))
}
// kv writes a bare number: context rather than a figure with a target.
func kv(w *bytes.Buffer, label string, n int) {
line(w, fmt.Sprintf("%-*s %5d", labelWidth, label, n))
}
// sub writes a bare number indented under the figure it breaks down.
func sub(w *bytes.Buffer, label string, n int) {
line(w, fmt.Sprintf(" %-*s %5d", labelWidth-4, label, n))
}
// countIndented writes a count heading for zero, indented under the figure it
// breaks down. Used for per-field and per-file detail rows.
func countIndented(w *bytes.Buffer, label string, n int, tasks string) {
line(w, fmt.Sprintf(" %-*s %5d %-9s%s", labelWidth-4, label, n, "-> 0", taskTag(tasks)))
}
// emptyList writes a placeholder when a list has no rows, so that a heading
// with nothing under it reads as an empty list rather than a truncated one.
func emptyList(w *bytes.Buffer, n int) {
if n == 0 {
line(w, " (none)")
}
}
const lineWrap = 78
// wrap writes lead followed by a list of words, breaking before the line
// wrapping budget and aligning continuations under the first word. The lead
// labels the list, so that an inventory nested under a table row says what it
// is rather than leaving the reader to infer it from position.
func wrap(w *bytes.Buffer, indent, lead string, words []string) {
first, rest := indent+lead, indent+strings.Repeat(" ", len(lead))
cur := first
for _, s := range words {
if len(cur) > len(rest) && len(cur)+1+len(s) > lineWrap {
line(w, cur)
cur = rest
}
if len(cur) > len(rest) {
cur += " "
}
cur += s
}
if len(cur) > len(rest) {
line(w, cur)
}
}
func line(w *bytes.Buffer, s string) {
fmt.Fprintf(w, "%s\n", strings.TrimRight(s, " "))
}
func taskTag(tasks string) string {
if tasks == "" || tasks == "-" {
return ""
}
return " [" + tasks + "]"
}