1package sheet23import (4 "strings"5 "unicode/utf8"67 "src.rest/cat/012/internal/locale"8 "src.rest/cat/012/internal/numfmt"9)1011// Display renders v under format f for a cell width columns wide, as the12// grid shows it: the text without padding, and where it goes. Numbers go13// right, text left, booleans and errors center. A formatted number that14// doesn't fit in width-1 columns becomes a run of #, as in Sheets;15// Automatic first drops decimals and falls back to scientific notation.16// Accounting returns AlignFill: exactly width columns, $ at the left.17func Display(v Value, f Format, width int) (string, Align) {18 return DisplayIn(v, f, width, locale.Canonical)19}2021// DisplayIn is Display as shown in loc: its separators, currency and22// date order (see Format.CodeIn).23func DisplayIn(v Value, f Format, width int, loc *locale.Locale) (string, Align) {24 inner := max(width-1, 0)25 switch v.Kind {26 case Empty:27 return "", AlignLeft28 case Text:29 return v.Str, AlignLeft30 case Bool, Error:31 return v.String(), AlignCenter32 }33 switch f.Kind {34 case FmtAuto:35 return numfmt.GeneralFitIn(v.Num, inner, loc), AlignRight36 case FmtText:37 return numfmt.GeneralIn(v.Num, loc), AlignLeft38 case FmtAccounting:39 if s, ok := numfmt.AccountingIn(v.Num, f.Decimals, width, loc); ok {40 return s, AlignFill41 }42 return strings.Repeat("#", inner), AlignRight43 }44 s := formatNumberIn(v.Num, f, loc)45 if utf8.RuneCountInString(s) > inner {46 s = strings.Repeat("#", inner)47 }48 return s, AlignRight49}5051// FormatPattern renders v with a number format pattern, as TEXT() does.52func FormatPattern(v float64, pat string) string { return numfmt.Format(v, pat) }5354// FormatText renders v under f with no width limit, e.g. for TEXT() or55// copying out of the grid.56func FormatText(v Value, f Format) string { return FormatTextIn(v, f, locale.Canonical) }5758// FormatTextIn is FormatText as shown in loc, e.g. for a CSV file59// written in loc.60func FormatTextIn(v Value, f Format, loc *locale.Locale) string {61 if v.Kind != Number {62 return text(v)63 }64 switch f.Kind {65 case FmtAuto, FmtText:66 return numfmt.GeneralIn(v.Num, loc)67 }68 return formatNumberIn(v.Num, f, loc)69}7071// formatNumberIn renders a number under a format with a pattern, or72// the Size format, which picks its unit by the number.73func formatNumberIn(v float64, f Format, loc *locale.Locale) string {74 if f.Kind == FmtSize {75 return numfmt.SizeIn(v, f.Decimals, loc)76 }77 return numfmt.FormatIn(v, f.CodeIn(loc), loc)78}7980// FormatValue renders v in width columns with one column of padding, the81// way the grid shows it in Automatic format: numbers right-aligned,82// booleans and errors centered. Text is not handled here because it can83// overflow into neighboring cells.84func FormatValue(v Value, width int) string {85 switch v.Kind {86 case Number:87 s, _ := Display(v, Format{}, width)88 return padLeft(s, width-1) + " "89 case Bool, Error:90 return centerPad(v.String(), width)91 }92 return ""93}9495// FormatNumber formats a number in the General format within width96// columns, for use outside the grid (e.g. the status line).97func FormatNumber(v float64, width int) string {98 return numfmt.GeneralFit(v, width)99}100101func padLeft(s string, width int) string {102 if len(s) >= width {103 return s104 }105 return strings.Repeat(" ", width-len(s)) + s106}107108func centerPad(s string, width int) string {109 if len(s) >= width {110 return s[:width]111 }112 pad := width - len(s)113 return strings.Repeat(" ", pad/2) + s + strings.Repeat(" ", pad-pad/2)114}