update vendor/ dir to latest w/o heroku, moby

had to lock a lot of things in place
This commit is contained in:
Reed Allman
2017-08-03 02:38:15 -07:00
parent 780791da1c
commit 30f3c45dbc
5637 changed files with 191713 additions and 1133103 deletions

View File

@@ -144,13 +144,10 @@
package catalog // import "golang.org/x/text/message/catalog"
// TODO:
// Some way to freeze a catalog.
// - Locking on each lockup turns out to be about 50% of the total running time
// for some of the benchmarks in the message package.
// Consider these:
// - allow compiling messages. Implemented by Messages:
// type Compiler interface { compile(w io.Writer) Handler }
// where Handler is defined as:
// type Handler interface { Format(c Context, data string) error }
// Alternatively, the Compile method could be required in the Message
// interface.
// - Sequence type to support sequences in user-defined messages.
// - Garbage collection: Remove dictionaries that can no longer be reached
// as other dictionaries have been added that cover all possible keys.

View File

@@ -53,15 +53,15 @@ func (c *Catalog) set(tag language.Tag, key string, s *store, msg ...Message) er
}
type store struct {
mutex sync.Mutex
mutex sync.RWMutex
index map[language.Tag]msgMap
}
type msgMap map[string]string
func (s *store) lookup(tag language.Tag, key string) (data string, ok bool) {
s.mutex.Lock()
defer s.mutex.Unlock()
s.mutex.RLock()
defer s.mutex.RUnlock()
for ; ; tag = tag.Parent() {
if msgs, ok := s.index[tag]; ok {
@@ -78,8 +78,8 @@ func (s *store) lookup(tag language.Tag, key string) (data string, ok bool) {
// Languages returns all languages for which the store contains variants.
func (s *store) languages() []language.Tag {
s.mutex.Lock()
defer s.mutex.Unlock()
s.mutex.RLock()
defer s.mutex.RUnlock()
tags := make([]language.Tag, 0, len(s.index))
for t := range s.index {

View File

@@ -158,9 +158,15 @@ var fmtTests = []struct {
// Extra argument errors should format without flags set.
{"%010.2", "12345", "%!(NOVERB)"},
// All following tests are identical to that of the fmt package.
{"%d", 12345, "12345"},
{"%v", 12345, "12345"},
// Some key other differences, asides from localized values:
// - NaN values should not use affixes; so no signs (CLDR requirement)
// - Infinity uses patterns, so signs may be different (CLDR requirement)
// - The # flag is used to disable localization.
// All following tests are analogous to those of the fmt package, but with
// localized numbers when appropriate.
{"%d", 12345, "12,345"},
{"%v", 12345, "12,345"},
{"%t", true, "true"},
// basic string
@@ -307,9 +313,9 @@ var fmtTests = []struct {
// Runes that are not valid.
{"%q", int32(-1), "%!q(int32=-1)"},
{"%q", 0xDC80, `'<27>'`},
{"%q", rune(0x110000), "%!q(int32=1114112)"},
{"%q", int64(0xFFFFFFFFF), "%!q(int64=68719476735)"},
{"%q", uint64(0xFFFFFFFFF), "%!q(uint64=68719476735)"},
{"%q", rune(0x110000), "%!q(int32=1,114,112)"},
{"%q", int64(0xFFFFFFFFF), "%!q(int64=68,719,476,735)"},
{"%q", uint64(0xFFFFFFFFF), "%!q(uint64=68,719,476,735)"},
// width
{"%5s", "abc", " abc"},
@@ -339,23 +345,23 @@ var fmtTests = []struct {
{"%-10v", nil, "<nil> "},
// integers
{"%d", uint(12345), "12345"},
{"%d", int(-12345), "-12345"},
{"%d", uint(12345), "12,345"},
{"%d", int(-12345), "-12,345"},
{"%d", ^uint8(0), "255"},
{"%d", ^uint16(0), "65535"},
{"%d", ^uint32(0), "4294967295"},
{"%d", ^uint64(0), "18446744073709551615"},
{"%d", ^uint16(0), "65,535"},
{"%d", ^uint32(0), "4,294,967,295"},
{"%d", ^uint64(0), "18,446,744,073,709,551,615"},
{"%d", int8(-1 << 7), "-128"},
{"%d", int16(-1 << 15), "-32768"},
{"%d", int32(-1 << 31), "-2147483648"},
{"%d", int64(-1 << 63), "-9223372036854775808"},
{"%d", int16(-1 << 15), "-32,768"},
{"%d", int32(-1 << 31), "-2,147,483,648"},
{"%d", int64(-1 << 63), "-9,223,372,036,854,775,808"},
{"%.d", 0, ""},
{"%.0d", 0, ""},
{"%6.0d", 0, " "},
{"%06.0d", 0, " "},
{"% d", 12345, " 12345"},
{"%+d", 12345, "+12345"},
{"%+d", -12345, "-12345"},
{"% d", 12345, " 12,345"},
{"%+d", 12345, "+12,345"},
{"%+d", -12345, "-12,345"},
{"%b", 7, "111"},
{"%b", -6, "-110"},
{"%b", ^uint32(0), "11111111111111111111111111111111"},
@@ -371,29 +377,29 @@ var fmtTests = []struct {
{"%x", ^uint32(0), "ffffffff"},
{"%X", ^uint64(0), "FFFFFFFFFFFFFFFF"},
{"%.20b", 7, "00000000000000000111"},
{"%10d", 12345, " 12345"},
{"%10d", -12345, " -12345"},
{"%+10d", 12345, " +12345"},
{"%010d", 12345, "0000012345"},
{"%010d", -12345, "-000012345"},
{"%20.8d", 1234, " 00001234"},
{"%20.8d", -1234, " -00001234"},
{"%020.8d", 1234, " 00001234"},
{"%020.8d", -1234, " -00001234"},
{"%-20.8d", 1234, "00001234 "},
{"%-20.8d", -1234, "-00001234 "},
{"%10d", 12345, " 12,345"},
{"%10d", -12345, " -12,345"},
{"%+10d", 12345, " +12,345"},
{"%010d", 12345, "0,000,012,345"},
{"%010d", -12345, "-0,000,012,345"},
{"%20.8d", 1234, " 00,001,234"},
{"%20.8d", -1234, " -00,001,234"},
{"%020.8d", 1234, " 00,001,234"},
{"%020.8d", -1234, " -00,001,234"},
{"%-20.8d", 1234, "00,001,234 "},
{"%-20.8d", -1234, "-00,001,234 "},
{"%-#20.8x", 0x1234abc, "0x01234abc "},
{"%-#20.8X", 0x1234abc, "0X01234ABC "},
{"%-#20.8o", 01234, "00001234 "},
// Test correct f.intbuf overflow checks.
{"%068d", 1, zeroFill("", 68, "1")},
{"%068d", -1, zeroFill("-", 67, "1")},
{"%068d", 1, "00," + strings.Repeat("000,", 21) + "001"},
{"%068d", -1, "-00," + strings.Repeat("000,", 21) + "001"},
{"%#.68x", 42, zeroFill("0x", 68, "2a")},
{"%.68d", -42, zeroFill("-", 68, "42")},
{"%+.68d", 42, zeroFill("+", 68, "42")},
{"% .68d", 42, zeroFill(" ", 68, "42")},
{"% +.68d", 42, zeroFill("+", 68, "42")},
{"%.68d", -42, "-00," + strings.Repeat("000,", 21) + "042"},
{"%+.68d", 42, "+00," + strings.Repeat("000,", 21) + "042"},
{"% .68d", 42, " 00," + strings.Repeat("000,", 21) + "042"},
{"% +.68d", 42, "+00," + strings.Repeat("000,", 21) + "042"},
// unicode format
{"%U", 0, "U+0000"},
@@ -415,8 +421,8 @@ var fmtTests = []struct {
{"%#.68U", '日', zeroFill("U+", 68, "65E5") + " '日'"},
// floats
{"%+.3e", 0.0, "+0.000e+00"},
{"%+.3e", 1.0, "+1.000e+00"},
{"%+.3e", 0.0, "+0.000\u202f×\u202f10⁰⁰"},
{"%+.3e", 1.0, "+1.000\u202f×\u202f10⁰⁰"},
{"%+.3f", -1.0, "-1.000"},
{"%+.3F", -1.0, "-1.000"},
{"%+.3F", float32(-1.0), "-1.000"},
@@ -430,8 +436,8 @@ var fmtTests = []struct {
{"%-+07.2f", -1.0, "-1.00 "},
{"%+10.2f", +1.0, " +1.00"},
{"%+10.2f", -1.0, " -1.00"},
{"% .3E", -1.0, "-1.000E+00"},
{"% .3e", 1.0, " 1.000e+00"},
{"% .3E", -1.0, "-1.000\u202f×\u202f10⁰⁰"},
{"% .3e", 1.0, " 1.000\u202f×\u202f10⁰⁰"},
{"%+.3g", 0.0, "+0"},
{"%+.3g", 1.0, "+1"},
{"%+.3g", -1.0, "-1"},
@@ -469,25 +475,27 @@ var fmtTests = []struct {
{"%.4b", float32(1.0), "8388608p-23"},
{"%.4b", -1.0, "-4503599627370496p-52"},
// Test correct f.intbuf boundary checks.
{"%.68f", 1.0, zeroFill("1.", 68, "")},
{"%.68f", -1.0, zeroFill("-1.", 68, "")},
// TODO: the following cases won't work because of rounding errors. We can
// fix this if we expose the internals of strconv.
// {"%.68f", 1.0, zeroFill("1.", 68, "")}, // TODO(bug): rounding error
// {"%.68f", -1.0, zeroFill("-1.", 68, "")}, // TODO(bug): rounding error
// float infinites and NaNs
{"%f", posInf, "+Inf"},
{"%.1f", negInf, "-Inf"},
{"% f", NaN, " NaN"},
{"%20f", posInf, " +Inf"},
{"% 20F", posInf, " Inf"},
{"% 20e", negInf, " -Inf"},
{"%+20E", negInf, " -Inf"},
{"% +20g", negInf, " -Inf"},
{"%+-20G", posInf, "+Inf "},
{"%f", posInf, ""},
{"%.1f", negInf, "-"},
{"% f", NaN, "NaN"},
{"%20f", posInf, " "},
{"% 20F", posInf, " "},
{"% 20e", negInf, " -∞"},
{"%+20E", negInf, " -∞"},
{"% +20g", negInf, " -∞"},
{"%+-20G", posInf, "+ "},
{"%20e", NaN, " NaN"},
{"% +20E", NaN, " +NaN"},
{"% -20g", NaN, " NaN "},
{"%+-20G", NaN, "+NaN "},
{"% +20E", NaN, " NaN"},
{"% -20g", NaN, "NaN "},
{"%+-20G", NaN, "NaN "},
// Zero padding does not apply to infinities and NaN.
{"%+020e", posInf, " +Inf"},
{"%-020f", negInf, "-Inf "},
{"%+020e", posInf, " +∞"},
{"%-020f", negInf, "- "},
{"%-020E", NaN, "NaN "},
// complex values
@@ -495,24 +503,24 @@ var fmtTests = []struct {
{"% .f", 0i, "( 0+0i)"},
{"%+.f", 0i, "(+0+0i)"},
{"% +.f", 0i, "(+0+0i)"},
{"%+.3e", 0i, "(+0.000e+00+0.000e+00i)"},
{"%+.3e", 0i, "(+0.000\u202f×\u202f10⁰⁰+0.000\u202f×\u202f10⁰⁰i)"},
{"%+.3f", 0i, "(+0.000+0.000i)"},
{"%+.3g", 0i, "(+0+0i)"},
{"%+.3e", 1 + 2i, "(+1.000e+00+2.000e+00i)"},
{"%+.3e", 1 + 2i, "(+1.000\u202f×\u202f10⁰⁰+2.000\u202f×\u202f10⁰⁰i)"},
{"%+.3f", 1 + 2i, "(+1.000+2.000i)"},
{"%+.3g", 1 + 2i, "(+1+2i)"},
{"%.3e", 0i, "(0.000e+00+0.000e+00i)"},
{"%.3e", 0i, "(0.000\u202f×\u202f10⁰⁰+0.000\u202f×\u202f10⁰⁰i)"},
{"%.3f", 0i, "(0.000+0.000i)"},
{"%.3F", 0i, "(0.000+0.000i)"},
{"%.3F", complex64(0i), "(0.000+0.000i)"},
{"%.3g", 0i, "(0+0i)"},
{"%.3e", 1 + 2i, "(1.000e+00+2.000e+00i)"},
{"%.3e", 1 + 2i, "(1.000\u202f×\u202f10⁰⁰+2.000\u202f×\u202f10⁰⁰i)"},
{"%.3f", 1 + 2i, "(1.000+2.000i)"},
{"%.3g", 1 + 2i, "(1+2i)"},
{"%.3e", -1 - 2i, "(-1.000e+00-2.000e+00i)"},
{"%.3e", -1 - 2i, "(-1.000\u202f×\u202f10⁰⁰-2.000\u202f×\u202f10⁰⁰i)"},
{"%.3f", -1 - 2i, "(-1.000-2.000i)"},
{"%.3g", -1 - 2i, "(-1-2i)"},
{"% .3E", -1 - 2i, "(-1.000E+00-2.000E+00i)"},
{"% .3E", -1 - 2i, "(-1.000\u202f×\u202f10⁰⁰-2.000\u202f×\u202f10⁰⁰i)"},
{"%+.3g", 1 + 2i, "(+1+2i)"},
{"%+.3g", complex64(1 + 2i), "(+1+2i)"},
{"%#g", 1 + 2i, "(1.00000+2.00000i)"},
@@ -537,70 +545,70 @@ var fmtTests = []struct {
{"%.4b", 1 + 2i, "(4503599627370496p-52+4503599627370496p-51i)"},
{"%.4b", complex64(1 + 2i), "(8388608p-23+8388608p-22i)"},
// complex infinites and NaNs
{"%f", complex(posInf, posInf), "(+Inf+Infi)"},
{"%f", complex(negInf, negInf), "(-Inf-Infi)"},
{"%f", complex(posInf, posInf), "(∞+∞i)"},
{"%f", complex(negInf, negInf), "(-∞-∞i)"},
{"%f", complex(NaN, NaN), "(NaN+NaNi)"},
{"%.1f", complex(posInf, posInf), "(+Inf+Infi)"},
{"% f", complex(posInf, posInf), "( Inf+Infi)"},
{"% f", complex(negInf, negInf), "(-Inf-Infi)"},
{"% f", complex(NaN, NaN), "( NaN+NaNi)"},
{"%8e", complex(posInf, posInf), "( +Inf +Infi)"},
{"% 8E", complex(posInf, posInf), "( Inf +Infi)"},
{"%+8f", complex(negInf, negInf), "( -Inf -Infi)"},
{"% +8g", complex(negInf, negInf), "( -Inf -Infi)"},
{"% -8G", complex(NaN, NaN), "( NaN +NaN i)"},
{"%.1f", complex(posInf, posInf), "(∞+∞i)"},
{"% f", complex(posInf, posInf), "( ∞+∞i)"},
{"% f", complex(negInf, negInf), "(-∞-∞i)"},
{"% f", complex(NaN, NaN), "(NaN+NaNi)"},
{"%8e", complex(posInf, posInf), "( +i)"},
{"% 8E", complex(posInf, posInf), "( +∞i)"},
{"%+8f", complex(negInf, negInf), "( -∞ -∞i)"},
{"% +8g", complex(negInf, negInf), "( -∞ -i)"}, // TODO(g)
{"% -8G", complex(NaN, NaN), "(NaN +NaN i)"},
{"%+-8b", complex(NaN, NaN), "(+NaN +NaN i)"},
// Zero padding does not apply to infinities and NaN.
{"%08f", complex(posInf, posInf), "( +Inf +Infi)"},
{"%-08g", complex(negInf, negInf), "(-Inf -Inf i)"},
{"%08f", complex(posInf, posInf), "( +i)"},
{"%-08g", complex(negInf, negInf), "(- - i)"},
{"%-08G", complex(NaN, NaN), "(NaN +NaN i)"},
// old test/fmt_test.go
{"%e", 1.0, "1.000000e+00"},
{"%e", 1234.5678e3, "1.234568e+06"},
{"%e", 1234.5678e-8, "1.234568e-05"},
{"%e", -7.0, "-7.000000e+00"},
{"%e", -1e-9, "-1.000000e-09"},
{"%f", 1234.5678e3, "1234567.800000"},
{"%e", 1.0, "1.000000\u202f×\u202f10⁰⁰"},
{"%e", 1234.5678e3, "1.234570\u202f×\u202f10⁰⁶"},
{"%e", 1234.5678e-8, "1.234570\u202f×\u202f10⁻⁰⁵"},
{"%e", -7.0, "-7.000000\u202f×\u202f10⁰⁰"},
{"%e", -1e-9, "-1.000000\u202f×\u202f10⁻⁰⁹"},
{"%f", 1234.5678e3, "1,234,567.800000"},
{"%f", 1234.5678e-8, "0.000012"},
{"%f", -7.0, "-7.000000"},
{"%f", -1e-9, "-0.000000"},
{"%g", 1234.5678e3, "1.2345678e+06"},
{"%g", float32(1234.5678e3), "1.2345678e+06"},
{"%g", 1234.5678e-8, "1.2345678e-05"},
{"%g", 1234.5678e3, "1.2345678\u202f×\u202f10⁰⁶"},
{"%g", float32(1234.5678e3), "1.2345678\u202f×\u202f10⁰⁶"},
{"%g", 1234.5678e-8, "1.2345678\u202f×\u202f10⁻⁰⁵"},
{"%g", -7.0, "-7"},
{"%g", -1e-9, "-1e-09"},
{"%g", float32(-1e-9), "-1e-09"},
{"%E", 1.0, "1.000000E+00"},
{"%E", 1234.5678e3, "1.234568E+06"},
{"%E", 1234.5678e-8, "1.234568E-05"},
{"%E", -7.0, "-7.000000E+00"},
{"%E", -1e-9, "-1.000000E-09"},
{"%G", 1234.5678e3, "1.2345678E+06"},
{"%G", float32(1234.5678e3), "1.2345678E+06"},
{"%G", 1234.5678e-8, "1.2345678E-05"},
{"%g", -1e-9, "-1\u202f×\u202f10⁻⁰⁹"},
{"%g", float32(-1e-9), "-1\u202f×\u202f10⁻⁰⁹"},
{"%E", 1.0, "1.000000\u202f×\u202f10⁰⁰"},
{"%E", 1234.5678e3, "1.234570\u202f×\u202f10⁰⁶"},
{"%E", 1234.5678e-8, "1.234570\u202f×\u202f10⁻⁰⁵"},
{"%E", -7.0, "-7.000000\u202f×\u202f10⁰⁰"},
{"%E", -1e-9, "-1.000000\u202f×\u202f10⁻⁰⁹"},
{"%G", 1234.5678e3, "1.2345678\u202f×\u202f10⁰⁶"},
{"%G", float32(1234.5678e3), "1.2345678\u202f×\u202f10⁰⁶"},
{"%G", 1234.5678e-8, "1.2345678\u202f×\u202f10⁻⁰⁵"},
{"%G", -7.0, "-7"},
{"%G", -1e-9, "-1E-09"},
{"%G", float32(-1e-9), "-1E-09"},
{"%G", -1e-9, "-1\u202f×\u202f10⁻⁰⁹"},
{"%G", float32(-1e-9), "-1\u202f×\u202f10⁻⁰⁹"},
{"%20.5s", "qwertyuiop", " qwert"},
{"%.5s", "qwertyuiop", "qwert"},
{"%-20.5s", "qwertyuiop", "qwert "},
{"%20c", 'x', " x"},
{"%-20c", 'x', "x "},
{"%20.6e", 1.2345e3, " 1.234500e+03"},
{"%20.6e", 1.2345e-3, " 1.234500e-03"},
{"%20e", 1.2345e3, " 1.234500e+03"},
{"%20e", 1.2345e-3, " 1.234500e-03"},
{"%20.8e", 1.2345e3, " 1.23450000e+03"},
{"%20f", 1.23456789e3, " 1234.567890"},
{"%20.6e", 1.2345e3, " 1.234500\u202f×\u202f10⁰³"},
{"%20.6e", 1.2345e-3, " 1.234500\u202f×\u202f10⁻⁰³"},
{"%20e", 1.2345e3, " 1.234500\u202f×\u202f10⁰³"},
{"%20e", 1.2345e-3, " 1.234500\u202f×\u202f10⁻⁰³"},
{"%20.8e", 1.2345e3, " 1.23450000\u202f×\u202f10⁰³"},
{"%20f", 1.23456789e3, " 1,234.567890"},
{"%20f", 1.23456789e-3, " 0.001235"},
{"%20f", 12345678901.23456789, " 12345678901.234568"},
{"%-20f", 1.23456789e3, "1234.567890 "},
{"%20.8f", 1.23456789e3, " 1234.56789000"},
{"%20f", 12345678901.23456789, "12,345,678,901.234568"},
{"%-20f", 1.23456789e3, "1,234.567890 "},
{"%20.8f", 1.23456789e3, " 1,234.56789000"},
{"%20.8f", 1.23456789e-3, " 0.00123457"},
{"%g", 1.23456789e3, "1234.56789"},
{"%g", 1.23456789e3, "1,234.56789"},
{"%g", 1.23456789e-3, "0.00123456789"},
{"%g", 1.23456789e20, "1.23456789e+20"},
{"%g", 1.23456789e20, "1.23456789\u202f×\u202f10²⁰"},
// arrays
{"%v", array, "[1 2 3 4 5]"},
@@ -930,8 +938,11 @@ var fmtTests = []struct {
{"%+7.2f", -1.0, " -1.00"},
{"% +7.2f", 1.0, " +1.00"},
{"% +7.2f", -1.0, " -1.00"},
{"%07.2f", 1.0, "0001.00"},
{"%07.2f", -1.0, "-001.00"},
// Padding with 0's indicates minimum number of integer digits minus the
// period, if present, and minus the sign if it is fixed.
// TODO: consider making this number the number of significant digits.
{"%07.2f", 1.0, "0,001.00"},
{"%07.2f", -1.0, "-0,001.00"},
{"% 07.2f", 1.0, " 001.00"},
{"% 07.2f", -1.0, "-001.00"},
{"%+07.2f", 1.0, "+001.00"},
@@ -949,13 +960,13 @@ var fmtTests = []struct {
// float and complex formatting should not change the padding width
// for other elements. See issue 14642.
{"%06v", []interface{}{+10.0, 10}, "[000010 000010]"},
{"%06v", []interface{}{-10.0, 10}, "[-00010 000010]"},
{"%06v", []interface{}{+10.0 + 10i, 10}, "[(000010+00010i) 000010]"},
{"%06v", []interface{}{-10.0 + 10i, 10}, "[(-00010+00010i) 000010]"},
{"%06v", []interface{}{+10.0, 10}, "[000,010 000,010]"},
{"%06v", []interface{}{-10.0, 10}, "[-000,010 000,010]"},
{"%06v", []interface{}{+10.0 + 10i, 10}, "[(000,010+00,010i) 000,010]"},
{"%06v", []interface{}{-10.0 + 10i, 10}, "[(-000,010+00,010i) 000,010]"},
// integer formatting should not alter padding for other elements.
{"%03.6v", []interface{}{1, 2.0, "x"}, "[000001 002 00x]"},
{"%03.6v", []interface{}{1, 2.0, "x"}, "[000,001 002 00x]"},
{"%03.0v", []interface{}{0, 2.0, "x"}, "[ 002 000]"},
// Complex fmt used to leave the plus flag set for future entries in the array
@@ -971,10 +982,11 @@ var fmtTests = []struct {
{"%+10.2f", -104.66 + 440.51i, "( -104.66 +440.51i)"},
{"%+10.2f", +104.66 - 440.51i, "( +104.66 -440.51i)"},
{"%+10.2f", -104.66 - 440.51i, "( -104.66 -440.51i)"},
{"%+010.2f", +104.66 + 440.51i, "(+000104.66+000440.51i)"},
{"%+010.2f", -104.66 + 440.51i, "(-000104.66+000440.51i)"},
{"%+010.2f", +104.66 - 440.51i, "(+000104.66-000440.51i)"},
{"%+010.2f", -104.66 - 440.51i, "(-000104.66-000440.51i)"},
{"%010.2f", +104.66 + 440.51i, "(0,000,104.66+000,440.51i)"},
{"%+010.2f", +104.66 + 440.51i, "(+000,104.66+000,440.51i)"},
{"%+010.2f", -104.66 + 440.51i, "(-000,104.66+000,440.51i)"},
{"%+010.2f", +104.66 - 440.51i, "(+000,104.66-000,440.51i)"},
{"%+010.2f", -104.66 - 440.51i, "(-000,104.66-000,440.51i)"},
// []T where type T is a byte with a Stringer method.
{"%v", byteStringerSlice, "[X X X X X]"},
@@ -1043,7 +1055,7 @@ func zeroFill(prefix string, width int, suffix string) string {
func TestSprintf(t *testing.T) {
p := NewPrinter(language.Und)
for _, tt := range fmtTests {
t.Run(fmt.Sprint(tt.fmt, tt.val), func(t *testing.T) {
t.Run(fmt.Sprint(tt.fmt, "/", tt.val), func(t *testing.T) {
s := p.Sprintf(tt.fmt, tt.val)
i := strings.Index(tt.out, "PTR")
if i >= 0 && i < len(s) {
@@ -1126,6 +1138,14 @@ func TestComplexFormatting(t *testing.T) {
for _, imagValue := range values {
one := p.Sprintf(realFmt, complex(realValue, imagValue))
two := p.Sprintf("("+realFmt+imagFmt+"i)", realValue, imagValue)
if math.IsNaN(imagValue) {
p := len(two) - len("NaNi)") - 1
if two[p] == ' ' {
two = two[:p] + "+" + two[p+1:]
} else {
two = two[:p+1] + "+" + two[p+1:]
}
}
if one != two {
t.Error(f, one, two)
}
@@ -1626,14 +1646,14 @@ var startests = []struct {
{"%-*d", args(4, 42), "42 "},
{"%*d", args(-4, 42), "42 "},
{"%-*d", args(-4, 42), "42 "},
{"%.*d", args(4, 42), "0042"},
{"%*.*d", args(8, 4, 42), " 0042"},
{"%0*d", args(4, 42), "0042"},
{"%.*d", args(4, 42), "0,042"},
{"%*.*d", args(8, 4, 42), " 0,042"},
{"%0*d", args(4, 42), "0,042"},
// Some non-int types for width. (Issue 10732).
{"%0*d", args(uint(4), 42), "0042"},
{"%0*d", args(uint64(4), 42), "0042"},
{"%0*d", args('\x04', 42), "0042"},
{"%0*d", args(uintptr(4), 42), "0042"},
{"%0*d", args(uint(4), 42), "0,042"},
{"%0*d", args(uint64(4), 42), "0,042"},
{"%0*d", args('\x04', 42), "0,042"},
{"%0*d", args(uintptr(4), 42), "0,042"},
// erroneous
{"%*d", args(nil, 42), "%!(BADWIDTH)42"},

View File

@@ -5,6 +5,10 @@
// Package message implements formatted I/O for localized strings with functions
// analogous to the fmt's print functions.
//
// These are the important differences with fmt:
// - Output varies per locale.
// - The '#' flag is used to bypass localization.
//
// NOTE: Under construction. See https://golang.org/design/12750-localization
// and its corresponding proposal issue https://golang.org/issues/12750.
package message // import "golang.org/x/text/message"
@@ -17,6 +21,9 @@ import (
"golang.org/x/text/message/catalog"
)
// TODO: allow more than one goroutine per printer. This will allow porting from
// fmt much less error prone.
// A Printer implements language-specific formatted I/O analogous to the fmt
// package. Only one goroutine may use a Printer at the same time.
type Printer struct {
@@ -28,12 +35,37 @@ type Printer struct {
// road if it the benefits do not seem to outweigh the disadvantages.
}
type options struct {
cat *catalog.Catalog
// TODO:
// - allow %s to print integers in written form (tables are likely too large
// to enable this by default).
// - list behavior
//
}
// An Option defines an option of a Printer.
type Option func(o *options)
// Catalog defines the catalog to be used.
func Catalog(c *catalog.Catalog) Option {
return func(o *options) { o.cat = c }
}
// NewPrinter returns a Printer that formats messages tailored to language t.
func NewPrinter(t language.Tag) *Printer {
func NewPrinter(t language.Tag, opts ...Option) *Printer {
options := &options{
cat: defaultCatalog,
}
for _, o := range opts {
o(options)
}
p := &Printer{printer{
tag: t,
}}
p.printer.catContext = defaultCatalog.Context(t, &p.printer)
p.printer.toDecimal.InitDecimal(t)
p.printer.toScientific.InitScientific(t)
p.printer.catContext = options.cat.Context(t, &p.printer)
return p
}

View File

@@ -50,13 +50,14 @@ func TestBinding(t *testing.T) {
}
}
func TestFormatSelection(t *testing.T) {
func TestLocalization(t *testing.T) {
type test struct {
tag string
key Reference
args []interface{}
want string
}
args := func(x ...interface{}) []interface{} { return x }
empty := []interface{}{}
joe := []interface{}{"Joe"}
joeAndMary := []interface{}{"Joe", "Mary"}
@@ -126,6 +127,20 @@ func TestFormatSelection(t *testing.T) {
{"und", "hello %+%%s", joeAndMary, "hello %Joe%!(EXTRA string=Mary)"},
{"und", "hello %-42%%s ", joeAndMary, "hello %Joe %!(EXTRA string=Mary)"},
},
}, {
desc: "number formatting", // work around limitation of fmt
cat: []entry{
{"und", "files", "%d files left"},
{"und", "meters", "%.2f meters"},
{"de", "files", "%d Dateien übrig"},
},
test: []test{
{"en", "meters", args(3000.2), "3,000.20 meters"},
{"en-u-nu-gujr", "files", args(123456), "૧૨૩,૪૫૬ files left"},
{"de", "files", args(1234), "1.234 Dateien übrig"},
{"de-CH", "files", args(1234), "1234 Dateien übrig"},
{"de-CH-u-nu-mong", "files", args(1234), "᠑’᠒᠓᠔ Dateien übrig"},
},
}}
for _, tc := range testCases {
@@ -133,11 +148,7 @@ func TestFormatSelection(t *testing.T) {
for i, pt := range tc.test {
t.Run(fmt.Sprintf("%s:%d", tc.desc, i), func(t *testing.T) {
tag := language.MustParse(pt.tag)
p := Printer{printer{
tag: tag,
}}
p.printer.catContext = cat.Context(tag, &p.printer)
p := NewPrinter(language.MustParse(pt.tag), Catalog(cat))
if got := p.Sprintf(pt.key, pt.args...); got != pt.want {
t.Errorf("Sprintf(%q, %v) = %s; want %s",

View File

@@ -6,11 +6,12 @@ package message
import (
"bytes"
// TODO: consider copying interfaces from package fmt to avoid dependency.
"fmt"
"fmt" // TODO: consider copying interfaces from package fmt to avoid dependency.
"math"
"reflect"
"unicode/utf8"
"golang.org/x/text/internal/number"
"golang.org/x/text/language"
"golang.org/x/text/message/catalog"
)
@@ -66,6 +67,9 @@ type printer struct {
panicking bool
// erroring is set when printing an error string to guard against calling handleMethods.
erroring bool
toDecimal number.Formatter
toScientific number.Formatter
}
func (p *printer) reset() {
@@ -188,11 +192,15 @@ func (p *printer) fmtInteger(v uint64, isSigned bool, verb rune) {
case 'v':
if p.fmt.sharpV && !isSigned {
p.fmt0x64(v, true)
} else {
p.fmt.fmt_integer(v, 10, isSigned, ldigits)
return
}
fallthrough
case 'd':
p.fmt.fmt_integer(v, 10, isSigned, ldigits)
if p.fmt.sharp || p.fmt.sharpV {
p.fmt.fmt_integer(v, 10, isSigned, ldigits)
} else {
p.fmtDecimalInt(v, isSigned)
}
case 'b':
p.fmt.fmt_integer(v, 2, isSigned, ldigits)
case 'o':
@@ -220,19 +228,195 @@ func (p *printer) fmtInteger(v uint64, isSigned bool, verb rune) {
// is specified as last argument in the call to fmt_float.
func (p *printer) fmtFloat(v float64, size int, verb rune) {
switch verb {
case 'v':
p.fmt.fmt_float(v, size, 'g', -1)
case 'b', 'g', 'G':
case 'b':
p.fmt.fmt_float(v, size, verb, -1)
case 'f', 'e', 'E':
p.fmt.fmt_float(v, size, verb, 6)
case 'F':
p.fmt.fmt_float(v, size, 'f', 6)
case 'v':
verb = 'g'
fallthrough
case 'g', 'G':
if p.fmt.sharp || p.fmt.sharpV {
p.fmt.fmt_float(v, size, verb, -1)
} else {
p.fmtVariableFloat(v, size, -1)
}
case 'e', 'E':
if p.fmt.sharp || p.fmt.sharpV {
p.fmt.fmt_float(v, size, verb, 6)
} else {
p.fmtScientific(v, size, 6)
}
case 'f', 'F':
if p.fmt.sharp || p.fmt.sharpV {
p.fmt.fmt_float(v, size, verb, 6)
} else {
p.fmtDecimalFloat(v, size, 6)
}
default:
p.badVerb(verb)
}
}
func (p *printer) setFlags(f *number.Formatter) {
f.Flags &^= number.ElideSign
if p.fmt.plus || p.fmt.space {
f.Flags |= number.AlwaysSign
if !p.fmt.plus {
f.Flags |= number.ElideSign
}
} else {
f.Flags &^= number.AlwaysSign
}
}
func (p *printer) updatePadding(f *number.Formatter) {
f.Flags &^= number.PadMask
if p.fmt.minus {
f.Flags |= number.PadAfterSuffix
} else {
f.Flags |= number.PadBeforePrefix
}
f.PadRune = ' '
f.FormatWidth = uint16(p.fmt.wid)
}
func (p *printer) initDecimal(minFrac, maxFrac int) {
f := &p.toDecimal
f.MinIntegerDigits = 1
f.MaxIntegerDigits = 0
f.MinFractionDigits = uint8(minFrac)
f.MaxFractionDigits = uint8(maxFrac)
p.setFlags(f)
f.PadRune = 0
if p.fmt.widPresent {
if p.fmt.zero {
wid := p.fmt.wid
// Use significant integers for this.
// TODO: this is not the same as width, but so be it.
if f.MinFractionDigits > 0 {
wid -= 1 + int(f.MinFractionDigits)
}
if p.fmt.plus || p.fmt.space {
wid--
}
if wid > 0 && wid > int(f.MinIntegerDigits) {
f.MinIntegerDigits = uint8(wid)
}
}
p.updatePadding(f)
}
}
func (p *printer) initScientific(minFrac, maxFrac int) {
f := &p.toScientific
f.MinFractionDigits = uint8(minFrac)
f.MaxFractionDigits = uint8(maxFrac)
f.MinExponentDigits = 2
p.setFlags(f)
f.PadRune = 0
if p.fmt.widPresent {
f.Flags &^= number.PadMask
if p.fmt.zero {
f.PadRune = f.Digit(0)
f.Flags |= number.PadAfterPrefix
} else {
f.PadRune = ' '
f.Flags |= number.PadBeforePrefix
}
p.updatePadding(f)
}
}
func (p *printer) fmtDecimalInt(v uint64, isSigned bool) {
var d number.Decimal
p.toDecimal.RoundingContext.Scale = 0
d.ConvertInt(&p.toDecimal.RoundingContext, isSigned, v)
f := &p.toDecimal
if p.fmt.precPresent {
p.setFlags(f)
f.MinIntegerDigits = uint8(p.fmt.prec)
f.MaxIntegerDigits = 0
f.MinFractionDigits = 0
f.MaxFractionDigits = 0
if p.fmt.widPresent {
p.updatePadding(f)
}
} else {
p.initDecimal(0, 0)
}
out := p.toDecimal.Format([]byte(nil), &d)
p.Buffer.Write(out)
}
func (p *printer) fmtDecimalFloat(v float64, size, prec int) {
var d number.Decimal
if p.fmt.precPresent {
prec = p.fmt.prec
}
p.toDecimal.RoundingContext.Scale = int32(prec)
d.ConvertFloat(&p.toDecimal.RoundingContext, v, size)
p.initDecimal(prec, prec)
out := p.toDecimal.Format([]byte(nil), &d)
p.Buffer.Write(out)
}
func (p *printer) fmtVariableFloat(v float64, size, prec int) {
if p.fmt.precPresent {
prec = p.fmt.prec
}
var d number.Decimal
p.toScientific.RoundingContext.Precision = int32(prec)
d.ConvertFloat(&p.toScientific.RoundingContext, v, size)
// Copy logic of 'g' formatting from strconv. It is simplified a bit as
// we don't have to mind having prec > len(d.Digits).
shortest := prec < 0
ePrec := prec
if shortest {
prec = len(d.Digits)
ePrec = 6
} else if prec == 0 {
prec = 1
ePrec = 1
}
exp := int(d.Exp) - 1
if exp < -4 || exp >= ePrec {
p.initScientific(0, prec)
out := p.toScientific.Format([]byte(nil), &d)
p.Buffer.Write(out)
} else {
if prec > int(d.Exp) {
prec = len(d.Digits)
}
if prec -= int(d.Exp); prec < 0 {
prec = 0
}
p.initDecimal(0, prec)
out := p.toDecimal.Format([]byte(nil), &d)
p.Buffer.Write(out)
}
}
func (p *printer) fmtScientific(v float64, size, prec int) {
var d number.Decimal
if p.fmt.precPresent {
prec = p.fmt.prec
}
p.toScientific.RoundingContext.Precision = int32(prec)
d.ConvertFloat(&p.toScientific.RoundingContext, v, size)
p.initScientific(prec, prec)
out := p.toScientific.Format([]byte(nil), &d)
p.Buffer.Write(out)
}
// fmtComplex formats a complex number v with
// r = real(v) and j = imag(v) as (r+ji) using
// fmtFloat for r and j formatting.
@@ -241,13 +425,39 @@ func (p *printer) fmtComplex(v complex128, size int, verb rune) {
// calls to fmtFloat to not generate an incorrect error string.
switch verb {
case 'v', 'b', 'g', 'G', 'f', 'F', 'e', 'E':
oldPlus := p.fmt.plus
p.WriteByte('(')
p.fmtFloat(real(v), size/2, verb)
// Imaginary part always has a sign.
if math.IsNaN(imag(v)) {
// By CLDR's rules, NaNs do not use patterns or signs. As this code
// relies on AlwaysSign working for imaginary parts, we need to
// manually handle NaNs.
f := &p.toScientific
p.setFlags(f)
p.updatePadding(f)
p.setFlags(f)
nan := f.Symbol(number.SymNan)
extra := 0
if w, ok := p.Width(); ok {
extra = w - utf8.RuneCountInString(nan) - 1
}
if f.Flags&number.PadAfterNumber == 0 {
for ; extra > 0; extra-- {
p.WriteRune(f.PadRune)
}
}
p.WriteString(f.Symbol(number.SymPlusSign))
p.WriteString(nan)
for ; extra > 0; extra-- {
p.WriteRune(f.PadRune)
}
p.WriteString("i)")
return
}
oldPlus := p.fmt.plus
p.fmt.plus = true
p.fmtFloat(imag(v), size/2, verb)
p.WriteString("i)")
p.WriteString("i)") // TODO: use symbol?
p.fmt.plus = oldPlus
default:
p.badVerb(verb)
@@ -347,6 +557,9 @@ func (p *printer) fmtPointer(value reflect.Value, verb rune) {
case 'p':
p.fmt0x64(uint64(u), !p.fmt.sharp)
case 'b', 'o', 'd', 'x', 'X':
if verb == 'd' {
p.fmt.sharp = true // Print as standard go. TODO: does this make sense?
}
p.fmtInteger(uint64(u), unsigned, verb)
default:
p.badVerb(verb)