├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── cell.go ├── cell_test.go ├── html.go ├── html_test.go ├── row.go ├── row_test.go ├── separator.go ├── straight_separator.go ├── style.go ├── table.go ├── table_test.go ├── term ├── env.go ├── getsize.go ├── sizes_unix.go ├── sizes_windows.go └── wrapper.go └── vendor ├── github.com └── mattn │ └── go-runewidth │ ├── LICENSE │ ├── runewidth.go │ ├── runewidth_js.go │ ├── runewidth_posix.go │ └── runewidth_windows.go └── manifest /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.dll 4 | *.so 5 | *.dylib 6 | 7 | # Development binary, built with makefile 8 | *.dev 9 | 10 | # Test binary, built with `go test -c` 11 | *.test 12 | 13 | # Output of the go coverage tool, specifically when used with LiteIDE 14 | *.out 15 | 16 | # IDE 17 | .idea/* 18 | .vscode/* 19 | .history 20 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | go: 3 | - 1.6 4 | - 1.7 5 | - tip 6 | 7 | script: 8 | - go vet $(go list ./...|grep -v "/vendor/") 9 | - go test -v -race ./... 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Termtables 2 | 3 | [![Build Status](https://travis-ci.org/scylladb/termtables.svg?branch=master)](https://travis-ci.org/scylladb/termtables) 4 | 5 | A [Go](http://golang.org) port of the Ruby library [terminal-tables](https://github.com/visionmedia/terminal-table) for 6 | fast and simple ASCII table generation. 7 | 8 | ## Installation 9 | 10 | ```bash 11 | go get github.com/scylladb/termtables 12 | ``` 13 | 14 | ## Go Style Documentation 15 | 16 | [http://godoc.org/github.com/scylladb/termtables](http://godoc.org/github.com/scylladb/termtables) 17 | 18 | ## APC Command Line usage 19 | 20 | `--markdown` — output a markdown table, e.g. `apc app list --markdown` 21 | 22 | `--html` — output an html table, e.g. `apc app list --html` 23 | 24 | `--ascii` — output an ascii table, e.g. `apc app list --ascii` 25 | 26 | ## Basic Usage 27 | 28 | ```go 29 | package main 30 | 31 | import ( 32 | "fmt" 33 | "github.com/scylladb/termtables" 34 | ) 35 | 36 | func main() { 37 | table := termtables.CreateTable() 38 | 39 | table.AddHeaders("Name", "Age") 40 | table.AddRow("John", "30") 41 | table.AddRow("Sam", 18) 42 | table.AddRow("Julie", 20.14) 43 | 44 | fmt.Println(table.Render()) 45 | } 46 | ``` 47 | 48 | Result: 49 | 50 | ``` 51 | +-------+-------+ 52 | | Name | Age | 53 | +-------+-------+ 54 | | John | 30 | 55 | | Sam | 18 | 56 | | Julie | 20.14 | 57 | +-------+-------+ 58 | ``` 59 | 60 | ## Advanced Usage 61 | 62 | The package function-call `EnableUTF8()` will cause any tables created after 63 | that point to use Unicode box-drawing characters for the table lines. 64 | 65 | Calling `EnableUTF8PerLocale()` uses the C library's locale functionality to 66 | determine if the current locale environment variables say that the current 67 | character map is UTF-8. If, and only if, so, then `EnableUTF8()` will be 68 | called. 69 | 70 | Calling `SetModeHTML(true)` will cause any tables created after that point 71 | to be emitted in HTML, while `SetModeMarkdown(true)` will trigger Markdown. 72 | Neither should result in changes to later API to get the different results; 73 | the primary intended use-case is extracting the same table, but for 74 | documentation. 75 | 76 | The table method `.AddSeparator()` inserts a rule line in the output. This 77 | only applies in normal terminal output mode. 78 | 79 | The table method `.AddTitle()` adds a title to the table; in terminal output, 80 | this is an initial row; in HTML, it's a caption. In Markdown, it's a line of 81 | text before the table, prefixed by `Table: `. 82 | 83 | The table method `.SetAlign()` takes an alignment and a column number 84 | (indexing starts at 1) and changes all _current_ cells in that column to have 85 | the given alignment. It does not change the alignment of cells added to the 86 | table after this call. Alignment is only stored on a per-cell basis. 87 | 88 | ## Known Issues 89 | 90 | Normal output: 91 | 92 | * `.SetAlign()` does not affect headers. 93 | 94 | Markdown output mode: 95 | 96 | * When emitting Markdown, the column markers are not re-flowed if a vertical 97 | bar is an element of a cell, causing an escape to take place; since Markdown 98 | is often converted to HTML, this only affects text viewing. 99 | * A title in Markdown is not escaped against all possible forms of Markdown 100 | markup (to avoid adding a dependency upon a Markdown library, as supported 101 | syntax can vary). 102 | * Markdown requires headers, so a dummy header will be inserted if needed. 103 | * Table alignment is not reflected in Markdown output. 104 | -------------------------------------------------------------------------------- /cell.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Apcera Inc. All rights reserved. 2 | 3 | package termtables 4 | 5 | import ( 6 | "fmt" 7 | "math" 8 | "regexp" 9 | "strconv" 10 | "strings" 11 | "unicode/utf8" 12 | 13 | runewidth "github.com/mattn/go-runewidth" 14 | ) 15 | 16 | var ( 17 | // Must match SGR escape sequence, which is "CSI Pm m", where the Control 18 | // Sequence Introducer (CSI) is "ESC ["; where Pm is "A multiple numeric 19 | // parameter composed of any number of single numeric parameters, separated 20 | // by ; character(s). Individual values for the parameters are listed with 21 | // Ps" and where Ps is A single (usually optional) numeric parameter, 22 | // composed of one of [sic] more digits." 23 | // 24 | // In practice, the end sequence is usually given as \e[0m but reading that 25 | // definition, it's clear that the 0 is optional and some testing confirms 26 | // that it is certainly optional with MacOS Terminal 2.3, so we need to 27 | // support the string \e[m as a terminator too. 28 | colorFilter = regexp.MustCompile(`\033\[(?:\d+(?:;\d+)*)?m`) 29 | ) 30 | 31 | // A Cell denotes one cell of a table; it spans one row and a variable number 32 | // of columns. A given Cell can only be used at one place in a table; the act 33 | // of adding the Cell to the table mutates it with position information, so 34 | // do not create one "const" Cell to add it multiple times. 35 | type Cell struct { 36 | column int 37 | formattedValue string 38 | alignment *TableAlignment 39 | colSpan int 40 | } 41 | 42 | // CreateCell returns a Cell where the content is the supplied value, with the 43 | // optional supplied style (which may be given as nil). The style can include 44 | // a non-zero ColSpan to cause the cell to become column-spanning. Changing 45 | // the style afterwards will not adjust the column-spanning state of the cell 46 | // itself. 47 | func CreateCell(v interface{}, style *CellStyle) *Cell { 48 | return createCell(0, v, style) 49 | } 50 | 51 | func createCell(column int, v interface{}, style *CellStyle) *Cell { 52 | cell := &Cell{column: column, formattedValue: renderValue(v), colSpan: 1} 53 | if style != nil { 54 | cell.alignment = &style.Alignment 55 | if style.ColSpan != 0 { 56 | cell.colSpan = style.ColSpan 57 | } 58 | } 59 | return cell 60 | } 61 | 62 | // Width returns the width of the content of the cell, measured in runes as best 63 | // as possible considering sophisticated Unicode. 64 | func (c *Cell) Width() int { 65 | return runewidth.StringWidth(filterColorCodes(c.formattedValue)) 66 | } 67 | 68 | // Filter out terminal bold/color sequences in a string. 69 | // This supports only basic bold/color escape sequences. 70 | func filterColorCodes(s string) string { 71 | return colorFilter.ReplaceAllString(s, "") 72 | } 73 | 74 | // Render returns a string representing the content of the cell, together with 75 | // padding (to the widths specified) and handling any alignment. 76 | func (c *Cell) Render(style *renderStyle) (buffer string) { 77 | // if no alignment is set, import the table's default 78 | if c.alignment == nil { 79 | c.alignment = &style.Alignment 80 | } 81 | 82 | // left padding 83 | buffer += strings.Repeat(" ", style.PaddingLeft) 84 | 85 | // append the main value and handle alignment 86 | buffer += c.alignCell(style) 87 | 88 | // right padding 89 | buffer += strings.Repeat(" ", style.PaddingRight) 90 | 91 | // this handles escaping for, eg, Markdown, where we don't care about the 92 | // alignment quite as much 93 | if style.replaceContent != nil { 94 | buffer = style.replaceContent(buffer) 95 | } 96 | 97 | return buffer 98 | } 99 | 100 | func (c *Cell) alignCell(style *renderStyle) string { 101 | buffer := "" 102 | width := style.CellWidth(c.column) 103 | 104 | if c.colSpan > 1 { 105 | for i := 1; i < c.colSpan; i++ { 106 | w := style.CellWidth(c.column + i) 107 | if w == 0 { 108 | break 109 | } 110 | width += style.PaddingLeft + w + style.PaddingRight + utf8.RuneCountInString(style.BorderY) 111 | } 112 | } 113 | 114 | switch *c.alignment { 115 | 116 | default: 117 | buffer += c.formattedValue 118 | if l := width - c.Width(); l > 0 { 119 | buffer += strings.Repeat(" ", l) 120 | } 121 | 122 | case AlignLeft: 123 | buffer += c.formattedValue 124 | if l := width - c.Width(); l > 0 { 125 | buffer += strings.Repeat(" ", l) 126 | } 127 | 128 | case AlignRight: 129 | if l := width - c.Width(); l > 0 { 130 | buffer += strings.Repeat(" ", l) 131 | } 132 | buffer += c.formattedValue 133 | 134 | case AlignCenter: 135 | left, right := 0, 0 136 | if l := width - c.Width(); l > 0 { 137 | lf := float64(l) 138 | left = int(math.Floor(lf / 2)) 139 | right = int(math.Ceil(lf / 2)) 140 | } 141 | buffer += strings.Repeat(" ", left) 142 | buffer += c.formattedValue 143 | buffer += strings.Repeat(" ", right) 144 | } 145 | 146 | return buffer 147 | } 148 | 149 | // Format the raw value as a string depending on the type 150 | func renderValue(v interface{}) string { 151 | switch vv := v.(type) { 152 | case string: 153 | return vv 154 | case bool: 155 | return strconv.FormatBool(vv) 156 | case int: 157 | return strconv.Itoa(vv) 158 | case int64: 159 | return strconv.FormatInt(vv, 10) 160 | case uint64: 161 | return strconv.FormatUint(vv, 10) 162 | case float64: 163 | return strconv.FormatFloat(vv, 'f', 2, 64) 164 | case fmt.Stringer: 165 | return vv.String() 166 | } 167 | return fmt.Sprintf("%v", v) 168 | } 169 | -------------------------------------------------------------------------------- /cell_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012-2015 Apcera Inc. All rights reserved. 2 | 3 | package termtables 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestCellRenderString(t *testing.T) { 10 | style := &renderStyle{TableStyle: TableStyle{}, cellWidths: map[int]int{}} 11 | cell := createCell(0, "foobar", nil) 12 | 13 | output := cell.Render(style) 14 | if output != "foobar" { 15 | t.Fatal("Unexpected output:", output) 16 | } 17 | } 18 | 19 | func TestCellRenderBool(t *testing.T) { 20 | style := &renderStyle{TableStyle: TableStyle{}, cellWidths: map[int]int{}} 21 | cell := createCell(0, true, nil) 22 | 23 | output := cell.Render(style) 24 | if output != "true" { 25 | t.Fatal("Unexpected output:", output) 26 | } 27 | } 28 | 29 | func TestCellRenderInteger(t *testing.T) { 30 | style := &renderStyle{TableStyle: TableStyle{}, cellWidths: map[int]int{}} 31 | cell := createCell(0, 12345, nil) 32 | 33 | output := cell.Render(style) 34 | if output != "12345" { 35 | t.Fatal("Unexpected output:", output) 36 | } 37 | } 38 | 39 | func TestCellRenderFloat(t *testing.T) { 40 | style := &renderStyle{TableStyle: TableStyle{}, cellWidths: map[int]int{}} 41 | cell := createCell(0, 12.345, nil) 42 | 43 | output := cell.Render(style) 44 | if output != "12.35" { 45 | t.Fatal("Unexpected output:", output) 46 | } 47 | } 48 | 49 | func TestCellRenderPadding(t *testing.T) { 50 | style := &renderStyle{TableStyle: TableStyle{PaddingLeft: 3, PaddingRight: 4}, cellWidths: map[int]int{}} 51 | 52 | cell := createCell(0, "foobar", nil) 53 | 54 | output := cell.Render(style) 55 | if output != " foobar " { 56 | t.Fatal("Unexpected output:", output) 57 | } 58 | } 59 | 60 | type foo struct { 61 | v string 62 | } 63 | 64 | func (f *foo) String() string { 65 | return f.v 66 | } 67 | 68 | func TestCellRenderStringerStruct(t *testing.T) { 69 | style := &renderStyle{TableStyle: TableStyle{}, cellWidths: map[int]int{}} 70 | cell := createCell(0, &foo{v: "bar"}, nil) 71 | 72 | output := cell.Render(style) 73 | if output != "bar" { 74 | t.Fatal("Unexpected output:", output) 75 | } 76 | } 77 | 78 | type fooString string 79 | 80 | func TestCellRenderGeneric(t *testing.T) { 81 | style := &renderStyle{TableStyle: TableStyle{}, cellWidths: map[int]int{}} 82 | cell := createCell(0, fooString("baz"), nil) 83 | 84 | output := cell.Render(style) 85 | if output != "baz" { 86 | t.Fatal("Unexpected output:", output) 87 | } 88 | } 89 | 90 | func TestFilterColorCodes(t *testing.T) { 91 | tests := []struct { 92 | in string 93 | out string 94 | }{ 95 | {"abc", "abc"}, 96 | {"", ""}, 97 | {"\033[31m\033[0m", ""}, 98 | {"a\033[31mb\033[0mc", "abc"}, 99 | {"\033[31mabc\033[0m", "abc"}, 100 | {"\033[31mfoo\033[0mbar", "foobar"}, 101 | {"\033[31mfoo\033[mbar", "foobar"}, 102 | {"\033[31mfoo\033[0;0mbar", "foobar"}, 103 | {"\033[31;4mfoo\033[0mbar", "foobar"}, 104 | {"\033[31;4;43mfoo\033[0mbar", "foobar"}, 105 | } 106 | for _, test := range tests { 107 | got := filterColorCodes(test.in) 108 | if got != test.out { 109 | t.Errorf("Invalid color-code filter result; expected %q but got %q from input %q", 110 | test.out, got, test.in) 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /html.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Apcera Inc. All rights reserved. 2 | 3 | package termtables 4 | 5 | import ( 6 | "bytes" 7 | "fmt" 8 | "html" 9 | "strings" 10 | ) 11 | 12 | type titleStyle int 13 | 14 | const ( 15 | TitleAsCaption titleStyle = iota 16 | TitleAsThSpan 17 | ) 18 | 19 | // htmlStyleRules defines attributes which we can use, and might be set on a 20 | // table by accessors, to influence the type of HTML which is output. 21 | type htmlStyleRules struct { 22 | title titleStyle 23 | } 24 | 25 | // HTML returns an HTML representations of the contents of one row of a table. 26 | func (r *Row) HTML(tag string, style *renderStyle) string { 27 | attrs := make([]string, len(r.cells)) 28 | elems := make([]string, len(r.cells)) 29 | for i := range r.cells { 30 | if r.cells[i].alignment != nil { 31 | switch *r.cells[i].alignment { 32 | case AlignLeft: 33 | attrs[i] = " align='left'" 34 | case AlignCenter: 35 | attrs[i] = " align='center'" 36 | case AlignRight: 37 | attrs[i] = " align='right'" 38 | } 39 | } 40 | elems[i] = html.EscapeString(strings.TrimSpace(r.cells[i].Render(style))) 41 | } 42 | // WAG as to max capacity, plus a bit 43 | buf := bytes.NewBuffer(make([]byte, 0, 8192)) 44 | buf.WriteString("") 45 | for i := range elems { 46 | fmt.Fprintf(buf, "<%s%s>%s", tag, attrs[i], elems[i], tag) 47 | } 48 | buf.WriteString("\n") 49 | return buf.String() 50 | } 51 | 52 | func generateHtmlTitleRow(title interface{}, t *Table, style *renderStyle) string { 53 | elContent := html.EscapeString( 54 | strings.TrimSpace(CreateCell(t.title, &CellStyle{}).Render(style)), 55 | ) 56 | 57 | switch style.htmlRules.title { 58 | case TitleAsCaption: 59 | return "" + elContent + "\n" 60 | case TitleAsThSpan: 61 | return fmt.Sprintf("%s\n", 62 | style.columns, elContent) 63 | default: 64 | return "" 65 | } 66 | } 67 | 68 | // RenderHTML returns a string representation of a the table, suitable for 69 | // inclusion as HTML elsewhere. Primary use-case controlling layout style 70 | // is for inclusion into Markdown documents, documenting normal table use. 71 | // Thus we leave the padding in place to have columns align when viewed as 72 | // plain text and rely upon HTML ignoring extra whitespace. 73 | func (t *Table) RenderHTML() (buffer string) { 74 | // elements is already populated with row data 75 | 76 | // generate the runtime style 77 | style := createRenderStyle(t) 78 | style.PaddingLeft = 0 79 | style.PaddingRight = 0 80 | 81 | // TODO: control CSS styles to suppress border based upon t.Style.SkipBorder 82 | rowsText := make([]string, 0, len(t.elements)+6) 83 | 84 | if t.title != nil || t.headers != nil { 85 | rowsText = append(rowsText, "\n") 86 | if t.title != nil { 87 | rowsText = append(rowsText, generateHtmlTitleRow(t.title, t, style)) 88 | } 89 | if t.headers != nil { 90 | rowsText = append(rowsText, CreateRow(t.headers).HTML("th", style)) 91 | } 92 | rowsText = append(rowsText, "\n") 93 | } 94 | 95 | rowsText = append(rowsText, "\n") 96 | // loop over the elements and render them 97 | for i := range t.elements { 98 | if row, ok := t.elements[i].(*Row); ok { 99 | rowsText = append(rowsText, row.HTML("td", style)) 100 | } else { 101 | rowsText = append(rowsText, fmt.Sprintf("\n", i)) 102 | } 103 | } 104 | rowsText = append(rowsText, "\n") 105 | 106 | return "\n" + strings.Join(rowsText, "") + "
\n" 107 | } 108 | -------------------------------------------------------------------------------- /html_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Apcera Inc. All rights reserved. 2 | 3 | package termtables 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestCreateTableHTML(t *testing.T) { 10 | expected := "\n" + 11 | "\n" + 12 | "\n" + 13 | "\n" + 14 | "\n" + 15 | "\n" + 16 | "\n" + 17 | "\n" + 18 | "\n" + 19 | "\n" + 20 | "
NameValue
heyyou
ken1234
derek3.14
derek too3.15
\n" 21 | 22 | table := CreateTable() 23 | table.SetModeHTML() 24 | 25 | table.AddHeaders("Name", "Value") 26 | table.AddRow("hey", "you") 27 | table.AddRow("ken", 1234) 28 | table.AddRow("derek", 3.14) 29 | table.AddRow("derek too", 3.1456788) 30 | 31 | output := table.Render() 32 | if output != expected { 33 | t.Fatal(DisplayFailedOutput(output, expected)) 34 | } 35 | } 36 | 37 | func TestTableWithHeaderHTML(t *testing.T) { 38 | expected := "\n" + 39 | "\n" + 40 | "\n" + 41 | "\n" + 42 | "\n" + 43 | "\n" + 44 | "\n" + 45 | "\n" + 46 | "\n" + 47 | "\n" + 48 | "\n" + 49 | "
Example
NameValue
heyyou
ken1234
derek3.14
derek too3.15
\n" 50 | 51 | table := CreateTable() 52 | table.SetModeHTML() 53 | 54 | table.AddTitle("Example") 55 | table.AddHeaders("Name", "Value") 56 | table.AddRow("hey", "you") 57 | table.AddRow("ken", 1234) 58 | table.AddRow("derek", 3.14) 59 | table.AddRow("derek too", 3.1456788) 60 | 61 | output := table.Render() 62 | if output != expected { 63 | t.Fatal(DisplayFailedOutput(output, expected)) 64 | } 65 | } 66 | 67 | func TestTableTitleWidthAdjustsHTML(t *testing.T) { 68 | expected := "\n" + 69 | "\n" + 70 | "\n" + 71 | "\n" + 72 | "\n" + 73 | "\n" + 74 | "\n" + 75 | "\n" + 76 | "\n" + 77 | "\n" + 78 | "\n" + 79 | "
Example My Foo Bar'd Test
NameValue
heyyou
ken1234
derek3.14
derek too3.15
\n" 80 | 81 | table := CreateTable() 82 | table.SetModeHTML() 83 | 84 | table.AddTitle("Example My Foo Bar'd Test") 85 | table.AddHeaders("Name", "Value") 86 | table.AddRow("hey", "you") 87 | table.AddRow("ken", 1234) 88 | table.AddRow("derek", 3.14) 89 | table.AddRow("derek too", 3.1456788) 90 | 91 | output := table.Render() 92 | if output != expected { 93 | t.Fatal(DisplayFailedOutput(output, expected)) 94 | } 95 | } 96 | 97 | func TestTableWithNoHeadersHTML(t *testing.T) { 98 | expected := "\n" + 99 | "\n" + 100 | "\n" + 101 | "\n" + 102 | "\n" + 103 | "\n" + 104 | "\n" + 105 | "
heyyou
ken1234
derek3.14
derek too3.15
\n" 106 | 107 | table := CreateTable() 108 | table.SetModeHTML() 109 | 110 | table.AddRow("hey", "you") 111 | table.AddRow("ken", 1234) 112 | table.AddRow("derek", 3.14) 113 | table.AddRow("derek too", 3.1456788) 114 | 115 | output := table.Render() 116 | if output != expected { 117 | t.Fatal(DisplayFailedOutput(output, expected)) 118 | } 119 | } 120 | 121 | func TestTableUnicodeWidthsHTML(t *testing.T) { 122 | expected := "\n" + 123 | "\n" + 124 | "\n" + 125 | "\n" + 126 | "\n" + 127 | "\n" + 128 | "\n" + 129 | "\n" + 130 | "\n" + 131 | "\n" + 132 | "
NameCost
Currency¤10
US Dollar$30
Euro€27
Thai฿70
\n" 133 | 134 | table := CreateTable() 135 | table.SetModeHTML() 136 | table.AddHeaders("Name", "Cost") 137 | table.AddRow("Currency", "¤10") 138 | table.AddRow("US Dollar", "$30") 139 | table.AddRow("Euro", "€27") 140 | table.AddRow("Thai", "฿70") 141 | 142 | output := table.Render() 143 | if output != expected { 144 | t.Fatal(DisplayFailedOutput(output, expected)) 145 | } 146 | } 147 | 148 | func TestTableWithAlignment(t *testing.T) { 149 | expected := "\n" + 150 | "\n" + 151 | "\n" + 152 | "\n" + 153 | "\n" + 154 | "\n" + 155 | "\n" + 156 | "\n" + 157 | "
FooBar
humptydumpty
r<- on right
\n" 158 | 159 | table := CreateTable() 160 | table.SetModeHTML() 161 | table.AddHeaders("Foo", "Bar") 162 | table.AddRow("humpty", "dumpty") 163 | table.AddRow(CreateCell("r", &CellStyle{Alignment: AlignRight}), "<- on right") 164 | 165 | output := table.Render() 166 | if output != expected { 167 | t.Fatal(DisplayFailedOutput(output, expected)) 168 | } 169 | } 170 | 171 | func TestTableAfterSetAlign(t *testing.T) { 172 | expected := "\n" + 173 | "\n" + 174 | "\n" + 175 | "\n" + 176 | "\n" + 177 | "\n" + 178 | "\n" + 179 | "\n" + 180 | "\n" + 181 | "
AlphabeticalNum
alfa1
bravo2
charlie3
\n" 182 | 183 | table := CreateTable() 184 | table.SetModeHTML() 185 | table.AddHeaders("Alphabetical", "Num") 186 | table.AddRow("alfa", 1) 187 | table.AddRow("bravo", 2) 188 | table.AddRow("charlie", 3) 189 | table.SetAlign(AlignRight, 1) 190 | 191 | output := table.Render() 192 | if output != expected { 193 | t.Fatal(DisplayFailedOutput(output, expected)) 194 | } 195 | } 196 | 197 | func TestTableWithAltTitleStyle(t *testing.T) { 198 | expected := "" + 199 | "\n" + 200 | "\n" + 201 | "\n" + 202 | "\n" + 203 | "\n" + 204 | "\n" + 205 | "\n" + 206 | "\n" + 207 | "\n" + 208 | "
Metasyntactic
FooBarBaz
abc
αβγ
\n" 209 | 210 | table := CreateTable() 211 | table.SetModeHTML() 212 | table.SetHTMLStyleTitle(TitleAsThSpan) 213 | table.AddTitle("Metasyntactic") 214 | table.AddHeaders("Foo", "Bar", "Baz") 215 | table.AddRow("a", "b", "c") 216 | table.AddRow("α", "β", "γ") 217 | 218 | output := table.Render() 219 | if output != expected { 220 | t.Fatal(DisplayFailedOutput(output, expected)) 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /row.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Apcera Inc. All rights reserved. 2 | 3 | package termtables 4 | 5 | import "strings" 6 | 7 | // A Row represents one row of a Table, consisting of some number of Cell 8 | // items. 9 | type Row struct { 10 | cells []*Cell 11 | } 12 | 13 | // CreateRow returns a Row where the cells are created as needed to hold each 14 | // item given; each item can be a Cell or content to go into a Cell created 15 | // to hold it. 16 | func CreateRow(items []interface{}) *Row { 17 | row := &Row{cells: []*Cell{}} 18 | for _, item := range items { 19 | row.AddCell(item) 20 | } 21 | return row 22 | } 23 | 24 | // AddCell adds one item to a row as a new cell, where the item is either a 25 | // Cell or content to be put into a cell. 26 | func (r *Row) AddCell(item interface{}) { 27 | if c, ok := item.(*Cell); ok { 28 | c.column = len(r.cells) 29 | r.cells = append(r.cells, c) 30 | } else { 31 | r.cells = append(r.cells, createCell(len(r.cells), item, nil)) 32 | } 33 | } 34 | 35 | // Render returns a string representing the content of one row of a table, where 36 | // the Row contains Cells (not Separators) and the representation includes any 37 | // vertical borders needed. 38 | func (r *Row) Render(style *renderStyle) string { 39 | // pre-render and shove into an array... helps with cleanly adding borders 40 | renderedCells := []string{} 41 | for _, c := range r.cells { 42 | renderedCells = append(renderedCells, c.Render(style)) 43 | } 44 | 45 | // format final output 46 | return style.BorderY + strings.Join(renderedCells, style.BorderY) + style.BorderY 47 | } 48 | -------------------------------------------------------------------------------- /row_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012-2015 Apcera Inc. All rights reserved. 2 | 3 | package termtables 4 | 5 | import ( 6 | "testing" 7 | ) 8 | 9 | func TestBasicRowRender(t *testing.T) { 10 | row := CreateRow([]interface{}{"foo", "bar"}) 11 | style := &renderStyle{TableStyle: TableStyle{BorderX: "-", BorderY: "|", BorderI: "+", 12 | PaddingLeft: 1, PaddingRight: 1}, cellWidths: map[int]int{0: 3, 1: 3}} 13 | 14 | output := row.Render(style) 15 | if output != "| foo | bar |" { 16 | t.Fatal("Unexpected output:", output) 17 | } 18 | } 19 | 20 | func TestRowRenderWidthBasedPadding(t *testing.T) { 21 | row := CreateRow([]interface{}{"foo", "bar"}) 22 | style := &renderStyle{TableStyle: TableStyle{BorderX: "-", BorderY: "|", BorderI: "+", 23 | PaddingLeft: 1, PaddingRight: 1}, cellWidths: map[int]int{0: 3, 1: 5}} 24 | 25 | output := row.Render(style) 26 | if output != "| foo | bar |" { 27 | t.Fatal("Unexpected output:", output) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /separator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Apcera Inc. All rights reserved. 2 | 3 | package termtables 4 | 5 | import "strings" 6 | 7 | type lineType int 8 | 9 | // These lines are for horizontal rules; these indicate desired styling, 10 | // but simplistic (pure ASCII) markup characters may end up leaving the 11 | // variant lines indistinguishable from LINE_INNER. 12 | const ( 13 | // LINE_INNER *must* be the default; where there are vertical lines drawn 14 | // across an inner line, the character at that position should indicate 15 | // that the vertical line goes both up and down from this horizontal line. 16 | LINE_INNER lineType = iota 17 | 18 | // LINE_TOP has only descenders 19 | LINE_TOP 20 | 21 | // LINE_SUBTOP has only descenders in the middle, but goes both up and 22 | // down at the far left & right edges. 23 | LINE_SUBTOP 24 | 25 | // LINE_BOTTOM has only ascenders. 26 | LINE_BOTTOM 27 | ) 28 | 29 | // A Separator is a horizontal rule line, with associated information which 30 | // indicates where in a table it is, sufficient for simple cases to let 31 | // clean tables be drawn. If a row-spanning cell is created, then this will 32 | // be insufficient: we can get away with hand-waving of "well, it's showing 33 | // where the border would be" but a more capable handling will require 34 | // structure reworking. Patches welcome. 35 | type Separator struct { 36 | where lineType 37 | } 38 | 39 | // Render returns the string representation of a horizontal rule line in the 40 | // table. 41 | func (s *Separator) Render(style *renderStyle) string { 42 | // loop over getting dashes 43 | parts := []string{} 44 | for i := 0; i < style.columns; i++ { 45 | w := style.PaddingLeft + style.CellWidth(i) + style.PaddingRight 46 | parts = append(parts, strings.Repeat(style.BorderX, w)) 47 | } 48 | 49 | switch s.where { 50 | case LINE_TOP: 51 | return style.BorderTopLeft + strings.Join(parts, style.BorderTop) + style.BorderTopRight 52 | case LINE_SUBTOP: 53 | return style.BorderLeft + strings.Join(parts, style.BorderTop) + style.BorderRight 54 | case LINE_BOTTOM: 55 | return style.BorderBottomLeft + strings.Join(parts, style.BorderBottom) + style.BorderBottomRight 56 | case LINE_INNER: 57 | return style.BorderLeft + strings.Join(parts, style.BorderI) + style.BorderRight 58 | } 59 | panic("not reached") 60 | } 61 | -------------------------------------------------------------------------------- /straight_separator.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012 Apcera Inc. All rights reserved. 2 | 3 | package termtables 4 | 5 | import ( 6 | "strings" 7 | "unicode/utf8" 8 | ) 9 | 10 | // A StraightSeparator is a horizontal line with associated information about 11 | // what sort of position it takes in the table, so as to control which shapes 12 | // will be used where vertical lines are expected to touch this horizontal 13 | // line. 14 | type StraightSeparator struct { 15 | where lineType 16 | } 17 | 18 | // Render returns a string representing this separator, with all border 19 | // crossings appropriately chosen. 20 | func (s *StraightSeparator) Render(style *renderStyle) string { 21 | // loop over getting dashes 22 | width := 0 23 | for i := 0; i < style.columns; i++ { 24 | width += style.PaddingLeft + style.CellWidth(i) + style.PaddingRight + utf8.RuneCountInString(style.BorderI) 25 | } 26 | 27 | switch s.where { 28 | case LINE_TOP: 29 | return style.BorderTopLeft + strings.Repeat(style.BorderX, width-1) + style.BorderTopRight 30 | case LINE_INNER, LINE_SUBTOP: 31 | return style.BorderLeft + strings.Repeat(style.BorderX, width-1) + style.BorderRight 32 | case LINE_BOTTOM: 33 | return style.BorderBottomLeft + strings.Repeat(style.BorderX, width-1) + style.BorderBottomRight 34 | } 35 | panic("not reached") 36 | } 37 | -------------------------------------------------------------------------------- /style.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012-2013 Apcera Inc. All rights reserved. 2 | 3 | package termtables 4 | 5 | import ( 6 | "fmt" 7 | "strings" 8 | "unicode/utf8" 9 | ) 10 | 11 | type TableAlignment int 12 | 13 | // These constants control the alignment which should be used when rendering 14 | // the content of a cell. 15 | const ( 16 | AlignLeft = TableAlignment(1) 17 | AlignCenter = TableAlignment(2) 18 | AlignRight = TableAlignment(3) 19 | ) 20 | 21 | // TableStyle controls styling information for a Table as a whole. 22 | // 23 | // For the Border rules, only X, Y and I are needed, and all have defaults. 24 | // The others will all default to the same as BorderI. 25 | type TableStyle struct { 26 | SkipBorder bool 27 | BorderX string 28 | BorderY string 29 | BorderI string 30 | BorderTop string 31 | BorderBottom string 32 | BorderRight string 33 | BorderLeft string 34 | BorderTopLeft string 35 | BorderTopRight string 36 | BorderBottomLeft string 37 | BorderBottomRight string 38 | PaddingLeft int 39 | PaddingRight int 40 | Width int 41 | Alignment TableAlignment 42 | htmlRules htmlStyleRules 43 | } 44 | 45 | // A CellStyle controls all style applicable to one Cell. 46 | type CellStyle struct { 47 | // Alignment indicates the alignment to be used in rendering the content 48 | Alignment TableAlignment 49 | 50 | // ColSpan indicates how many columns this Cell is expected to consume. 51 | ColSpan int 52 | } 53 | 54 | // DefaultStyle is a TableStyle which can be used to get some simple 55 | // default styling for a table, using ASCII characters for drawing borders. 56 | var DefaultStyle = &TableStyle{ 57 | SkipBorder: false, 58 | BorderX: "-", BorderY: "|", BorderI: "+", 59 | PaddingLeft: 1, PaddingRight: 1, 60 | Width: 80, 61 | Alignment: AlignLeft, 62 | 63 | // FIXME: the use of a Width here may interact poorly with a changing 64 | // MaxColumns value; we don't set MaxColumns here because the evaluation 65 | // order of a var and an init value adds undesired subtlety. 66 | } 67 | 68 | type renderStyle struct { 69 | cellWidths map[int]int 70 | columns int 71 | 72 | // used for markdown rendering 73 | replaceContent func(string) string 74 | 75 | TableStyle 76 | } 77 | 78 | // setUtfBoxStyle changes the border characters to be suitable for use when 79 | // the output stream can render UTF-8 characters. 80 | func (s *TableStyle) setUtfBoxStyle() { 81 | s.BorderX = "─" 82 | s.BorderY = "│" 83 | s.BorderI = "┼" 84 | s.BorderTop = "┬" 85 | s.BorderBottom = "┴" 86 | s.BorderLeft = "├" 87 | s.BorderRight = "┤" 88 | s.BorderTopLeft = "╭" 89 | s.BorderTopRight = "╮" 90 | s.BorderBottomLeft = "╰" 91 | s.BorderBottomRight = "╯" 92 | } 93 | 94 | // setAsciiBoxStyle changes the border characters back to their defaults 95 | func (s *TableStyle) setAsciiBoxStyle() { 96 | s.BorderX = "-" 97 | s.BorderY = "|" 98 | s.BorderI = "+" 99 | s.BorderTop, s.BorderBottom, s.BorderLeft, s.BorderRight = "", "", "", "" 100 | s.BorderTopLeft, s.BorderTopRight, s.BorderBottomLeft, s.BorderBottomRight = "", "", "", "" 101 | s.fillStyleRules() 102 | } 103 | 104 | // fillStyleRules populates members of the TableStyle box-drawing specification 105 | // with BorderI as the default. 106 | func (s *TableStyle) fillStyleRules() { 107 | if s.BorderTop == "" { 108 | s.BorderTop = s.BorderI 109 | } 110 | if s.BorderBottom == "" { 111 | s.BorderBottom = s.BorderI 112 | } 113 | if s.BorderLeft == "" { 114 | s.BorderLeft = s.BorderI 115 | } 116 | if s.BorderRight == "" { 117 | s.BorderRight = s.BorderI 118 | } 119 | if s.BorderTopLeft == "" { 120 | s.BorderTopLeft = s.BorderI 121 | } 122 | if s.BorderTopRight == "" { 123 | s.BorderTopRight = s.BorderI 124 | } 125 | if s.BorderBottomLeft == "" { 126 | s.BorderBottomLeft = s.BorderI 127 | } 128 | if s.BorderBottomRight == "" { 129 | s.BorderBottomRight = s.BorderI 130 | } 131 | } 132 | 133 | func createRenderStyle(table *Table) *renderStyle { 134 | style := &renderStyle{TableStyle: *table.Style, cellWidths: map[int]int{}} 135 | style.TableStyle.fillStyleRules() 136 | 137 | if table.outputMode == outputMarkdown { 138 | style.buildReplaceContent(table.Style.BorderY) 139 | } 140 | 141 | // FIXME: handle actually defined width condition 142 | 143 | // loop over the rows and cells to calculate widths 144 | for _, element := range table.elements { 145 | // skip separators 146 | if _, ok := element.(*Separator); ok { 147 | continue 148 | } 149 | 150 | // iterate over cells 151 | if row, ok := element.(*Row); ok { 152 | for i, cell := range row.cells { 153 | // FIXME: need to support sizing with colspan handling 154 | if cell.colSpan > 1 { 155 | continue 156 | } 157 | if style.cellWidths[i] < cell.Width() { 158 | style.cellWidths[i] = cell.Width() 159 | } 160 | } 161 | } 162 | } 163 | style.columns = len(style.cellWidths) 164 | 165 | // calculate actual width 166 | width := utf8.RuneCountInString(style.BorderLeft) // start at '1' for left border 167 | internalBorderWidth := utf8.RuneCountInString(style.BorderI) 168 | 169 | lastIndex := 0 170 | for i, v := range style.cellWidths { 171 | width += v + style.PaddingLeft + style.PaddingRight + internalBorderWidth 172 | if i > lastIndex { 173 | lastIndex = i 174 | } 175 | } 176 | if internalBorderWidth != utf8.RuneCountInString(style.BorderRight) { 177 | width += utf8.RuneCountInString(style.BorderRight) - internalBorderWidth 178 | } 179 | 180 | if table.titleCell != nil { 181 | titleMinWidth := 0 + 182 | table.titleCell.Width() + 183 | utf8.RuneCountInString(style.BorderLeft) + 184 | utf8.RuneCountInString(style.BorderRight) + 185 | style.PaddingLeft + 186 | style.PaddingRight 187 | 188 | if width < titleMinWidth { 189 | // minWidth must be set to include padding of the title, as required 190 | style.cellWidths[lastIndex] += (titleMinWidth - width) 191 | width = titleMinWidth 192 | } 193 | } 194 | 195 | // right border is covered in loop 196 | style.Width = width 197 | 198 | return style 199 | } 200 | 201 | // CellWidth returns the width of the cell at the supplied index, where the 202 | // width is the number of tty character-cells required to draw the glyphs. 203 | func (s *renderStyle) CellWidth(i int) int { 204 | return s.cellWidths[i] 205 | } 206 | 207 | // buildReplaceContent creates a function closure, with minimal bound lexical 208 | // state, which replaces content 209 | func (s *renderStyle) buildReplaceContent(bad string) { 210 | replacement := fmt.Sprintf("&#x%02x;", bad) 211 | s.replaceContent = func(old string) string { 212 | return strings.Replace(old, bad, replacement, -1) 213 | } 214 | } 215 | -------------------------------------------------------------------------------- /table.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012-2013 Apcera Inc. All rights reserved. 2 | 3 | package termtables 4 | 5 | import ( 6 | "bytes" 7 | "os" 8 | "regexp" 9 | "runtime" 10 | "strings" 11 | 12 | "github.com/scylladb/termtables/term" 13 | ) 14 | 15 | // MaxColumns represents the maximum number of columns that are available for 16 | // display without wrapping around the right-hand side of the terminal window. 17 | // At program initialization, the value will be automatically set according 18 | // to available sources of information, including the $COLUMNS environment 19 | // variable and, on Unix, tty information. 20 | var MaxColumns = 80 21 | 22 | // Element the interface that can draw a representation of the contents of a 23 | // table cell. 24 | type Element interface { 25 | Render(*renderStyle) string 26 | } 27 | 28 | type outputMode int 29 | 30 | const ( 31 | outputTerminal outputMode = iota 32 | outputMarkdown 33 | outputHTML 34 | ) 35 | 36 | // Open question: should UTF-8 become an output mode? It does require more 37 | // tracking when resetting, if the locale-enabling had been used. 38 | 39 | var outputsEnabled struct { 40 | UTF8 bool 41 | HTML bool 42 | Markdown bool 43 | titleStyle titleStyle 44 | } 45 | 46 | var defaultOutputMode outputMode = outputTerminal 47 | 48 | // Table represents a terminal table. The Style can be directly accessed 49 | // and manipulated; all other access is via methods. 50 | type Table struct { 51 | Style *TableStyle 52 | 53 | elements []Element 54 | headers []interface{} 55 | title interface{} 56 | titleCell *Cell 57 | outputMode outputMode 58 | } 59 | 60 | // EnableUTF8 will unconditionally enable using UTF-8 box-drawing characters 61 | // for any tables created after this call, as the default style. 62 | func EnableUTF8() { 63 | outputsEnabled.UTF8 = true 64 | } 65 | 66 | // SetModeHTML will control whether or not new tables generated will be in HTML 67 | // mode by default; HTML-or-not takes precedence over options which control how 68 | // a terminal output will be rendered, such as whether or not to use UTF8. 69 | // This affects any tables created after this call. 70 | func SetModeHTML(onoff bool) { 71 | outputsEnabled.HTML = onoff 72 | chooseDefaultOutput() 73 | } 74 | 75 | // SetModeMarkdown will control whether or not new tables generated will be 76 | // in Markdown mode by default. HTML-mode takes precedence. 77 | func SetModeMarkdown(onoff bool) { 78 | outputsEnabled.Markdown = onoff 79 | chooseDefaultOutput() 80 | } 81 | 82 | var utfRe = regexp.MustCompile(`utf\-8|utf8|UTF\-8|UTF8`) 83 | 84 | // EnableUTF8PerLocale will use current locale character map information to 85 | // determine if UTF-8 is expected and, if so, is equivalent to EnableUTF8. 86 | func EnableUTF8PerLocale() { 87 | locale := getLocale() 88 | if utfRe.MatchString(locale) { 89 | EnableUTF8() 90 | } 91 | } 92 | 93 | // getLocale returns the current locale name. 94 | func getLocale() string { 95 | if runtime.GOOS == "windows" { 96 | // TODO: detect windows locale 97 | return "US-ASCII" 98 | } 99 | return unixLocale() 100 | } 101 | 102 | // unixLocale returns the locale by checking the $LC_ALL, $LC_CTYPE, and $LANG 103 | // environment variables. If none of those are set, it returns "US-ASCII". 104 | func unixLocale() string { 105 | for _, env := range []string{"LC_ALL", "LC_CTYPE", "LANG"} { 106 | if locale := os.Getenv(env); locale != "" { 107 | return locale 108 | } 109 | } 110 | return "US-ASCII" 111 | } 112 | 113 | // SetHTMLStyleTitle lets an HTML title output mode be chosen. 114 | func SetHTMLStyleTitle(want titleStyle) { 115 | outputsEnabled.titleStyle = want 116 | } 117 | 118 | // chooseDefaultOutput sets defaultOutputMode based on priority 119 | // choosing amongst the options which are enabled. Pros: simpler 120 | // encapsulation; cons: setting markdown doesn't disable HTML if 121 | // HTML was previously enabled and was later disabled. 122 | // This seems fairly reasonable. 123 | func chooseDefaultOutput() { 124 | if outputsEnabled.HTML { 125 | defaultOutputMode = outputHTML 126 | } else if outputsEnabled.Markdown { 127 | defaultOutputMode = outputMarkdown 128 | } else { 129 | defaultOutputMode = outputTerminal 130 | } 131 | } 132 | 133 | func init() { 134 | // Do not enable UTF-8 per locale by default, breaks tests. 135 | sz, err := term.GetSize() 136 | if err == nil && sz.Columns != 0 { 137 | MaxColumns = sz.Columns 138 | } 139 | } 140 | 141 | // CreateTable creates an empty Table using defaults for style. 142 | func CreateTable() *Table { 143 | t := &Table{elements: []Element{}, Style: DefaultStyle} 144 | if outputsEnabled.UTF8 { 145 | t.Style.setUtfBoxStyle() 146 | } 147 | if outputsEnabled.titleStyle != titleStyle(0) { 148 | t.Style.htmlRules.title = outputsEnabled.titleStyle 149 | } 150 | t.outputMode = defaultOutputMode 151 | return t 152 | } 153 | 154 | // AddSeparator adds a line to the table content, where the line 155 | // consists of separator characters. 156 | func (t *Table) AddSeparator() { 157 | t.elements = append(t.elements, &Separator{}) 158 | } 159 | 160 | // AddRow adds the supplied items as cells in one row of the table. 161 | func (t *Table) AddRow(items ...interface{}) *Row { 162 | row := CreateRow(items) 163 | t.elements = append(t.elements, row) 164 | return row 165 | } 166 | 167 | // AddTitle supplies a table title, which if present will be rendered as 168 | // one cell across the width of the table, as the first row. 169 | func (t *Table) AddTitle(title interface{}) { 170 | t.title = title 171 | } 172 | 173 | // AddHeaders supplies column headers for the table. 174 | func (t *Table) AddHeaders(headers ...interface{}) { 175 | t.headers = append(t.headers, headers...) 176 | } 177 | 178 | // SetAlign changes the alignment for elements in a column of the table; 179 | // alignments are stored with each cell, so cells added after a call to 180 | // SetAlign will not pick up the change. Columns are numbered from 1. 181 | func (t *Table) SetAlign(align TableAlignment, columns ...int) { 182 | for i := range t.elements { 183 | row, ok := t.elements[i].(*Row) 184 | if !ok { 185 | continue 186 | } 187 | for _, column := range columns { 188 | if column < 0 || column > len(row.cells) { 189 | continue 190 | } 191 | row.cells[column-1].alignment = &align 192 | } 193 | } 194 | } 195 | 196 | // UTF8Box sets the table style to use UTF-8 box-drawing characters, 197 | // overriding all relevant style elements at the time of the call. 198 | func (t *Table) UTF8Box() { 199 | t.Style.setUtfBoxStyle() 200 | } 201 | 202 | // SetModeHTML switches this table to be in HTML when rendered; the 203 | // default depends upon whether the package function SetModeHTML() has been 204 | // called, and with what value. This method forces the feature on for this 205 | // table. Turning off involves choosing a different mode, per-table. 206 | func (t *Table) SetModeHTML() { 207 | t.outputMode = outputHTML 208 | } 209 | 210 | // SetModeMarkdown switches this table to be in Markdown mode 211 | func (t *Table) SetModeMarkdown() { 212 | t.outputMode = outputMarkdown 213 | } 214 | 215 | // SetModeTerminal switches this table to be in terminal mode. 216 | func (t *Table) SetModeTerminal() { 217 | t.outputMode = outputTerminal 218 | } 219 | 220 | // SetHTMLStyleTitle lets an HTML output mode be chosen; we should rework this 221 | // into a more generic and extensible API as we clean up termtables. 222 | func (t *Table) SetHTMLStyleTitle(want titleStyle) { 223 | t.Style.htmlRules.title = want 224 | } 225 | 226 | // Render returns a string representation of a fully rendered table, drawn 227 | // out for display, with embedded newlines. If this table is in HTML mode, 228 | // then this is equivalent to RenderHTML(). 229 | func (t *Table) Render() string { 230 | // Elements is already populated with row data. 231 | switch t.outputMode { 232 | case outputTerminal: 233 | return t.renderTerminal() 234 | case outputMarkdown: 235 | return t.renderMarkdown() 236 | case outputHTML: 237 | return t.RenderHTML() 238 | default: 239 | panic("unknown output mode set") 240 | } 241 | } 242 | 243 | // renderTerminal returns a string representation of a fully rendered table, 244 | // drawn out for display, with embedded newlines. 245 | func (t *Table) renderTerminal() string { 246 | // Use a placeholder rather than adding titles/headers to the tables 247 | // elements or else successive calls will compound them. 248 | tt := t.clone() 249 | 250 | // Initial top line. 251 | if !tt.Style.SkipBorder { 252 | if tt.title != nil && tt.headers == nil { 253 | tt.elements = append([]Element{&Separator{where: LINE_SUBTOP}}, tt.elements...) 254 | } else if tt.title == nil && tt.headers == nil { 255 | tt.elements = append([]Element{&Separator{where: LINE_TOP}}, tt.elements...) 256 | } else { 257 | tt.elements = append([]Element{&Separator{where: LINE_INNER}}, tt.elements...) 258 | } 259 | } 260 | 261 | // If we have headers, include them. 262 | if tt.headers != nil { 263 | ne := make([]Element, 2) 264 | ne[1] = CreateRow(tt.headers) 265 | if tt.title != nil { 266 | ne[0] = &Separator{where: LINE_SUBTOP} 267 | } else { 268 | ne[0] = &Separator{where: LINE_TOP} 269 | } 270 | tt.elements = append(ne, tt.elements...) 271 | } 272 | 273 | // If we have a title, write it. 274 | if tt.title != nil { 275 | // Match changes to this into renderMarkdown too. 276 | tt.titleCell = CreateCell(tt.title, &CellStyle{Alignment: AlignCenter, ColSpan: 999}) 277 | ne := []Element{ 278 | &StraightSeparator{where: LINE_TOP}, 279 | CreateRow([]interface{}{tt.titleCell}), 280 | } 281 | tt.elements = append(ne, tt.elements...) 282 | } 283 | 284 | // Create a new table from the 285 | // generate the runtime style. Must include all cells being printed. 286 | style := createRenderStyle(tt) 287 | 288 | // Loop over the elements and render them. 289 | b := bytes.NewBuffer(nil) 290 | for _, e := range tt.elements { 291 | b.WriteString(e.Render(style)) 292 | b.WriteString("\n") 293 | } 294 | 295 | // Add bottom line. 296 | if !style.SkipBorder { 297 | b.WriteString((&Separator{where: LINE_BOTTOM}).Render(style) + "\n") 298 | } 299 | 300 | return b.String() 301 | } 302 | 303 | // renderMarkdown returns a string representation of a table in Markdown 304 | // markup format using GitHub Flavored Markdown's notation (since tables 305 | // are not in the core Markdown spec). 306 | func (t *Table) renderMarkdown() string { 307 | // We need ASCII drawing characters; we need a line after the header; 308 | // *do* need a header! Do not need to markdown-escape contents of 309 | // tables as markdown is ignored in there. Do need to do _something_ 310 | // with a '|' character shown as a member of a table. 311 | 312 | t.Style.setAsciiBoxStyle() 313 | 314 | firstLines := make([]Element, 0, 2) 315 | 316 | if t.headers == nil { 317 | initial := createRenderStyle(t) 318 | if initial.columns > 1 { 319 | row := CreateRow([]interface{}{}) 320 | for i := 0; i < initial.columns; i++ { 321 | row.AddCell(CreateCell(i+1, &CellStyle{})) 322 | } 323 | } 324 | } 325 | 326 | firstLines = append(firstLines, CreateRow(t.headers)) 327 | // This is a dummy line, swapped out below. 328 | firstLines = append(firstLines, firstLines[0]) 329 | t.elements = append(firstLines, t.elements...) 330 | // Generate the runtime style. 331 | style := createRenderStyle(t) 332 | // We know that the second line is a dummy, we can replace it. 333 | mdRow := CreateRow([]interface{}{}) 334 | for i := 0; i < style.columns; i++ { 335 | mdRow.AddCell(CreateCell(strings.Repeat("-", style.cellWidths[i]), &CellStyle{})) 336 | } 337 | t.elements[1] = mdRow 338 | 339 | b := bytes.NewBuffer(nil) 340 | // Comes after style is generated, which must come after all width-affecting 341 | // changes are in. 342 | if t.title != nil { 343 | // Markdown doesn't support titles or column spanning; we _should_ 344 | // escape the title, but doing that to handle all possible forms of 345 | // markup would require a heavy dependency, so we punt. 346 | b.WriteString("Table: ") 347 | b.WriteString(strings.TrimSpace(CreateCell(t.title, &CellStyle{}).Render(style))) 348 | b.WriteString("\n\n") 349 | } 350 | 351 | // Loop over the elements and render them. 352 | for _, e := range t.elements { 353 | b.WriteString(e.Render(style)) 354 | b.WriteString("\n") 355 | } 356 | 357 | return b.String() 358 | } 359 | 360 | // clone returns a copy of the table with the underlying slices being copied; 361 | // the references to the Elements/cells are left as shallow copies. 362 | func (t *Table) clone() *Table { 363 | tt := &Table{outputMode: t.outputMode, Style: t.Style, title: t.title} 364 | if t.headers != nil { 365 | tt.headers = make([]interface{}, len(t.headers)) 366 | copy(tt.headers, t.headers) 367 | } 368 | if t.elements != nil { 369 | tt.elements = make([]Element, len(t.elements)) 370 | copy(tt.elements, t.elements) 371 | } 372 | return tt 373 | } 374 | -------------------------------------------------------------------------------- /table_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2012-2013 Apcera Inc. All rights reserved. 2 | package termtables 3 | 4 | import "testing" 5 | 6 | func DisplayFailedOutput(actual, expected string) string { 7 | return "Output didn't match expected\n\n" + 8 | "Actual:\n\n" + 9 | actual + "\n" + 10 | "Expected:\n\n" + 11 | expected 12 | } 13 | 14 | func checkRendersTo(t *testing.T, table *Table, expected string) { 15 | output := table.Render() 16 | if output != expected { 17 | t.Fatal(DisplayFailedOutput(output, expected)) 18 | } 19 | } 20 | 21 | func TestCreateTable(t *testing.T) { 22 | expected := "" + 23 | "+-----------+-------+\n" + 24 | "| Name | Value |\n" + 25 | "+-----------+-------+\n" + 26 | "| hey | you |\n" + 27 | "| ken | 1234 |\n" + 28 | "| derek | 3.14 |\n" + 29 | "| derek too | 3.15 |\n" + 30 | "| escaping | rox%% |\n" + 31 | "+-----------+-------+\n" 32 | 33 | table := CreateTable() 34 | 35 | table.AddHeaders("Name", "Value") 36 | table.AddRow("hey", "you") 37 | table.AddRow("ken", 1234) 38 | table.AddRow("derek", 3.14) 39 | table.AddRow("derek too", 3.1456788) 40 | table.AddRow("escaping", "rox%%") 41 | 42 | checkRendersTo(t, table, expected) 43 | } 44 | 45 | func TestStyleResets(t *testing.T) { 46 | expected := "" + 47 | "+-----------+-------+\n" + 48 | "| Name | Value |\n" + 49 | "+-----------+-------+\n" + 50 | "| hey | you |\n" + 51 | "| ken | 1234 |\n" + 52 | "| derek | 3.14 |\n" + 53 | "| derek too | 3.15 |\n" + 54 | "+-----------+-------+\n" 55 | 56 | table := CreateTable() 57 | table.UTF8Box() 58 | table.Style.setAsciiBoxStyle() 59 | 60 | table.AddHeaders("Name", "Value") 61 | table.AddRow("hey", "you") 62 | table.AddRow("ken", 1234) 63 | table.AddRow("derek", 3.14) 64 | table.AddRow("derek too", 3.1456788) 65 | 66 | checkRendersTo(t, table, expected) 67 | } 68 | 69 | func TestTableWithHeader(t *testing.T) { 70 | expected := "" + 71 | "+-------------------+\n" + 72 | "| Example |\n" + 73 | "+-----------+-------+\n" + 74 | "| Name | Value |\n" + 75 | "+-----------+-------+\n" + 76 | "| hey | you |\n" + 77 | "| ken | 1234 |\n" + 78 | "| derek | 3.14 |\n" + 79 | "| derek too | 3.15 |\n" + 80 | "+-----------+-------+\n" 81 | 82 | table := CreateTable() 83 | 84 | table.AddTitle("Example") 85 | table.AddHeaders("Name", "Value") 86 | table.AddRow("hey", "you") 87 | table.AddRow("ken", 1234) 88 | table.AddRow("derek", 3.14) 89 | table.AddRow("derek too", 3.1456788) 90 | 91 | checkRendersTo(t, table, expected) 92 | } 93 | 94 | // TestTableWithHeaderMultipleTimes ensures that printing a table with headers 95 | // multiple times continues to render correctly. 96 | func TestTableWithHeaderMultipleTimes(t *testing.T) { 97 | expected := "" + 98 | "+-------------------+\n" + 99 | "| Example |\n" + 100 | "+-----------+-------+\n" + 101 | "| Name | Value |\n" + 102 | "+-----------+-------+\n" + 103 | "| hey | you |\n" + 104 | "| ken | 1234 |\n" + 105 | "| derek | 3.14 |\n" + 106 | "| derek too | 3.15 |\n" + 107 | "+-----------+-------+\n" 108 | 109 | table := CreateTable() 110 | 111 | table.AddTitle("Example") 112 | table.AddHeaders("Name", "Value") 113 | table.AddRow("hey", "you") 114 | table.AddRow("ken", 1234) 115 | table.AddRow("derek", 3.14) 116 | table.AddRow("derek too", 3.1456788) 117 | 118 | checkRendersTo(t, table, expected) 119 | checkRendersTo(t, table, expected) 120 | } 121 | 122 | func TestTableTitleWidthAdjusts(t *testing.T) { 123 | expected := "" + 124 | "+---------------------------+\n" + 125 | "| Example My Foo Bar'd Test |\n" + 126 | "+-----------+---------------+\n" + 127 | "| Name | Value |\n" + 128 | "+-----------+---------------+\n" + 129 | "| hey | you |\n" + 130 | "| ken | 1234 |\n" + 131 | "| derek | 3.14 |\n" + 132 | "| derek too | 3.15 |\n" + 133 | "+-----------+---------------+\n" 134 | 135 | table := CreateTable() 136 | 137 | table.AddTitle("Example My Foo Bar'd Test") 138 | table.AddHeaders("Name", "Value") 139 | table.AddRow("hey", "you") 140 | table.AddRow("ken", 1234) 141 | table.AddRow("derek", 3.14) 142 | table.AddRow("derek too", 3.1456788) 143 | 144 | checkRendersTo(t, table, expected) 145 | } 146 | 147 | func TestTableHeaderWidthAdjusts(t *testing.T) { 148 | expected := "" + 149 | "+---------------+---------------------+\n" + 150 | "| Slightly Long | More than 2 columns |\n" + 151 | "+---------------+---------------------+\n" + 152 | "| a | b |\n" + 153 | "+---------------+---------------------+\n" 154 | 155 | table := CreateTable() 156 | 157 | table.AddHeaders("Slightly Long", "More than 2 columns") 158 | table.AddRow("a", "b") 159 | 160 | checkRendersTo(t, table, expected) 161 | } 162 | 163 | func TestTableWithNoHeaders(t *testing.T) { 164 | expected := "" + 165 | "+-----------+------+\n" + 166 | "| hey | you |\n" + 167 | "| ken | 1234 |\n" + 168 | "| derek | 3.14 |\n" + 169 | "| derek too | 3.15 |\n" + 170 | "+-----------+------+\n" 171 | 172 | table := CreateTable() 173 | 174 | table.AddRow("hey", "you") 175 | table.AddRow("ken", 1234) 176 | table.AddRow("derek", 3.14) 177 | table.AddRow("derek too", 3.1456788) 178 | 179 | checkRendersTo(t, table, expected) 180 | } 181 | 182 | func TestTableUnicodeWidths(t *testing.T) { 183 | expected := "" + 184 | "+-----------+------+\n" + 185 | "| Name | Cost |\n" + 186 | "+-----------+------+\n" + 187 | "| Currency | ¤10 |\n" + 188 | "| US Dollar | $30 |\n" + 189 | "| Euro | €27 |\n" + 190 | "| Thai | ฿70 |\n" + 191 | "+-----------+------+\n" 192 | 193 | table := CreateTable() 194 | table.AddHeaders("Name", "Cost") 195 | table.AddRow("Currency", "¤10") 196 | table.AddRow("US Dollar", "$30") 197 | table.AddRow("Euro", "€27") 198 | table.AddRow("Thai", "฿70") 199 | 200 | checkRendersTo(t, table, expected) 201 | } 202 | 203 | func TestTableInUTF8(t *testing.T) { 204 | expected := "" + 205 | "╭───────────────────╮\n" + 206 | "│ Example │\n" + 207 | "├───────────┬───────┤\n" + 208 | "│ Name │ Value │\n" + 209 | "├───────────┼───────┤\n" + 210 | "│ hey │ you │\n" + 211 | "│ ken │ 1234 │\n" + 212 | "│ derek │ 3.14 │\n" + 213 | "│ derek too │ 3.15 │\n" + 214 | "│ escaping │ rox%% │\n" + 215 | "╰───────────┴───────╯\n" 216 | 217 | table := CreateTable() 218 | table.UTF8Box() 219 | 220 | table.AddTitle("Example") 221 | table.AddHeaders("Name", "Value") 222 | table.AddRow("hey", "you") 223 | table.AddRow("ken", 1234) 224 | table.AddRow("derek", 3.14) 225 | table.AddRow("derek too", 3.1456788) 226 | table.AddRow("escaping", "rox%%") 227 | 228 | checkRendersTo(t, table, expected) 229 | } 230 | 231 | func TestTableUnicodeUTF8AndSGR(t *testing.T) { 232 | // at present, this mostly just tests that alignment still works 233 | expected := "" + 234 | "╭───────────────────────╮\n" + 235 | "│ \033[1mFanciness\033[0m │\n" + 236 | "├──────────┬────────────┤\n" + 237 | "│ \033[31mred\033[0m │ \033[32mgreen\033[0m │\n" + 238 | "├──────────┼────────────┤\n" + 239 | "│ plain │ text │\n" + 240 | "│ Καλημέρα │ κόσμε │\n" + 241 | "│ \033[1mvery\033[0m │ \033[4munderlined\033[0m │\n" + 242 | "│ a\033[1mb\033[0mc │ \033[45mmagenta\033[0m │\n" + 243 | "│ \033[31m→\033[0m │ \033[32m←\033[0m │\n" + 244 | "╰──────────┴────────────╯\n" 245 | 246 | sgred := func(in string, sgrPm string) string { 247 | return "\033[" + sgrPm + "m" + in + "\033[0m" 248 | } 249 | bold := func(in string) string { return sgred(in, "1") } 250 | 251 | table := CreateTable() 252 | table.UTF8Box() 253 | 254 | table.AddTitle(bold("Fanciness")) 255 | table.AddHeaders(sgred("red", "31"), sgred("green", "32")) 256 | table.AddRow("plain", "text") 257 | table.AddRow("Καλημέρα", "κόσμε") // from http://plan9.bell-labs.com/sys/doc/utf.html 258 | table.AddRow(bold("very"), sgred("underlined", "4")) 259 | table.AddRow("a"+bold("b")+"c", sgred("magenta", "45")) 260 | table.AddRow(sgred("→", "31"), sgred("←", "32")) 261 | // TODO: in future, if we start detecting presence of SGR sequences, we 262 | // should ensure that the SGR reset is done at the end of the cell content, 263 | // so that SGR doesn't "bleed across" (cells or rows). We would then add 264 | // tests for that here. 265 | // 266 | // Of course, at that point, we'd also want to support automatic HTML 267 | // styling conversion too, so would need a test for that also. 268 | 269 | checkRendersTo(t, table, expected) 270 | } 271 | 272 | func TestTableInMarkdown(t *testing.T) { 273 | expected := "" + 274 | "Table: Example\n\n" + 275 | "| Name | Value |\n" + 276 | "| ----- | ----- |\n" + 277 | "| hey | you |\n" + 278 | "| a | b | esc |\n" + 279 | "| esc | rox%% |\n" 280 | 281 | table := CreateTable() 282 | table.SetModeMarkdown() 283 | 284 | table.AddTitle("Example") 285 | table.AddHeaders("Name", "Value") 286 | table.AddRow("hey", "you") 287 | table.AddRow("a | b", "esc") 288 | table.AddRow("esc", "rox%%") 289 | 290 | checkRendersTo(t, table, expected) 291 | } 292 | 293 | func TestTitleUnicodeWidths(t *testing.T) { 294 | expected := "" + 295 | "+-------+\n" + 296 | "| ← 5 → |\n" + 297 | "+---+---+\n" + 298 | "| a | b |\n" + 299 | "| c | d |\n" + 300 | "| e | 3 |\n" + 301 | "+---+---+\n" 302 | 303 | // minimum width for a table of two columns is 9 characters, given 304 | // one space of padding, and non-empty tables. 305 | 306 | table := CreateTable() 307 | 308 | // We have 4 characters down for left and right columns and padding, so 309 | // a width of 5 for us should match the minimum per the columns 310 | 311 | // 5 characters; each arrow is three octets in UTF-8, giving 9 bytes 312 | // so, same in character-count-width, longer in bytes 313 | table.AddTitle("← 5 →") 314 | 315 | // a single character per cell, here; use ASCII characters 316 | table.AddRow("a", "b") 317 | table.AddRow("c", "d") 318 | table.AddRow("e", 3) 319 | 320 | checkRendersTo(t, table, expected) 321 | } 322 | 323 | // We identified two error conditions wherein length wrapping would not correctly 324 | // wrap width when, for instance, in a two-column table, the longest row in the 325 | // right-hand column was not the same as the longest row in the left-hand column. 326 | // This tests that we correctly accumulate the maximum width across all rows of 327 | // the termtable and adjust width accordingly. 328 | func TestTableWidthHandling(t *testing.T) { 329 | expected := "" + 330 | "+-----------------------------------------+\n" + 331 | "| Example... to Fix My Test |\n" + 332 | "+-----------------+-----------------------+\n" + 333 | "| hey foo bar baz | you |\n" + 334 | "| ken | you should write code |\n" + 335 | "| derek | 3.14 |\n" + 336 | "| derek too | 3.15 |\n" + 337 | "+-----------------+-----------------------+\n" 338 | 339 | table := CreateTable() 340 | 341 | table.AddTitle("Example... to Fix My Test") 342 | table.AddRow("hey foo bar baz", "you") 343 | table.AddRow("ken", "you should write code") 344 | table.AddRow("derek", 3.14) 345 | table.AddRow("derek too", 3.1456788) 346 | 347 | output := table.Render() 348 | if output != expected { 349 | t.Fatal(DisplayFailedOutput(output, expected)) 350 | } 351 | 352 | } 353 | 354 | func TestTableWidthHandling_SecondErrorCondition(t *testing.T) { 355 | expected := "" + 356 | "+----------------------------------------+\n" + 357 | "| Example... to Fix My Test |\n" + 358 | "+-----------------+----------------------+\n" + 359 | "| hey foo bar baz | you |\n" + 360 | "| ken | you should sell cod! |\n" + 361 | "| derek | 3.14 |\n" + 362 | "| derek too | 3.15 |\n" + 363 | "+-----------------+----------------------+\n" 364 | 365 | table := CreateTable() 366 | 367 | table.AddTitle("Example... to Fix My Test") 368 | table.AddRow("hey foo bar baz", "you") 369 | table.AddRow("ken", "you should sell cod!") 370 | table.AddRow("derek", 3.14) 371 | table.AddRow("derek too", 3.1456788) 372 | 373 | output := table.Render() 374 | if output != expected { 375 | t.Fatal(DisplayFailedOutput(output, expected)) 376 | } 377 | } 378 | 379 | func TestTableAlignPostsetting(t *testing.T) { 380 | expected := "" + 381 | "+-----------+-------+----------+\n"+ 382 | "| Name | Value | Value 2 |\n"+ 383 | "+-----------+-------+----------+\n"+ 384 | "| hey | you | man |\n"+ 385 | "| ken | 1234 | 4321 |\n"+ 386 | "| derek | 3.14 | bob |\n"+ 387 | "| derek too | 3.15 | long bob |\n"+ 388 | "| escaping | rox%% | :) |\n"+ 389 | "+-----------+-------+----------+\n" 390 | 391 | table := CreateTable() 392 | 393 | table.AddHeaders("Name", "Value", "Value 2") 394 | table.AddRow("hey", "you", "man") 395 | table.AddRow("ken", 1234, 4321) 396 | table.AddRow("derek", 3.14, "bob") 397 | table.AddRow("derek too", 3.1456788, "long bob") 398 | table.AddRow("escaping", "rox%%", ":)") 399 | 400 | table.SetAlign(AlignRight, 2, 3) 401 | 402 | checkRendersTo(t, table, expected) 403 | } 404 | 405 | func TestTableMissingCells(t *testing.T) { 406 | expected := "" + 407 | "+----------+---------+---------+\n" + 408 | "| Name | Value 1 | Value 2 |\n" + 409 | "+----------+---------+---------+\n" + 410 | "| hey | you | person |\n" + 411 | "| ken | 1234 |\n" + 412 | "| escaping | rox%s%% |\n" + 413 | "+----------+---------+---------+\n" 414 | // FIXME: missing extra cells there 415 | 416 | table := CreateTable() 417 | 418 | table.AddHeaders("Name", "Value 1", "Value 2") 419 | table.AddRow("hey", "you", "person") 420 | table.AddRow("ken", 1234) 421 | table.AddRow("escaping", "rox%s%%") 422 | 423 | checkRendersTo(t, table, expected) 424 | } 425 | 426 | // We don't yet support combining characters, double-width characters or 427 | // anything to do with estimating a tty-style "character width" for what in 428 | // Unicode is a grapheme cluster. This disabled test shows what we want 429 | // to support, but don't yet. 430 | func TestTableWithCombiningChars(t *testing.T) { 431 | expected := "" + 432 | "+------+---+\n" + 433 | "| noel | 1 |\n" + 434 | "| noël | 2 |\n" + 435 | "| noël | 3 |\n" + 436 | "+------+---+\n" 437 | 438 | table := CreateTable() 439 | 440 | table.AddRow("noel", "1") 441 | table.AddRow("noe\u0308l", "2") // LATIN SMALL LETTER E + COMBINING DIAERESIS 442 | table.AddRow("noël", "3") // Hex EB; LATIN SMALL LETTER E WITH DIAERESIS 443 | 444 | checkRendersTo(t, table, expected) 445 | } 446 | 447 | // another unicode length issue 448 | func TestTableWithFullwidthChars(t *testing.T) { 449 | expected := "" + 450 | "+----------+------------+\n" + 451 | "| wide | not really |\n" + 452 | "| wide | fullwidth |\n" + 453 | "+----------+------------+\n" 454 | 455 | table := CreateTable() 456 | table.AddRow("wide", "not really") 457 | table.AddRow("wide", "fullwidth") // FULLWIDTH LATIN SMALL LETTER 458 | 459 | checkRendersTo(t, table, expected) 460 | } 461 | 462 | // Tests CJK characters using examples given in issue #33. The examples may not 463 | // look like they line up but you can visually confirm its accuracy with a 464 | // fmt.Print. 465 | func TestCJKChars(t *testing.T) { 466 | expected := "" + 467 | "+-------+---------+----------+\n" + 468 | "| KeyID | ValueID | ValueCN |\n" + 469 | "+-------+---------+----------+\n" + 470 | "| 8 | 51 | 精钢 |\n" + 471 | "| 8 | 52 | 鳄鱼皮 |\n" + 472 | "| 8 | 53 | 镀金皮带 |\n" + 473 | "| 8 | 54 | 精钢 |\n" + 474 | "+-------+---------+----------+\n" 475 | 476 | table := CreateTable() 477 | table.AddHeaders("KeyID", "ValueID", "ValueCN") 478 | table.AddRow("8", 51, "精钢") 479 | table.AddRow("8", 52, "鳄鱼皮") 480 | table.AddRow("8", 53, "镀金皮带") 481 | table.AddRow("8", 54, "精钢") 482 | checkRendersTo(t, table, expected) 483 | 484 | expected2 := "" + 485 | "+--------------------+----------------------+\n" + 486 | "| field | value |\n" + 487 | "+--------------------+----------------------+\n" + 488 | "| GoodsPropertyKeyID | 9 |\n" + 489 | "| MerchantAccountID | 0 |\n" + 490 | "| GoodsCategoryCode | 100001 |\n" + 491 | "| NameCN | 机芯类型 |\n" + 492 | "| NameJP | ムーブメントのタイプ |\n" + 493 | "+--------------------+----------------------+\n" 494 | table = CreateTable() 495 | table.AddHeaders("field", "value") 496 | table.AddRow("GoodsPropertyKeyID", 9) 497 | table.AddRow("MerchantAccountID", 0) 498 | table.AddRow("GoodsCategoryCode", 100001) 499 | table.AddRow("NameCN", "机芯类型") 500 | table.AddRow("NameJP", "ムーブメントのタイプ") 501 | checkRendersTo(t, table, expected2) 502 | } 503 | 504 | func TestTableMultipleAddHeader(t *testing.T) { 505 | expected := "" + 506 | "+--------------+--------+-------+\n" + 507 | "| First column | Second | Third |\n" + 508 | "+--------------+--------+-------+\n" + 509 | "| 2 | 3 | 5 |\n" + 510 | "+--------------+--------+-------+\n" 511 | 512 | table := CreateTable() 513 | table.AddHeaders("First column", "Second") 514 | table.AddHeaders("Third") 515 | table.AddRow(2, 3, 5) 516 | 517 | checkRendersTo(t, table, expected) 518 | } 519 | 520 | func createTestTable() *Table { 521 | table := CreateTable() 522 | header := []interface{}{} 523 | for i := 0; i < 50; i++ { 524 | header = append(header, "First Column") 525 | } 526 | table.AddHeaders(header...) 527 | for i := 0; i < 3000; i++ { 528 | row := []interface{}{} 529 | for i := 0; i < 50; i++ { 530 | row = append(row, "First row value") 531 | } 532 | table.AddRow(row...) 533 | } 534 | return table 535 | } 536 | 537 | func BenchmarkTableRenderTerminal(b *testing.B) { 538 | table := createTestTable() 539 | table.SetModeTerminal() 540 | b.ResetTimer() 541 | for i := 0; i < b.N; i++ { 542 | table.Render() 543 | } 544 | } 545 | 546 | func BenchmarkTableRenderMarkdown(b *testing.B) { 547 | table := createTestTable() 548 | table.SetModeMarkdown() 549 | b.ResetTimer() 550 | for i := 0; i < b.N; i++ { 551 | table.Render() 552 | } 553 | } 554 | 555 | func BenchmarkTableRenderHTML(b *testing.B) { 556 | table := createTestTable() 557 | table.SetModeHTML() 558 | b.ResetTimer() 559 | for i := 0; i < b.N; i++ { 560 | table.Render() 561 | } 562 | } 563 | -------------------------------------------------------------------------------- /term/env.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Apcera Inc. All rights reserved. 2 | 3 | package term 4 | 5 | import ( 6 | "os" 7 | "strconv" 8 | ) 9 | 10 | // GetEnvWindowSize returns the window Size, as determined by process 11 | // environment; if either LINES or COLUMNS is present, and whichever is 12 | // present is also numeric, the Size will be non-nil. If Size is nil, 13 | // there's insufficient data in environ. If one entry is 0, that means 14 | // that the environment does not include that data. If a value is 15 | // negative, we treat that as an error. 16 | func GetEnvWindowSize() *Size { 17 | lines := os.Getenv("LINES") 18 | columns := os.Getenv("COLUMNS") 19 | if lines == "" && columns == "" { 20 | return nil 21 | } 22 | 23 | nLines := 0 24 | nColumns := 0 25 | var err error 26 | if lines != "" { 27 | nLines, err = strconv.Atoi(lines) 28 | if err != nil || nLines < 0 { 29 | return nil 30 | } 31 | } 32 | if columns != "" { 33 | nColumns, err = strconv.Atoi(columns) 34 | if err != nil || nColumns < 0 { 35 | return nil 36 | } 37 | } 38 | 39 | return &Size{ 40 | Lines: nLines, 41 | Columns: nColumns, 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /term/getsize.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Apcera Inc. All rights reserved. 2 | 3 | package term 4 | 5 | import ( 6 | "os" 7 | ) 8 | 9 | // Size is the size of a terminal, expressed in character cells, as Lines and 10 | // Columns. This might come from environment variables or OS-dependent 11 | // resources. 12 | type Size struct { 13 | Lines int 14 | Columns int 15 | } 16 | 17 | // GetSize will return the terminal window size. 18 | // 19 | // We prefer environ $LINES/$COLUMNS, then fall back to tty-held information. 20 | // We do not support use of termcap/terminfo to derive default size information. 21 | func GetSize() (*Size, error) { 22 | envSize := GetEnvWindowSize() 23 | if envSize != nil && envSize.Lines != 0 && envSize.Columns != 0 { 24 | return envSize, nil 25 | } 26 | 27 | fh, err := os.Open("/dev/tty") 28 | if err != nil { 29 | // no tty, no point continuing; we only let the environ 30 | // avoid an error in this case because if someone has faked 31 | // up an environ with LINES/COLUMNS _both_ set, we should let 32 | // them 33 | return nil, err 34 | } 35 | 36 | size, err := GetTerminalWindowSize(fh) 37 | if err != nil { 38 | if envSize != nil { 39 | return envSize, nil 40 | } 41 | return nil, err 42 | } 43 | if envSize == nil { 44 | return size, err 45 | } 46 | 47 | if envSize.Lines == 0 { 48 | envSize.Lines = size.Lines 49 | } 50 | if envSize.Columns == 0 { 51 | envSize.Columns = size.Columns 52 | } 53 | return envSize, nil 54 | } 55 | -------------------------------------------------------------------------------- /term/sizes_unix.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Apcera Inc. All rights reserved. 2 | 3 | // +build !windows 4 | 5 | package term 6 | 7 | import ( 8 | "errors" 9 | "os" 10 | "syscall" 11 | "unsafe" 12 | ) 13 | 14 | // ErrGetWinsizeFailed indicates that the system call to extract the size of 15 | // a Unix tty from the kernel failed. 16 | var ErrGetWinsizeFailed = errors.New("term: syscall.TIOCGWINSZ failed") 17 | 18 | // GetTerminalWindowSize returns the terminal size maintained by the kernel 19 | // for a Unix TTY, passed in as an *os.File. This information can be seen 20 | // with the stty(1) command, and changes in size (eg, terminal emulator 21 | // resized) should trigger a SIGWINCH signal delivery to the foreground process 22 | // group at the time of the change, so a long-running process might reasonably 23 | // watch for SIGWINCH and arrange to re-fetch the size when that happens. 24 | func GetTerminalWindowSize(file *os.File) (*Size, error) { 25 | // Based on source from from golang.org/x/crypto/ssh/terminal/util.go 26 | var dimensions [4]uint16 27 | if _, _, err := syscall.Syscall6(syscall.SYS_IOCTL, file.Fd(), uintptr(syscall.TIOCGWINSZ), uintptr(unsafe.Pointer(&dimensions)), 0, 0, 0); err != 0 { 28 | return nil, err 29 | } 30 | 31 | return &Size{ 32 | Lines: int(dimensions[0]), 33 | Columns: int(dimensions[1]), 34 | }, nil 35 | } 36 | -------------------------------------------------------------------------------- /term/sizes_windows.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Apcera Inc. All rights reserved. 2 | 3 | // +build windows 4 | 5 | package term 6 | 7 | // Used when we have no other source for getting platform-specific information 8 | // about the terminal sizes available. 9 | 10 | import ( 11 | "os" 12 | "syscall" 13 | "unsafe" 14 | ) 15 | 16 | // Based on source from from golang.org/x/crypto/ssh/terminal/util_windows.go 17 | var ( 18 | kernel32 = syscall.NewLazyDLL("kernel32.dll") 19 | 20 | procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo") 21 | ) 22 | 23 | type ( 24 | short int16 25 | word uint16 26 | 27 | coord struct { 28 | x short 29 | y short 30 | } 31 | smallRect struct { 32 | left short 33 | top short 34 | right short 35 | bottom short 36 | } 37 | consoleScreenBufferInfo struct { 38 | size coord 39 | cursorPosition coord 40 | attributes word 41 | window smallRect 42 | maximumWindowSize coord 43 | } 44 | ) 45 | 46 | // GetTerminalWindowSize returns the width and height of a terminal in Windows. 47 | func GetTerminalWindowSize(file *os.File) (*Size, error) { 48 | var info consoleScreenBufferInfo 49 | _, _, e := syscall.Syscall(procGetConsoleScreenBufferInfo.Addr(), 2, file.Fd(), uintptr(unsafe.Pointer(&info)), 0) 50 | if e != 0 { 51 | return nil, error(e) 52 | } 53 | return &Size{ 54 | Lines: int(info.size.y), 55 | Columns: int(info.size.x), 56 | }, nil 57 | } 58 | -------------------------------------------------------------------------------- /term/wrapper.go: -------------------------------------------------------------------------------- 1 | // Copyright 2013 Apcera Inc. All rights reserved. 2 | 3 | // +build ignore 4 | 5 | package main 6 | 7 | import ( 8 | "fmt" 9 | "os" 10 | 11 | "github.com/scylladb/termtables/term" 12 | ) 13 | 14 | func main() { 15 | size, err := term.GetSize() 16 | if err != nil { 17 | fmt.Fprintf(os.Stderr, "failed: %s\n", err) 18 | os.Exit(1) 19 | } 20 | 21 | fmt.Printf("Lines %d Columns %d\n", size.Lines, size.Columns) 22 | } 23 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-runewidth/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Yasuhiro Matsumoto 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-runewidth/runewidth.go: -------------------------------------------------------------------------------- 1 | package runewidth 2 | 3 | var ( 4 | // EastAsianWidth will be set true if the current locale is CJK 5 | EastAsianWidth = IsEastAsian() 6 | 7 | // DefaultCondition is a condition in current locale 8 | DefaultCondition = &Condition{EastAsianWidth} 9 | ) 10 | 11 | type interval struct { 12 | first rune 13 | last rune 14 | } 15 | 16 | type table []interval 17 | 18 | func inTables(r rune, ts ...table) bool { 19 | for _, t := range ts { 20 | if inTable(r, t) { 21 | return true 22 | } 23 | } 24 | return false 25 | } 26 | 27 | func inTable(r rune, t table) bool { 28 | // func (t table) IncludesRune(r rune) bool { 29 | if r < t[0].first { 30 | return false 31 | } 32 | 33 | bot := 0 34 | top := len(t) - 1 35 | for top >= bot { 36 | mid := (bot + top) / 2 37 | 38 | switch { 39 | case t[mid].last < r: 40 | bot = mid + 1 41 | case t[mid].first > r: 42 | top = mid - 1 43 | default: 44 | return true 45 | } 46 | } 47 | 48 | return false 49 | } 50 | 51 | var private = table{ 52 | {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD}, 53 | } 54 | 55 | var nonprint = table{ 56 | {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD}, 57 | {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F}, 58 | {0x202A, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF}, 59 | {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF}, 60 | } 61 | 62 | var combining = table{ 63 | {0x0300, 0x036F}, {0x0483, 0x0489}, {0x0591, 0x05BD}, 64 | {0x05BF, 0x05BF}, {0x05C1, 0x05C2}, {0x05C4, 0x05C5}, 65 | {0x05C7, 0x05C7}, {0x0610, 0x061A}, {0x064B, 0x065F}, 66 | {0x0670, 0x0670}, {0x06D6, 0x06DC}, {0x06DF, 0x06E4}, 67 | {0x06E7, 0x06E8}, {0x06EA, 0x06ED}, {0x0711, 0x0711}, 68 | {0x0730, 0x074A}, {0x07A6, 0x07B0}, {0x07EB, 0x07F3}, 69 | {0x0816, 0x0819}, {0x081B, 0x0823}, {0x0825, 0x0827}, 70 | {0x0829, 0x082D}, {0x0859, 0x085B}, {0x08D4, 0x08E1}, 71 | {0x08E3, 0x0903}, {0x093A, 0x093C}, {0x093E, 0x094F}, 72 | {0x0951, 0x0957}, {0x0962, 0x0963}, {0x0981, 0x0983}, 73 | {0x09BC, 0x09BC}, {0x09BE, 0x09C4}, {0x09C7, 0x09C8}, 74 | {0x09CB, 0x09CD}, {0x09D7, 0x09D7}, {0x09E2, 0x09E3}, 75 | {0x0A01, 0x0A03}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42}, 76 | {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, 77 | {0x0A70, 0x0A71}, {0x0A75, 0x0A75}, {0x0A81, 0x0A83}, 78 | {0x0ABC, 0x0ABC}, {0x0ABE, 0x0AC5}, {0x0AC7, 0x0AC9}, 79 | {0x0ACB, 0x0ACD}, {0x0AE2, 0x0AE3}, {0x0B01, 0x0B03}, 80 | {0x0B3C, 0x0B3C}, {0x0B3E, 0x0B44}, {0x0B47, 0x0B48}, 81 | {0x0B4B, 0x0B4D}, {0x0B56, 0x0B57}, {0x0B62, 0x0B63}, 82 | {0x0B82, 0x0B82}, {0x0BBE, 0x0BC2}, {0x0BC6, 0x0BC8}, 83 | {0x0BCA, 0x0BCD}, {0x0BD7, 0x0BD7}, {0x0C00, 0x0C03}, 84 | {0x0C3E, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, 85 | {0x0C55, 0x0C56}, {0x0C62, 0x0C63}, {0x0C81, 0x0C83}, 86 | {0x0CBC, 0x0CBC}, {0x0CBE, 0x0CC4}, {0x0CC6, 0x0CC8}, 87 | {0x0CCA, 0x0CCD}, {0x0CD5, 0x0CD6}, {0x0CE2, 0x0CE3}, 88 | {0x0D01, 0x0D03}, {0x0D3E, 0x0D44}, {0x0D46, 0x0D48}, 89 | {0x0D4A, 0x0D4D}, {0x0D57, 0x0D57}, {0x0D62, 0x0D63}, 90 | {0x0D82, 0x0D83}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, 91 | {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, {0x0DF2, 0x0DF3}, 92 | {0x0E31, 0x0E31}, {0x0E34, 0x0E3A}, {0x0E47, 0x0E4E}, 93 | {0x0EB1, 0x0EB1}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, 94 | {0x0EC8, 0x0ECD}, {0x0F18, 0x0F19}, {0x0F35, 0x0F35}, 95 | {0x0F37, 0x0F37}, {0x0F39, 0x0F39}, {0x0F3E, 0x0F3F}, 96 | {0x0F71, 0x0F84}, {0x0F86, 0x0F87}, {0x0F8D, 0x0F97}, 97 | {0x0F99, 0x0FBC}, {0x0FC6, 0x0FC6}, {0x102B, 0x103E}, 98 | {0x1056, 0x1059}, {0x105E, 0x1060}, {0x1062, 0x1064}, 99 | {0x1067, 0x106D}, {0x1071, 0x1074}, {0x1082, 0x108D}, 100 | {0x108F, 0x108F}, {0x109A, 0x109D}, {0x135D, 0x135F}, 101 | {0x1712, 0x1714}, {0x1732, 0x1734}, {0x1752, 0x1753}, 102 | {0x1772, 0x1773}, {0x17B4, 0x17D3}, {0x17DD, 0x17DD}, 103 | {0x180B, 0x180D}, {0x1885, 0x1886}, {0x18A9, 0x18A9}, 104 | {0x1920, 0x192B}, {0x1930, 0x193B}, {0x1A17, 0x1A1B}, 105 | {0x1A55, 0x1A5E}, {0x1A60, 0x1A7C}, {0x1A7F, 0x1A7F}, 106 | {0x1AB0, 0x1ABE}, {0x1B00, 0x1B04}, {0x1B34, 0x1B44}, 107 | {0x1B6B, 0x1B73}, {0x1B80, 0x1B82}, {0x1BA1, 0x1BAD}, 108 | {0x1BE6, 0x1BF3}, {0x1C24, 0x1C37}, {0x1CD0, 0x1CD2}, 109 | {0x1CD4, 0x1CE8}, {0x1CED, 0x1CED}, {0x1CF2, 0x1CF4}, 110 | {0x1CF8, 0x1CF9}, {0x1DC0, 0x1DF5}, {0x1DFB, 0x1DFF}, 111 | {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2D7F, 0x2D7F}, 112 | {0x2DE0, 0x2DFF}, {0x302A, 0x302F}, {0x3099, 0x309A}, 113 | {0xA66F, 0xA672}, {0xA674, 0xA67D}, {0xA69E, 0xA69F}, 114 | {0xA6F0, 0xA6F1}, {0xA802, 0xA802}, {0xA806, 0xA806}, 115 | {0xA80B, 0xA80B}, {0xA823, 0xA827}, {0xA880, 0xA881}, 116 | {0xA8B4, 0xA8C5}, {0xA8E0, 0xA8F1}, {0xA926, 0xA92D}, 117 | {0xA947, 0xA953}, {0xA980, 0xA983}, {0xA9B3, 0xA9C0}, 118 | {0xA9E5, 0xA9E5}, {0xAA29, 0xAA36}, {0xAA43, 0xAA43}, 119 | {0xAA4C, 0xAA4D}, {0xAA7B, 0xAA7D}, {0xAAB0, 0xAAB0}, 120 | {0xAAB2, 0xAAB4}, {0xAAB7, 0xAAB8}, {0xAABE, 0xAABF}, 121 | {0xAAC1, 0xAAC1}, {0xAAEB, 0xAAEF}, {0xAAF5, 0xAAF6}, 122 | {0xABE3, 0xABEA}, {0xABEC, 0xABED}, {0xFB1E, 0xFB1E}, 123 | {0xFE00, 0xFE0F}, {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, 124 | {0x102E0, 0x102E0}, {0x10376, 0x1037A}, {0x10A01, 0x10A03}, 125 | {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, {0x10A38, 0x10A3A}, 126 | {0x10A3F, 0x10A3F}, {0x10AE5, 0x10AE6}, {0x11000, 0x11002}, 127 | {0x11038, 0x11046}, {0x1107F, 0x11082}, {0x110B0, 0x110BA}, 128 | {0x11100, 0x11102}, {0x11127, 0x11134}, {0x11173, 0x11173}, 129 | {0x11180, 0x11182}, {0x111B3, 0x111C0}, {0x111CA, 0x111CC}, 130 | {0x1122C, 0x11237}, {0x1123E, 0x1123E}, {0x112DF, 0x112EA}, 131 | {0x11300, 0x11303}, {0x1133C, 0x1133C}, {0x1133E, 0x11344}, 132 | {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11357, 0x11357}, 133 | {0x11362, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374}, 134 | {0x11435, 0x11446}, {0x114B0, 0x114C3}, {0x115AF, 0x115B5}, 135 | {0x115B8, 0x115C0}, {0x115DC, 0x115DD}, {0x11630, 0x11640}, 136 | {0x116AB, 0x116B7}, {0x1171D, 0x1172B}, {0x11C2F, 0x11C36}, 137 | {0x11C38, 0x11C3F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CB6}, 138 | {0x16AF0, 0x16AF4}, {0x16B30, 0x16B36}, {0x16F51, 0x16F7E}, 139 | {0x16F8F, 0x16F92}, {0x1BC9D, 0x1BC9E}, {0x1D165, 0x1D169}, 140 | {0x1D16D, 0x1D172}, {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, 141 | {0x1D1AA, 0x1D1AD}, {0x1D242, 0x1D244}, {0x1DA00, 0x1DA36}, 142 | {0x1DA3B, 0x1DA6C}, {0x1DA75, 0x1DA75}, {0x1DA84, 0x1DA84}, 143 | {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006}, 144 | {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, 145 | {0x1E026, 0x1E02A}, {0x1E8D0, 0x1E8D6}, {0x1E944, 0x1E94A}, 146 | {0xE0100, 0xE01EF}, 147 | } 148 | 149 | var doublewidth = table{ 150 | {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A}, 151 | {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3}, 152 | {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653}, 153 | {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1}, 154 | {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5}, 155 | {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA}, 156 | {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA}, 157 | {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B}, 158 | {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E}, 159 | {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797}, 160 | {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C}, 161 | {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99}, 162 | {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB}, 163 | {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF}, 164 | {0x3105, 0x312D}, {0x3131, 0x318E}, {0x3190, 0x31BA}, 165 | {0x31C0, 0x31E3}, {0x31F0, 0x321E}, {0x3220, 0x3247}, 166 | {0x3250, 0x32FE}, {0x3300, 0x4DBF}, {0x4E00, 0xA48C}, 167 | {0xA490, 0xA4C6}, {0xA960, 0xA97C}, {0xAC00, 0xD7A3}, 168 | {0xF900, 0xFAFF}, {0xFE10, 0xFE19}, {0xFE30, 0xFE52}, 169 | {0xFE54, 0xFE66}, {0xFE68, 0xFE6B}, {0xFF01, 0xFF60}, 170 | {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE0}, {0x17000, 0x187EC}, 171 | {0x18800, 0x18AF2}, {0x1B000, 0x1B001}, {0x1F004, 0x1F004}, 172 | {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A}, 173 | {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248}, 174 | {0x1F250, 0x1F251}, {0x1F300, 0x1F320}, {0x1F32D, 0x1F335}, 175 | {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393}, {0x1F3A0, 0x1F3CA}, 176 | {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0}, {0x1F3F4, 0x1F3F4}, 177 | {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440}, {0x1F442, 0x1F4FC}, 178 | {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E}, {0x1F550, 0x1F567}, 179 | {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596}, {0x1F5A4, 0x1F5A4}, 180 | {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5}, {0x1F6CC, 0x1F6CC}, 181 | {0x1F6D0, 0x1F6D2}, {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6F6}, 182 | {0x1F910, 0x1F91E}, {0x1F920, 0x1F927}, {0x1F930, 0x1F930}, 183 | {0x1F933, 0x1F93E}, {0x1F940, 0x1F94B}, {0x1F950, 0x1F95E}, 184 | {0x1F980, 0x1F991}, {0x1F9C0, 0x1F9C0}, {0x20000, 0x2FFFD}, 185 | {0x30000, 0x3FFFD}, 186 | } 187 | 188 | var ambiguous = table{ 189 | {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8}, 190 | {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4}, 191 | {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6}, 192 | {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1}, 193 | {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED}, 194 | {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA}, 195 | {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101}, 196 | {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B}, 197 | {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133}, 198 | {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144}, 199 | {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153}, 200 | {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE}, 201 | {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4}, 202 | {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA}, 203 | {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261}, 204 | {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB}, 205 | {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB}, 206 | {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F}, 207 | {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1}, 208 | {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F}, 209 | {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016}, 210 | {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022}, 211 | {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033}, 212 | {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E}, 213 | {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084}, 214 | {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105}, 215 | {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116}, 216 | {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B}, 217 | {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B}, 218 | {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199}, 219 | {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4}, 220 | {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203}, 221 | {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F}, 222 | {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A}, 223 | {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225}, 224 | {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237}, 225 | {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C}, 226 | {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267}, 227 | {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283}, 228 | {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299}, 229 | {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312}, 230 | {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573}, 231 | {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1}, 232 | {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7}, 233 | {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8}, 234 | {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5}, 235 | {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609}, 236 | {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E}, 237 | {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661}, 238 | {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D}, 239 | {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF}, 240 | {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1}, 241 | {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1}, 242 | {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC}, 243 | {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F}, 244 | {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF}, 245 | {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A}, 246 | {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D}, 247 | {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF}, 248 | {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD}, 249 | } 250 | 251 | var emoji = table{ 252 | {0x1F1E6, 0x1F1FF}, {0x1F321, 0x1F321}, {0x1F324, 0x1F32C}, 253 | {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, {0x1F396, 0x1F397}, 254 | {0x1F399, 0x1F39B}, {0x1F39E, 0x1F39F}, {0x1F3CB, 0x1F3CE}, 255 | {0x1F3D4, 0x1F3DF}, {0x1F3F3, 0x1F3F5}, {0x1F3F7, 0x1F3F7}, 256 | {0x1F43F, 0x1F43F}, {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FD}, 257 | {0x1F549, 0x1F54A}, {0x1F56F, 0x1F570}, {0x1F573, 0x1F579}, 258 | {0x1F587, 0x1F587}, {0x1F58A, 0x1F58D}, {0x1F590, 0x1F590}, 259 | {0x1F5A5, 0x1F5A5}, {0x1F5A8, 0x1F5A8}, {0x1F5B1, 0x1F5B2}, 260 | {0x1F5BC, 0x1F5BC}, {0x1F5C2, 0x1F5C4}, {0x1F5D1, 0x1F5D3}, 261 | {0x1F5DC, 0x1F5DE}, {0x1F5E1, 0x1F5E1}, {0x1F5E3, 0x1F5E3}, 262 | {0x1F5E8, 0x1F5E8}, {0x1F5EF, 0x1F5EF}, {0x1F5F3, 0x1F5F3}, 263 | {0x1F5FA, 0x1F5FA}, {0x1F6CB, 0x1F6CF}, {0x1F6E0, 0x1F6E5}, 264 | {0x1F6E9, 0x1F6E9}, {0x1F6F0, 0x1F6F0}, {0x1F6F3, 0x1F6F3}, 265 | } 266 | 267 | var notassigned = table{ 268 | {0x0378, 0x0379}, {0x0380, 0x0383}, {0x038B, 0x038B}, 269 | {0x038D, 0x038D}, {0x03A2, 0x03A2}, {0x0530, 0x0530}, 270 | {0x0557, 0x0558}, {0x0560, 0x0560}, {0x0588, 0x0588}, 271 | {0x058B, 0x058C}, {0x0590, 0x0590}, {0x05C8, 0x05CF}, 272 | {0x05EB, 0x05EF}, {0x05F5, 0x05FF}, {0x061D, 0x061D}, 273 | {0x070E, 0x070E}, {0x074B, 0x074C}, {0x07B2, 0x07BF}, 274 | {0x07FB, 0x07FF}, {0x082E, 0x082F}, {0x083F, 0x083F}, 275 | {0x085C, 0x085D}, {0x085F, 0x089F}, {0x08B5, 0x08B5}, 276 | {0x08BE, 0x08D3}, {0x0984, 0x0984}, {0x098D, 0x098E}, 277 | {0x0991, 0x0992}, {0x09A9, 0x09A9}, {0x09B1, 0x09B1}, 278 | {0x09B3, 0x09B5}, {0x09BA, 0x09BB}, {0x09C5, 0x09C6}, 279 | {0x09C9, 0x09CA}, {0x09CF, 0x09D6}, {0x09D8, 0x09DB}, 280 | {0x09DE, 0x09DE}, {0x09E4, 0x09E5}, {0x09FC, 0x0A00}, 281 | {0x0A04, 0x0A04}, {0x0A0B, 0x0A0E}, {0x0A11, 0x0A12}, 282 | {0x0A29, 0x0A29}, {0x0A31, 0x0A31}, {0x0A34, 0x0A34}, 283 | {0x0A37, 0x0A37}, {0x0A3A, 0x0A3B}, {0x0A3D, 0x0A3D}, 284 | {0x0A43, 0x0A46}, {0x0A49, 0x0A4A}, {0x0A4E, 0x0A50}, 285 | {0x0A52, 0x0A58}, {0x0A5D, 0x0A5D}, {0x0A5F, 0x0A65}, 286 | {0x0A76, 0x0A80}, {0x0A84, 0x0A84}, {0x0A8E, 0x0A8E}, 287 | {0x0A92, 0x0A92}, {0x0AA9, 0x0AA9}, {0x0AB1, 0x0AB1}, 288 | {0x0AB4, 0x0AB4}, {0x0ABA, 0x0ABB}, {0x0AC6, 0x0AC6}, 289 | {0x0ACA, 0x0ACA}, {0x0ACE, 0x0ACF}, {0x0AD1, 0x0ADF}, 290 | {0x0AE4, 0x0AE5}, {0x0AF2, 0x0AF8}, {0x0AFA, 0x0B00}, 291 | {0x0B04, 0x0B04}, {0x0B0D, 0x0B0E}, {0x0B11, 0x0B12}, 292 | {0x0B29, 0x0B29}, {0x0B31, 0x0B31}, {0x0B34, 0x0B34}, 293 | {0x0B3A, 0x0B3B}, {0x0B45, 0x0B46}, {0x0B49, 0x0B4A}, 294 | {0x0B4E, 0x0B55}, {0x0B58, 0x0B5B}, {0x0B5E, 0x0B5E}, 295 | {0x0B64, 0x0B65}, {0x0B78, 0x0B81}, {0x0B84, 0x0B84}, 296 | {0x0B8B, 0x0B8D}, {0x0B91, 0x0B91}, {0x0B96, 0x0B98}, 297 | {0x0B9B, 0x0B9B}, {0x0B9D, 0x0B9D}, {0x0BA0, 0x0BA2}, 298 | {0x0BA5, 0x0BA7}, {0x0BAB, 0x0BAD}, {0x0BBA, 0x0BBD}, 299 | {0x0BC3, 0x0BC5}, {0x0BC9, 0x0BC9}, {0x0BCE, 0x0BCF}, 300 | {0x0BD1, 0x0BD6}, {0x0BD8, 0x0BE5}, {0x0BFB, 0x0BFF}, 301 | {0x0C04, 0x0C04}, {0x0C0D, 0x0C0D}, {0x0C11, 0x0C11}, 302 | {0x0C29, 0x0C29}, {0x0C3A, 0x0C3C}, {0x0C45, 0x0C45}, 303 | {0x0C49, 0x0C49}, {0x0C4E, 0x0C54}, {0x0C57, 0x0C57}, 304 | {0x0C5B, 0x0C5F}, {0x0C64, 0x0C65}, {0x0C70, 0x0C77}, 305 | {0x0C84, 0x0C84}, {0x0C8D, 0x0C8D}, {0x0C91, 0x0C91}, 306 | {0x0CA9, 0x0CA9}, {0x0CB4, 0x0CB4}, {0x0CBA, 0x0CBB}, 307 | {0x0CC5, 0x0CC5}, {0x0CC9, 0x0CC9}, {0x0CCE, 0x0CD4}, 308 | {0x0CD7, 0x0CDD}, {0x0CDF, 0x0CDF}, {0x0CE4, 0x0CE5}, 309 | {0x0CF0, 0x0CF0}, {0x0CF3, 0x0D00}, {0x0D04, 0x0D04}, 310 | {0x0D0D, 0x0D0D}, {0x0D11, 0x0D11}, {0x0D3B, 0x0D3C}, 311 | {0x0D45, 0x0D45}, {0x0D49, 0x0D49}, {0x0D50, 0x0D53}, 312 | {0x0D64, 0x0D65}, {0x0D80, 0x0D81}, {0x0D84, 0x0D84}, 313 | {0x0D97, 0x0D99}, {0x0DB2, 0x0DB2}, {0x0DBC, 0x0DBC}, 314 | {0x0DBE, 0x0DBF}, {0x0DC7, 0x0DC9}, {0x0DCB, 0x0DCE}, 315 | {0x0DD5, 0x0DD5}, {0x0DD7, 0x0DD7}, {0x0DE0, 0x0DE5}, 316 | {0x0DF0, 0x0DF1}, {0x0DF5, 0x0E00}, {0x0E3B, 0x0E3E}, 317 | {0x0E5C, 0x0E80}, {0x0E83, 0x0E83}, {0x0E85, 0x0E86}, 318 | {0x0E89, 0x0E89}, {0x0E8B, 0x0E8C}, {0x0E8E, 0x0E93}, 319 | {0x0E98, 0x0E98}, {0x0EA0, 0x0EA0}, {0x0EA4, 0x0EA4}, 320 | {0x0EA6, 0x0EA6}, {0x0EA8, 0x0EA9}, {0x0EAC, 0x0EAC}, 321 | {0x0EBA, 0x0EBA}, {0x0EBE, 0x0EBF}, {0x0EC5, 0x0EC5}, 322 | {0x0EC7, 0x0EC7}, {0x0ECE, 0x0ECF}, {0x0EDA, 0x0EDB}, 323 | {0x0EE0, 0x0EFF}, {0x0F48, 0x0F48}, {0x0F6D, 0x0F70}, 324 | {0x0F98, 0x0F98}, {0x0FBD, 0x0FBD}, {0x0FCD, 0x0FCD}, 325 | {0x0FDB, 0x0FFF}, {0x10C6, 0x10C6}, {0x10C8, 0x10CC}, 326 | {0x10CE, 0x10CF}, {0x1249, 0x1249}, {0x124E, 0x124F}, 327 | {0x1257, 0x1257}, {0x1259, 0x1259}, {0x125E, 0x125F}, 328 | {0x1289, 0x1289}, {0x128E, 0x128F}, {0x12B1, 0x12B1}, 329 | {0x12B6, 0x12B7}, {0x12BF, 0x12BF}, {0x12C1, 0x12C1}, 330 | {0x12C6, 0x12C7}, {0x12D7, 0x12D7}, {0x1311, 0x1311}, 331 | {0x1316, 0x1317}, {0x135B, 0x135C}, {0x137D, 0x137F}, 332 | {0x139A, 0x139F}, {0x13F6, 0x13F7}, {0x13FE, 0x13FF}, 333 | {0x169D, 0x169F}, {0x16F9, 0x16FF}, {0x170D, 0x170D}, 334 | {0x1715, 0x171F}, {0x1737, 0x173F}, {0x1754, 0x175F}, 335 | {0x176D, 0x176D}, {0x1771, 0x1771}, {0x1774, 0x177F}, 336 | {0x17DE, 0x17DF}, {0x17EA, 0x17EF}, {0x17FA, 0x17FF}, 337 | {0x180F, 0x180F}, {0x181A, 0x181F}, {0x1878, 0x187F}, 338 | {0x18AB, 0x18AF}, {0x18F6, 0x18FF}, {0x191F, 0x191F}, 339 | {0x192C, 0x192F}, {0x193C, 0x193F}, {0x1941, 0x1943}, 340 | {0x196E, 0x196F}, {0x1975, 0x197F}, {0x19AC, 0x19AF}, 341 | {0x19CA, 0x19CF}, {0x19DB, 0x19DD}, {0x1A1C, 0x1A1D}, 342 | {0x1A5F, 0x1A5F}, {0x1A7D, 0x1A7E}, {0x1A8A, 0x1A8F}, 343 | {0x1A9A, 0x1A9F}, {0x1AAE, 0x1AAF}, {0x1ABF, 0x1AFF}, 344 | {0x1B4C, 0x1B4F}, {0x1B7D, 0x1B7F}, {0x1BF4, 0x1BFB}, 345 | {0x1C38, 0x1C3A}, {0x1C4A, 0x1C4C}, {0x1C89, 0x1CBF}, 346 | {0x1CC8, 0x1CCF}, {0x1CF7, 0x1CF7}, {0x1CFA, 0x1CFF}, 347 | {0x1DF6, 0x1DFA}, {0x1F16, 0x1F17}, {0x1F1E, 0x1F1F}, 348 | {0x1F46, 0x1F47}, {0x1F4E, 0x1F4F}, {0x1F58, 0x1F58}, 349 | {0x1F5A, 0x1F5A}, {0x1F5C, 0x1F5C}, {0x1F5E, 0x1F5E}, 350 | {0x1F7E, 0x1F7F}, {0x1FB5, 0x1FB5}, {0x1FC5, 0x1FC5}, 351 | {0x1FD4, 0x1FD5}, {0x1FDC, 0x1FDC}, {0x1FF0, 0x1FF1}, 352 | {0x1FF5, 0x1FF5}, {0x1FFF, 0x1FFF}, {0x2065, 0x2065}, 353 | {0x2072, 0x2073}, {0x208F, 0x208F}, {0x209D, 0x209F}, 354 | {0x20BF, 0x20CF}, {0x20F1, 0x20FF}, {0x218C, 0x218F}, 355 | {0x23FF, 0x23FF}, {0x2427, 0x243F}, {0x244B, 0x245F}, 356 | {0x2B74, 0x2B75}, {0x2B96, 0x2B97}, {0x2BBA, 0x2BBC}, 357 | {0x2BC9, 0x2BC9}, {0x2BD2, 0x2BEB}, {0x2BF0, 0x2BFF}, 358 | {0x2C2F, 0x2C2F}, {0x2C5F, 0x2C5F}, {0x2CF4, 0x2CF8}, 359 | {0x2D26, 0x2D26}, {0x2D28, 0x2D2C}, {0x2D2E, 0x2D2F}, 360 | {0x2D68, 0x2D6E}, {0x2D71, 0x2D7E}, {0x2D97, 0x2D9F}, 361 | {0x2DA7, 0x2DA7}, {0x2DAF, 0x2DAF}, {0x2DB7, 0x2DB7}, 362 | {0x2DBF, 0x2DBF}, {0x2DC7, 0x2DC7}, {0x2DCF, 0x2DCF}, 363 | {0x2DD7, 0x2DD7}, {0x2DDF, 0x2DDF}, {0x2E45, 0x2E7F}, 364 | {0x2E9A, 0x2E9A}, {0x2EF4, 0x2EFF}, {0x2FD6, 0x2FEF}, 365 | {0x2FFC, 0x2FFF}, {0x3040, 0x3040}, {0x3097, 0x3098}, 366 | {0x3100, 0x3104}, {0x312E, 0x3130}, {0x318F, 0x318F}, 367 | {0x31BB, 0x31BF}, {0x31E4, 0x31EF}, {0x321F, 0x321F}, 368 | {0x32FF, 0x32FF}, {0x4DB6, 0x4DBF}, {0x9FD6, 0x9FFF}, 369 | {0xA48D, 0xA48F}, {0xA4C7, 0xA4CF}, {0xA62C, 0xA63F}, 370 | {0xA6F8, 0xA6FF}, {0xA7AF, 0xA7AF}, {0xA7B8, 0xA7F6}, 371 | {0xA82C, 0xA82F}, {0xA83A, 0xA83F}, {0xA878, 0xA87F}, 372 | {0xA8C6, 0xA8CD}, {0xA8DA, 0xA8DF}, {0xA8FE, 0xA8FF}, 373 | {0xA954, 0xA95E}, {0xA97D, 0xA97F}, {0xA9CE, 0xA9CE}, 374 | {0xA9DA, 0xA9DD}, {0xA9FF, 0xA9FF}, {0xAA37, 0xAA3F}, 375 | {0xAA4E, 0xAA4F}, {0xAA5A, 0xAA5B}, {0xAAC3, 0xAADA}, 376 | {0xAAF7, 0xAB00}, {0xAB07, 0xAB08}, {0xAB0F, 0xAB10}, 377 | {0xAB17, 0xAB1F}, {0xAB27, 0xAB27}, {0xAB2F, 0xAB2F}, 378 | {0xAB66, 0xAB6F}, {0xABEE, 0xABEF}, {0xABFA, 0xABFF}, 379 | {0xD7A4, 0xD7AF}, {0xD7C7, 0xD7CA}, {0xD7FC, 0xD7FF}, 380 | {0xFA6E, 0xFA6F}, {0xFADA, 0xFAFF}, {0xFB07, 0xFB12}, 381 | {0xFB18, 0xFB1C}, {0xFB37, 0xFB37}, {0xFB3D, 0xFB3D}, 382 | {0xFB3F, 0xFB3F}, {0xFB42, 0xFB42}, {0xFB45, 0xFB45}, 383 | {0xFBC2, 0xFBD2}, {0xFD40, 0xFD4F}, {0xFD90, 0xFD91}, 384 | {0xFDC8, 0xFDEF}, {0xFDFE, 0xFDFF}, {0xFE1A, 0xFE1F}, 385 | {0xFE53, 0xFE53}, {0xFE67, 0xFE67}, {0xFE6C, 0xFE6F}, 386 | {0xFE75, 0xFE75}, {0xFEFD, 0xFEFE}, {0xFF00, 0xFF00}, 387 | {0xFFBF, 0xFFC1}, {0xFFC8, 0xFFC9}, {0xFFD0, 0xFFD1}, 388 | {0xFFD8, 0xFFD9}, {0xFFDD, 0xFFDF}, {0xFFE7, 0xFFE7}, 389 | {0xFFEF, 0xFFF8}, {0xFFFE, 0xFFFF}, {0x1000C, 0x1000C}, 390 | {0x10027, 0x10027}, {0x1003B, 0x1003B}, {0x1003E, 0x1003E}, 391 | {0x1004E, 0x1004F}, {0x1005E, 0x1007F}, {0x100FB, 0x100FF}, 392 | {0x10103, 0x10106}, {0x10134, 0x10136}, {0x1018F, 0x1018F}, 393 | {0x1019C, 0x1019F}, {0x101A1, 0x101CF}, {0x101FE, 0x1027F}, 394 | {0x1029D, 0x1029F}, {0x102D1, 0x102DF}, {0x102FC, 0x102FF}, 395 | {0x10324, 0x1032F}, {0x1034B, 0x1034F}, {0x1037B, 0x1037F}, 396 | {0x1039E, 0x1039E}, {0x103C4, 0x103C7}, {0x103D6, 0x103FF}, 397 | {0x1049E, 0x1049F}, {0x104AA, 0x104AF}, {0x104D4, 0x104D7}, 398 | {0x104FC, 0x104FF}, {0x10528, 0x1052F}, {0x10564, 0x1056E}, 399 | {0x10570, 0x105FF}, {0x10737, 0x1073F}, {0x10756, 0x1075F}, 400 | {0x10768, 0x107FF}, {0x10806, 0x10807}, {0x10809, 0x10809}, 401 | {0x10836, 0x10836}, {0x10839, 0x1083B}, {0x1083D, 0x1083E}, 402 | {0x10856, 0x10856}, {0x1089F, 0x108A6}, {0x108B0, 0x108DF}, 403 | {0x108F3, 0x108F3}, {0x108F6, 0x108FA}, {0x1091C, 0x1091E}, 404 | {0x1093A, 0x1093E}, {0x10940, 0x1097F}, {0x109B8, 0x109BB}, 405 | {0x109D0, 0x109D1}, {0x10A04, 0x10A04}, {0x10A07, 0x10A0B}, 406 | {0x10A14, 0x10A14}, {0x10A18, 0x10A18}, {0x10A34, 0x10A37}, 407 | {0x10A3B, 0x10A3E}, {0x10A48, 0x10A4F}, {0x10A59, 0x10A5F}, 408 | {0x10AA0, 0x10ABF}, {0x10AE7, 0x10AEA}, {0x10AF7, 0x10AFF}, 409 | {0x10B36, 0x10B38}, {0x10B56, 0x10B57}, {0x10B73, 0x10B77}, 410 | {0x10B92, 0x10B98}, {0x10B9D, 0x10BA8}, {0x10BB0, 0x10BFF}, 411 | {0x10C49, 0x10C7F}, {0x10CB3, 0x10CBF}, {0x10CF3, 0x10CF9}, 412 | {0x10D00, 0x10E5F}, {0x10E7F, 0x10FFF}, {0x1104E, 0x11051}, 413 | {0x11070, 0x1107E}, {0x110C2, 0x110CF}, {0x110E9, 0x110EF}, 414 | {0x110FA, 0x110FF}, {0x11135, 0x11135}, {0x11144, 0x1114F}, 415 | {0x11177, 0x1117F}, {0x111CE, 0x111CF}, {0x111E0, 0x111E0}, 416 | {0x111F5, 0x111FF}, {0x11212, 0x11212}, {0x1123F, 0x1127F}, 417 | {0x11287, 0x11287}, {0x11289, 0x11289}, {0x1128E, 0x1128E}, 418 | {0x1129E, 0x1129E}, {0x112AA, 0x112AF}, {0x112EB, 0x112EF}, 419 | {0x112FA, 0x112FF}, {0x11304, 0x11304}, {0x1130D, 0x1130E}, 420 | {0x11311, 0x11312}, {0x11329, 0x11329}, {0x11331, 0x11331}, 421 | {0x11334, 0x11334}, {0x1133A, 0x1133B}, {0x11345, 0x11346}, 422 | {0x11349, 0x1134A}, {0x1134E, 0x1134F}, {0x11351, 0x11356}, 423 | {0x11358, 0x1135C}, {0x11364, 0x11365}, {0x1136D, 0x1136F}, 424 | {0x11375, 0x113FF}, {0x1145A, 0x1145A}, {0x1145C, 0x1145C}, 425 | {0x1145E, 0x1147F}, {0x114C8, 0x114CF}, {0x114DA, 0x1157F}, 426 | {0x115B6, 0x115B7}, {0x115DE, 0x115FF}, {0x11645, 0x1164F}, 427 | {0x1165A, 0x1165F}, {0x1166D, 0x1167F}, {0x116B8, 0x116BF}, 428 | {0x116CA, 0x116FF}, {0x1171A, 0x1171C}, {0x1172C, 0x1172F}, 429 | {0x11740, 0x1189F}, {0x118F3, 0x118FE}, {0x11900, 0x11ABF}, 430 | {0x11AF9, 0x11BFF}, {0x11C09, 0x11C09}, {0x11C37, 0x11C37}, 431 | {0x11C46, 0x11C4F}, {0x11C6D, 0x11C6F}, {0x11C90, 0x11C91}, 432 | {0x11CA8, 0x11CA8}, {0x11CB7, 0x11FFF}, {0x1239A, 0x123FF}, 433 | {0x1246F, 0x1246F}, {0x12475, 0x1247F}, {0x12544, 0x12FFF}, 434 | {0x1342F, 0x143FF}, {0x14647, 0x167FF}, {0x16A39, 0x16A3F}, 435 | {0x16A5F, 0x16A5F}, {0x16A6A, 0x16A6D}, {0x16A70, 0x16ACF}, 436 | {0x16AEE, 0x16AEF}, {0x16AF6, 0x16AFF}, {0x16B46, 0x16B4F}, 437 | {0x16B5A, 0x16B5A}, {0x16B62, 0x16B62}, {0x16B78, 0x16B7C}, 438 | {0x16B90, 0x16EFF}, {0x16F45, 0x16F4F}, {0x16F7F, 0x16F8E}, 439 | {0x16FA0, 0x16FDF}, {0x16FE1, 0x16FFF}, {0x187ED, 0x187FF}, 440 | {0x18AF3, 0x1AFFF}, {0x1B002, 0x1BBFF}, {0x1BC6B, 0x1BC6F}, 441 | {0x1BC7D, 0x1BC7F}, {0x1BC89, 0x1BC8F}, {0x1BC9A, 0x1BC9B}, 442 | {0x1BCA4, 0x1CFFF}, {0x1D0F6, 0x1D0FF}, {0x1D127, 0x1D128}, 443 | {0x1D1E9, 0x1D1FF}, {0x1D246, 0x1D2FF}, {0x1D357, 0x1D35F}, 444 | {0x1D372, 0x1D3FF}, {0x1D455, 0x1D455}, {0x1D49D, 0x1D49D}, 445 | {0x1D4A0, 0x1D4A1}, {0x1D4A3, 0x1D4A4}, {0x1D4A7, 0x1D4A8}, 446 | {0x1D4AD, 0x1D4AD}, {0x1D4BA, 0x1D4BA}, {0x1D4BC, 0x1D4BC}, 447 | {0x1D4C4, 0x1D4C4}, {0x1D506, 0x1D506}, {0x1D50B, 0x1D50C}, 448 | {0x1D515, 0x1D515}, {0x1D51D, 0x1D51D}, {0x1D53A, 0x1D53A}, 449 | {0x1D53F, 0x1D53F}, {0x1D545, 0x1D545}, {0x1D547, 0x1D549}, 450 | {0x1D551, 0x1D551}, {0x1D6A6, 0x1D6A7}, {0x1D7CC, 0x1D7CD}, 451 | {0x1DA8C, 0x1DA9A}, {0x1DAA0, 0x1DAA0}, {0x1DAB0, 0x1DFFF}, 452 | {0x1E007, 0x1E007}, {0x1E019, 0x1E01A}, {0x1E022, 0x1E022}, 453 | {0x1E025, 0x1E025}, {0x1E02B, 0x1E7FF}, {0x1E8C5, 0x1E8C6}, 454 | {0x1E8D7, 0x1E8FF}, {0x1E94B, 0x1E94F}, {0x1E95A, 0x1E95D}, 455 | {0x1E960, 0x1EDFF}, {0x1EE04, 0x1EE04}, {0x1EE20, 0x1EE20}, 456 | {0x1EE23, 0x1EE23}, {0x1EE25, 0x1EE26}, {0x1EE28, 0x1EE28}, 457 | {0x1EE33, 0x1EE33}, {0x1EE38, 0x1EE38}, {0x1EE3A, 0x1EE3A}, 458 | {0x1EE3C, 0x1EE41}, {0x1EE43, 0x1EE46}, {0x1EE48, 0x1EE48}, 459 | {0x1EE4A, 0x1EE4A}, {0x1EE4C, 0x1EE4C}, {0x1EE50, 0x1EE50}, 460 | {0x1EE53, 0x1EE53}, {0x1EE55, 0x1EE56}, {0x1EE58, 0x1EE58}, 461 | {0x1EE5A, 0x1EE5A}, {0x1EE5C, 0x1EE5C}, {0x1EE5E, 0x1EE5E}, 462 | {0x1EE60, 0x1EE60}, {0x1EE63, 0x1EE63}, {0x1EE65, 0x1EE66}, 463 | {0x1EE6B, 0x1EE6B}, {0x1EE73, 0x1EE73}, {0x1EE78, 0x1EE78}, 464 | {0x1EE7D, 0x1EE7D}, {0x1EE7F, 0x1EE7F}, {0x1EE8A, 0x1EE8A}, 465 | {0x1EE9C, 0x1EEA0}, {0x1EEA4, 0x1EEA4}, {0x1EEAA, 0x1EEAA}, 466 | {0x1EEBC, 0x1EEEF}, {0x1EEF2, 0x1EFFF}, {0x1F02C, 0x1F02F}, 467 | {0x1F094, 0x1F09F}, {0x1F0AF, 0x1F0B0}, {0x1F0C0, 0x1F0C0}, 468 | {0x1F0D0, 0x1F0D0}, {0x1F0F6, 0x1F0FF}, {0x1F10D, 0x1F10F}, 469 | {0x1F12F, 0x1F12F}, {0x1F16C, 0x1F16F}, {0x1F1AD, 0x1F1E5}, 470 | {0x1F203, 0x1F20F}, {0x1F23C, 0x1F23F}, {0x1F249, 0x1F24F}, 471 | {0x1F252, 0x1F2FF}, {0x1F6D3, 0x1F6DF}, {0x1F6ED, 0x1F6EF}, 472 | {0x1F6F7, 0x1F6FF}, {0x1F774, 0x1F77F}, {0x1F7D5, 0x1F7FF}, 473 | {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F}, {0x1F85A, 0x1F85F}, 474 | {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F90F}, {0x1F91F, 0x1F91F}, 475 | {0x1F928, 0x1F92F}, {0x1F931, 0x1F932}, {0x1F93F, 0x1F93F}, 476 | {0x1F94C, 0x1F94F}, {0x1F95F, 0x1F97F}, {0x1F992, 0x1F9BF}, 477 | {0x1F9C1, 0x1FFFF}, {0x2A6D7, 0x2A6FF}, {0x2B735, 0x2B73F}, 478 | {0x2B81E, 0x2B81F}, {0x2CEA2, 0x2F7FF}, {0x2FA1E, 0xE0000}, 479 | {0xE0002, 0xE001F}, {0xE0080, 0xE00FF}, {0xE01F0, 0xEFFFF}, 480 | {0xFFFFE, 0xFFFFF}, 481 | } 482 | 483 | var neutral = table{ 484 | {0x0000, 0x001F}, {0x007F, 0x007F}, {0x0080, 0x009F}, 485 | {0x00A0, 0x00A0}, {0x00A9, 0x00A9}, {0x00AB, 0x00AB}, 486 | {0x00B5, 0x00B5}, {0x00BB, 0x00BB}, {0x00C0, 0x00C5}, 487 | {0x00C7, 0x00CF}, {0x00D1, 0x00D6}, {0x00D9, 0x00DD}, 488 | {0x00E2, 0x00E5}, {0x00E7, 0x00E7}, {0x00EB, 0x00EB}, 489 | {0x00EE, 0x00EF}, {0x00F1, 0x00F1}, {0x00F4, 0x00F6}, 490 | {0x00FB, 0x00FB}, {0x00FD, 0x00FD}, {0x00FF, 0x00FF}, 491 | {0x0100, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112}, 492 | {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A}, 493 | {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E}, 494 | {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C}, 495 | {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A}, 496 | {0x016C, 0x017F}, {0x0180, 0x01BA}, {0x01BB, 0x01BB}, 497 | {0x01BC, 0x01BF}, {0x01C0, 0x01C3}, {0x01C4, 0x01CD}, 498 | {0x01CF, 0x01CF}, {0x01D1, 0x01D1}, {0x01D3, 0x01D3}, 499 | {0x01D5, 0x01D5}, {0x01D7, 0x01D7}, {0x01D9, 0x01D9}, 500 | {0x01DB, 0x01DB}, {0x01DD, 0x024F}, {0x0250, 0x0250}, 501 | {0x0252, 0x0260}, {0x0262, 0x0293}, {0x0294, 0x0294}, 502 | {0x0295, 0x02AF}, {0x02B0, 0x02C1}, {0x02C2, 0x02C3}, 503 | {0x02C5, 0x02C5}, {0x02C6, 0x02C6}, {0x02C8, 0x02C8}, 504 | {0x02CC, 0x02CC}, {0x02CE, 0x02CF}, {0x02D1, 0x02D1}, 505 | {0x02D2, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE}, 506 | {0x02E0, 0x02E4}, {0x02E5, 0x02EB}, {0x02EC, 0x02EC}, 507 | {0x02ED, 0x02ED}, {0x02EE, 0x02EE}, {0x02EF, 0x02FF}, 508 | {0x0370, 0x0373}, {0x0374, 0x0374}, {0x0375, 0x0375}, 509 | {0x0376, 0x0377}, {0x037A, 0x037A}, {0x037B, 0x037D}, 510 | {0x037E, 0x037E}, {0x037F, 0x037F}, {0x0384, 0x0385}, 511 | {0x0386, 0x0386}, {0x0387, 0x0387}, {0x0388, 0x038A}, 512 | {0x038C, 0x038C}, {0x038E, 0x0390}, {0x03AA, 0x03B0}, 513 | {0x03C2, 0x03C2}, {0x03CA, 0x03F5}, {0x03F6, 0x03F6}, 514 | {0x03F7, 0x03FF}, {0x0400, 0x0400}, {0x0402, 0x040F}, 515 | {0x0450, 0x0450}, {0x0452, 0x0481}, {0x0482, 0x0482}, 516 | {0x0483, 0x0487}, {0x0488, 0x0489}, {0x048A, 0x04FF}, 517 | {0x0500, 0x052F}, {0x0531, 0x0556}, {0x0559, 0x0559}, 518 | {0x055A, 0x055F}, {0x0561, 0x0587}, {0x0589, 0x0589}, 519 | {0x058A, 0x058A}, {0x058D, 0x058E}, {0x058F, 0x058F}, 520 | {0x0591, 0x05BD}, {0x05BE, 0x05BE}, {0x05BF, 0x05BF}, 521 | {0x05C0, 0x05C0}, {0x05C1, 0x05C2}, {0x05C3, 0x05C3}, 522 | {0x05C4, 0x05C5}, {0x05C6, 0x05C6}, {0x05C7, 0x05C7}, 523 | {0x05D0, 0x05EA}, {0x05F0, 0x05F2}, {0x05F3, 0x05F4}, 524 | {0x0600, 0x0605}, {0x0606, 0x0608}, {0x0609, 0x060A}, 525 | {0x060B, 0x060B}, {0x060C, 0x060D}, {0x060E, 0x060F}, 526 | {0x0610, 0x061A}, {0x061B, 0x061B}, {0x061C, 0x061C}, 527 | {0x061E, 0x061F}, {0x0620, 0x063F}, {0x0640, 0x0640}, 528 | {0x0641, 0x064A}, {0x064B, 0x065F}, {0x0660, 0x0669}, 529 | {0x066A, 0x066D}, {0x066E, 0x066F}, {0x0670, 0x0670}, 530 | {0x0671, 0x06D3}, {0x06D4, 0x06D4}, {0x06D5, 0x06D5}, 531 | {0x06D6, 0x06DC}, {0x06DD, 0x06DD}, {0x06DE, 0x06DE}, 532 | {0x06DF, 0x06E4}, {0x06E5, 0x06E6}, {0x06E7, 0x06E8}, 533 | {0x06E9, 0x06E9}, {0x06EA, 0x06ED}, {0x06EE, 0x06EF}, 534 | {0x06F0, 0x06F9}, {0x06FA, 0x06FC}, {0x06FD, 0x06FE}, 535 | {0x06FF, 0x06FF}, {0x0700, 0x070D}, {0x070F, 0x070F}, 536 | {0x0710, 0x0710}, {0x0711, 0x0711}, {0x0712, 0x072F}, 537 | {0x0730, 0x074A}, {0x074D, 0x074F}, {0x0750, 0x077F}, 538 | {0x0780, 0x07A5}, {0x07A6, 0x07B0}, {0x07B1, 0x07B1}, 539 | {0x07C0, 0x07C9}, {0x07CA, 0x07EA}, {0x07EB, 0x07F3}, 540 | {0x07F4, 0x07F5}, {0x07F6, 0x07F6}, {0x07F7, 0x07F9}, 541 | {0x07FA, 0x07FA}, {0x0800, 0x0815}, {0x0816, 0x0819}, 542 | {0x081A, 0x081A}, {0x081B, 0x0823}, {0x0824, 0x0824}, 543 | {0x0825, 0x0827}, {0x0828, 0x0828}, {0x0829, 0x082D}, 544 | {0x0830, 0x083E}, {0x0840, 0x0858}, {0x0859, 0x085B}, 545 | {0x085E, 0x085E}, {0x08A0, 0x08B4}, {0x08B6, 0x08BD}, 546 | {0x08D4, 0x08E1}, {0x08E2, 0x08E2}, {0x08E3, 0x08FF}, 547 | {0x0900, 0x0902}, {0x0903, 0x0903}, {0x0904, 0x0939}, 548 | {0x093A, 0x093A}, {0x093B, 0x093B}, {0x093C, 0x093C}, 549 | {0x093D, 0x093D}, {0x093E, 0x0940}, {0x0941, 0x0948}, 550 | {0x0949, 0x094C}, {0x094D, 0x094D}, {0x094E, 0x094F}, 551 | {0x0950, 0x0950}, {0x0951, 0x0957}, {0x0958, 0x0961}, 552 | {0x0962, 0x0963}, {0x0964, 0x0965}, {0x0966, 0x096F}, 553 | {0x0970, 0x0970}, {0x0971, 0x0971}, {0x0972, 0x097F}, 554 | {0x0980, 0x0980}, {0x0981, 0x0981}, {0x0982, 0x0983}, 555 | {0x0985, 0x098C}, {0x098F, 0x0990}, {0x0993, 0x09A8}, 556 | {0x09AA, 0x09B0}, {0x09B2, 0x09B2}, {0x09B6, 0x09B9}, 557 | {0x09BC, 0x09BC}, {0x09BD, 0x09BD}, {0x09BE, 0x09C0}, 558 | {0x09C1, 0x09C4}, {0x09C7, 0x09C8}, {0x09CB, 0x09CC}, 559 | {0x09CD, 0x09CD}, {0x09CE, 0x09CE}, {0x09D7, 0x09D7}, 560 | {0x09DC, 0x09DD}, {0x09DF, 0x09E1}, {0x09E2, 0x09E3}, 561 | {0x09E6, 0x09EF}, {0x09F0, 0x09F1}, {0x09F2, 0x09F3}, 562 | {0x09F4, 0x09F9}, {0x09FA, 0x09FA}, {0x09FB, 0x09FB}, 563 | {0x0A01, 0x0A02}, {0x0A03, 0x0A03}, {0x0A05, 0x0A0A}, 564 | {0x0A0F, 0x0A10}, {0x0A13, 0x0A28}, {0x0A2A, 0x0A30}, 565 | {0x0A32, 0x0A33}, {0x0A35, 0x0A36}, {0x0A38, 0x0A39}, 566 | {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A40}, {0x0A41, 0x0A42}, 567 | {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51}, 568 | {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A6F}, 569 | {0x0A70, 0x0A71}, {0x0A72, 0x0A74}, {0x0A75, 0x0A75}, 570 | {0x0A81, 0x0A82}, {0x0A83, 0x0A83}, {0x0A85, 0x0A8D}, 571 | {0x0A8F, 0x0A91}, {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, 572 | {0x0AB2, 0x0AB3}, {0x0AB5, 0x0AB9}, {0x0ABC, 0x0ABC}, 573 | {0x0ABD, 0x0ABD}, {0x0ABE, 0x0AC0}, {0x0AC1, 0x0AC5}, 574 | {0x0AC7, 0x0AC8}, {0x0AC9, 0x0AC9}, {0x0ACB, 0x0ACC}, 575 | {0x0ACD, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE1}, 576 | {0x0AE2, 0x0AE3}, {0x0AE6, 0x0AEF}, {0x0AF0, 0x0AF0}, 577 | {0x0AF1, 0x0AF1}, {0x0AF9, 0x0AF9}, {0x0B01, 0x0B01}, 578 | {0x0B02, 0x0B03}, {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, 579 | {0x0B13, 0x0B28}, {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, 580 | {0x0B35, 0x0B39}, {0x0B3C, 0x0B3C}, {0x0B3D, 0x0B3D}, 581 | {0x0B3E, 0x0B3E}, {0x0B3F, 0x0B3F}, {0x0B40, 0x0B40}, 582 | {0x0B41, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4C}, 583 | {0x0B4D, 0x0B4D}, {0x0B56, 0x0B56}, {0x0B57, 0x0B57}, 584 | {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B61}, {0x0B62, 0x0B63}, 585 | {0x0B66, 0x0B6F}, {0x0B70, 0x0B70}, {0x0B71, 0x0B71}, 586 | {0x0B72, 0x0B77}, {0x0B82, 0x0B82}, {0x0B83, 0x0B83}, 587 | {0x0B85, 0x0B8A}, {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, 588 | {0x0B99, 0x0B9A}, {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, 589 | {0x0BA3, 0x0BA4}, {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, 590 | {0x0BBE, 0x0BBF}, {0x0BC0, 0x0BC0}, {0x0BC1, 0x0BC2}, 591 | {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCC}, {0x0BCD, 0x0BCD}, 592 | {0x0BD0, 0x0BD0}, {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BEF}, 593 | {0x0BF0, 0x0BF2}, {0x0BF3, 0x0BF8}, {0x0BF9, 0x0BF9}, 594 | {0x0BFA, 0x0BFA}, {0x0C00, 0x0C00}, {0x0C01, 0x0C03}, 595 | {0x0C05, 0x0C0C}, {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, 596 | {0x0C2A, 0x0C39}, {0x0C3D, 0x0C3D}, {0x0C3E, 0x0C40}, 597 | {0x0C41, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D}, 598 | {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C61}, 599 | {0x0C62, 0x0C63}, {0x0C66, 0x0C6F}, {0x0C78, 0x0C7E}, 600 | {0x0C7F, 0x0C7F}, {0x0C80, 0x0C80}, {0x0C81, 0x0C81}, 601 | {0x0C82, 0x0C83}, {0x0C85, 0x0C8C}, {0x0C8E, 0x0C90}, 602 | {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9}, 603 | {0x0CBC, 0x0CBC}, {0x0CBD, 0x0CBD}, {0x0CBE, 0x0CBE}, 604 | {0x0CBF, 0x0CBF}, {0x0CC0, 0x0CC4}, {0x0CC6, 0x0CC6}, 605 | {0x0CC7, 0x0CC8}, {0x0CCA, 0x0CCB}, {0x0CCC, 0x0CCD}, 606 | {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE1}, 607 | {0x0CE2, 0x0CE3}, {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, 608 | {0x0D01, 0x0D01}, {0x0D02, 0x0D03}, {0x0D05, 0x0D0C}, 609 | {0x0D0E, 0x0D10}, {0x0D12, 0x0D3A}, {0x0D3D, 0x0D3D}, 610 | {0x0D3E, 0x0D40}, {0x0D41, 0x0D44}, {0x0D46, 0x0D48}, 611 | {0x0D4A, 0x0D4C}, {0x0D4D, 0x0D4D}, {0x0D4E, 0x0D4E}, 612 | {0x0D4F, 0x0D4F}, {0x0D54, 0x0D56}, {0x0D57, 0x0D57}, 613 | {0x0D58, 0x0D5E}, {0x0D5F, 0x0D61}, {0x0D62, 0x0D63}, 614 | {0x0D66, 0x0D6F}, {0x0D70, 0x0D78}, {0x0D79, 0x0D79}, 615 | {0x0D7A, 0x0D7F}, {0x0D82, 0x0D83}, {0x0D85, 0x0D96}, 616 | {0x0D9A, 0x0DB1}, {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, 617 | {0x0DC0, 0x0DC6}, {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD1}, 618 | {0x0DD2, 0x0DD4}, {0x0DD6, 0x0DD6}, {0x0DD8, 0x0DDF}, 619 | {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF3}, {0x0DF4, 0x0DF4}, 620 | {0x0E01, 0x0E30}, {0x0E31, 0x0E31}, {0x0E32, 0x0E33}, 621 | {0x0E34, 0x0E3A}, {0x0E3F, 0x0E3F}, {0x0E40, 0x0E45}, 622 | {0x0E46, 0x0E46}, {0x0E47, 0x0E4E}, {0x0E4F, 0x0E4F}, 623 | {0x0E50, 0x0E59}, {0x0E5A, 0x0E5B}, {0x0E81, 0x0E82}, 624 | {0x0E84, 0x0E84}, {0x0E87, 0x0E88}, {0x0E8A, 0x0E8A}, 625 | {0x0E8D, 0x0E8D}, {0x0E94, 0x0E97}, {0x0E99, 0x0E9F}, 626 | {0x0EA1, 0x0EA3}, {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EA7}, 627 | {0x0EAA, 0x0EAB}, {0x0EAD, 0x0EB0}, {0x0EB1, 0x0EB1}, 628 | {0x0EB2, 0x0EB3}, {0x0EB4, 0x0EB9}, {0x0EBB, 0x0EBC}, 629 | {0x0EBD, 0x0EBD}, {0x0EC0, 0x0EC4}, {0x0EC6, 0x0EC6}, 630 | {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9}, {0x0EDC, 0x0EDF}, 631 | {0x0F00, 0x0F00}, {0x0F01, 0x0F03}, {0x0F04, 0x0F12}, 632 | {0x0F13, 0x0F13}, {0x0F14, 0x0F14}, {0x0F15, 0x0F17}, 633 | {0x0F18, 0x0F19}, {0x0F1A, 0x0F1F}, {0x0F20, 0x0F29}, 634 | {0x0F2A, 0x0F33}, {0x0F34, 0x0F34}, {0x0F35, 0x0F35}, 635 | {0x0F36, 0x0F36}, {0x0F37, 0x0F37}, {0x0F38, 0x0F38}, 636 | {0x0F39, 0x0F39}, {0x0F3A, 0x0F3A}, {0x0F3B, 0x0F3B}, 637 | {0x0F3C, 0x0F3C}, {0x0F3D, 0x0F3D}, {0x0F3E, 0x0F3F}, 638 | {0x0F40, 0x0F47}, {0x0F49, 0x0F6C}, {0x0F71, 0x0F7E}, 639 | {0x0F7F, 0x0F7F}, {0x0F80, 0x0F84}, {0x0F85, 0x0F85}, 640 | {0x0F86, 0x0F87}, {0x0F88, 0x0F8C}, {0x0F8D, 0x0F97}, 641 | {0x0F99, 0x0FBC}, {0x0FBE, 0x0FC5}, {0x0FC6, 0x0FC6}, 642 | {0x0FC7, 0x0FCC}, {0x0FCE, 0x0FCF}, {0x0FD0, 0x0FD4}, 643 | {0x0FD5, 0x0FD8}, {0x0FD9, 0x0FDA}, {0x1000, 0x102A}, 644 | {0x102B, 0x102C}, {0x102D, 0x1030}, {0x1031, 0x1031}, 645 | {0x1032, 0x1037}, {0x1038, 0x1038}, {0x1039, 0x103A}, 646 | {0x103B, 0x103C}, {0x103D, 0x103E}, {0x103F, 0x103F}, 647 | {0x1040, 0x1049}, {0x104A, 0x104F}, {0x1050, 0x1055}, 648 | {0x1056, 0x1057}, {0x1058, 0x1059}, {0x105A, 0x105D}, 649 | {0x105E, 0x1060}, {0x1061, 0x1061}, {0x1062, 0x1064}, 650 | {0x1065, 0x1066}, {0x1067, 0x106D}, {0x106E, 0x1070}, 651 | {0x1071, 0x1074}, {0x1075, 0x1081}, {0x1082, 0x1082}, 652 | {0x1083, 0x1084}, {0x1085, 0x1086}, {0x1087, 0x108C}, 653 | {0x108D, 0x108D}, {0x108E, 0x108E}, {0x108F, 0x108F}, 654 | {0x1090, 0x1099}, {0x109A, 0x109C}, {0x109D, 0x109D}, 655 | {0x109E, 0x109F}, {0x10A0, 0x10C5}, {0x10C7, 0x10C7}, 656 | {0x10CD, 0x10CD}, {0x10D0, 0x10FA}, {0x10FB, 0x10FB}, 657 | {0x10FC, 0x10FC}, {0x10FD, 0x10FF}, {0x1160, 0x11FF}, 658 | {0x1200, 0x1248}, {0x124A, 0x124D}, {0x1250, 0x1256}, 659 | {0x1258, 0x1258}, {0x125A, 0x125D}, {0x1260, 0x1288}, 660 | {0x128A, 0x128D}, {0x1290, 0x12B0}, {0x12B2, 0x12B5}, 661 | {0x12B8, 0x12BE}, {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, 662 | {0x12C8, 0x12D6}, {0x12D8, 0x1310}, {0x1312, 0x1315}, 663 | {0x1318, 0x135A}, {0x135D, 0x135F}, {0x1360, 0x1368}, 664 | {0x1369, 0x137C}, {0x1380, 0x138F}, {0x1390, 0x1399}, 665 | {0x13A0, 0x13F5}, {0x13F8, 0x13FD}, {0x1400, 0x1400}, 666 | {0x1401, 0x166C}, {0x166D, 0x166E}, {0x166F, 0x167F}, 667 | {0x1680, 0x1680}, {0x1681, 0x169A}, {0x169B, 0x169B}, 668 | {0x169C, 0x169C}, {0x16A0, 0x16EA}, {0x16EB, 0x16ED}, 669 | {0x16EE, 0x16F0}, {0x16F1, 0x16F8}, {0x1700, 0x170C}, 670 | {0x170E, 0x1711}, {0x1712, 0x1714}, {0x1720, 0x1731}, 671 | {0x1732, 0x1734}, {0x1735, 0x1736}, {0x1740, 0x1751}, 672 | {0x1752, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770}, 673 | {0x1772, 0x1773}, {0x1780, 0x17B3}, {0x17B4, 0x17B5}, 674 | {0x17B6, 0x17B6}, {0x17B7, 0x17BD}, {0x17BE, 0x17C5}, 675 | {0x17C6, 0x17C6}, {0x17C7, 0x17C8}, {0x17C9, 0x17D3}, 676 | {0x17D4, 0x17D6}, {0x17D7, 0x17D7}, {0x17D8, 0x17DA}, 677 | {0x17DB, 0x17DB}, {0x17DC, 0x17DC}, {0x17DD, 0x17DD}, 678 | {0x17E0, 0x17E9}, {0x17F0, 0x17F9}, {0x1800, 0x1805}, 679 | {0x1806, 0x1806}, {0x1807, 0x180A}, {0x180B, 0x180D}, 680 | {0x180E, 0x180E}, {0x1810, 0x1819}, {0x1820, 0x1842}, 681 | {0x1843, 0x1843}, {0x1844, 0x1877}, {0x1880, 0x1884}, 682 | {0x1885, 0x1886}, {0x1887, 0x18A8}, {0x18A9, 0x18A9}, 683 | {0x18AA, 0x18AA}, {0x18B0, 0x18F5}, {0x1900, 0x191E}, 684 | {0x1920, 0x1922}, {0x1923, 0x1926}, {0x1927, 0x1928}, 685 | {0x1929, 0x192B}, {0x1930, 0x1931}, {0x1932, 0x1932}, 686 | {0x1933, 0x1938}, {0x1939, 0x193B}, {0x1940, 0x1940}, 687 | {0x1944, 0x1945}, {0x1946, 0x194F}, {0x1950, 0x196D}, 688 | {0x1970, 0x1974}, {0x1980, 0x19AB}, {0x19B0, 0x19C9}, 689 | {0x19D0, 0x19D9}, {0x19DA, 0x19DA}, {0x19DE, 0x19DF}, 690 | {0x19E0, 0x19FF}, {0x1A00, 0x1A16}, {0x1A17, 0x1A18}, 691 | {0x1A19, 0x1A1A}, {0x1A1B, 0x1A1B}, {0x1A1E, 0x1A1F}, 692 | {0x1A20, 0x1A54}, {0x1A55, 0x1A55}, {0x1A56, 0x1A56}, 693 | {0x1A57, 0x1A57}, {0x1A58, 0x1A5E}, {0x1A60, 0x1A60}, 694 | {0x1A61, 0x1A61}, {0x1A62, 0x1A62}, {0x1A63, 0x1A64}, 695 | {0x1A65, 0x1A6C}, {0x1A6D, 0x1A72}, {0x1A73, 0x1A7C}, 696 | {0x1A7F, 0x1A7F}, {0x1A80, 0x1A89}, {0x1A90, 0x1A99}, 697 | {0x1AA0, 0x1AA6}, {0x1AA7, 0x1AA7}, {0x1AA8, 0x1AAD}, 698 | {0x1AB0, 0x1ABD}, {0x1ABE, 0x1ABE}, {0x1B00, 0x1B03}, 699 | {0x1B04, 0x1B04}, {0x1B05, 0x1B33}, {0x1B34, 0x1B34}, 700 | {0x1B35, 0x1B35}, {0x1B36, 0x1B3A}, {0x1B3B, 0x1B3B}, 701 | {0x1B3C, 0x1B3C}, {0x1B3D, 0x1B41}, {0x1B42, 0x1B42}, 702 | {0x1B43, 0x1B44}, {0x1B45, 0x1B4B}, {0x1B50, 0x1B59}, 703 | {0x1B5A, 0x1B60}, {0x1B61, 0x1B6A}, {0x1B6B, 0x1B73}, 704 | {0x1B74, 0x1B7C}, {0x1B80, 0x1B81}, {0x1B82, 0x1B82}, 705 | {0x1B83, 0x1BA0}, {0x1BA1, 0x1BA1}, {0x1BA2, 0x1BA5}, 706 | {0x1BA6, 0x1BA7}, {0x1BA8, 0x1BA9}, {0x1BAA, 0x1BAA}, 707 | {0x1BAB, 0x1BAD}, {0x1BAE, 0x1BAF}, {0x1BB0, 0x1BB9}, 708 | {0x1BBA, 0x1BBF}, {0x1BC0, 0x1BE5}, {0x1BE6, 0x1BE6}, 709 | {0x1BE7, 0x1BE7}, {0x1BE8, 0x1BE9}, {0x1BEA, 0x1BEC}, 710 | {0x1BED, 0x1BED}, {0x1BEE, 0x1BEE}, {0x1BEF, 0x1BF1}, 711 | {0x1BF2, 0x1BF3}, {0x1BFC, 0x1BFF}, {0x1C00, 0x1C23}, 712 | {0x1C24, 0x1C2B}, {0x1C2C, 0x1C33}, {0x1C34, 0x1C35}, 713 | {0x1C36, 0x1C37}, {0x1C3B, 0x1C3F}, {0x1C40, 0x1C49}, 714 | {0x1C4D, 0x1C4F}, {0x1C50, 0x1C59}, {0x1C5A, 0x1C77}, 715 | {0x1C78, 0x1C7D}, {0x1C7E, 0x1C7F}, {0x1C80, 0x1C88}, 716 | {0x1CC0, 0x1CC7}, {0x1CD0, 0x1CD2}, {0x1CD3, 0x1CD3}, 717 | {0x1CD4, 0x1CE0}, {0x1CE1, 0x1CE1}, {0x1CE2, 0x1CE8}, 718 | {0x1CE9, 0x1CEC}, {0x1CED, 0x1CED}, {0x1CEE, 0x1CF1}, 719 | {0x1CF2, 0x1CF3}, {0x1CF4, 0x1CF4}, {0x1CF5, 0x1CF6}, 720 | {0x1CF8, 0x1CF9}, {0x1D00, 0x1D2B}, {0x1D2C, 0x1D6A}, 721 | {0x1D6B, 0x1D77}, {0x1D78, 0x1D78}, {0x1D79, 0x1D7F}, 722 | {0x1D80, 0x1D9A}, {0x1D9B, 0x1DBF}, {0x1DC0, 0x1DF5}, 723 | {0x1DFB, 0x1DFF}, {0x1E00, 0x1EFF}, {0x1F00, 0x1F15}, 724 | {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D}, 725 | {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B}, 726 | {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4}, 727 | {0x1FB6, 0x1FBC}, {0x1FBD, 0x1FBD}, {0x1FBE, 0x1FBE}, 728 | {0x1FBF, 0x1FC1}, {0x1FC2, 0x1FC4}, {0x1FC6, 0x1FCC}, 729 | {0x1FCD, 0x1FCF}, {0x1FD0, 0x1FD3}, {0x1FD6, 0x1FDB}, 730 | {0x1FDD, 0x1FDF}, {0x1FE0, 0x1FEC}, {0x1FED, 0x1FEF}, 731 | {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFC}, {0x1FFD, 0x1FFE}, 732 | {0x2000, 0x200A}, {0x200B, 0x200F}, {0x2011, 0x2012}, 733 | {0x2017, 0x2017}, {0x201A, 0x201A}, {0x201B, 0x201B}, 734 | {0x201E, 0x201E}, {0x201F, 0x201F}, {0x2023, 0x2023}, 735 | {0x2028, 0x2028}, {0x2029, 0x2029}, {0x202A, 0x202E}, 736 | {0x202F, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034}, 737 | {0x2036, 0x2038}, {0x2039, 0x2039}, {0x203A, 0x203A}, 738 | {0x203C, 0x203D}, {0x203F, 0x2040}, {0x2041, 0x2043}, 739 | {0x2044, 0x2044}, {0x2045, 0x2045}, {0x2046, 0x2046}, 740 | {0x2047, 0x2051}, {0x2052, 0x2052}, {0x2053, 0x2053}, 741 | {0x2054, 0x2054}, {0x2055, 0x205E}, {0x205F, 0x205F}, 742 | {0x2060, 0x2064}, {0x2066, 0x206F}, {0x2070, 0x2070}, 743 | {0x2071, 0x2071}, {0x2075, 0x2079}, {0x207A, 0x207C}, 744 | {0x207D, 0x207D}, {0x207E, 0x207E}, {0x2080, 0x2080}, 745 | {0x2085, 0x2089}, {0x208A, 0x208C}, {0x208D, 0x208D}, 746 | {0x208E, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8}, 747 | {0x20AA, 0x20AB}, {0x20AD, 0x20BE}, {0x20D0, 0x20DC}, 748 | {0x20DD, 0x20E0}, {0x20E1, 0x20E1}, {0x20E2, 0x20E4}, 749 | {0x20E5, 0x20F0}, {0x2100, 0x2101}, {0x2102, 0x2102}, 750 | {0x2104, 0x2104}, {0x2106, 0x2106}, {0x2107, 0x2107}, 751 | {0x2108, 0x2108}, {0x210A, 0x2112}, {0x2114, 0x2114}, 752 | {0x2115, 0x2115}, {0x2117, 0x2117}, {0x2118, 0x2118}, 753 | {0x2119, 0x211D}, {0x211E, 0x2120}, {0x2123, 0x2123}, 754 | {0x2124, 0x2124}, {0x2125, 0x2125}, {0x2127, 0x2127}, 755 | {0x2128, 0x2128}, {0x2129, 0x2129}, {0x212A, 0x212A}, 756 | {0x212C, 0x212D}, {0x212E, 0x212E}, {0x212F, 0x2134}, 757 | {0x2135, 0x2138}, {0x2139, 0x2139}, {0x213A, 0x213B}, 758 | {0x213C, 0x213F}, {0x2140, 0x2144}, {0x2145, 0x2149}, 759 | {0x214A, 0x214A}, {0x214B, 0x214B}, {0x214C, 0x214D}, 760 | {0x214E, 0x214E}, {0x214F, 0x214F}, {0x2150, 0x2152}, 761 | {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F}, 762 | {0x217A, 0x2182}, {0x2183, 0x2184}, {0x2185, 0x2188}, 763 | {0x218A, 0x218B}, {0x219A, 0x219B}, {0x219C, 0x219F}, 764 | {0x21A0, 0x21A0}, {0x21A1, 0x21A2}, {0x21A3, 0x21A3}, 765 | {0x21A4, 0x21A5}, {0x21A6, 0x21A6}, {0x21A7, 0x21AD}, 766 | {0x21AE, 0x21AE}, {0x21AF, 0x21B7}, {0x21BA, 0x21CD}, 767 | {0x21CE, 0x21CF}, {0x21D0, 0x21D1}, {0x21D3, 0x21D3}, 768 | {0x21D5, 0x21E6}, {0x21E8, 0x21F3}, {0x21F4, 0x21FF}, 769 | {0x2201, 0x2201}, {0x2204, 0x2206}, {0x2209, 0x220A}, 770 | {0x220C, 0x220E}, {0x2210, 0x2210}, {0x2212, 0x2214}, 771 | {0x2216, 0x2219}, {0x221B, 0x221C}, {0x2221, 0x2222}, 772 | {0x2224, 0x2224}, {0x2226, 0x2226}, {0x222D, 0x222D}, 773 | {0x222F, 0x2233}, {0x2238, 0x223B}, {0x223E, 0x2247}, 774 | {0x2249, 0x224B}, {0x224D, 0x2251}, {0x2253, 0x225F}, 775 | {0x2262, 0x2263}, {0x2268, 0x2269}, {0x226C, 0x226D}, 776 | {0x2270, 0x2281}, {0x2284, 0x2285}, {0x2288, 0x2294}, 777 | {0x2296, 0x2298}, {0x229A, 0x22A4}, {0x22A6, 0x22BE}, 778 | {0x22C0, 0x22FF}, {0x2300, 0x2307}, {0x2308, 0x2308}, 779 | {0x2309, 0x2309}, {0x230A, 0x230A}, {0x230B, 0x230B}, 780 | {0x230C, 0x2311}, {0x2313, 0x2319}, {0x231C, 0x231F}, 781 | {0x2320, 0x2321}, {0x2322, 0x2328}, {0x232B, 0x237B}, 782 | {0x237C, 0x237C}, {0x237D, 0x239A}, {0x239B, 0x23B3}, 783 | {0x23B4, 0x23DB}, {0x23DC, 0x23E1}, {0x23E2, 0x23E8}, 784 | {0x23ED, 0x23EF}, {0x23F1, 0x23F2}, {0x23F4, 0x23FE}, 785 | {0x2400, 0x2426}, {0x2440, 0x244A}, {0x24EA, 0x24EA}, 786 | {0x254C, 0x254F}, {0x2574, 0x257F}, {0x2590, 0x2591}, 787 | {0x2596, 0x259F}, {0x25A2, 0x25A2}, {0x25AA, 0x25B1}, 788 | {0x25B4, 0x25B5}, {0x25B8, 0x25BB}, {0x25BE, 0x25BF}, 789 | {0x25C2, 0x25C5}, {0x25C9, 0x25CA}, {0x25CC, 0x25CD}, 790 | {0x25D2, 0x25E1}, {0x25E6, 0x25EE}, {0x25F0, 0x25F7}, 791 | {0x25F8, 0x25FC}, {0x25FF, 0x25FF}, {0x2600, 0x2604}, 792 | {0x2607, 0x2608}, {0x260A, 0x260D}, {0x2610, 0x2613}, 793 | {0x2616, 0x261B}, {0x261D, 0x261D}, {0x261F, 0x263F}, 794 | {0x2641, 0x2641}, {0x2643, 0x2647}, {0x2654, 0x265F}, 795 | {0x2662, 0x2662}, {0x2666, 0x2666}, {0x266B, 0x266B}, 796 | {0x266E, 0x266E}, {0x2670, 0x267E}, {0x2680, 0x2692}, 797 | {0x2694, 0x269D}, {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, 798 | {0x26AC, 0x26BC}, {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, 799 | {0x26E4, 0x26E7}, {0x2700, 0x2704}, {0x2706, 0x2709}, 800 | {0x270C, 0x2727}, {0x2729, 0x273C}, {0x273E, 0x274B}, 801 | {0x274D, 0x274D}, {0x274F, 0x2752}, {0x2756, 0x2756}, 802 | {0x2758, 0x2767}, {0x2768, 0x2768}, {0x2769, 0x2769}, 803 | {0x276A, 0x276A}, {0x276B, 0x276B}, {0x276C, 0x276C}, 804 | {0x276D, 0x276D}, {0x276E, 0x276E}, {0x276F, 0x276F}, 805 | {0x2770, 0x2770}, {0x2771, 0x2771}, {0x2772, 0x2772}, 806 | {0x2773, 0x2773}, {0x2774, 0x2774}, {0x2775, 0x2775}, 807 | {0x2780, 0x2793}, {0x2794, 0x2794}, {0x2798, 0x27AF}, 808 | {0x27B1, 0x27BE}, {0x27C0, 0x27C4}, {0x27C5, 0x27C5}, 809 | {0x27C6, 0x27C6}, {0x27C7, 0x27E5}, {0x27EE, 0x27EE}, 810 | {0x27EF, 0x27EF}, {0x27F0, 0x27FF}, {0x2800, 0x28FF}, 811 | {0x2900, 0x297F}, {0x2980, 0x2982}, {0x2983, 0x2983}, 812 | {0x2984, 0x2984}, {0x2987, 0x2987}, {0x2988, 0x2988}, 813 | {0x2989, 0x2989}, {0x298A, 0x298A}, {0x298B, 0x298B}, 814 | {0x298C, 0x298C}, {0x298D, 0x298D}, {0x298E, 0x298E}, 815 | {0x298F, 0x298F}, {0x2990, 0x2990}, {0x2991, 0x2991}, 816 | {0x2992, 0x2992}, {0x2993, 0x2993}, {0x2994, 0x2994}, 817 | {0x2995, 0x2995}, {0x2996, 0x2996}, {0x2997, 0x2997}, 818 | {0x2998, 0x2998}, {0x2999, 0x29D7}, {0x29D8, 0x29D8}, 819 | {0x29D9, 0x29D9}, {0x29DA, 0x29DA}, {0x29DB, 0x29DB}, 820 | {0x29DC, 0x29FB}, {0x29FC, 0x29FC}, {0x29FD, 0x29FD}, 821 | {0x29FE, 0x29FF}, {0x2A00, 0x2AFF}, {0x2B00, 0x2B1A}, 822 | {0x2B1D, 0x2B2F}, {0x2B30, 0x2B44}, {0x2B45, 0x2B46}, 823 | {0x2B47, 0x2B4C}, {0x2B4D, 0x2B4F}, {0x2B51, 0x2B54}, 824 | {0x2B5A, 0x2B73}, {0x2B76, 0x2B95}, {0x2B98, 0x2BB9}, 825 | {0x2BBD, 0x2BC8}, {0x2BCA, 0x2BD1}, {0x2BEC, 0x2BEF}, 826 | {0x2C00, 0x2C2E}, {0x2C30, 0x2C5E}, {0x2C60, 0x2C7B}, 827 | {0x2C7C, 0x2C7D}, {0x2C7E, 0x2C7F}, {0x2C80, 0x2CE4}, 828 | {0x2CE5, 0x2CEA}, {0x2CEB, 0x2CEE}, {0x2CEF, 0x2CF1}, 829 | {0x2CF2, 0x2CF3}, {0x2CF9, 0x2CFC}, {0x2CFD, 0x2CFD}, 830 | {0x2CFE, 0x2CFF}, {0x2D00, 0x2D25}, {0x2D27, 0x2D27}, 831 | {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D6F}, 832 | {0x2D70, 0x2D70}, {0x2D7F, 0x2D7F}, {0x2D80, 0x2D96}, 833 | {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE}, {0x2DB0, 0x2DB6}, 834 | {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6}, {0x2DC8, 0x2DCE}, 835 | {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE}, {0x2DE0, 0x2DFF}, 836 | {0x2E00, 0x2E01}, {0x2E02, 0x2E02}, {0x2E03, 0x2E03}, 837 | {0x2E04, 0x2E04}, {0x2E05, 0x2E05}, {0x2E06, 0x2E08}, 838 | {0x2E09, 0x2E09}, {0x2E0A, 0x2E0A}, {0x2E0B, 0x2E0B}, 839 | {0x2E0C, 0x2E0C}, {0x2E0D, 0x2E0D}, {0x2E0E, 0x2E16}, 840 | {0x2E17, 0x2E17}, {0x2E18, 0x2E19}, {0x2E1A, 0x2E1A}, 841 | {0x2E1B, 0x2E1B}, {0x2E1C, 0x2E1C}, {0x2E1D, 0x2E1D}, 842 | {0x2E1E, 0x2E1F}, {0x2E20, 0x2E20}, {0x2E21, 0x2E21}, 843 | {0x2E22, 0x2E22}, {0x2E23, 0x2E23}, {0x2E24, 0x2E24}, 844 | {0x2E25, 0x2E25}, {0x2E26, 0x2E26}, {0x2E27, 0x2E27}, 845 | {0x2E28, 0x2E28}, {0x2E29, 0x2E29}, {0x2E2A, 0x2E2E}, 846 | {0x2E2F, 0x2E2F}, {0x2E30, 0x2E39}, {0x2E3A, 0x2E3B}, 847 | {0x2E3C, 0x2E3F}, {0x2E40, 0x2E40}, {0x2E41, 0x2E41}, 848 | {0x2E42, 0x2E42}, {0x2E43, 0x2E44}, {0x303F, 0x303F}, 849 | {0x4DC0, 0x4DFF}, {0xA4D0, 0xA4F7}, {0xA4F8, 0xA4FD}, 850 | {0xA4FE, 0xA4FF}, {0xA500, 0xA60B}, {0xA60C, 0xA60C}, 851 | {0xA60D, 0xA60F}, {0xA610, 0xA61F}, {0xA620, 0xA629}, 852 | {0xA62A, 0xA62B}, {0xA640, 0xA66D}, {0xA66E, 0xA66E}, 853 | {0xA66F, 0xA66F}, {0xA670, 0xA672}, {0xA673, 0xA673}, 854 | {0xA674, 0xA67D}, {0xA67E, 0xA67E}, {0xA67F, 0xA67F}, 855 | {0xA680, 0xA69B}, {0xA69C, 0xA69D}, {0xA69E, 0xA69F}, 856 | {0xA6A0, 0xA6E5}, {0xA6E6, 0xA6EF}, {0xA6F0, 0xA6F1}, 857 | {0xA6F2, 0xA6F7}, {0xA700, 0xA716}, {0xA717, 0xA71F}, 858 | {0xA720, 0xA721}, {0xA722, 0xA76F}, {0xA770, 0xA770}, 859 | {0xA771, 0xA787}, {0xA788, 0xA788}, {0xA789, 0xA78A}, 860 | {0xA78B, 0xA78E}, {0xA78F, 0xA78F}, {0xA790, 0xA7AE}, 861 | {0xA7B0, 0xA7B7}, {0xA7F7, 0xA7F7}, {0xA7F8, 0xA7F9}, 862 | {0xA7FA, 0xA7FA}, {0xA7FB, 0xA7FF}, {0xA800, 0xA801}, 863 | {0xA802, 0xA802}, {0xA803, 0xA805}, {0xA806, 0xA806}, 864 | {0xA807, 0xA80A}, {0xA80B, 0xA80B}, {0xA80C, 0xA822}, 865 | {0xA823, 0xA824}, {0xA825, 0xA826}, {0xA827, 0xA827}, 866 | {0xA828, 0xA82B}, {0xA830, 0xA835}, {0xA836, 0xA837}, 867 | {0xA838, 0xA838}, {0xA839, 0xA839}, {0xA840, 0xA873}, 868 | {0xA874, 0xA877}, {0xA880, 0xA881}, {0xA882, 0xA8B3}, 869 | {0xA8B4, 0xA8C3}, {0xA8C4, 0xA8C5}, {0xA8CE, 0xA8CF}, 870 | {0xA8D0, 0xA8D9}, {0xA8E0, 0xA8F1}, {0xA8F2, 0xA8F7}, 871 | {0xA8F8, 0xA8FA}, {0xA8FB, 0xA8FB}, {0xA8FC, 0xA8FC}, 872 | {0xA8FD, 0xA8FD}, {0xA900, 0xA909}, {0xA90A, 0xA925}, 873 | {0xA926, 0xA92D}, {0xA92E, 0xA92F}, {0xA930, 0xA946}, 874 | {0xA947, 0xA951}, {0xA952, 0xA953}, {0xA95F, 0xA95F}, 875 | {0xA980, 0xA982}, {0xA983, 0xA983}, {0xA984, 0xA9B2}, 876 | {0xA9B3, 0xA9B3}, {0xA9B4, 0xA9B5}, {0xA9B6, 0xA9B9}, 877 | {0xA9BA, 0xA9BB}, {0xA9BC, 0xA9BC}, {0xA9BD, 0xA9C0}, 878 | {0xA9C1, 0xA9CD}, {0xA9CF, 0xA9CF}, {0xA9D0, 0xA9D9}, 879 | {0xA9DE, 0xA9DF}, {0xA9E0, 0xA9E4}, {0xA9E5, 0xA9E5}, 880 | {0xA9E6, 0xA9E6}, {0xA9E7, 0xA9EF}, {0xA9F0, 0xA9F9}, 881 | {0xA9FA, 0xA9FE}, {0xAA00, 0xAA28}, {0xAA29, 0xAA2E}, 882 | {0xAA2F, 0xAA30}, {0xAA31, 0xAA32}, {0xAA33, 0xAA34}, 883 | {0xAA35, 0xAA36}, {0xAA40, 0xAA42}, {0xAA43, 0xAA43}, 884 | {0xAA44, 0xAA4B}, {0xAA4C, 0xAA4C}, {0xAA4D, 0xAA4D}, 885 | {0xAA50, 0xAA59}, {0xAA5C, 0xAA5F}, {0xAA60, 0xAA6F}, 886 | {0xAA70, 0xAA70}, {0xAA71, 0xAA76}, {0xAA77, 0xAA79}, 887 | {0xAA7A, 0xAA7A}, {0xAA7B, 0xAA7B}, {0xAA7C, 0xAA7C}, 888 | {0xAA7D, 0xAA7D}, {0xAA7E, 0xAA7F}, {0xAA80, 0xAAAF}, 889 | {0xAAB0, 0xAAB0}, {0xAAB1, 0xAAB1}, {0xAAB2, 0xAAB4}, 890 | {0xAAB5, 0xAAB6}, {0xAAB7, 0xAAB8}, {0xAAB9, 0xAABD}, 891 | {0xAABE, 0xAABF}, {0xAAC0, 0xAAC0}, {0xAAC1, 0xAAC1}, 892 | {0xAAC2, 0xAAC2}, {0xAADB, 0xAADC}, {0xAADD, 0xAADD}, 893 | {0xAADE, 0xAADF}, {0xAAE0, 0xAAEA}, {0xAAEB, 0xAAEB}, 894 | {0xAAEC, 0xAAED}, {0xAAEE, 0xAAEF}, {0xAAF0, 0xAAF1}, 895 | {0xAAF2, 0xAAF2}, {0xAAF3, 0xAAF4}, {0xAAF5, 0xAAF5}, 896 | {0xAAF6, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E}, 897 | {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E}, 898 | {0xAB30, 0xAB5A}, {0xAB5B, 0xAB5B}, {0xAB5C, 0xAB5F}, 899 | {0xAB60, 0xAB65}, {0xAB70, 0xABBF}, {0xABC0, 0xABE2}, 900 | {0xABE3, 0xABE4}, {0xABE5, 0xABE5}, {0xABE6, 0xABE7}, 901 | {0xABE8, 0xABE8}, {0xABE9, 0xABEA}, {0xABEB, 0xABEB}, 902 | {0xABEC, 0xABEC}, {0xABED, 0xABED}, {0xABF0, 0xABF9}, 903 | {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDB7F}, 904 | {0xDB80, 0xDBFF}, {0xDC00, 0xDFFF}, {0xFB00, 0xFB06}, 905 | {0xFB13, 0xFB17}, {0xFB1D, 0xFB1D}, {0xFB1E, 0xFB1E}, 906 | {0xFB1F, 0xFB28}, {0xFB29, 0xFB29}, {0xFB2A, 0xFB36}, 907 | {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41}, 908 | {0xFB43, 0xFB44}, {0xFB46, 0xFB4F}, {0xFB50, 0xFBB1}, 909 | {0xFBB2, 0xFBC1}, {0xFBD3, 0xFD3D}, {0xFD3E, 0xFD3E}, 910 | {0xFD3F, 0xFD3F}, {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, 911 | {0xFDF0, 0xFDFB}, {0xFDFC, 0xFDFC}, {0xFDFD, 0xFDFD}, 912 | {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC}, 913 | {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFC, 0xFFFC}, 914 | {0x10000, 0x1000B}, {0x1000D, 0x10026}, {0x10028, 0x1003A}, 915 | {0x1003C, 0x1003D}, {0x1003F, 0x1004D}, {0x10050, 0x1005D}, 916 | {0x10080, 0x100FA}, {0x10100, 0x10102}, {0x10107, 0x10133}, 917 | {0x10137, 0x1013F}, {0x10140, 0x10174}, {0x10175, 0x10178}, 918 | {0x10179, 0x10189}, {0x1018A, 0x1018B}, {0x1018C, 0x1018E}, 919 | {0x10190, 0x1019B}, {0x101A0, 0x101A0}, {0x101D0, 0x101FC}, 920 | {0x101FD, 0x101FD}, {0x10280, 0x1029C}, {0x102A0, 0x102D0}, 921 | {0x102E0, 0x102E0}, {0x102E1, 0x102FB}, {0x10300, 0x1031F}, 922 | {0x10320, 0x10323}, {0x10330, 0x10340}, {0x10341, 0x10341}, 923 | {0x10342, 0x10349}, {0x1034A, 0x1034A}, {0x10350, 0x10375}, 924 | {0x10376, 0x1037A}, {0x10380, 0x1039D}, {0x1039F, 0x1039F}, 925 | {0x103A0, 0x103C3}, {0x103C8, 0x103CF}, {0x103D0, 0x103D0}, 926 | {0x103D1, 0x103D5}, {0x10400, 0x1044F}, {0x10450, 0x1047F}, 927 | {0x10480, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3}, 928 | {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563}, 929 | {0x1056F, 0x1056F}, {0x10600, 0x10736}, {0x10740, 0x10755}, 930 | {0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808}, 931 | {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C}, 932 | {0x1083F, 0x1083F}, {0x10840, 0x10855}, {0x10857, 0x10857}, 933 | {0x10858, 0x1085F}, {0x10860, 0x10876}, {0x10877, 0x10878}, 934 | {0x10879, 0x1087F}, {0x10880, 0x1089E}, {0x108A7, 0x108AF}, 935 | {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x108FF}, 936 | {0x10900, 0x10915}, {0x10916, 0x1091B}, {0x1091F, 0x1091F}, 937 | {0x10920, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x1099F}, 938 | {0x109A0, 0x109B7}, {0x109BC, 0x109BD}, {0x109BE, 0x109BF}, 939 | {0x109C0, 0x109CF}, {0x109D2, 0x109FF}, {0x10A00, 0x10A00}, 940 | {0x10A01, 0x10A03}, {0x10A05, 0x10A06}, {0x10A0C, 0x10A0F}, 941 | {0x10A10, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A33}, 942 | {0x10A38, 0x10A3A}, {0x10A3F, 0x10A3F}, {0x10A40, 0x10A47}, 943 | {0x10A50, 0x10A58}, {0x10A60, 0x10A7C}, {0x10A7D, 0x10A7E}, 944 | {0x10A7F, 0x10A7F}, {0x10A80, 0x10A9C}, {0x10A9D, 0x10A9F}, 945 | {0x10AC0, 0x10AC7}, {0x10AC8, 0x10AC8}, {0x10AC9, 0x10AE4}, 946 | {0x10AE5, 0x10AE6}, {0x10AEB, 0x10AEF}, {0x10AF0, 0x10AF6}, 947 | {0x10B00, 0x10B35}, {0x10B39, 0x10B3F}, {0x10B40, 0x10B55}, 948 | {0x10B58, 0x10B5F}, {0x10B60, 0x10B72}, {0x10B78, 0x10B7F}, 949 | {0x10B80, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF}, 950 | {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2}, 951 | {0x10CFA, 0x10CFF}, {0x10E60, 0x10E7E}, {0x11000, 0x11000}, 952 | {0x11001, 0x11001}, {0x11002, 0x11002}, {0x11003, 0x11037}, 953 | {0x11038, 0x11046}, {0x11047, 0x1104D}, {0x11052, 0x11065}, 954 | {0x11066, 0x1106F}, {0x1107F, 0x1107F}, {0x11080, 0x11081}, 955 | {0x11082, 0x11082}, {0x11083, 0x110AF}, {0x110B0, 0x110B2}, 956 | {0x110B3, 0x110B6}, {0x110B7, 0x110B8}, {0x110B9, 0x110BA}, 957 | {0x110BB, 0x110BC}, {0x110BD, 0x110BD}, {0x110BE, 0x110C1}, 958 | {0x110D0, 0x110E8}, {0x110F0, 0x110F9}, {0x11100, 0x11102}, 959 | {0x11103, 0x11126}, {0x11127, 0x1112B}, {0x1112C, 0x1112C}, 960 | {0x1112D, 0x11134}, {0x11136, 0x1113F}, {0x11140, 0x11143}, 961 | {0x11150, 0x11172}, {0x11173, 0x11173}, {0x11174, 0x11175}, 962 | {0x11176, 0x11176}, {0x11180, 0x11181}, {0x11182, 0x11182}, 963 | {0x11183, 0x111B2}, {0x111B3, 0x111B5}, {0x111B6, 0x111BE}, 964 | {0x111BF, 0x111C0}, {0x111C1, 0x111C4}, {0x111C5, 0x111C9}, 965 | {0x111CA, 0x111CC}, {0x111CD, 0x111CD}, {0x111D0, 0x111D9}, 966 | {0x111DA, 0x111DA}, {0x111DB, 0x111DB}, {0x111DC, 0x111DC}, 967 | {0x111DD, 0x111DF}, {0x111E1, 0x111F4}, {0x11200, 0x11211}, 968 | {0x11213, 0x1122B}, {0x1122C, 0x1122E}, {0x1122F, 0x11231}, 969 | {0x11232, 0x11233}, {0x11234, 0x11234}, {0x11235, 0x11235}, 970 | {0x11236, 0x11237}, {0x11238, 0x1123D}, {0x1123E, 0x1123E}, 971 | {0x11280, 0x11286}, {0x11288, 0x11288}, {0x1128A, 0x1128D}, 972 | {0x1128F, 0x1129D}, {0x1129F, 0x112A8}, {0x112A9, 0x112A9}, 973 | {0x112B0, 0x112DE}, {0x112DF, 0x112DF}, {0x112E0, 0x112E2}, 974 | {0x112E3, 0x112EA}, {0x112F0, 0x112F9}, {0x11300, 0x11301}, 975 | {0x11302, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310}, 976 | {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333}, 977 | {0x11335, 0x11339}, {0x1133C, 0x1133C}, {0x1133D, 0x1133D}, 978 | {0x1133E, 0x1133F}, {0x11340, 0x11340}, {0x11341, 0x11344}, 979 | {0x11347, 0x11348}, {0x1134B, 0x1134D}, {0x11350, 0x11350}, 980 | {0x11357, 0x11357}, {0x1135D, 0x11361}, {0x11362, 0x11363}, 981 | {0x11366, 0x1136C}, {0x11370, 0x11374}, {0x11400, 0x11434}, 982 | {0x11435, 0x11437}, {0x11438, 0x1143F}, {0x11440, 0x11441}, 983 | {0x11442, 0x11444}, {0x11445, 0x11445}, {0x11446, 0x11446}, 984 | {0x11447, 0x1144A}, {0x1144B, 0x1144F}, {0x11450, 0x11459}, 985 | {0x1145B, 0x1145B}, {0x1145D, 0x1145D}, {0x11480, 0x114AF}, 986 | {0x114B0, 0x114B2}, {0x114B3, 0x114B8}, {0x114B9, 0x114B9}, 987 | {0x114BA, 0x114BA}, {0x114BB, 0x114BE}, {0x114BF, 0x114C0}, 988 | {0x114C1, 0x114C1}, {0x114C2, 0x114C3}, {0x114C4, 0x114C5}, 989 | {0x114C6, 0x114C6}, {0x114C7, 0x114C7}, {0x114D0, 0x114D9}, 990 | {0x11580, 0x115AE}, {0x115AF, 0x115B1}, {0x115B2, 0x115B5}, 991 | {0x115B8, 0x115BB}, {0x115BC, 0x115BD}, {0x115BE, 0x115BE}, 992 | {0x115BF, 0x115C0}, {0x115C1, 0x115D7}, {0x115D8, 0x115DB}, 993 | {0x115DC, 0x115DD}, {0x11600, 0x1162F}, {0x11630, 0x11632}, 994 | {0x11633, 0x1163A}, {0x1163B, 0x1163C}, {0x1163D, 0x1163D}, 995 | {0x1163E, 0x1163E}, {0x1163F, 0x11640}, {0x11641, 0x11643}, 996 | {0x11644, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C}, 997 | {0x11680, 0x116AA}, {0x116AB, 0x116AB}, {0x116AC, 0x116AC}, 998 | {0x116AD, 0x116AD}, {0x116AE, 0x116AF}, {0x116B0, 0x116B5}, 999 | {0x116B6, 0x116B6}, {0x116B7, 0x116B7}, {0x116C0, 0x116C9}, 1000 | {0x11700, 0x11719}, {0x1171D, 0x1171F}, {0x11720, 0x11721}, 1001 | {0x11722, 0x11725}, {0x11726, 0x11726}, {0x11727, 0x1172B}, 1002 | {0x11730, 0x11739}, {0x1173A, 0x1173B}, {0x1173C, 0x1173E}, 1003 | {0x1173F, 0x1173F}, {0x118A0, 0x118DF}, {0x118E0, 0x118E9}, 1004 | {0x118EA, 0x118F2}, {0x118FF, 0x118FF}, {0x11AC0, 0x11AF8}, 1005 | {0x11C00, 0x11C08}, {0x11C0A, 0x11C2E}, {0x11C2F, 0x11C2F}, 1006 | {0x11C30, 0x11C36}, {0x11C38, 0x11C3D}, {0x11C3E, 0x11C3E}, 1007 | {0x11C3F, 0x11C3F}, {0x11C40, 0x11C40}, {0x11C41, 0x11C45}, 1008 | {0x11C50, 0x11C59}, {0x11C5A, 0x11C6C}, {0x11C70, 0x11C71}, 1009 | {0x11C72, 0x11C8F}, {0x11C92, 0x11CA7}, {0x11CA9, 0x11CA9}, 1010 | {0x11CAA, 0x11CB0}, {0x11CB1, 0x11CB1}, {0x11CB2, 0x11CB3}, 1011 | {0x11CB4, 0x11CB4}, {0x11CB5, 0x11CB6}, {0x12000, 0x12399}, 1012 | {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543}, 1013 | {0x13000, 0x1342E}, {0x14400, 0x14646}, {0x16800, 0x16A38}, 1014 | {0x16A40, 0x16A5E}, {0x16A60, 0x16A69}, {0x16A6E, 0x16A6F}, 1015 | {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF4}, {0x16AF5, 0x16AF5}, 1016 | {0x16B00, 0x16B2F}, {0x16B30, 0x16B36}, {0x16B37, 0x16B3B}, 1017 | {0x16B3C, 0x16B3F}, {0x16B40, 0x16B43}, {0x16B44, 0x16B44}, 1018 | {0x16B45, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61}, 1019 | {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16F00, 0x16F44}, 1020 | {0x16F50, 0x16F50}, {0x16F51, 0x16F7E}, {0x16F8F, 0x16F92}, 1021 | {0x16F93, 0x16F9F}, {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, 1022 | {0x1BC80, 0x1BC88}, {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BC9C}, 1023 | {0x1BC9D, 0x1BC9E}, {0x1BC9F, 0x1BC9F}, {0x1BCA0, 0x1BCA3}, 1024 | {0x1D000, 0x1D0F5}, {0x1D100, 0x1D126}, {0x1D129, 0x1D164}, 1025 | {0x1D165, 0x1D166}, {0x1D167, 0x1D169}, {0x1D16A, 0x1D16C}, 1026 | {0x1D16D, 0x1D172}, {0x1D173, 0x1D17A}, {0x1D17B, 0x1D182}, 1027 | {0x1D183, 0x1D184}, {0x1D185, 0x1D18B}, {0x1D18C, 0x1D1A9}, 1028 | {0x1D1AA, 0x1D1AD}, {0x1D1AE, 0x1D1E8}, {0x1D200, 0x1D241}, 1029 | {0x1D242, 0x1D244}, {0x1D245, 0x1D245}, {0x1D300, 0x1D356}, 1030 | {0x1D360, 0x1D371}, {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, 1031 | {0x1D49E, 0x1D49F}, {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, 1032 | {0x1D4A9, 0x1D4AC}, {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, 1033 | {0x1D4BD, 0x1D4C3}, {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, 1034 | {0x1D50D, 0x1D514}, {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, 1035 | {0x1D53B, 0x1D53E}, {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, 1036 | {0x1D54A, 0x1D550}, {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D6C0}, 1037 | {0x1D6C1, 0x1D6C1}, {0x1D6C2, 0x1D6DA}, {0x1D6DB, 0x1D6DB}, 1038 | {0x1D6DC, 0x1D6FA}, {0x1D6FB, 0x1D6FB}, {0x1D6FC, 0x1D714}, 1039 | {0x1D715, 0x1D715}, {0x1D716, 0x1D734}, {0x1D735, 0x1D735}, 1040 | {0x1D736, 0x1D74E}, {0x1D74F, 0x1D74F}, {0x1D750, 0x1D76E}, 1041 | {0x1D76F, 0x1D76F}, {0x1D770, 0x1D788}, {0x1D789, 0x1D789}, 1042 | {0x1D78A, 0x1D7A8}, {0x1D7A9, 0x1D7A9}, {0x1D7AA, 0x1D7C2}, 1043 | {0x1D7C3, 0x1D7C3}, {0x1D7C4, 0x1D7CB}, {0x1D7CE, 0x1D7FF}, 1044 | {0x1D800, 0x1D9FF}, {0x1DA00, 0x1DA36}, {0x1DA37, 0x1DA3A}, 1045 | {0x1DA3B, 0x1DA6C}, {0x1DA6D, 0x1DA74}, {0x1DA75, 0x1DA75}, 1046 | {0x1DA76, 0x1DA83}, {0x1DA84, 0x1DA84}, {0x1DA85, 0x1DA86}, 1047 | {0x1DA87, 0x1DA8B}, {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, 1048 | {0x1E000, 0x1E006}, {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, 1049 | {0x1E023, 0x1E024}, {0x1E026, 0x1E02A}, {0x1E800, 0x1E8C4}, 1050 | {0x1E8C7, 0x1E8CF}, {0x1E8D0, 0x1E8D6}, {0x1E900, 0x1E943}, 1051 | {0x1E944, 0x1E94A}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F}, 1052 | {0x1EE00, 0x1EE03}, {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, 1053 | {0x1EE24, 0x1EE24}, {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, 1054 | {0x1EE34, 0x1EE37}, {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, 1055 | {0x1EE42, 0x1EE42}, {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, 1056 | {0x1EE4B, 0x1EE4B}, {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, 1057 | {0x1EE54, 0x1EE54}, {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, 1058 | {0x1EE5B, 0x1EE5B}, {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, 1059 | {0x1EE61, 0x1EE62}, {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, 1060 | {0x1EE6C, 0x1EE72}, {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, 1061 | {0x1EE7E, 0x1EE7E}, {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, 1062 | {0x1EEA1, 0x1EEA3}, {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, 1063 | {0x1EEF0, 0x1EEF1}, {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, 1064 | {0x1F030, 0x1F093}, {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, 1065 | {0x1F0C1, 0x1F0CE}, {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10C}, 1066 | {0x1F12E, 0x1F12E}, {0x1F16A, 0x1F16B}, {0x1F1E6, 0x1F1FF}, 1067 | {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D}, 1068 | {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF}, 1069 | {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F}, 1070 | {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A}, 1071 | {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594}, 1072 | {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F}, 1073 | {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6E0, 0x1F6EA}, 1074 | {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773}, {0x1F780, 0x1F7D4}, 1075 | {0x1F800, 0x1F80B}, {0x1F810, 0x1F847}, {0x1F850, 0x1F859}, 1076 | {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD}, {0xE0001, 0xE0001}, 1077 | {0xE0020, 0xE007F}, 1078 | } 1079 | 1080 | // Condition have flag EastAsianWidth whether the current locale is CJK or not. 1081 | type Condition struct { 1082 | EastAsianWidth bool 1083 | } 1084 | 1085 | // NewCondition return new instance of Condition which is current locale. 1086 | func NewCondition() *Condition { 1087 | return &Condition{EastAsianWidth} 1088 | } 1089 | 1090 | // RuneWidth returns the number of cells in r. 1091 | // See http://www.unicode.org/reports/tr11/ 1092 | func (c *Condition) RuneWidth(r rune) int { 1093 | switch { 1094 | case r < 0 || r > 0x10FFFF || 1095 | inTables(r, nonprint, combining, notassigned): 1096 | return 0 1097 | case (c.EastAsianWidth && IsAmbiguousWidth(r)) || 1098 | inTables(r, doublewidth, emoji): 1099 | return 2 1100 | default: 1101 | return 1 1102 | } 1103 | } 1104 | 1105 | // StringWidth return width as you can see 1106 | func (c *Condition) StringWidth(s string) (width int) { 1107 | for _, r := range []rune(s) { 1108 | width += c.RuneWidth(r) 1109 | } 1110 | return width 1111 | } 1112 | 1113 | // Truncate return string truncated with w cells 1114 | func (c *Condition) Truncate(s string, w int, tail string) string { 1115 | if c.StringWidth(s) <= w { 1116 | return s 1117 | } 1118 | r := []rune(s) 1119 | tw := c.StringWidth(tail) 1120 | w -= tw 1121 | width := 0 1122 | i := 0 1123 | for ; i < len(r); i++ { 1124 | cw := c.RuneWidth(r[i]) 1125 | if width+cw > w { 1126 | break 1127 | } 1128 | width += cw 1129 | } 1130 | return string(r[0:i]) + tail 1131 | } 1132 | 1133 | // Wrap return string wrapped with w cells 1134 | func (c *Condition) Wrap(s string, w int) string { 1135 | width := 0 1136 | out := "" 1137 | for _, r := range []rune(s) { 1138 | cw := RuneWidth(r) 1139 | if r == '\n' { 1140 | out += string(r) 1141 | width = 0 1142 | continue 1143 | } else if width+cw > w { 1144 | out += "\n" 1145 | width = 0 1146 | out += string(r) 1147 | width += cw 1148 | continue 1149 | } 1150 | out += string(r) 1151 | width += cw 1152 | } 1153 | return out 1154 | } 1155 | 1156 | // FillLeft return string filled in left by spaces in w cells 1157 | func (c *Condition) FillLeft(s string, w int) string { 1158 | width := c.StringWidth(s) 1159 | count := w - width 1160 | if count > 0 { 1161 | b := make([]byte, count) 1162 | for i := range b { 1163 | b[i] = ' ' 1164 | } 1165 | return string(b) + s 1166 | } 1167 | return s 1168 | } 1169 | 1170 | // FillRight return string filled in left by spaces in w cells 1171 | func (c *Condition) FillRight(s string, w int) string { 1172 | width := c.StringWidth(s) 1173 | count := w - width 1174 | if count > 0 { 1175 | b := make([]byte, count) 1176 | for i := range b { 1177 | b[i] = ' ' 1178 | } 1179 | return s + string(b) 1180 | } 1181 | return s 1182 | } 1183 | 1184 | // RuneWidth returns the number of cells in r. 1185 | // See http://www.unicode.org/reports/tr11/ 1186 | func RuneWidth(r rune) int { 1187 | return DefaultCondition.RuneWidth(r) 1188 | } 1189 | 1190 | // IsAmbiguousWidth returns whether is ambiguous width or not. 1191 | func IsAmbiguousWidth(r rune) bool { 1192 | return inTables(r, private, ambiguous) 1193 | } 1194 | 1195 | // IsNeutralWidth returns whether is neutral width or not. 1196 | func IsNeutralWidth(r rune) bool { 1197 | return inTable(r, neutral) 1198 | } 1199 | 1200 | // StringWidth return width as you can see 1201 | func StringWidth(s string) (width int) { 1202 | return DefaultCondition.StringWidth(s) 1203 | } 1204 | 1205 | // Truncate return string truncated with w cells 1206 | func Truncate(s string, w int, tail string) string { 1207 | return DefaultCondition.Truncate(s, w, tail) 1208 | } 1209 | 1210 | // Wrap return string wrapped with w cells 1211 | func Wrap(s string, w int) string { 1212 | return DefaultCondition.Wrap(s, w) 1213 | } 1214 | 1215 | // FillLeft return string filled in left by spaces in w cells 1216 | func FillLeft(s string, w int) string { 1217 | return DefaultCondition.FillLeft(s, w) 1218 | } 1219 | 1220 | // FillRight return string filled in left by spaces in w cells 1221 | func FillRight(s string, w int) string { 1222 | return DefaultCondition.FillRight(s, w) 1223 | } 1224 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-runewidth/runewidth_js.go: -------------------------------------------------------------------------------- 1 | // +build js 2 | 3 | package runewidth 4 | 5 | func IsEastAsian() bool { 6 | // TODO: Implement this for the web. Detect east asian in a compatible way, and return true. 7 | return false 8 | } 9 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-runewidth/runewidth_posix.go: -------------------------------------------------------------------------------- 1 | // +build !windows,!js 2 | 3 | package runewidth 4 | 5 | import ( 6 | "os" 7 | "regexp" 8 | "strings" 9 | ) 10 | 11 | var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`) 12 | 13 | var mblenTable = map[string]int{ 14 | "utf-8": 6, 15 | "utf8": 6, 16 | "jis": 8, 17 | "eucjp": 3, 18 | "euckr": 2, 19 | "euccn": 2, 20 | "sjis": 2, 21 | "cp932": 2, 22 | "cp51932": 2, 23 | "cp936": 2, 24 | "cp949": 2, 25 | "cp950": 2, 26 | "big5": 2, 27 | "gbk": 2, 28 | "gb2312": 2, 29 | } 30 | 31 | func isEastAsian(locale string) bool { 32 | charset := strings.ToLower(locale) 33 | r := reLoc.FindStringSubmatch(locale) 34 | if len(r) == 2 { 35 | charset = strings.ToLower(r[1]) 36 | } 37 | 38 | if strings.HasSuffix(charset, "@cjk_narrow") { 39 | return false 40 | } 41 | 42 | for pos, b := range []byte(charset) { 43 | if b == '@' { 44 | charset = charset[:pos] 45 | break 46 | } 47 | } 48 | max := 1 49 | if m, ok := mblenTable[charset]; ok { 50 | max = m 51 | } 52 | if max > 1 && (charset[0] != 'u' || 53 | strings.HasPrefix(locale, "ja") || 54 | strings.HasPrefix(locale, "ko") || 55 | strings.HasPrefix(locale, "zh")) { 56 | return true 57 | } 58 | return false 59 | } 60 | 61 | // IsEastAsian return true if the current locale is CJK 62 | func IsEastAsian() bool { 63 | locale := os.Getenv("LC_CTYPE") 64 | if locale == "" { 65 | locale = os.Getenv("LANG") 66 | } 67 | 68 | // ignore C locale 69 | if locale == "POSIX" || locale == "C" { 70 | return false 71 | } 72 | if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') { 73 | return false 74 | } 75 | 76 | return isEastAsian(locale) 77 | } 78 | -------------------------------------------------------------------------------- /vendor/github.com/mattn/go-runewidth/runewidth_windows.go: -------------------------------------------------------------------------------- 1 | package runewidth 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | var ( 8 | kernel32 = syscall.NewLazyDLL("kernel32") 9 | procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP") 10 | ) 11 | 12 | // IsEastAsian return true if the current locale is CJK 13 | func IsEastAsian() bool { 14 | r1, _, _ := procGetConsoleOutputCP.Call() 15 | if r1 == 0 { 16 | return false 17 | } 18 | 19 | switch int(r1) { 20 | case 932, 51932, 936, 949, 950: 21 | return true 22 | } 23 | 24 | return false 25 | } 26 | -------------------------------------------------------------------------------- /vendor/manifest: -------------------------------------------------------------------------------- 1 | { 2 | "version": 0, 3 | "dependencies": [ 4 | { 5 | "importpath": "github.com/mattn/go-runewidth", 6 | "repository": "https://github.com/mattn/go-runewidth", 7 | "vcs": "git", 8 | "revision": "14207d285c6c197daabb5c9793d63e7af9ab2d50", 9 | "branch": "master", 10 | "notests": true 11 | } 12 | ] 13 | } --------------------------------------------------------------------------------