64 lines
1.6 KiB
Go
64 lines
1.6 KiB
Go
package formatter
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"royalroadupdates/internal/royalroad"
|
|
)
|
|
|
|
// PrintTable writes a plain-text aligned table of stories to w.
|
|
func PrintTable(w io.Writer, stories []royalroad.Story) {
|
|
if len(stories) == 0 {
|
|
return
|
|
}
|
|
|
|
nameHeader := "Story Name"
|
|
updateHeader := "Last Update"
|
|
|
|
// 1. Measure natural widths in runes
|
|
maxNameLen := utf8.RuneCountInString(nameHeader)
|
|
maxUpdateLen := utf8.RuneCountInString(updateHeader)
|
|
|
|
for _, s := range stories {
|
|
if l := utf8.RuneCountInString(s.Name); l > maxNameLen {
|
|
maxNameLen = l
|
|
}
|
|
if l := utf8.RuneCountInString(Relative(s.DateModified)); l > maxUpdateLen {
|
|
maxUpdateLen = l
|
|
}
|
|
}
|
|
|
|
// 2. Determine final name column width based on terminal availability
|
|
nameWidth := maxNameLen
|
|
const minNameWidth = 30
|
|
const gutter = 2
|
|
|
|
termWidth, err := getTerminalWidth()
|
|
if err == nil {
|
|
// We have a terminal. check if it's wide enough to support a cap above the min.
|
|
if termWidth >= minNameWidth+gutter+maxUpdateLen {
|
|
availableForName := termWidth - gutter - maxUpdateLen
|
|
if nameWidth > availableForName {
|
|
nameWidth = availableForName
|
|
}
|
|
}
|
|
}
|
|
|
|
// 3. Render Table
|
|
fmt.Fprintf(w, "%-*s %s\n", nameWidth, nameHeader, updateHeader)
|
|
fmt.Fprintf(w, "%s %s\n", strings.Repeat("-", nameWidth), strings.Repeat("-", maxUpdateLen))
|
|
|
|
for _, s := range stories {
|
|
displayName := s.Name
|
|
runeName := []rune(displayName)
|
|
if len(runeName) > nameWidth {
|
|
// Truncate to (nameWidth - 3) and add ellipsis
|
|
displayName = string(runeName[:nameWidth-3]) + "..."
|
|
}
|
|
fmt.Fprintf(w, "%-*s %s\n", nameWidth, displayName, Relative(s.DateModified))
|
|
}
|
|
}
|