pax_global_header00006660000000000000000000000064146474515020014522gustar00rootroot0000000000000052 comment=6ceadc68530e7bfea8cba17d6523bed32912d4fa go-runewidth-0.0.16/000077500000000000000000000000001464745150200142225ustar00rootroot00000000000000go-runewidth-0.0.16/.github/000077500000000000000000000000001464745150200155625ustar00rootroot00000000000000go-runewidth-0.0.16/.github/FUNDING.yml000066400000000000000000000013151464745150200173770ustar00rootroot00000000000000# These are supported funding model platforms github: mattn # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] go-runewidth-0.0.16/.github/workflows/000077500000000000000000000000001464745150200176175ustar00rootroot00000000000000go-runewidth-0.0.16/.github/workflows/test.yaml000066400000000000000000000013011464745150200214550ustar00rootroot00000000000000name: test on: push: branches: - master pull_request: branches: - master jobs: test: strategy: matrix: os: [windows-latest, macos-latest, ubuntu-latest] go: - "1.15" - "1.16" - "1.17" - "1.18" - "1.19" - "1.20" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v3 - uses: actions/setup-go@v4 with: go-version: ${{ matrix.go }} - run: go generate ./... - run: git diff --cached --exit-code - run: go test ./... -v -cover -coverprofile coverage.out - run: go test -bench . -benchmem - uses: codecov/codecov-action@v3 go-runewidth-0.0.16/LICENSE000066400000000000000000000020751464745150200152330ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Yasuhiro Matsumoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. go-runewidth-0.0.16/README.md000066400000000000000000000015011464745150200154760ustar00rootroot00000000000000go-runewidth ============ [![Build Status](https://github.com/mattn/go-runewidth/workflows/test/badge.svg?branch=master)](https://github.com/mattn/go-runewidth/actions?query=workflow%3Atest) [![Codecov](https://codecov.io/gh/mattn/go-runewidth/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-runewidth) [![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth) [![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth) Provides functions to get fixed width of the character or string. Usage ----- ```go runewidth.StringWidth("つのだ☆HIRO") == 12 ``` Author ------ Yasuhiro Matsumoto License ------- under the MIT License: http://mattn.mit-license.org/2013 go-runewidth-0.0.16/benchmark_test.go000066400000000000000000000072371464745150200175530ustar00rootroot00000000000000package runewidth import ( "testing" "unicode/utf8" ) var benchSink int // // RuneWidth // func benchRuneWidth(b *testing.B, eastAsianWidth bool, start, stop rune, want int) int { b.Helper() n := 0 b.Run("regular", func(b *testing.B) { got := -1 c := NewCondition() c.EastAsianWidth = eastAsianWidth b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { got = n for r := start; r < stop; r++ { n += c.RuneWidth(r) } got = n - got } if want != 0 && got != want { // some extra checks b.Errorf("got %d, want %d\n", got, want) } }) b.Run("lut", func(b *testing.B) { got := -1 n = 0 c := NewCondition() c.EastAsianWidth = eastAsianWidth c.CreateLUT() b.ReportAllocs() b.ResetTimer() for i := 0; i < b.N; i++ { got = n for r := start; r < stop; r++ { n += c.RuneWidth(r) } got = n - got } if want != 0 && got != want { // some extra checks b.Errorf("got %d, want %d\n", got, want) } }) return n } func BenchmarkRuneWidthAll(b *testing.B) { benchSink = benchRuneWidth(b, false, 0, utf8.MaxRune+1, 1293942) } func BenchmarkRuneWidth768(b *testing.B) { benchSink = benchRuneWidth(b, false, 0, 0x300, 702) } func BenchmarkRuneWidthAllEastAsian(b *testing.B) { benchSink = benchRuneWidth(b, true, 0, utf8.MaxRune+1, 1432568) } func BenchmarkRuneWidth768EastAsian(b *testing.B) { benchSink = benchRuneWidth(b, true, 0, 0x300, 794) } // // String1Width - strings which consist of a single rune // func benchString1Width(b *testing.B, eastAsianWidth bool, start, stop rune, want int) int { b.Helper() n := 0 b.Run("regular", func(b *testing.B) { got := -1 c := NewCondition() c.EastAsianWidth = eastAsianWidth b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { got = n for r := start; r < stop; r++ { s := string(r) n += c.StringWidth(s) } got = n - got } if want != 0 && got != want { // some extra checks b.Errorf("got %d, want %d\n", got, want) } }) b.Run("lut", func(b *testing.B) { got := -1 n = 0 c := NewCondition() c.EastAsianWidth = eastAsianWidth c.CreateLUT() b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { got = n for r := start; r < stop; r++ { s := string(r) n += c.StringWidth(s) } got = n - got } if want != 0 && got != want { // some extra checks b.Errorf("got %d, want %d\n", got, want) } }) return n } func BenchmarkString1WidthAll(b *testing.B) { benchSink = benchString1Width(b, false, 0, utf8.MaxRune+1, 1295990) } func BenchmarkString1Width768(b *testing.B) { benchSink = benchString1Width(b, false, 0, 0x300, 702) } func BenchmarkString1WidthAllEastAsian(b *testing.B) { benchSink = benchString1Width(b, true, 0, utf8.MaxRune+1, 1436664) } func BenchmarkString1Width768EastAsian(b *testing.B) { benchSink = benchString1Width(b, true, 0, 0x300, 794) } // // tables // func benchTable(b *testing.B, tbl table) int { n := 0 for i := 0; i < b.N; i++ { for r := rune(0); r <= utf8.MaxRune; r++ { if inTable(r, tbl) { n++ } } } return n } func BenchmarkTablePrivate(b *testing.B) { benchSink = benchTable(b, private) } func BenchmarkTableNonprint(b *testing.B) { benchSink = benchTable(b, nonprint) } func BenchmarkTableCombining(b *testing.B) { benchSink = benchTable(b, combining) } func BenchmarkTableDoublewidth(b *testing.B) { benchSink = benchTable(b, doublewidth) } func BenchmarkTableAmbiguous(b *testing.B) { benchSink = benchTable(b, ambiguous) } func BenchmarkTableEmoji(b *testing.B) { benchSink = benchTable(b, emoji) } func BenchmarkTableNarrow(b *testing.B) { benchSink = benchTable(b, narrow) } func BenchmarkTableNeutral(b *testing.B) { benchSink = benchTable(b, neutral) } go-runewidth-0.0.16/go.mod000066400000000000000000000001241464745150200153250ustar00rootroot00000000000000module github.com/mattn/go-runewidth go 1.9 require github.com/rivo/uniseg v0.2.0 go-runewidth-0.0.16/go.sum000066400000000000000000000002431464745150200153540ustar00rootroot00000000000000github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= go-runewidth-0.0.16/runewidth.go000066400000000000000000000170401464745150200165640ustar00rootroot00000000000000package runewidth import ( "os" "strings" "github.com/rivo/uniseg" ) //go:generate go run script/generate.go var ( // EastAsianWidth will be set true if the current locale is CJK EastAsianWidth bool // StrictEmojiNeutral should be set false if handle broken fonts StrictEmojiNeutral bool = true // DefaultCondition is a condition in current locale DefaultCondition = &Condition{ EastAsianWidth: false, StrictEmojiNeutral: true, } ) func init() { handleEnv() } func handleEnv() { env := os.Getenv("RUNEWIDTH_EASTASIAN") if env == "" { EastAsianWidth = IsEastAsian() } else { EastAsianWidth = env == "1" } // update DefaultCondition if DefaultCondition.EastAsianWidth != EastAsianWidth { DefaultCondition.EastAsianWidth = EastAsianWidth if len(DefaultCondition.combinedLut) > 0 { DefaultCondition.combinedLut = DefaultCondition.combinedLut[:0] CreateLUT() } } } type interval struct { first rune last rune } type table []interval func inTables(r rune, ts ...table) bool { for _, t := range ts { if inTable(r, t) { return true } } return false } func inTable(r rune, t table) bool { if r < t[0].first { return false } bot := 0 top := len(t) - 1 for top >= bot { mid := (bot + top) >> 1 switch { case t[mid].last < r: bot = mid + 1 case t[mid].first > r: top = mid - 1 default: return true } } return false } var private = table{ {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, } var nonprint = table{ {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, {0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, } // Condition have flag EastAsianWidth whether the current locale is CJK or not. type Condition struct { combinedLut []byte EastAsianWidth bool StrictEmojiNeutral bool } // NewCondition return new instance of Condition which is current locale. func NewCondition() *Condition { return &Condition{ EastAsianWidth: EastAsianWidth, StrictEmojiNeutral: StrictEmojiNeutral, } } // RuneWidth returns the number of cells in r. // See http://www.unicode.org/reports/tr11/ func (c *Condition) RuneWidth(r rune) int { if r < 0 || r > 0x10FFFF { return 0 } if len(c.combinedLut) > 0 { return int(c.combinedLut[r>>1]>>(uint(r&1)*4)) & 3 } // optimized version, verified by TestRuneWidthChecksums() if !c.EastAsianWidth { switch { case r < 0x20: return 0 case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint return 0 case r < 0x300: return 1 case inTable(r, narrow): return 1 case inTables(r, nonprint, combining): return 0 case inTable(r, doublewidth): return 2 default: return 1 } } else { switch { case inTables(r, nonprint, combining): return 0 case inTable(r, narrow): return 1 case inTables(r, ambiguous, doublewidth): return 2 case !c.StrictEmojiNeutral && inTables(r, ambiguous, emoji, narrow): return 2 default: return 1 } } } // CreateLUT will create an in-memory lookup table of 557056 bytes for faster operation. // This should not be called concurrently with other operations on c. // If options in c is changed, CreateLUT should be called again. func (c *Condition) CreateLUT() { const max = 0x110000 lut := c.combinedLut if len(c.combinedLut) != 0 { // Remove so we don't use it. c.combinedLut = nil } else { lut = make([]byte, max/2) } for i := range lut { i32 := int32(i * 2) x0 := c.RuneWidth(i32) x1 := c.RuneWidth(i32 + 1) lut[i] = uint8(x0) | uint8(x1)<<4 } c.combinedLut = lut } // StringWidth return width as you can see func (c *Condition) StringWidth(s string) (width int) { g := uniseg.NewGraphemes(s) for g.Next() { var chWidth int for _, r := range g.Runes() { chWidth = c.RuneWidth(r) if chWidth > 0 { break // Our best guess at this point is to use the width of the first non-zero-width rune. } } width += chWidth } return } // Truncate return string truncated with w cells func (c *Condition) Truncate(s string, w int, tail string) string { if c.StringWidth(s) <= w { return s } w -= c.StringWidth(tail) var width int pos := len(s) g := uniseg.NewGraphemes(s) for g.Next() { var chWidth int for _, r := range g.Runes() { chWidth = c.RuneWidth(r) if chWidth > 0 { break // See StringWidth() for details. } } if width+chWidth > w { pos, _ = g.Positions() break } width += chWidth } return s[:pos] + tail } // TruncateLeft cuts w cells from the beginning of the `s`. func (c *Condition) TruncateLeft(s string, w int, prefix string) string { if c.StringWidth(s) <= w { return prefix } var width int pos := len(s) g := uniseg.NewGraphemes(s) for g.Next() { var chWidth int for _, r := range g.Runes() { chWidth = c.RuneWidth(r) if chWidth > 0 { break // See StringWidth() for details. } } if width+chWidth > w { if width < w { _, pos = g.Positions() prefix += strings.Repeat(" ", width+chWidth-w) } else { pos, _ = g.Positions() } break } width += chWidth } return prefix + s[pos:] } // Wrap return string wrapped with w cells func (c *Condition) Wrap(s string, w int) string { width := 0 out := "" for _, r := range s { cw := c.RuneWidth(r) if r == '\n' { out += string(r) width = 0 continue } else if width+cw > w { out += "\n" width = 0 out += string(r) width += cw continue } out += string(r) width += cw } return out } // FillLeft return string filled in left by spaces in w cells func (c *Condition) FillLeft(s string, w int) string { width := c.StringWidth(s) count := w - width if count > 0 { b := make([]byte, count) for i := range b { b[i] = ' ' } return string(b) + s } return s } // FillRight return string filled in left by spaces in w cells func (c *Condition) FillRight(s string, w int) string { width := c.StringWidth(s) count := w - width if count > 0 { b := make([]byte, count) for i := range b { b[i] = ' ' } return s + string(b) } return s } // RuneWidth returns the number of cells in r. // See http://www.unicode.org/reports/tr11/ func RuneWidth(r rune) int { return DefaultCondition.RuneWidth(r) } // IsAmbiguousWidth returns whether is ambiguous width or not. func IsAmbiguousWidth(r rune) bool { return inTables(r, private, ambiguous) } // IsNeutralWidth returns whether is neutral width or not. func IsNeutralWidth(r rune) bool { return inTable(r, neutral) } // StringWidth return width as you can see func StringWidth(s string) (width int) { return DefaultCondition.StringWidth(s) } // Truncate return string truncated with w cells func Truncate(s string, w int, tail string) string { return DefaultCondition.Truncate(s, w, tail) } // TruncateLeft cuts w cells from the beginning of the `s`. func TruncateLeft(s string, w int, prefix string) string { return DefaultCondition.TruncateLeft(s, w, prefix) } // Wrap return string wrapped with w cells func Wrap(s string, w int) string { return DefaultCondition.Wrap(s, w) } // FillLeft return string filled in left by spaces in w cells func FillLeft(s string, w int) string { return DefaultCondition.FillLeft(s, w) } // FillRight return string filled in left by spaces in w cells func FillRight(s string, w int) string { return DefaultCondition.FillRight(s, w) } // CreateLUT will create an in-memory lookup table of 557055 bytes for faster operation. // This should not be called concurrently with other operations. func CreateLUT() { if len(DefaultCondition.combinedLut) > 0 { return } DefaultCondition.CreateLUT() } go-runewidth-0.0.16/runewidth_appengine.go000066400000000000000000000002371464745150200206120ustar00rootroot00000000000000//go:build appengine // +build appengine package runewidth // IsEastAsian return true if the current locale is CJK func IsEastAsian() bool { return false } go-runewidth-0.0.16/runewidth_js.go000066400000000000000000000003201464745150200172510ustar00rootroot00000000000000//go:build js && !appengine // +build js,!appengine package runewidth func IsEastAsian() bool { // TODO: Implement this for the web. Detect east asian in a compatible way, and return true. return false } go-runewidth-0.0.16/runewidth_posix.go000066400000000000000000000027111464745150200200050ustar00rootroot00000000000000//go:build !windows && !js && !appengine // +build !windows,!js,!appengine package runewidth import ( "os" "regexp" "strings" ) var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) var mblenTable = map[string]int{ "utf-8": 6, "utf8": 6, "jis": 8, "eucjp": 3, "euckr": 2, "euccn": 2, "sjis": 2, "cp932": 2, "cp51932": 2, "cp936": 2, "cp949": 2, "cp950": 2, "big5": 2, "gbk": 2, "gb2312": 2, } func isEastAsian(locale string) bool { charset := strings.ToLower(locale) r := reLoc.FindStringSubmatch(locale) if len(r) == 2 { charset = strings.ToLower(r[1]) } if strings.HasSuffix(charset, "@cjk_narrow") { return false } for pos, b := range []byte(charset) { if b == '@' { charset = charset[:pos] break } } max := 1 if m, ok := mblenTable[charset]; ok { max = m } if max > 1 && (charset[0] != 'u' || strings.HasPrefix(locale, "ja") || strings.HasPrefix(locale, "ko") || strings.HasPrefix(locale, "zh")) { return true } return false } // IsEastAsian return true if the current locale is CJK func IsEastAsian() bool { locale := os.Getenv("LC_ALL") if locale == "" { locale = os.Getenv("LC_CTYPE") } if locale == "" { locale = os.Getenv("LANG") } // ignore C locale if locale == "POSIX" || locale == "C" { return false } if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { return false } return isEastAsian(locale) } go-runewidth-0.0.16/runewidth_posix_test.go000066400000000000000000000034111464745150200210420ustar00rootroot00000000000000//go:build !windows && !js && !appengine // +build !windows,!js,!appengine package runewidth import ( "os" "testing" ) type envVars struct { lang string lcall string lcctype string } func saveEnv() envVars { return envVars{ lang: os.Getenv("LANG"), lcall: os.Getenv("LC_ALL"), lcctype: os.Getenv("LC_CTYPE"), } } func restoreEnv(env *envVars) { os.Setenv("LANG", env.lang) os.Setenv("LC_ALL", env.lcall) os.Setenv("LC_CTYPE", env.lcctype) } func TestIsEastAsian(t *testing.T) { testcases := []struct { locale string want bool }{ {"foo@cjk_narrow", false}, {"foo@cjk", false}, {"utf-8@cjk", false}, {"ja_JP.CP932", true}, } for _, tt := range testcases { got := isEastAsian(tt.locale) if got != tt.want { t.Fatalf("isEastAsian(%q) should be %v", tt.locale, tt.want) } } } func TestIsEastAsianLCCTYPE(t *testing.T) { env := saveEnv() defer restoreEnv(&env) os.Setenv("LC_ALL", "") testcases := []struct { lcctype string want bool }{ {"ja_JP.UTF-8", true}, {"C", false}, {"POSIX", false}, {"en_US.UTF-8", false}, } for _, tt := range testcases { os.Setenv("LC_CTYPE", tt.lcctype) got := IsEastAsian() if got != tt.want { t.Fatalf("IsEastAsian() for LC_CTYPE=%v should be %v", tt.lcctype, tt.want) } } } func TestIsEastAsianLANG(t *testing.T) { env := saveEnv() defer restoreEnv(&env) os.Setenv("LC_ALL", "") os.Setenv("LC_CTYPE", "") testcases := []struct { lcctype string want bool }{ {"ja_JP.UTF-8", true}, {"C", false}, {"POSIX", false}, {"en_US.UTF-8", false}, {"C.UTF-8", false}, } for _, tt := range testcases { os.Setenv("LANG", tt.lcctype) got := IsEastAsian() if got != tt.want { t.Fatalf("IsEastAsian() for LANG=%v should be %v", tt.lcctype, tt.want) } } } go-runewidth-0.0.16/runewidth_table.go000066400000000000000000000602051464745150200177340ustar00rootroot00000000000000// Code generated by script/generate.go. DO NOT EDIT. package runewidth var combining = table{ {0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3}, {0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0CF3, 0x0CF3}, {0x0D00, 0x0D01}, {0x135D, 0x135F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1ACE}, {0x1B6B, 0x1B73}, {0x1DC0, 0x1DFF}, {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF}, {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A}, {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x10F82, 0x10F85}, {0x11300, 0x11301}, {0x1133B, 0x1133C}, {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x16AF0, 0x16AF4}, {0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E08F, 0x1E08F}, {0x1E8D0, 0x1E8D6}, } var doublewidth = table{ {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, {0x3105, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31E3}, {0x31EF, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x4DBF}, {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, {0x16FF0, 0x16FF1}, {0x17000, 0x187F7}, {0x18800, 0x18CD5}, {0x18D00, 0x18D08}, {0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB}, {0x1AFFD, 0x1AFFE}, {0x1B000, 0x1B122}, {0x1B132, 0x1B132}, {0x1B150, 0x1B152}, {0x1B155, 0x1B155}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004}, {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, {0x1F250, 0x1F251}, {0x1F260, 0x1F265}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335}, {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA}, {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4}, {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, {0x1F442, 0x1F4FC}, {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D7}, {0x1F6DC, 0x1F6DF}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, {0x1F7F0, 0x1F7F0}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F9FF}, {0x1FA70, 0x1FA7C}, {0x1FA80, 0x1FA88}, {0x1FA90, 0x1FABD}, {0x1FABF, 0x1FAC5}, {0x1FACE, 0x1FADB}, {0x1FAE0, 0x1FAE8}, {0x1FAF0, 0x1FAF8}, {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD}, } var ambiguous = table{ {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, } var narrow = table{ {0x0020, 0x007E}, {0x00A2, 0x00A3}, {0x00A5, 0x00A6}, {0x00AC, 0x00AC}, {0x00AF, 0x00AF}, {0x27E6, 0x27ED}, {0x2985, 0x2986}, } var neutral = table{ {0x0000, 0x001F}, {0x007F, 0x00A0}, {0x00A9, 0x00A9}, {0x00AB, 0x00AB}, {0x00B5, 0x00B5}, {0x00BB, 0x00BB}, {0x00C0, 0x00C5}, {0x00C7, 0x00CF}, {0x00D1, 0x00D6}, {0x00D9, 0x00DD}, {0x00E2, 0x00E5}, {0x00E7, 0x00E7}, {0x00EB, 0x00EB}, {0x00EE, 0x00EF}, {0x00F1, 0x00F1}, {0x00F4, 0x00F6}, {0x00FB, 0x00FB}, {0x00FD, 0x00FD}, {0x00FF, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112}, {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A}, {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E}, {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C}, {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A}, {0x016C, 0x01CD}, {0x01CF, 0x01CF}, {0x01D1, 0x01D1}, {0x01D3, 0x01D3}, {0x01D5, 0x01D5}, {0x01D7, 0x01D7}, {0x01D9, 0x01D9}, {0x01DB, 0x01DB}, {0x01DD, 0x0250}, {0x0252, 0x0260}, {0x0262, 0x02C3}, {0x02C5, 0x02C6}, {0x02C8, 0x02C8}, {0x02CC, 0x02CC}, {0x02CE, 0x02CF}, {0x02D1, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE}, {0x02E0, 0x02FF}, {0x0370, 0x0377}, {0x037A, 0x037F}, {0x0384, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x0390}, {0x03AA, 0x03B0}, {0x03C2, 0x03C2}, {0x03CA, 0x0400}, {0x0402, 0x040F}, {0x0450, 0x0450}, {0x0452, 0x052F}, {0x0531, 0x0556}, {0x0559, 0x058A}, {0x058D, 0x058F}, {0x0591, 0x05C7}, {0x05D0, 0x05EA}, {0x05EF, 0x05F4}, {0x0600, 0x070D}, {0x070F, 0x074A}, {0x074D, 0x07B1}, {0x07C0, 0x07FA}, {0x07FD, 0x082D}, {0x0830, 0x083E}, {0x0840, 0x085B}, {0x085E, 0x085E}, {0x0860, 0x086A}, {0x0870, 0x088E}, {0x0890, 0x0891}, {0x0898, 0x0983}, {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, {0x09BC, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CE}, {0x09D7, 0x09D7}, {0x09DC, 0x09DD}, {0x09DF, 0x09E3}, {0x09E6, 0x09FE}, {0x0A01, 0x0A03}, {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A76}, {0x0A81, 0x0A83}, {0x0A85, 0x0A8D}, {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9}, {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3}, {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF}, {0x0B01, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B35, 0x0B39}, {0x0B3C, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D}, {0x0B55, 0x0B57}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63}, {0x0B66, 0x0B77}, {0x0B82, 0x0B83}, {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C39}, {0x0C3C, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C5D, 0x0C5D}, {0x0C60, 0x0C63}, {0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, {0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, {0x0CDD, 0x0CDE}, {0x0CE0, 0x0CE3}, {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF3}, {0x0D00, 0x0D0C}, {0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, {0x0D46, 0x0D48}, {0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, {0x0D66, 0x0D7F}, {0x0D81, 0x0D83}, {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4}, {0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82}, {0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3}, {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECE}, {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C}, {0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC}, {0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7}, {0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248}, {0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258}, {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D}, {0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE}, {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6}, {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A}, {0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8}, {0x1700, 0x1715}, {0x171F, 0x1736}, {0x1740, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770}, {0x1772, 0x1773}, {0x1780, 0x17DD}, {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x1819}, {0x1820, 0x1878}, {0x1880, 0x18AA}, {0x18B0, 0x18F5}, {0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1940, 0x1940}, {0x1944, 0x196D}, {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA}, {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD}, {0x1AB0, 0x1ACE}, {0x1B00, 0x1B4C}, {0x1B50, 0x1B7E}, {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49}, {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7}, {0x1CD0, 0x1CFA}, {0x1D00, 0x1F15}, {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB}, {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE}, {0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017}, {0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023}, {0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034}, {0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064}, {0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080}, {0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8}, {0x20AA, 0x20AB}, {0x20AD, 0x20C0}, {0x20D0, 0x20F0}, {0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108}, {0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120}, {0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152}, {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F}, {0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7}, {0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6}, {0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206}, {0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210}, {0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C}, {0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226}, {0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B}, {0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251}, {0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269}, {0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285}, {0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4}, {0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319}, {0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF}, {0x23F1, 0x23F2}, {0x23F4, 0x2426}, {0x2440, 0x244A}, {0x24EA, 0x24EA}, {0x254C, 0x254F}, {0x2574, 0x257F}, {0x2590, 0x2591}, {0x2596, 0x259F}, {0x25A2, 0x25A2}, {0x25AA, 0x25B1}, {0x25B4, 0x25B5}, {0x25B8, 0x25BB}, {0x25BE, 0x25BF}, {0x25C2, 0x25C5}, {0x25C9, 0x25CA}, {0x25CC, 0x25CD}, {0x25D2, 0x25E1}, {0x25E6, 0x25EE}, {0x25F0, 0x25FC}, {0x25FF, 0x2604}, {0x2607, 0x2608}, {0x260A, 0x260D}, {0x2610, 0x2613}, {0x2616, 0x261B}, {0x261D, 0x261D}, {0x261F, 0x263F}, {0x2641, 0x2641}, {0x2643, 0x2647}, {0x2654, 0x265F}, {0x2662, 0x2662}, {0x2666, 0x2666}, {0x266B, 0x266B}, {0x266E, 0x266E}, {0x2670, 0x267E}, {0x2680, 0x2692}, {0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, {0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, {0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709}, {0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B}, {0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756}, {0x2758, 0x2775}, {0x2780, 0x2794}, {0x2798, 0x27AF}, {0x27B1, 0x27BE}, {0x27C0, 0x27E5}, {0x27EE, 0x2984}, {0x2987, 0x2B1A}, {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, {0x2B5A, 0x2B73}, {0x2B76, 0x2B95}, {0x2B97, 0x2CF3}, {0x2CF9, 0x2D25}, {0x2D27, 0x2D27}, {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D70}, {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x2DE0, 0x2E5D}, {0x303F, 0x303F}, {0x4DC0, 0x4DFF}, {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7CA}, {0xA7D0, 0xA7D1}, {0xA7D3, 0xA7D3}, {0xA7D5, 0xA7D9}, {0xA7F2, 0xA82C}, {0xA830, 0xA839}, {0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9}, {0xA8E0, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD}, {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36}, {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2}, {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, {0xAB30, 0xAB6B}, {0xAB70, 0xABED}, {0xABF0, 0xABF9}, {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF}, {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36}, {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, {0xFB43, 0xFB44}, {0xFB46, 0xFBC2}, {0xFBD3, 0xFD8F}, {0xFD92, 0xFDC7}, {0xFDCF, 0xFDCF}, {0xFDF0, 0xFDFF}, {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E}, {0x10190, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD}, {0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB}, {0x10300, 0x10323}, {0x1032D, 0x1034A}, {0x10350, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5}, {0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3}, {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563}, {0x1056F, 0x1057A}, {0x1057C, 0x1058A}, {0x1058C, 0x10592}, {0x10594, 0x10595}, {0x10597, 0x105A1}, {0x105A3, 0x105B1}, {0x105B3, 0x105B9}, {0x105BB, 0x105BC}, {0x10600, 0x10736}, {0x10740, 0x10755}, {0x10760, 0x10767}, {0x10780, 0x10785}, {0x10787, 0x107B0}, {0x107B2, 0x107BA}, {0x10800, 0x10805}, {0x10808, 0x10808}, {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C}, {0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF}, {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x1091B}, {0x1091F, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x109B7}, {0x109BC, 0x109CF}, {0x109D2, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35}, {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, {0x10A50, 0x10A58}, {0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6}, {0x10B00, 0x10B35}, {0x10B39, 0x10B55}, {0x10B58, 0x10B72}, {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF}, {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, {0x10CFA, 0x10D27}, {0x10D30, 0x10D39}, {0x10E60, 0x10E7E}, {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD}, {0x10EB0, 0x10EB1}, {0x10EFD, 0x10F27}, {0x10F30, 0x10F59}, {0x10F70, 0x10F89}, {0x10FB0, 0x10FCB}, {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x11075}, {0x1107F, 0x110C2}, {0x110CD, 0x110CD}, {0x110D0, 0x110E8}, {0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11147}, {0x11150, 0x11176}, {0x11180, 0x111DF}, {0x111E1, 0x111F4}, {0x11200, 0x11211}, {0x11213, 0x11241}, {0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D}, {0x1128F, 0x1129D}, {0x1129F, 0x112A9}, {0x112B0, 0x112EA}, {0x112F0, 0x112F9}, {0x11300, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, {0x11335, 0x11339}, {0x1133B, 0x11344}, {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350}, {0x11357, 0x11357}, {0x1135D, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7}, {0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD}, {0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B9}, {0x116C0, 0x116C9}, {0x11700, 0x1171A}, {0x1171D, 0x1172B}, {0x11730, 0x11746}, {0x11800, 0x1183B}, {0x118A0, 0x118F2}, {0x118FF, 0x11906}, {0x11909, 0x11909}, {0x1190C, 0x11913}, {0x11915, 0x11916}, {0x11918, 0x11935}, {0x11937, 0x11938}, {0x1193B, 0x11946}, {0x11950, 0x11959}, {0x119A0, 0x119A7}, {0x119AA, 0x119D7}, {0x119DA, 0x119E4}, {0x11A00, 0x11A47}, {0x11A50, 0x11AA2}, {0x11AB0, 0x11AF8}, {0x11B00, 0x11B09}, {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45}, {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09}, {0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D}, {0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, {0x11D60, 0x11D65}, {0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, {0x11D90, 0x11D91}, {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8}, {0x11F00, 0x11F10}, {0x11F12, 0x11F3A}, {0x11F3E, 0x11F59}, {0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, {0x12F90, 0x12FF2}, {0x13000, 0x13455}, {0x14400, 0x14646}, {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, {0x16A6E, 0x16ABE}, {0x16AC0, 0x16AC9}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A}, {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1CF50, 0x1CFC3}, {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D1EA}, {0x1D200, 0x1D245}, {0x1D2C0, 0x1D2D3}, {0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356}, {0x1D360, 0x1D378}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B}, {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1DF00, 0x1DF1E}, {0x1DF25, 0x1DF2A}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E030, 0x1E06D}, {0x1E08F, 0x1E08F}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D}, {0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E290, 0x1E2AE}, {0x1E2C0, 0x1E2F9}, {0x1E2FF, 0x1E2FF}, {0x1E4D0, 0x1E4F9}, {0x1E7E0, 0x1E7E6}, {0x1E7E8, 0x1E7EB}, {0x1E7ED, 0x1E7EE}, {0x1E7F0, 0x1E7FE}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6}, {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F}, {0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4}, {0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F776}, {0x1F77B, 0x1F7D9}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B}, {0x1F946, 0x1F946}, {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D}, {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F}, } var emoji = table{ {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122}, {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA}, {0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388}, {0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, {0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, {0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605}, {0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705}, {0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716}, {0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728}, {0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747}, {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797}, {0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C}, {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030}, {0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299}, {0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F}, {0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F}, {0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, {0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D}, {0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F}, {0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FAFF}, {0x1FC00, 0x1FFFD}, } go-runewidth-0.0.16/runewidth_test.go000066400000000000000000000310411464745150200176200ustar00rootroot00000000000000//go:build !js && !appengine // +build !js,!appengine package runewidth import ( "crypto/sha256" "fmt" "os" "sort" "testing" "unicode/utf8" ) var _ sort.Interface = (*table)(nil) // ensure that type "table" does implement sort.Interface func init() { os.Setenv("RUNEWIDTH_EASTASIAN", "") handleEnv() } func (t table) Len() int { return len(t) } func (t table) Less(i, j int) bool { return t[i].first < t[j].first } func (t *table) Swap(i, j int) { (*t)[i], (*t)[j] = (*t)[j], (*t)[i] } type tableInfo struct { tbl table name string wantN int wantSHA string } var tables = []tableInfo{ {private, "private", 137468, "a4a641206dc8c5de80bd9f03515a54a706a5a4904c7684dc6a33d65c967a51b2"}, {nonprint, "nonprint", 2143, "288904683eb225e7c4c0bd3ee481b53e8dace404ec31d443afdbc4d13729fe95"}, {combining, "combining", 555, "bf1cafd5aa2c3734b07a609ffd4d981cd3184e322a1b261431ff746031305cb4"}, {doublewidth, "doublewidth", 182521, "88f214dc0a0c31eb2bc083d1e4b3ad58f720634c6708be8b61f10446a8967b37"}, {ambiguous, "ambiguous", 138739, "d05e339a10f296de6547ff3d6c5aee32f627f6555477afebd4a3b7e3cf74c9e3"}, {emoji, "emoji", 3535, "9ec17351601d49c535658de8d129c1d0ccda2e620669fc39a2faaee7dedcef6d"}, {narrow, "narrow", 111, "fa897699c5e3cd9141c638d539331b0bdd508b874e22996c5e929767d455fc5a"}, {neutral, "neutral", 28382, "1cbccfec7db52c7bd0e6c97c26229278a221b68afc0ca7830f1ba7e86c9b6dbc"}, } func TestTableChecksums(t *testing.T) { for _, ti := range tables { gotN := 0 buf := make([]byte, utf8.MaxRune+1) for r := rune(0); r <= utf8.MaxRune; r++ { if inTable(r, ti.tbl) { gotN++ buf[r] = 1 } } gotSHA := fmt.Sprintf("%x", sha256.Sum256(buf)) if gotN != ti.wantN || gotSHA != ti.wantSHA { t.Errorf("table = %s,\n\tn = %d want %d,\n\tsha256 = %s want %s", ti.name, gotN, ti.wantN, gotSHA, ti.wantSHA) } } } func TestRuneWidthChecksums(t *testing.T) { var testcases = []struct { name string eastAsianWidth bool wantSHA string }{ {"ea-no", false, "a98d2a32d1b3407a3037636a279a73c3d549f6a9fbc8e92bee91dd991acdf0e1"}, {"ea-yes", true, "cac3940e576bfd67d8312b762ddee862caf388d30a137359a8d9b07ba09166de"}, } for _, testcase := range testcases { c := NewCondition() c.EastAsianWidth = testcase.eastAsianWidth buf := make([]byte, utf8.MaxRune+1) for r := rune(0); r <= utf8.MaxRune; r++ { buf[r] = byte(c.RuneWidth(r)) } gotSHA := fmt.Sprintf("%x", sha256.Sum256(buf)) if gotSHA != testcase.wantSHA { t.Errorf("TestRuneWidthChecksums = %s,\n\tsha256 = %s want %s", testcase.name, gotSHA, testcase.wantSHA) } // Test with LUT c.CreateLUT() for r := rune(0); r <= utf8.MaxRune; r++ { buf[r] = byte(c.RuneWidth(r)) } gotSHA = fmt.Sprintf("%x", sha256.Sum256(buf)) if gotSHA != testcase.wantSHA { t.Errorf("TestRuneWidthChecksums = %s,\n\tsha256 = %s want %s", testcase.name, gotSHA, testcase.wantSHA) } } } func TestDefaultLUT(t *testing.T) { var testcases = []struct { name string eastAsianWidth bool wantSHA string }{ {"ea-no", false, "a98d2a32d1b3407a3037636a279a73c3d549f6a9fbc8e92bee91dd991acdf0e1"}, {"ea-yes", true, "cac3940e576bfd67d8312b762ddee862caf388d30a137359a8d9b07ba09166de"}, } old := os.Getenv("RUNEWIDTH_EASTASIAN") defer os.Setenv("RUNEWIDTH_EASTASIAN", old) CreateLUT() for _, testcase := range testcases { c := DefaultCondition if testcase.eastAsianWidth { os.Setenv("RUNEWIDTH_EASTASIAN", "1") } else { os.Setenv("RUNEWIDTH_EASTASIAN", "0") } handleEnv() buf := make([]byte, utf8.MaxRune+1) for r := rune(0); r <= utf8.MaxRune; r++ { buf[r] = byte(c.RuneWidth(r)) } gotSHA := fmt.Sprintf("%x", sha256.Sum256(buf)) if gotSHA != testcase.wantSHA { t.Errorf("TestRuneWidthChecksums = %s,\n\tsha256 = %s want %s", testcase.name, gotSHA, testcase.wantSHA) } } // Remove for other tests. DefaultCondition.combinedLut = nil } func checkInterval(first, last rune) bool { return first >= 0 && first <= utf8.MaxRune && last >= 0 && last <= utf8.MaxRune && first <= last } func isCompact(t *testing.T, ti *tableInfo) bool { tbl := ti.tbl for i := range tbl { e := tbl[i] if !checkInterval(e.first, e.last) { // sanity check t.Errorf("table invalid: table = %s index = %d %v", ti.name, i, e) return false } if i+1 < len(tbl) && e.last+1 >= tbl[i+1].first { // can be combined into one entry t.Errorf("table not compact: table = %s index = %d %v %v", ti.name, i, e, tbl[i+1]) return false } } return true } func TestSorted(t *testing.T) { for _, ti := range tables { if !sort.IsSorted(&ti.tbl) { t.Errorf("table not sorted: %s", ti.name) } if !isCompact(t, &ti) { t.Errorf("table not compact: %s", ti.name) } } } var runewidthtests = []struct { in rune out int eaout int nseout int }{ {'世', 2, 2, 2}, {'界', 2, 2, 2}, {'セ', 1, 1, 1}, {'カ', 1, 1, 1}, {'イ', 1, 1, 1}, {'☆', 1, 2, 2}, // double width in ambiguous {'☺', 1, 1, 2}, {'☻', 1, 1, 2}, {'♥', 1, 2, 2}, {'♦', 1, 1, 2}, {'♣', 1, 2, 2}, {'♠', 1, 2, 2}, {'♂', 1, 2, 2}, {'♀', 1, 2, 2}, {'♪', 1, 2, 2}, {'♫', 1, 1, 2}, {'☼', 1, 1, 2}, {'↕', 1, 2, 2}, {'‼', 1, 1, 2}, {'↔', 1, 2, 2}, {'\x00', 0, 0, 0}, {'\x01', 0, 0, 0}, {'\u0300', 0, 0, 0}, {'\u2028', 0, 0, 0}, {'\u2029', 0, 0, 0}, {'a', 1, 1, 1}, // ASCII classified as "na" (narrow) {'⟦', 1, 1, 1}, // non-ASCII classified as "na" (narrow) {'👁', 1, 1, 2}, } func TestRuneWidth(t *testing.T) { c := NewCondition() c.EastAsianWidth = false for _, tt := range runewidthtests { if out := c.RuneWidth(tt.in); out != tt.out { t.Errorf("RuneWidth(%q) = %d, want %d (EastAsianWidth=false)", tt.in, out, tt.out) } } c.EastAsianWidth = true for _, tt := range runewidthtests { if out := c.RuneWidth(tt.in); out != tt.eaout { t.Errorf("RuneWidth(%q) = %d, want %d (EastAsianWidth=true)", tt.in, out, tt.eaout) } } c.StrictEmojiNeutral = false for _, tt := range runewidthtests { if out := c.RuneWidth(tt.in); out != tt.nseout { t.Errorf("RuneWidth(%q) = %d, want %d (StrictEmojiNeutral=false)", tt.in, out, tt.eaout) } } } var isambiguouswidthtests = []struct { in rune out bool }{ {'世', false}, {'■', true}, {'界', false}, {'○', true}, {'㈱', false}, {'①', true}, {'②', true}, {'③', true}, {'④', true}, {'⑤', true}, {'⑥', true}, {'⑦', true}, {'⑧', true}, {'⑨', true}, {'⑩', true}, {'⑪', true}, {'⑫', true}, {'⑬', true}, {'⑭', true}, {'⑮', true}, {'⑯', true}, {'⑰', true}, {'⑱', true}, {'⑲', true}, {'⑳', true}, {'☆', true}, } func TestIsAmbiguousWidth(t *testing.T) { for _, tt := range isambiguouswidthtests { if out := IsAmbiguousWidth(tt.in); out != tt.out { t.Errorf("IsAmbiguousWidth(%q) = %v, want %v", tt.in, out, tt.out) } } } var stringwidthtests = []struct { in string out int eaout int }{ {"■㈱の世界①", 10, 12}, {"スター☆", 7, 8}, {"つのだ☆HIRO", 11, 12}, } func TestStringWidth(t *testing.T) { c := NewCondition() c.EastAsianWidth = false for _, tt := range stringwidthtests { if out := c.StringWidth(tt.in); out != tt.out { t.Errorf("StringWidth(%q) = %d, want %d", tt.in, out, tt.out) } } c.EastAsianWidth = true for _, tt := range stringwidthtests { if out := c.StringWidth(tt.in); out != tt.eaout { t.Errorf("StringWidth(%q) = %d, want %d (EA)", tt.in, out, tt.eaout) } } } func TestStringWidthInvalid(t *testing.T) { s := "こんにちわ\x00世界" if out := StringWidth(s); out != 14 { t.Errorf("StringWidth(%q) = %d, want %d", s, out, 14) } } func TestTruncateSmaller(t *testing.T) { s := "あいうえお" expected := "あいうえお" if out := Truncate(s, 10, "..."); out != expected { t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) } } func TestTruncate(t *testing.T) { s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..." out := Truncate(s, 80, "...") if out != expected { t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) } width := StringWidth(out) if width != 79 { t.Errorf("width of Truncate(%q) should be %d, but %d", s, 79, width) } } func TestTruncateFit(t *testing.T) { s := "aあいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" expected := "aあいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおお..." out := Truncate(s, 80, "...") if out != expected { t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) } width := StringWidth(out) if width != 80 { t.Errorf("width of Truncate(%q) should be %d, but %d", s, 80, width) } } func TestTruncateJustFit(t *testing.T) { s := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" expected := "あいうえおあいうえおえおおおおおおおおおおおおおおおおおおおおおおおおおおおおお" out := Truncate(s, 80, "...") if out != expected { t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) } width := StringWidth(out) if width != 80 { t.Errorf("width of Truncate(%q) should be %d, but %d", s, 80, width) } } func TestWrap(t *testing.T) { s := `東京特許許可局局長はよく柿喰う客だ/東京特許許可局局長はよく柿喰う客だ 123456789012345678901234567890 END` expected := `東京特許許可局局長はよく柿喰う 客だ/東京特許許可局局長はよく 柿喰う客だ 123456789012345678901234567890 END` if out := Wrap(s, 30); out != expected { t.Errorf("Wrap(%q) = %q, want %q", s, out, expected) } } func TestTruncateNoNeeded(t *testing.T) { s := "あいうえおあい" expected := "あいうえおあい" if out := Truncate(s, 80, "..."); out != expected { t.Errorf("Truncate(%q) = %q, want %q", s, out, expected) } } var truncatelefttests = []struct { s string w int prefix string out string }{ {"source", 4, "", "ce"}, {"source", 4, "...", "...ce"}, {"あいうえお", 6, "", "えお"}, {"あいうえお", 6, "...", "...えお"}, {"あいうえお", 10, "", ""}, {"あいうえお", 10, "...", "..."}, {"あいうえお", 5, "", " えお"}, {"Aあいうえお", 5, "", "うえお"}, } func TestTruncateLeft(t *testing.T) { t.Parallel() for _, tt := range truncatelefttests { if out := TruncateLeft(tt.s, tt.w, tt.prefix); out != tt.out { t.Errorf("TruncateLeft(%q) = %q, want %q", tt.s, out, tt.out) } } } var isneutralwidthtests = []struct { in rune out bool }{ {'→', false}, {'┊', false}, {'┈', false}, {'~', false}, {'└', false}, {'⣀', true}, {'⣀', true}, } func TestIsNeutralWidth(t *testing.T) { for _, tt := range isneutralwidthtests { if out := IsNeutralWidth(tt.in); out != tt.out { t.Errorf("IsNeutralWidth(%q) = %v, want %v", tt.in, out, tt.out) } } } func TestFillLeft(t *testing.T) { s := "あxいうえお" expected := " あxいうえお" if out := FillLeft(s, 15); out != expected { t.Errorf("FillLeft(%q) = %q, want %q", s, out, expected) } } func TestFillLeftFit(t *testing.T) { s := "あいうえお" expected := "あいうえお" if out := FillLeft(s, 10); out != expected { t.Errorf("FillLeft(%q) = %q, want %q", s, out, expected) } } func TestFillRight(t *testing.T) { s := "あxいうえお" expected := "あxいうえお " if out := FillRight(s, 15); out != expected { t.Errorf("FillRight(%q) = %q, want %q", s, out, expected) } } func TestFillRightFit(t *testing.T) { s := "あいうえお" expected := "あいうえお" if out := FillRight(s, 10); out != expected { t.Errorf("FillRight(%q) = %q, want %q", s, out, expected) } } func TestEnv(t *testing.T) { old := os.Getenv("RUNEWIDTH_EASTASIAN") defer os.Setenv("RUNEWIDTH_EASTASIAN", old) os.Setenv("RUNEWIDTH_EASTASIAN", "0") handleEnv() if w := RuneWidth('│'); w != 1 { t.Errorf("RuneWidth('│') = %d, want %d", w, 1) } } func TestZeroWidthJoiner(t *testing.T) { c := NewCondition() var tests = []struct { in string want int }{ {"👩", 2}, {"👩\u200d", 2}, {"👩\u200d🍳", 2}, {"\u200d🍳", 2}, {"👨\u200d👨", 2}, {"👨\u200d👨\u200d👧", 2}, {"🏳️\u200d🌈", 1}, {"あ👩\u200d🍳い", 6}, {"あ\u200d🍳い", 6}, {"あ\u200dい", 4}, } for _, tt := range tests { if got := c.StringWidth(tt.in); got != tt.want { t.Errorf("StringWidth(%q) = %d, want %d", tt.in, got, tt.want) } } } go-runewidth-0.0.16/runewidth_windows.go000066400000000000000000000007371464745150200203430ustar00rootroot00000000000000//go:build windows && !appengine // +build windows,!appengine package runewidth import ( "syscall" ) var ( kernel32 = syscall.NewLazyDLL("kernel32") procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") ) // IsEastAsian return true if the current locale is CJK func IsEastAsian() bool { r1, _, _ := procGetConsoleOutputCP.Call() if r1 == 0 { return false } switch int(r1) { case 932, 51932, 936, 949, 950: return true } return false } go-runewidth-0.0.16/script/000077500000000000000000000000001464745150200155265ustar00rootroot00000000000000go-runewidth-0.0.16/script/generate.go000066400000000000000000000066431464745150200176600ustar00rootroot00000000000000//go:build ignore // +build ignore // Generate runewidth_table.go from data at https://unicode.org/ package main import ( "bufio" "bytes" "fmt" "go/format" "io" "io/ioutil" "log" "net/http" "strings" ) type rrange struct { lo rune hi rune } func generate(out io.Writer, v string, arr []rrange) { fmt.Fprintf(out, "var %s = table{\n\t", v) for i := 0; i < len(arr); i++ { fmt.Fprintf(out, "{0x%04X, 0x%04X},", arr[i].lo, arr[i].hi) if i < len(arr)-1 { if i%3 == 2 { fmt.Fprint(out, "\n\t") } else { fmt.Fprint(out, " ") } } } fmt.Fprintln(out, "\n}") } func shapeup(p *[]rrange) { arr := *p for i := 0; i < len(arr)-1; i++ { if arr[i].hi+1 == arr[i+1].lo { lo := arr[i].lo arr = append(arr[:i], arr[i+1:]...) arr[i].lo = lo i-- } } *p = arr } func eastasian(out io.Writer, in io.Reader) error { scanner := bufio.NewScanner(in) dbl := []rrange{} amb := []rrange{} cmb := []rrange{} na := []rrange{} nu := []rrange{} for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "#") { continue } var r1, r2 rune var ss string n, err := fmt.Sscanf(line, "%x..%x ; %s", &r1, &r2, &ss) if err != nil || n == 2 { n, err = fmt.Sscanf(line, "%x ; %s", &r1, &ss) if err != nil || n != 2 { continue } r2 = r1 } if strings.Index(line, "COMBINING") != -1 { cmb = append(cmb, rrange{ lo: r1, hi: r2, }) } switch ss { case "W", "F": dbl = append(dbl, rrange{ lo: r1, hi: r2, }) case "A": amb = append(amb, rrange{ lo: r1, hi: r2, }) case "Na": na = append(na, rrange{ lo: r1, hi: r2, }) case "N": nu = append(nu, rrange{ lo: r1, hi: r2, }) } } shapeup(&cmb) generate(out, "combining", cmb) fmt.Fprintln(out) shapeup(&dbl) generate(out, "doublewidth", dbl) fmt.Fprintln(out) shapeup(&amb) generate(out, "ambiguous", amb) fmt.Fprint(out) shapeup(&na) generate(out, "narrow", na) fmt.Fprintln(out) shapeup(&nu) generate(out, "neutral", nu) fmt.Fprintln(out) return nil } func emoji(out io.Writer, in io.Reader) error { scanner := bufio.NewScanner(in) for scanner.Scan() { line := scanner.Text() if strings.Index(line, "Extended_Pictographic=No") != -1 { break } } if scanner.Err() != nil { return scanner.Err() } arr := []rrange{} for scanner.Scan() { line := scanner.Text() if strings.HasPrefix(line, "#") { continue } var r1, r2 rune n, err := fmt.Sscanf(line, "%x..%x ", &r1, &r2) if err != nil || n == 1 { n, err = fmt.Sscanf(line, "%x ", &r1) if err != nil || n != 1 { continue } r2 = r1 } if r2 < 0xFF { continue } arr = append(arr, rrange{ lo: r1, hi: r2, }) } shapeup(&arr) generate(out, "emoji", arr) return nil } func main() { var buf bytes.Buffer f := &buf fmt.Fprint(f, "// Code generated by script/generate.go. DO NOT EDIT.\n\n") fmt.Fprint(f, "package runewidth\n\n") resp, err := http.Get("https://unicode.org/Public/15.1.0/ucd/EastAsianWidth.txt") if err != nil { log.Fatal(err) } defer resp.Body.Close() eastasian(f, resp.Body) resp, err = http.Get("https://unicode.org/Public/15.1.0/ucd/emoji/emoji-data.txt") if err != nil { log.Fatal(err) } defer resp.Body.Close() emoji(f, resp.Body) out, err := format.Source(f.Bytes()) if err != nil { log.Fatal(err) } err = ioutil.WriteFile("runewidth_table.go", out, 0666) if err != nil { log.Fatal(err) } }