summaryrefslogtreecommitdiffstats
path: root/vendor/golang.org/x/text/internal/number/format.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/golang.org/x/text/internal/number/format.go')
-rwxr-xr-xvendor/golang.org/x/text/internal/number/format.go252
1 files changed, 199 insertions, 53 deletions
diff --git a/vendor/golang.org/x/text/internal/number/format.go b/vendor/golang.org/x/text/internal/number/format.go
index 84903fad8..70ddf7df1 100755
--- a/vendor/golang.org/x/text/internal/number/format.go
+++ b/vendor/golang.org/x/text/internal/number/format.go
@@ -6,41 +6,136 @@ package number
import (
"strconv"
+ "unicode/utf8"
"golang.org/x/text/language"
)
// TODO:
-// - public (but internal) API for creating formatters
-// - split out the logic that computes the visible digits from the rest of the
-// formatting code (needed for plural).
// - grouping of fractions
-// - reuse percent pattern for permille
-// - padding
+// - allow user-defined superscript notation (such as <sup>4</sup>)
+// - same for non-breaking spaces, like &nbsp;
+
+// Formatting proceeds along the following lines:
+// 0) Compose rounding information from format and context.
+// 1) Convert a number into a Decimal.
+// 2) Sanitize Decimal by adding trailing zeros, removing leading digits, and
+// (non-increment) rounding. The Decimal that results from this is suitable
+// for determining the plural form.
+// 3) Render the Decimal in the localized form.
// Formatter contains all the information needed to render a number.
type Formatter struct {
- *Pattern
+ Pattern
Info
RoundingContext
- f func(dst []byte, f *Formatter, d *Decimal) []byte
}
-func lookupFormat(t language.Tag, tagToIndex []uint8) *Pattern {
+func (f *Formatter) init(t language.Tag, index []uint8) {
+ f.Info = InfoFromTag(t)
for ; ; t = t.Parent() {
if ci, ok := language.CompactIndex(t); ok {
- return &formats[tagToIndex[ci]]
+ f.Pattern = formats[index[ci]]
+ break
}
}
}
+// InitPattern initializes a Formatter for the given Pattern.
+func (f *Formatter) InitPattern(t language.Tag, pat *Pattern) {
+ f.Info = InfoFromTag(t)
+ f.Pattern = *pat
+}
+
+// InitDecimal initializes a Formatter using the default Pattern for the given
+// language.
+func (f *Formatter) InitDecimal(t language.Tag) {
+ f.init(t, tagToDecimal)
+}
+
+// InitScientific initializes a Formatter using the default Pattern for the
+// given language.
+func (f *Formatter) InitScientific(t language.Tag) {
+ f.init(t, tagToScientific)
+}
+
+// InitEngineering initializes a Formatter using the default Pattern for the
+// given language.
+func (f *Formatter) InitEngineering(t language.Tag) {
+ f.init(t, tagToScientific)
+ f.Pattern.MaxIntegerDigits = 3
+ f.Pattern.MinIntegerDigits = 1
+}
+
+// InitPercent initializes a Formatter using the default Pattern for the given
+// language.
+func (f *Formatter) InitPercent(t language.Tag) {
+ f.init(t, tagToPercent)
+}
+
+// InitPerMille initializes a Formatter using the default Pattern for the given
+// language.
+func (f *Formatter) InitPerMille(t language.Tag) {
+ f.init(t, tagToPercent)
+ f.Pattern.DigitShift = 3
+}
+
+func (f *Formatter) Append(dst []byte, x interface{}) []byte {
+ var d Decimal
+ d.Convert(&f.RoundingContext, x)
+ return f.Format(dst, &d)
+}
+
func (f *Formatter) Format(dst []byte, d *Decimal) []byte {
- return f.f(dst, f, d)
+ var result []byte
+ var postPrefix, preSuffix int
+ if f.MinExponentDigits > 0 {
+ result, postPrefix, preSuffix = appendScientific(dst, f, d)
+ } else {
+ result, postPrefix, preSuffix = appendDecimal(dst, f, d)
+ }
+ if f.PadRune == 0 {
+ return result
+ }
+ width := int(f.FormatWidth)
+ if count := utf8.RuneCount(result); count < width {
+ insertPos := 0
+ switch f.Flags & PadMask {
+ case PadAfterPrefix:
+ insertPos = postPrefix
+ case PadBeforeSuffix:
+ insertPos = preSuffix
+ case PadAfterSuffix:
+ insertPos = len(result)
+ }
+ num := width - count
+ pad := [utf8.UTFMax]byte{' '}
+ sz := 1
+ if r := f.PadRune; r != 0 {
+ sz = utf8.EncodeRune(pad[:], r)
+ }
+ extra := sz * num
+ if n := len(result) + extra; n < cap(result) {
+ result = result[:n]
+ copy(result[insertPos+extra:], result[insertPos:])
+ } else {
+ buf := make([]byte, n)
+ copy(buf, result[:insertPos])
+ copy(buf[insertPos+extra:], result[insertPos:])
+ result = buf
+ }
+ for ; num > 0; num-- {
+ insertPos += copy(result[insertPos:], pad[:sz])
+ }
+ }
+ return result
}
-func appendDecimal(dst []byte, f *Formatter, d *Decimal) []byte {
+// appendDecimal appends a formatted number to dst. It returns two possible
+// insertion points for padding.
+func appendDecimal(dst []byte, f *Formatter, d *Decimal) (b []byte, postPre, preSuf int) {
if dst, ok := f.renderSpecial(dst, d); ok {
- return dst
+ return dst, 0, len(dst)
}
n := d.normalize()
if maxSig := int(f.MaxSignificantDigits); maxSig > 0 {
@@ -48,6 +143,7 @@ func appendDecimal(dst []byte, f *Formatter, d *Decimal) []byte {
}
digits := n.Digits
exp := n.Exp
+ exp += int32(f.Pattern.DigitShift)
// Split in integer and fraction part.
var intDigits, fracDigits []byte
@@ -94,7 +190,7 @@ func appendDecimal(dst []byte, f *Formatter, d *Decimal) []byte {
}
}
- neg := d.Neg && numInt+numFrac > 0
+ neg := d.Neg
affix, suffix := f.getAffixes(neg)
dst = appendAffix(dst, f, affix, neg)
savedLen := len(dst)
@@ -104,9 +200,9 @@ func appendDecimal(dst []byte, f *Formatter, d *Decimal) []byte {
minInt = 1
}
// add leading zeros
- for i := numInt; i < minInt; i++ {
+ for i := minInt; i > numInt; i-- {
dst = f.AppendDigit(dst, 0)
- if f.needsSep(minInt - i) {
+ if f.needsSep(i) {
dst = append(dst, f.Symbol(SymGroup)...)
}
}
@@ -142,18 +238,14 @@ func appendDecimal(dst []byte, f *Formatter, d *Decimal) []byte {
for ; trailZero > 0; trailZero-- {
dst = f.AppendDigit(dst, 0)
}
- // Ensure that at least one digit is written no matter what. This makes
- // things more robust, even though a pattern should always require at least
- // one fraction or integer digit.
- if len(dst) == savedLen {
- dst = f.AppendDigit(dst, 0)
- }
- return appendAffix(dst, f, suffix, neg)
+ return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
}
-func appendScientific(dst []byte, f *Formatter, d *Decimal) []byte {
+// appendScientific appends a formatted number to dst. It returns two possible
+// insertion points for padding.
+func appendScientific(dst []byte, f *Formatter, d *Decimal) (b []byte, postPre, preSuf int) {
if dst, ok := f.renderSpecial(dst, d); ok {
- return dst
+ return dst, 0, 0
}
// Significant digits are transformed by parser for scientific notation and
// do not need to be handled here.
@@ -197,7 +289,7 @@ func appendScientific(dst []byte, f *Formatter, d *Decimal) []byte {
} else {
intDigits = digits
}
- neg := d.Neg && len(digits) > 0
+ neg := d.Neg
affix, suffix := f.getAffixes(neg)
dst = appendAffix(dst, f, affix, neg)
savedLen := len(dst)
@@ -227,33 +319,73 @@ func appendScientific(dst []byte, f *Formatter, d *Decimal) []byte {
for ; trailZero > 0; trailZero-- {
dst = f.AppendDigit(dst, 0)
}
- // Ensure that at least one digit is written no matter what. This makes
- // things more robust, even though a pattern should always require at least
- // one fraction or integer digit.
- if len(dst) == savedLen {
- dst = f.AppendDigit(dst, 0)
- }
// exp
- dst = append(dst, f.Symbol(SymExponential)...)
- switch {
- case exp < 0:
- dst = append(dst, f.Symbol(SymMinusSign)...)
- exp = -exp
- case f.Flags&AlwaysExpSign != 0:
- dst = append(dst, f.Symbol(SymPlusSign)...)
- }
buf := [12]byte{}
- b := strconv.AppendUint(buf[:0], uint64(exp), 10)
- for i := len(b); i < int(f.MinExponentDigits); i++ {
+ // TODO: use exponential if superscripting is not available (no Latin
+ // numbers or no tags) and use exponential in all other cases.
+ exponential := f.Symbol(SymExponential)
+ if exponential == "E" {
+ dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
+ dst = append(dst, f.Symbol(SymSuperscriptingExponent)...)
+ dst = append(dst, "\u202f"...) // NARROW NO-BREAK SPACE
+ dst = f.AppendDigit(dst, 1)
dst = f.AppendDigit(dst, 0)
+ switch {
+ case exp < 0:
+ dst = append(dst, superMinus...)
+ exp = -exp
+ case f.Flags&AlwaysExpSign != 0:
+ dst = append(dst, superPlus...)
+ }
+ b = strconv.AppendUint(buf[:0], uint64(exp), 10)
+ for i := len(b); i < int(f.MinExponentDigits); i++ {
+ dst = append(dst, superDigits[0]...)
+ }
+ for _, c := range b {
+ dst = append(dst, superDigits[c-'0']...)
+ }
+ } else {
+ dst = append(dst, exponential...)
+ switch {
+ case exp < 0:
+ dst = append(dst, f.Symbol(SymMinusSign)...)
+ exp = -exp
+ case f.Flags&AlwaysExpSign != 0:
+ dst = append(dst, f.Symbol(SymPlusSign)...)
+ }
+ b = strconv.AppendUint(buf[:0], uint64(exp), 10)
+ for i := len(b); i < int(f.MinExponentDigits); i++ {
+ dst = f.AppendDigit(dst, 0)
+ }
+ for _, c := range b {
+ dst = f.AppendDigit(dst, c-'0')
+ }
}
- for _, c := range b {
- dst = f.AppendDigit(dst, c-'0')
- }
- return appendAffix(dst, f, suffix, neg)
+ return appendAffix(dst, f, suffix, neg), savedLen, len(dst)
}
+const (
+ superMinus = "\u207B" // SUPERSCRIPT HYPHEN-MINUS
+ superPlus = "\u207A" // SUPERSCRIPT PLUS SIGN
+)
+
+var (
+ // Note: the digits are not sequential!!!
+ superDigits = []string{
+ "\u2070", // SUPERSCRIPT DIGIT ZERO
+ "\u00B9", // SUPERSCRIPT DIGIT ONE
+ "\u00B2", // SUPERSCRIPT DIGIT TWO
+ "\u00B3", // SUPERSCRIPT DIGIT THREE
+ "\u2074", // SUPERSCRIPT DIGIT FOUR
+ "\u2075", // SUPERSCRIPT DIGIT FIVE
+ "\u2076", // SUPERSCRIPT DIGIT SIX
+ "\u2077", // SUPERSCRIPT DIGIT SEVEN
+ "\u2078", // SUPERSCRIPT DIGIT EIGHT
+ "\u2079", // SUPERSCRIPT DIGIT NINE
+ }
+)
+
func (f *Formatter) getAffixes(neg bool) (affix, suffix string) {
str := f.Affix
if str != "" {
@@ -267,8 +399,11 @@ func (f *Formatter) getAffixes(neg bool) (affix, suffix string) {
sufStart := 1 + str[0]
affix = str[1:sufStart]
suffix = str[sufStart+1:]
- } else if neg {
- affix = "-"
+ }
+ // TODO: introduce a NeedNeg sign to indicate if the left pattern already
+ // has a sign marked?
+ if f.NegOffset == 0 && (neg || f.Flags&AlwaysSign != 0) {
+ affix = "-" + affix
}
return affix, suffix
}
@@ -288,10 +423,11 @@ func fmtNaN(dst []byte, f *Formatter) []byte {
}
func fmtInfinite(dst []byte, f *Formatter, d *Decimal) []byte {
- if d.Neg {
- dst = append(dst, f.Symbol(SymMinusSign)...)
- }
- return append(dst, f.Symbol(SymInfinity)...)
+ affix, suffix := f.getAffixes(d.Neg)
+ dst = appendAffix(dst, f, affix, d.Neg)
+ dst = append(dst, f.Symbol(SymInfinity)...)
+ dst = appendAffix(dst, f, suffix, d.Neg)
+ return dst
}
func appendAffix(dst []byte, f *Formatter, affix string, neg bool) []byte {
@@ -307,11 +443,21 @@ func appendAffix(dst []byte, f *Formatter, affix string, neg bool) []byte {
escaping = true
case r == '\'':
quoting = !quoting
- case !quoting && (r == '-' || r == '+'):
+ case quoting:
+ dst = append(dst, string(r)...)
+ case r == '%':
+ if f.DigitShift == 3 {
+ dst = append(dst, f.Symbol(SymPerMille)...)
+ } else {
+ dst = append(dst, f.Symbol(SymPercentSign)...)
+ }
+ case r == '-' || r == '+':
if neg {
dst = append(dst, f.Symbol(SymMinusSign)...)
- } else {
+ } else if f.Flags&ElideSign == 0 {
dst = append(dst, f.Symbol(SymPlusSign)...)
+ } else {
+ dst = append(dst, ' ')
}
default:
dst = append(dst, string(r)...)