pax_global_header00006660000000000000000000000064151636104050014513gustar00rootroot0000000000000052 comment=41dc6c50e6f69bfba372674d8e6f9923f88fcf7d go-runewidth-0.0.22/000077500000000000000000000000001516361040500142105ustar00rootroot00000000000000go-runewidth-0.0.22/.github/000077500000000000000000000000001516361040500155505ustar00rootroot00000000000000go-runewidth-0.0.22/.github/FUNDING.yml000066400000000000000000000013151516361040500173650ustar00rootroot00000000000000# 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.22/.github/workflows/000077500000000000000000000000001516361040500176055ustar00rootroot00000000000000go-runewidth-0.0.22/.github/workflows/test.yaml000066400000000000000000000012101516361040500214420ustar00rootroot00000000000000name: test on: push: branches: - master pull_request: branches: - master jobs: test: strategy: matrix: os: [windows-latest, macos-latest, ubuntu-latest] go: - "1.26" - "1.25" - "1.24" runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 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@v5 go-runewidth-0.0.22/LICENSE000066400000000000000000000020751516361040500152210ustar00rootroot00000000000000The 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.22/README.md000066400000000000000000000015011516361040500154640ustar00rootroot00000000000000go-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.22/benchmark_test.go000066400000000000000000000073051516361040500175350ustar00rootroot00000000000000package 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, 1294004) } 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, 1432374) } 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 ss := make([]string, stop-start) for r := start; r < stop; r++ { ss = append(ss, string(r)) } 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 _, s := range ss { 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 _, s := range ss { 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, 1296052) } 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, 1436470) } 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.22/benchstat.txt000066400000000000000000000062551516361040500167340ustar00rootroot00000000000000goos: darwin goarch: arm64 pkg: github.com/mattn/go-runewidth cpu: Apple M2 │ old.txt │ new.txt │ │ sec/op │ sec/op vs base │ String1WidthAll/regular-8 108.92m ± 0% 35.09m ± 3% -67.78% (p=0.002 n=6) String1WidthAll/lut-8 93.97m ± 0% 18.70m ± 0% -80.10% (p=0.002 n=6) String1Width768/regular-8 60.62µ ± 1% 11.54µ ± 0% -80.97% (p=0.002 n=6) String1Width768/lut-8 60.66µ ± 1% 11.43µ ± 0% -81.16% (p=0.002 n=6) String1WidthAllEastAsian/regular-8 115.13m ± 1% 40.79m ± 8% -64.57% (p=0.002 n=6) String1WidthAllEastAsian/lut-8 93.65m ± 0% 18.70m ± 2% -80.03% (p=0.002 n=6) String1Width768EastAsian/regular-8 75.32µ ± 0% 23.49µ ± 0% -68.82% (p=0.002 n=6) String1Width768EastAsian/lut-8 60.76µ ± 0% 11.50µ ± 0% -81.07% (p=0.002 n=6) geomean 2.562m 604.5µ -76.41% │ old.txt │ new.txt │ │ B/op │ B/op vs base │ String1WidthAll/regular-8 106.3Mi ± 0% 0.0Mi ± 0% -100.00% (p=0.002 n=6) String1WidthAll/lut-8 106.3Mi ± 0% 0.0Mi ± 0% -100.00% (p=0.002 n=6) String1Width768/regular-8 75.00Ki ± 0% 0.00Ki ± 0% -100.00% (p=0.002 n=6) String1Width768/lut-8 75.00Ki ± 0% 0.00Ki ± 0% -100.00% (p=0.002 n=6) String1WidthAllEastAsian/regular-8 106.3Mi ± 0% 0.0Mi ± 0% -100.00% (p=0.002 n=6) String1WidthAllEastAsian/lut-8 106.3Mi ± 0% 0.0Mi ± 0% -100.00% (p=0.002 n=6) String1Width768EastAsian/regular-8 75.00Ki ± 0% 0.00Ki ± 0% -100.00% (p=0.002 n=6) String1Width768EastAsian/lut-8 75.00Ki ± 0% 0.00Ki ± 0% -100.00% (p=0.002 n=6) geomean 2.790Mi ? ¹ ² ¹ summaries must be >0 to compute geomean ² ratios must be >0 to compute geomean │ old.txt │ new.txt │ │ allocs/op │ allocs/op vs base │ String1WidthAll/regular-8 3.342M ± 0% 0.000M ± 0% -100.00% (p=0.002 n=6) String1WidthAll/lut-8 3.342M ± 0% 0.000M ± 0% -100.00% (p=0.002 n=6) String1Width768/regular-8 2.304k ± 0% 0.000k ± 0% -100.00% (p=0.002 n=6) String1Width768/lut-8 2.304k ± 0% 0.000k ± 0% -100.00% (p=0.002 n=6) String1WidthAllEastAsian/regular-8 3.342M ± 0% 0.000M ± 0% -100.00% (p=0.002 n=6) String1WidthAllEastAsian/lut-8 3.342M ± 0% 0.000M ± 0% -100.00% (p=0.002 n=6) String1Width768EastAsian/regular-8 2.304k ± 0% 0.000k ± 0% -100.00% (p=0.002 n=6) String1Width768EastAsian/lut-8 2.304k ± 0% 0.000k ± 0% -100.00% (p=0.002 n=6) geomean 87.75k ? ¹ ² ¹ summaries must be >0 to compute geomean ² ratios must be >0 to compute geomean go-runewidth-0.0.22/go.mod000066400000000000000000000001371516361040500153170ustar00rootroot00000000000000module github.com/mattn/go-runewidth go 1.20 require github.com/clipperhouse/uax29/v2 v2.2.0 go-runewidth-0.0.22/go.sum000066400000000000000000000002671516361040500153500ustar00rootroot00000000000000github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY= github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM= go-runewidth-0.0.22/new.txt000066400000000000000000000120541516361040500155440ustar00rootroot00000000000000goos: darwin goarch: arm64 pkg: github.com/mattn/go-runewidth cpu: Apple M2 BenchmarkString1WidthAll/regular-8 33 35033923 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/regular-8 33 34965112 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/regular-8 33 36307234 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/regular-8 33 35007705 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/regular-8 33 35154182 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/regular-8 34 35155400 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/lut-8 63 18688500 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/lut-8 63 18712474 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/lut-8 63 18700211 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/lut-8 62 18694179 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/lut-8 62 18708392 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAll/lut-8 63 18770608 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/regular-8 104137 11526 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/regular-8 103986 11540 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/regular-8 104079 11552 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/regular-8 103963 11530 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/regular-8 103714 11538 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/regular-8 104181 11537 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/lut-8 105150 11420 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/lut-8 104778 11423 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/lut-8 105069 11422 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/lut-8 105127 11475 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/lut-8 104742 11433 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768/lut-8 105163 11432 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 28 40723347 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 28 40790299 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 28 40801338 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 28 40798216 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 28 44135253 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 28 40779546 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 62 18694165 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 62 18685047 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 62 18689273 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 62 19150346 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 63 19126154 ns/op 0 B/op 0 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 62 18712619 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/regular-8 50775 23595 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/regular-8 51061 23563 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/regular-8 51057 23492 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/regular-8 51138 23445 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/regular-8 51195 23469 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/regular-8 51087 23482 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/lut-8 104559 11549 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/lut-8 104508 11483 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/lut-8 104296 11503 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/lut-8 104606 11485 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/lut-8 104588 11495 ns/op 0 B/op 0 allocs/op BenchmarkString1Width768EastAsian/lut-8 104602 11518 ns/op 0 B/op 0 allocs/op PASS ok github.com/mattn/go-runewidth 64.455s go-runewidth-0.0.22/old.txt000066400000000000000000000121041516361040500155250ustar00rootroot00000000000000goos: darwin goarch: arm64 pkg: github.com/mattn/go-runewidth cpu: Apple M2 BenchmarkString1WidthAll/regular-8 10 108559258 ns/op 111412145 B/op 3342342 allocs/op BenchmarkString1WidthAll/regular-8 10 108968079 ns/op 111412364 B/op 3342343 allocs/op BenchmarkString1WidthAll/regular-8 10 108890338 ns/op 111412388 B/op 3342344 allocs/op BenchmarkString1WidthAll/regular-8 10 108940704 ns/op 111412584 B/op 3342346 allocs/op BenchmarkString1WidthAll/regular-8 10 108632796 ns/op 111412348 B/op 3342343 allocs/op BenchmarkString1WidthAll/regular-8 10 109354546 ns/op 111412777 B/op 3342343 allocs/op BenchmarkString1WidthAll/lut-8 12 93844406 ns/op 111412569 B/op 3342345 allocs/op BenchmarkString1WidthAll/lut-8 12 93991080 ns/op 111412512 B/op 3342344 allocs/op BenchmarkString1WidthAll/lut-8 12 93980632 ns/op 111412413 B/op 3342343 allocs/op BenchmarkString1WidthAll/lut-8 12 94004083 ns/op 111412396 B/op 3342343 allocs/op BenchmarkString1WidthAll/lut-8 12 93959795 ns/op 111412445 B/op 3342343 allocs/op BenchmarkString1WidthAll/lut-8 12 93846198 ns/op 111412556 B/op 3342345 allocs/op BenchmarkString1Width768/regular-8 19785 60696 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768/regular-8 19824 60520 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768/regular-8 19832 60547 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768/regular-8 19778 60543 ns/op 76800 B/op 2304 allocs/op BenchmarkString1Width768/regular-8 19842 61142 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768/regular-8 19780 60696 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768/lut-8 19598 61161 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768/lut-8 19731 60707 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768/lut-8 19738 60626 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768/lut-8 19764 60670 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768/lut-8 19797 60642 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768/lut-8 19738 60608 ns/op 76800 B/op 2304 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 9 115080431 ns/op 111412458 B/op 3342345 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 9 114908880 ns/op 111412476 B/op 3342345 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 9 115077134 ns/op 111412540 B/op 3342345 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 9 115175292 ns/op 111412467 B/op 3342345 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 9 115792653 ns/op 111412362 B/op 3342344 allocs/op BenchmarkString1WidthAllEastAsian/regular-8 9 115255417 ns/op 111412572 B/op 3342346 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 12 93761542 ns/op 111412538 B/op 3342345 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 12 94089990 ns/op 111412440 B/op 3342343 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 12 93721410 ns/op 111412514 B/op 3342344 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 12 93572951 ns/op 111412329 B/op 3342342 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 12 93536052 ns/op 111412206 B/op 3342341 allocs/op BenchmarkString1WidthAllEastAsian/lut-8 12 93532365 ns/op 111412412 B/op 3342343 allocs/op BenchmarkString1Width768EastAsian/regular-8 15904 75401 ns/op 76800 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/regular-8 15932 75449 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/regular-8 15944 75181 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/regular-8 15963 75311 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/regular-8 15879 75292 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/regular-8 15955 75334 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/lut-8 19692 60692 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/lut-8 19712 60699 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/lut-8 19741 60819 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/lut-8 19771 60653 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/lut-8 19737 61027 ns/op 76801 B/op 2304 allocs/op BenchmarkString1Width768EastAsian/lut-8 19657 60820 ns/op 76801 B/op 2304 allocs/op PASS ok github.com/mattn/go-runewidth 76.165s go-runewidth-0.0.22/runewidth.go000066400000000000000000000175521516361040500165620ustar00rootroot00000000000000package runewidth import ( "os" "strings" "unicode/utf8" "github.com/clipperhouse/uax29/v2/graphemes" ) //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 } if r > t[len(t)-1].last { 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) { if len(s) > 0 && len(s) <= utf8.UTFMax { r, size := utf8.DecodeRuneInString(s) if size == len(s) { return c.RuneWidth(r) } } g := graphemes.FromString(s) for g.Next() { var chWidth int for _, r := range g.Value() { 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 := graphemes.FromString(s) for g.Next() { var chWidth int for _, r := range g.Value() { chWidth = c.RuneWidth(r) if chWidth > 0 { break // See StringWidth() for details. } } if width+chWidth > w { pos = g.Start() 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 := graphemes.FromString(s) for g.Next() { var chWidth int for _, r := range g.Value() { chWidth = c.RuneWidth(r) if chWidth > 0 { break // See StringWidth() for details. } } if width+chWidth > w { if width < w { pos = g.End() prefix += strings.Repeat(" ", width+chWidth-w) } else { pos = g.Start() } 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) } // IsCombiningWidth returns whether is combining width or not. func IsCombiningWidth(r rune) bool { return inTable(r, combining) } // 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.22/runewidth_appengine.go000066400000000000000000000002371516361040500206000ustar00rootroot00000000000000//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.22/runewidth_js.go000066400000000000000000000003201516361040500172370ustar00rootroot00000000000000//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.22/runewidth_posix.go000066400000000000000000000027111516361040500177730ustar00rootroot00000000000000//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.22/runewidth_posix_test.go000066400000000000000000000034111516361040500210300ustar00rootroot00000000000000//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.22/runewidth_table.go000066400000000000000000000645371516361040500177360ustar00rootroot00000000000000// 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}, {0x180B, 0x180D}, {0x180F, 0x180F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1ADD}, {0x1AE0, 0x1AEB}, {0x1B6B, 0x1B73}, {0x1DC0, 0x1DFF}, {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF}, {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1}, {0xFE00, 0xFE0F}, {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}, {0xE0100, 0xE01EF}, } var doublewidth = table{ {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2630, 0x2637}, {0x2648, 0x2653}, {0x267F, 0x267F}, {0x268A, 0x268F}, {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, 0x31E5}, {0x31EF, 0x321E}, {0x3220, 0x3247}, {0x3250, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4}, {0x16FF0, 0x16FF6}, {0x17000, 0x18CD5}, {0x18CFF, 0x18D1E}, {0x18D80, 0x18DF2}, {0x1AFF0, 0x1AFF3}, {0x1AFF5, 0x1AFFB}, {0x1AFFD, 0x1AFFE}, {0x1B000, 0x1B122}, {0x1B132, 0x1B132}, {0x1B150, 0x1B152}, {0x1B155, 0x1B155}, {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1D300, 0x1D356}, {0x1D360, 0x1D376}, {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, 0x1F6D8}, {0x1F6DC, 0x1F6DF}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB}, {0x1F7F0, 0x1F7F0}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F9FF}, {0x1FA70, 0x1FA7C}, {0x1FA80, 0x1FA8A}, {0x1FA8E, 0x1FAC6}, {0x1FAC8, 0x1FAC8}, {0x1FACD, 0x1FADC}, {0x1FADF, 0x1FAEA}, {0x1FAEF, 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}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {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, 0x0891}, {0x0897, 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}, {0x0C5C, 0x0C5D}, {0x0C60, 0x0C63}, {0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90}, {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, {0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, {0x0CDC, 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, 0x180A}, {0x180E, 0x180E}, {0x1810, 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, 0x1ADD}, {0x1AE0, 0x1AEB}, {0x1B00, 0x1B4C}, {0x1B4E, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49}, {0x1C4D, 0x1C8A}, {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, 0x20C1}, {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, 0x2429}, {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, 0x262F}, {0x2638, 0x263F}, {0x2641, 0x2641}, {0x2643, 0x2647}, {0x2654, 0x265F}, {0x2662, 0x2662}, {0x2666, 0x2666}, {0x266B, 0x266B}, {0x266E, 0x266E}, {0x2670, 0x267E}, {0x2680, 0x2689}, {0x2690, 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, 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}, {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7DC}, {0xA7F1, 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, 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}, {0x105C0, 0x105F3}, {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, 0x10959}, {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}, {0x10D40, 0x10D65}, {0x10D69, 0x10D85}, {0x10D8E, 0x10D8F}, {0x10E60, 0x10E7E}, {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD}, {0x10EB0, 0x10EB1}, {0x10EC2, 0x10EC7}, {0x10ED0, 0x10ED8}, {0x10EFA, 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}, {0x11380, 0x11389}, {0x1138B, 0x1138B}, {0x1138E, 0x1138E}, {0x11390, 0x113B5}, {0x113B7, 0x113C0}, {0x113C2, 0x113C2}, {0x113C5, 0x113C5}, {0x113C7, 0x113CA}, {0x113CC, 0x113D5}, {0x113D7, 0x113D8}, {0x113E1, 0x113E2}, {0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7}, {0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD}, {0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, {0x11680, 0x116B9}, {0x116C0, 0x116C9}, {0x116D0, 0x116E3}, {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}, {0x11B60, 0x11B67}, {0x11BC0, 0x11BE1}, {0x11BF0, 0x11BF9}, {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}, {0x11DB0, 0x11DDB}, {0x11DE0, 0x11DE9}, {0x11EE0, 0x11EF8}, {0x11F00, 0x11F10}, {0x11F12, 0x11F3A}, {0x11F3E, 0x11F5A}, {0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399}, {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, {0x12F90, 0x12FF2}, {0x13000, 0x13455}, {0x13460, 0x143FA}, {0x14400, 0x14646}, {0x16100, 0x16139}, {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, {0x16A6E, 0x16ABE}, {0x16AC0, 0x16AC9}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5}, {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16D40, 0x16D79}, {0x16E40, 0x16E9A}, {0x16EA0, 0x16EB8}, {0x16EBB, 0x16ED3}, {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1CC00, 0x1CCFC}, {0x1CD00, 0x1CEB3}, {0x1CEBA, 0x1CED0}, {0x1CEE0, 0x1CEF0}, {0x1CF00, 0x1CF2D}, {0x1CF30, 0x1CF46}, {0x1CF50, 0x1CFC3}, {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D1EA}, {0x1D200, 0x1D245}, {0x1D2C0, 0x1D2D3}, {0x1D2E0, 0x1D2F3}, {0x1D377, 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}, {0x1E5D0, 0x1E5FA}, {0x1E5FF, 0x1E5FF}, {0x1E6C0, 0x1E6DE}, {0x1E6E0, 0x1E6F5}, {0x1E6FE, 0x1E6FF}, {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, 0x1F7D9}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0x1F8B0, 0x1F8BB}, {0x1F8C0, 0x1F8C1}, {0x1F8D0, 0x1F8D8}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B}, {0x1F946, 0x1F946}, {0x1FA00, 0x1FA57}, {0x1FA60, 0x1FA6D}, {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBFA}, {0xE0001, 0xE0001}, {0xE0020, 0xE007F}, } var emoji = table{ {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122}, {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA}, {0x231A, 0x231B}, {0x2328, 0x2328}, {0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA}, {0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6}, {0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2604}, {0x260E, 0x260E}, {0x2611, 0x2611}, {0x2614, 0x2615}, {0x2618, 0x2618}, {0x261D, 0x261D}, {0x2620, 0x2620}, {0x2622, 0x2623}, {0x2626, 0x2626}, {0x262A, 0x262A}, {0x262E, 0x262F}, {0x2638, 0x263A}, {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2648, 0x2653}, {0x265F, 0x2660}, {0x2663, 0x2663}, {0x2665, 0x2666}, {0x2668, 0x2668}, {0x267B, 0x267B}, {0x267E, 0x267F}, {0x2692, 0x2697}, {0x2699, 0x2699}, {0x269B, 0x269C}, {0x26A0, 0x26A1}, {0x26A7, 0x26A7}, {0x26AA, 0x26AB}, {0x26B0, 0x26B1}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, {0x26C8, 0x26C8}, {0x26CE, 0x26CF}, {0x26D1, 0x26D1}, {0x26D3, 0x26D4}, {0x26E9, 0x26EA}, {0x26F0, 0x26F5}, {0x26F7, 0x26FA}, {0x26FD, 0x26FD}, {0x2702, 0x2702}, {0x2705, 0x2705}, {0x2708, 0x270D}, {0x270F, 0x270F}, {0x2712, 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, 0x2764}, {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}, {0x1F004, 0x1F004}, {0x1F02C, 0x1F02F}, {0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0}, {0x1F0CF, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F170, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, {0x1F1AE, 0x1F1E5}, {0x1F201, 0x1F20F}, {0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A}, {0x1F23C, 0x1F23F}, {0x1F249, 0x1F25F}, {0x1F266, 0x1F321}, {0x1F324, 0x1F393}, {0x1F396, 0x1F397}, {0x1F399, 0x1F39B}, {0x1F39E, 0x1F3F0}, {0x1F3F3, 0x1F3F5}, {0x1F3F7, 0x1F3FA}, {0x1F400, 0x1F4FD}, {0x1F4FF, 0x1F53D}, {0x1F549, 0x1F54E}, {0x1F550, 0x1F567}, {0x1F56F, 0x1F570}, {0x1F573, 0x1F57A}, {0x1F587, 0x1F587}, {0x1F58A, 0x1F58D}, {0x1F590, 0x1F590}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A5}, {0x1F5A8, 0x1F5A8}, {0x1F5B1, 0x1F5B2}, {0x1F5BC, 0x1F5BC}, {0x1F5C2, 0x1F5C4}, {0x1F5D1, 0x1F5D3}, {0x1F5DC, 0x1F5DE}, {0x1F5E1, 0x1F5E1}, {0x1F5E3, 0x1F5E3}, {0x1F5E8, 0x1F5E8}, {0x1F5EF, 0x1F5EF}, {0x1F5F3, 0x1F5F3}, {0x1F5FA, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CB, 0x1F6D2}, {0x1F6D5, 0x1F6E5}, {0x1F6E9, 0x1F6E9}, {0x1F6EB, 0x1F6F0}, {0x1F6F3, 0x1F6FF}, {0x1F7DA, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8AF}, {0x1F8BC, 0x1F8BF}, {0x1F8C2, 0x1F8CF}, {0x1F8D9, 0x1F8FF}, {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F9FF}, {0x1FA58, 0x1FA5F}, {0x1FA6E, 0x1FAFF}, {0x1FC00, 0x1FFFD}, } go-runewidth-0.0.22/runewidth_test.go000066400000000000000000000321221516361040500176070ustar00rootroot00000000000000//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", 842, "a89cdf3daf597a953ca315f005316f19d52a0f83197207fe6b844f295cbade54"}, {doublewidth, "doublewidth", 182876, "55dcb1b999d6356d1a083085bb053bdeafc6dda05dec002617d85fda2a82d496"}, {ambiguous, "ambiguous", 138483, "f4ed2dd733c0821cf6297fc24be0baea527ec7cad10d23b0ac7f57dcdf344cdb"}, {emoji, "emoji", 2846, "09914b87febaa5493f2420a58f03dd6b026fa665b7c811abc7423a26a9b442c3"}, {narrow, "narrow", 111, "fa897699c5e3cd9141c638d539331b0bdd508b874e22996c5e929767d455fc5a"}, {neutral, "neutral", 33695, "f6af4edbdfe84d0c4c4419da8e2f86e49248010bad19c4ed7bf1897696db9084"}, } 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, "27fcc4f23635fb16651f9596517792cd497d4ac986020409215936e104d9eaa7"}, {"ea-yes", true, "587ef2296285b71e9a37252a2f829db3caf85eddc2152bc6b22203bc2bcad01f"}, } 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, "27fcc4f23635fb16651f9596517792cd497d4ac986020409215936e104d9eaa7"}, {"ea-yes", true, "587ef2296285b71e9a37252a2f829db3caf85eddc2152bc6b22203bc2bcad01f"}, } 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, 1}, {'♥', 1, 2, 2}, {'♦', 1, 1, 2}, {'♣', 1, 2, 2}, {'♠', 1, 2, 2}, {'♂', 1, 2, 2}, {'♀', 1, 2, 2}, {'♪', 1, 2, 2}, {'♫', 1, 1, 1}, {'☼', 1, 1, 1}, {'↕', 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) } } } var iscombiningwidthtests = []struct { in rune out bool }{ {'\u0300', true}, // COMBINING GRAVE ACCENT {'\uFE0F', true}, // VARIATION SELECTOR-16 {'\uFE0E', true}, // VARIATION SELECTOR-15 {'\u180B', true}, // MONGOLIAN FREE VARIATION SELECTOR ONE {'\U000E0100', true}, // VARIATION SELECTOR-17 {'A', false}, {'☆', false}, } func TestIsCombiningWidth(t *testing.T) { for _, tt := range iscombiningwidthtests { if out := IsCombiningWidth(tt.in); out != tt.out { t.Errorf("IsCombiningWidth(%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.22/runewidth_windows.go000066400000000000000000000011401516361040500203160ustar00rootroot00000000000000//go:build windows && !appengine // +build windows,!appengine package runewidth import ( "os" "syscall" ) var ( kernel32 = syscall.NewLazyDLL("kernel32") procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") ) // IsEastAsian return true if the current locale is CJK func IsEastAsian() bool { if os.Getenv("WT_SESSION") != "" { // Windows Terminal always not use East Asian Ambiguous Width(s). return false } 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.22/script/000077500000000000000000000000001516361040500155145ustar00rootroot00000000000000go-runewidth-0.0.22/script/generate.go000066400000000000000000000070431516361040500176410ustar00rootroot00000000000000//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, }) } if strings.Contains(line, "VARIATION SELECTOR") { cmb = append(cmb, rrange{ lo: r1, hi: r2, }) continue } 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/17.0.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/17.0.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) } }