diff --git a/cmd/bundle/main.go b/cmd/bundle/main.go
index 1db85beafc..a6cc065905 100644
--- a/cmd/bundle/main.go
+++ b/cmd/bundle/main.go
@@ -18,6 +18,7 @@ import (
"github.com/shipwright-io/build/pkg/bundle"
"github.com/shipwright-io/build/pkg/image"
+ "github.com/shipwright-io/build/pkg/util"
)
type settings struct {
@@ -28,6 +29,7 @@ type settings struct {
secretPath string
resultFileImageDigest string
resultFileSourceTimestamp string
+ showListing bool
}
var flagValues settings
@@ -44,6 +46,7 @@ func init() {
pflag.StringVar(&flagValues.secretPath, "secret-path", "", "A directory that contains access credentials (optional)")
pflag.BoolVar(&flagValues.prune, "prune", false, "Delete bundle image from registry after it was pulled")
+ pflag.BoolVar(&flagValues.showListing, "show-listing", false, "Print file listing of files unpacked from the bundle")
}
func main() {
@@ -57,6 +60,10 @@ func Do(ctx context.Context) error {
flagValues = settings{}
pflag.Parse()
+ if val, ok := os.LookupEnv("BUNDLE_SHOW_LISTING"); ok {
+ flagValues.showListing, _ = strconv.ParseBool(val)
+ }
+
if flagValues.help {
pflag.Usage()
return nil
@@ -96,6 +103,10 @@ func Do(ctx context.Context) error {
}
log.Printf("Image content was extracted to %s\n", flagValues.target)
+ if flagValues.showListing {
+ // ignore any errors when walking through the file system, the listing is only for informational purposes
+ _ = util.ListFiles(log.Writer(), flagValues.target)
+ }
digest, err := img.Digest()
if err != nil {
diff --git a/cmd/bundle/main_test.go b/cmd/bundle/main_test.go
index d1cc01ae83..fe083452bb 100644
--- a/cmd/bundle/main_test.go
+++ b/cmd/bundle/main_test.go
@@ -7,7 +7,6 @@ package main_test
import (
"context"
"fmt"
- "io"
"log"
"net/http/httptest"
"net/url"
@@ -34,8 +33,7 @@ var _ = Describe("Bundle Loader", func() {
const exampleImage = "ghcr.io/shipwright-io/sample-go/source-bundle:latest"
run := func(args ...string) error {
- // discard log output
- log.SetOutput(io.Discard)
+ log.SetOutput(GinkgoWriter)
// discard stderr output
var tmp = os.Stderr
@@ -316,4 +314,16 @@ var _ = Describe("Bundle Loader", func() {
})
})
})
+
+ Context("Using show listing flag", func() {
+ It("should run without issues", func() {
+ withTempDir(func(target string) {
+ Expect(run(
+ "--image", exampleImage,
+ "--target", target,
+ "--show-listing",
+ )).To(Succeed())
+ })
+ })
+ })
})
diff --git a/cmd/git/main.go b/cmd/git/main.go
index c1d0e6851b..ace87b77a7 100644
--- a/cmd/git/main.go
+++ b/cmd/git/main.go
@@ -13,9 +13,11 @@ import (
"os/exec"
"path/filepath"
"regexp"
+ "strconv"
"strings"
shpgit "github.com/shipwright-io/build/pkg/git"
+ "github.com/shipwright-io/build/pkg/util"
"github.com/spf13/pflag"
)
@@ -61,6 +63,7 @@ type settings struct {
resultFileErrorMessage string
resultFileErrorReason string
verbose bool
+ showListing bool
}
var flagValues settings
@@ -99,6 +102,7 @@ func init() {
pflag.BoolVar(&flagValues.skipValidation, "skip-validation", false, "skip pre-requisite validation")
pflag.BoolVar(&flagValues.gitURLRewrite, "git-url-rewrite", false, "set Git config to use url-insteadOf setting based on Git repository URL")
pflag.BoolVar(&flagValues.verbose, "verbose", false, "Verbose logging")
+ pflag.BoolVar(&flagValues.showListing, "show-listing", false, "Print file listing of files cloned from the Git repository")
}
func main() {
@@ -123,6 +127,10 @@ func Execute(ctx context.Context) error {
flagValues = settings{depth: 1}
pflag.Parse()
+ if val, ok := os.LookupEnv("GIT_SHOW_LISTING"); ok {
+ flagValues.showListing, _ = strconv.ParseBool(val)
+ }
+
if flagValues.help {
pflag.Usage()
return nil
@@ -160,6 +168,11 @@ func runGitClone(ctx context.Context) error {
return err
}
+ if flagValues.showListing {
+ // ignore any errors when walking through the file system, the listing is only for informational purposes
+ _ = util.ListFiles(log.Writer(), flagValues.target)
+ }
+
if flagValues.resultFileCommitSha != "" {
output, err := git(ctx, "-C", flagValues.target, "rev-parse", "--verify", "HEAD")
if err != nil {
diff --git a/cmd/git/main_test.go b/cmd/git/main_test.go
index 8ceeaacb25..2de6522197 100644
--- a/cmd/git/main_test.go
+++ b/cmd/git/main_test.go
@@ -668,4 +668,21 @@ var _ = Describe("Git Resource", func() {
})
})
})
+
+ Context("Using show listing flag", func() {
+ const exampleRepo = "https://github.com/shipwright-io/sample-go"
+
+ It("should run without issues", func() {
+ withTempDir(func(target string) {
+ Expect(run(
+ withLogOutput(io.Discard),
+ withArgs(
+ "--url", exampleRepo,
+ "--target", target,
+ "--show-listing",
+ ),
+ )).To(Succeed())
+ })
+ })
+ })
})
diff --git a/go.mod b/go.mod
index 745500c6e0..857269e533 100644
--- a/go.mod
+++ b/go.mod
@@ -8,6 +8,7 @@ require (
github.com/go-logr/logr v1.4.2
github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/go-containerregistry v0.20.2
+ github.com/jedib0t/go-pretty/v6 v6.6.1
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
github.com/onsi/ginkgo/v2 v2.20.2
github.com/onsi/gomega v1.34.2
@@ -43,7 +44,7 @@ require (
github.com/cloudflare/circl v1.3.7 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
- github.com/davecgh/go-spew v1.1.1 // indirect
+ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/docker/distribution v2.8.2+incompatible // indirect
github.com/docker/docker-credential-helpers v0.7.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
@@ -80,6 +81,7 @@ require (
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
+ github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
@@ -87,9 +89,11 @@ require (
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pjbgf/sha1cd v0.3.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
+ github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/statsd_exporter v0.22.7 // indirect
+ github.com/rivo/uniseg v0.2.0 // indirect
github.com/rogpeppe/go-internal v1.12.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
diff --git a/go.sum b/go.sum
index 946b8f0376..20f66dfca4 100644
--- a/go.sum
+++ b/go.sum
@@ -90,8 +90,9 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3
github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg=
github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
+github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/docker/cli v27.3.1+incompatible h1:qEGdFBF3Xu6SCvCYhc7CzaQTlBmqDuzxPDpigSyeKQQ=
github.com/docker/cli v27.3.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
@@ -251,6 +252,8 @@ github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A=
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
+github.com/jedib0t/go-pretty/v6 v6.6.1 h1:iJ65Xjb680rHcikRj6DSIbzCex2huitmc7bDtxYVWyc=
+github.com/jedib0t/go-pretty/v6 v6.6.1/go.mod h1:zbn98qrYlh95FIhwwsbIip0LYpwSG8SUOScs+v9/t0E=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
@@ -287,6 +290,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
+github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
+github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
@@ -315,8 +320,9 @@ github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
+github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
@@ -350,6 +356,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/statsd_exporter v0.22.7 h1:7Pji/i2GuhK6Lu7DHrtTkFmNBCudCPT1pX2CziuyQR0=
github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI=
+github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
+github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
diff --git a/pkg/util/filelisting.go b/pkg/util/filelisting.go
new file mode 100644
index 0000000000..de89c52a72
--- /dev/null
+++ b/pkg/util/filelisting.go
@@ -0,0 +1,115 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+package util
+
+import (
+ "fmt"
+ "io"
+ "io/fs"
+ "log"
+ "os"
+ "path/filepath"
+ "strconv"
+ "strings"
+ "syscall"
+
+ "github.com/jedib0t/go-pretty/v6/table"
+ "github.com/jedib0t/go-pretty/v6/text"
+)
+
+// ListFiles prints all files in a given directory to the provided writer
+func ListFiles(w io.Writer, dir string) error {
+ t := table.NewWriter()
+ defer t.Render()
+
+ t.SetOutputMirror(w)
+ t.SetColumnConfigs([]table.ColumnConfig{{Number: 5, Align: text.AlignRight}})
+ t.Style().Options.DrawBorder = false
+ t.Style().Options.SeparateColumns = false
+
+ return filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
+ if err != nil {
+ log.Printf("ignoring error for path %s: %v\n", path, err)
+ return nil
+ }
+
+ // Omit the target path itself
+ if path == dir {
+ return nil
+ }
+
+ // use relative paths to keep output compact
+ if l, err := filepath.Rel(dir, path); err == nil {
+ path = l
+ }
+
+ // if possible, try to obtain nlink count and user/group details
+ var nlink, user, group string = "?", "?", "?"
+ if stat, ok := info.Sys().(*syscall.Stat_t); ok {
+ user = strconv.FormatUint(uint64(stat.Uid), 10)
+ group = strconv.FormatUint(uint64(stat.Gid), 10)
+ nlink = strconv.FormatUint(uint64(stat.Nlink), 10)
+ }
+
+ t.AppendRow(table.Row{
+ filemode(info),
+ nlink,
+ user,
+ group,
+ humanReadableSize(info.Size()),
+ path,
+ })
+
+ return nil
+ })
+}
+
+// filemode is a minimal effort function to translate os.FileMode to the
+// commonly known human representation, i.e. rw-r--r--. However, it does
+// not implement all features such as sticky bits.
+func filemode(info fs.FileInfo) string {
+ var translate = func(i os.FileMode) string {
+ var result = []rune{'-', '-', '-'}
+ if i&0x1 != 0 {
+ result[2] = 'x'
+ }
+
+ if i&0x2 != 0 {
+ result[1] = 'w'
+ }
+
+ if i&0x4 != 0 {
+ result[0] = 'r'
+ }
+
+ return string(result)
+ }
+
+ var dirBit = func(i os.FileMode) string {
+ if i&fs.ModeDir != 0 {
+ return "d"
+ }
+
+ return "-"
+ }
+
+ var mode = info.Mode()
+ return dirBit(mode) + translate((mode>>6)&0x7) + translate((mode>>3)&0x7) + translate(mode&0x7)
+}
+
+// humanReadableSize is a minimal effort function to return a human readable
+// size of the given number of bytes in a compact form
+func humanReadableSize(bytes int64) string {
+ value := float64(bytes)
+
+ var mods = []string{"B", "K", "M", "G", "T"}
+ var i int
+ for value > 1023.9 {
+ value /= 1024.0
+ i++
+ }
+
+ return strings.TrimRight(fmt.Sprintf("%.1f", value), ".0") + mods[i]
+}
diff --git a/pkg/util/util.go b/pkg/util/util.go
new file mode 100644
index 0000000000..ee7e5bceb0
--- /dev/null
+++ b/pkg/util/util.go
@@ -0,0 +1,6 @@
+// Copyright The Shipwright Contributors
+//
+// SPDX-License-Identifier: Apache-2.0
+
+// package util contains helper and utility functions
+package util
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/LICENSE b/vendor/github.com/jedib0t/go-pretty/v6/LICENSE
new file mode 100644
index 0000000000..a284bc6305
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2018 jedib0t
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/README.md b/vendor/github.com/jedib0t/go-pretty/v6/table/README.md
new file mode 100644
index 0000000000..3375bd3c7d
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/README.md
@@ -0,0 +1,452 @@
+# Table
+[![Go Reference](https://pkg.go.dev/badge/github.com/jedib0t/go-pretty/v6/table.svg)](https://pkg.go.dev/github.com/jedib0t/go-pretty/v6/table)
+
+Pretty-print tables into ASCII/Unicode strings.
+
+ - Add Rows one-by-one or as a group (`AppendRow`/`AppendRows`)
+ - Add Header(s) and Footer(s) (`AppendHeader`/`AppendFooter`)
+ - Add a Separator manually after any Row (`AppendSeparator`)
+ - Auto Index Rows (1, 2, 3 ...) and Columns (A, B, C, ...) (`SetAutoIndex`)
+ - Auto Merge (_not supported in CSV/Markdown/TSV modes_)
+ - Cells in a Row (`RowConfig.AutoMerge`)
+ - Columns (`ColumnConfig.AutoMerge`) (_not supported in HTML mode_)
+ - Limit the length of
+ - Rows (`SetAllowedRowLength`)
+ - Columns (`ColumnConfig.Width*`)
+ - Auto-size Rows (`Style().Size.WidthMin` and `Style().Size.WidthMax`)
+ - Page results by a specified number of Lines (`SetPageSize`)
+ - Alignment - Horizontal & Vertical
+ - Auto (horizontal) Align (numeric columns aligned Right)
+ - Custom (horizontal) Align per column (`ColumnConfig.Align*`)
+ - Custom (vertical) VAlign per column with multi-line cell support (`ColumnConfig.VAlign*`)
+ - Mirror output to an `io.Writer` (ex. `os.StdOut`) (`SetOutputMirror`)
+ - Sort by one or more Columns (`SortBy`)
+ - Suppress/hide columns with no content (`SuppressEmptyColumns`)
+ - Suppress trailing spaces in the last column (`SupressTrailingSpaces`)
+ - Customizable Cell rendering per Column (`ColumnConfig.Transformer*`)
+ - Hide any columns that you don't want displayed (`ColumnConfig.Hidden`)
+ - Reset Headers/Rows/Footers at will to reuse the same Table Writer (`Reset*`)
+ - Completely customizable styles (`SetStyle`/`Style`)
+ - Many ready-to-use styles: [style.go](style.go)
+ - Colorize Headers/Body/Footers using [../text/color.go](../text/color.go)
+ - Custom text-case for Headers/Body/Footers
+ - Enable separators between each row
+ - Render table without a Border
+ - and a lot more...
+ - Render as:
+ - (ASCII/Unicode) Table
+ - CSV
+ - HTML Table (with custom CSS Class)
+ - Markdown Table
+ - TSV
+
+```
++---------------------------------------------------------------------+
+| Game of Thrones +
++-----+------------+-----------+--------+-----------------------------+
+| # | FIRST NAME | LAST NAME | SALARY | |
++-----+------------+-----------+--------+-----------------------------+
+| 1 | Arya | Stark | 3000 | |
+| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
+| 300 | Tyrion | Lannister | 5000 | |
++-----+------------+-----------+--------+-----------------------------+
+| | | TOTAL | 10000 | |
++-----+------------+-----------+--------+-----------------------------+
+```
+
+A demonstration of all the capabilities can be found here:
+[../cmd/demo-table](../cmd/demo-table)
+
+If you want very specific examples, read ahead.
+
+**Hint**: I've tried to ensure that almost all supported use-cases are covered
+by unit-tests and that they print the table rendered. Run
+`go test -v github.com/jedib0t/go-pretty/v6/table` to see the test outputs and
+help you figure out how to do something.
+
+# Examples
+
+All the examples below are going to start with the following block, although
+nothing except a single Row is mandatory for the `Render()` function to render
+something:
+```golang
+package main
+
+import (
+ "os"
+
+ "github.com/jedib0t/go-pretty/v6/table"
+)
+
+func main() {
+ t := table.NewWriter()
+ t.SetOutputMirror(os.Stdout)
+ t.AppendHeader(table.Row{"#", "First Name", "Last Name", "Salary"})
+ t.AppendRows([]table.Row{
+ {1, "Arya", "Stark", 3000},
+ {20, "Jon", "Snow", 2000, "You know nothing, Jon Snow!"},
+ })
+ t.AppendSeparator()
+ t.AppendRow([]interface{}{300, "Tyrion", "Lannister", 5000})
+ t.AppendFooter(table.Row{"", "", "Total", 10000})
+ t.Render()
+}
+```
+Running the above will result in:
+```
++-----+------------+-----------+--------+-----------------------------+
+| # | FIRST NAME | LAST NAME | SALARY | |
++-----+------------+-----------+--------+-----------------------------+
+| 1 | Arya | Stark | 3000 | |
+| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
++-----+------------+-----------+--------+-----------------------------+
+| 300 | Tyrion | Lannister | 5000 | |
++-----+------------+-----------+--------+-----------------------------+
+| | | TOTAL | 10000 | |
++-----+------------+-----------+--------+-----------------------------+
+```
+
+## Styles
+
+You can customize almost every single thing about the table above. The previous
+example just defaulted to `StyleDefault` during `Render()`. You can use a
+ready-to-use style (as in [style.go](style.go)) or customize it as you want.
+
+### Ready-to-use Styles
+
+Table comes with a bunch of ready-to-use Styles that make the table look really
+good. Set or Change the style using:
+```golang
+ t.SetStyle(table.StyleLight)
+ t.Render()
+```
+to get:
+```
+┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+│ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+│ 1 │ Arya │ Stark │ 3000 │ │
+│ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+│ 300 │ Tyrion │ Lannister │ 5000 │ │
+├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+│ │ │ TOTAL │ 10000 │ │
+└─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+```
+
+Or if you want to use a full-color mode, and don't care for boxes, use:
+```golang
+ t.SetStyle(table.StyleColoredBright)
+ t.Render()
+```
+to get:
+
+
+
+### Roll your own Style
+
+You can also roll your own style:
+```golang
+ t.SetStyle(table.Style{
+ Name: "myNewStyle",
+ Box: table.BoxStyle{
+ BottomLeft: "\\",
+ BottomRight: "/",
+ BottomSeparator: "v",
+ Left: "[",
+ LeftSeparator: "{",
+ MiddleHorizontal: "-",
+ MiddleSeparator: "+",
+ MiddleVertical: "|",
+ PaddingLeft: "<",
+ PaddingRight: ">",
+ Right: "]",
+ RightSeparator: "}",
+ TopLeft: "(",
+ TopRight: ")",
+ TopSeparator: "^",
+ UnfinishedRow: " ~~~",
+ },
+ Color: table.ColorOptions{
+ IndexColumn: text.Colors{text.BgCyan, text.FgBlack},
+ Footer: text.Colors{text.BgCyan, text.FgBlack},
+ Header: text.Colors{text.BgHiCyan, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ },
+ Format: table.FormatOptions{
+ Footer: text.FormatUpper,
+ Header: text.FormatUpper,
+ Row: text.FormatDefault,
+ },
+ Options: table.Options{
+ DrawBorder: true,
+ SeparateColumns: true,
+ SeparateFooter: true,
+ SeparateHeader: true,
+ SeparateRows: false,
+ },
+ })
+```
+
+Or you can use one of the ready-to-use Styles, and just make a few tweaks:
+```golang
+ t.SetStyle(table.StyleLight)
+ t.Style().Color.Header = text.Colors{text.BgHiCyan, text.FgBlack}
+ t.Style().Color.IndexColumn = text.Colors{text.BgHiCyan, text.FgBlack}
+ t.Style().Format.Footer = text.FormatLower
+ t.Style().Options.DrawBorder = false
+```
+
+## Auto-Merge
+
+You can auto-merge cells horizontally and vertically, but you have request for
+it specifically for each row/column using `RowConfig` or `ColumnConfig`.
+
+```golang
+ rowConfigAutoMerge := table.RowConfig{AutoMerge: true}
+
+ t := table.NewWriter()
+ t.AppendHeader(table.Row{"Node IP", "Pods", "Namespace", "Container", "RCE", "RCE"}, rowConfigAutoMerge)
+ t.AppendHeader(table.Row{"Node IP", "Pods", "Namespace", "Container", "EXE", "RUN"})
+ t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 1", "Y", "Y"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1A", "C 2", "Y", "N"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"1.1.1.1", "Pod 1A", "NS 1B", "C 3", "N", "N"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"1.1.1.1", "Pod 1B", "NS 2", "C 4", "N", "N"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"1.1.1.1", "Pod 1B", "NS 2", "C 5", "Y", "N"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"2.2.2.2", "Pod 2", "NS 3", "C 6", "Y", "Y"}, rowConfigAutoMerge)
+ t.AppendRow(table.Row{"2.2.2.2", "Pod 2", "NS 3", "C 7", "Y", "Y"}, rowConfigAutoMerge)
+ t.AppendFooter(table.Row{"", "", "", 7, 5, 3})
+ t.SetAutoIndex(true)
+ t.SetColumnConfigs([]table.ColumnConfig{
+ {Number: 1, AutoMerge: true},
+ {Number: 2, AutoMerge: true},
+ {Number: 3, AutoMerge: true},
+ {Number: 4, AutoMerge: true},
+ {Number: 5, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
+ {Number: 6, Align: text.AlignCenter, AlignFooter: text.AlignCenter, AlignHeader: text.AlignCenter},
+ })
+ t.SetStyle(table.StyleLight)
+ t.Style().Options.SeparateRows = true
+ fmt.Println(t.Render())
+```
+to get:
+```
+┌───┬─────────┬────────┬───────────┬───────────┬───────────┐
+│ │ NODE IP │ PODS │ NAMESPACE │ CONTAINER │ RCE │
+│ │ │ │ │ ├─────┬─────┤
+│ │ │ │ │ │ EXE │ RUN │
+├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
+│ 1 │ 1.1.1.1 │ Pod 1A │ NS 1A │ C 1 │ Y │
+├───┤ │ │ ├───────────┼─────┬─────┤
+│ 2 │ │ │ │ C 2 │ Y │ N │
+├───┤ │ ├───────────┼───────────┼─────┴─────┤
+│ 3 │ │ │ NS 1B │ C 3 │ N │
+├───┤ ├────────┼───────────┼───────────┼───────────┤
+│ 4 │ │ Pod 1B │ NS 2 │ C 4 │ N │
+├───┤ │ │ ├───────────┼─────┬─────┤
+│ 5 │ │ │ │ C 5 │ Y │ N │
+├───┼─────────┼────────┼───────────┼───────────┼─────┴─────┤
+│ 6 │ 2.2.2.2 │ Pod 2 │ NS 3 │ C 6 │ Y │
+├───┤ │ │ ├───────────┼───────────┤
+│ 7 │ │ │ │ C 7 │ Y │
+├───┼─────────┼────────┼───────────┼───────────┼─────┬─────┤
+│ │ │ │ │ 7 │ 5 │ 3 │
+└───┴─────────┴────────┴───────────┴───────────┴─────┴─────┘
+```
+
+## Paging
+
+You can limit then number of lines rendered in a single "Page". This logic
+can handle rows with multiple lines too. Here is a simple example:
+```golang
+ t.SetPageSize(1)
+ t.Render()
+```
+to get:
+```
++-----+------------+-----------+--------+-----------------------------+
+| # | FIRST NAME | LAST NAME | SALARY | |
++-----+------------+-----------+--------+-----------------------------+
+| 1 | Arya | Stark | 3000 | |
++-----+------------+-----------+--------+-----------------------------+
+| | | TOTAL | 10000 | |
++-----+------------+-----------+--------+-----------------------------+
+
++-----+------------+-----------+--------+-----------------------------+
+| # | FIRST NAME | LAST NAME | SALARY | |
++-----+------------+-----------+--------+-----------------------------+
+| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
++-----+------------+-----------+--------+-----------------------------+
+| | | TOTAL | 10000 | |
++-----+------------+-----------+--------+-----------------------------+
+
++-----+------------+-----------+--------+-----------------------------+
+| # | FIRST NAME | LAST NAME | SALARY | |
++-----+------------+-----------+--------+-----------------------------+
+| 300 | Tyrion | Lannister | 5000 | |
++-----+------------+-----------+--------+-----------------------------+
+| | | TOTAL | 10000 | |
++-----+------------+-----------+--------+-----------------------------+
+```
+
+## Sorting
+
+Sorting can be done on one or more columns. The following code will make the
+rows be sorted first by "First Name" and then by "Last Name" (in case of similar
+"First Name" entries).
+```golang
+ t.SortBy([]table.SortBy{
+ {Name: "First Name", Mode: table.Asc},
+ {Name: "Last Name", Mode: table.Asc},
+ })
+```
+
+## Wrapping (or) Row/Column Width restrictions
+
+You can restrict the maximum (text) width for a Row:
+```golang
+ t.SetAllowedRowLength(50)
+ t.Render()
+```
+to get:
+```
++-----+------------+-----------+--------+------- ~
+| # | FIRST NAME | LAST NAME | SALARY | ~
++-----+------------+-----------+--------+------- ~
+| 1 | Arya | Stark | 3000 | ~
+| 20 | Jon | Snow | 2000 | You kn ~
++-----+------------+-----------+--------+------- ~
+| 300 | Tyrion | Lannister | 5000 | ~
++-----+------------+-----------+--------+------- ~
+| | | TOTAL | 10000 | ~
++-----+------------+-----------+--------+------- ~
+```
+
+## Column Control - Alignment, Colors, Width and more
+
+You can control a lot of things about individual cells/columns which overrides
+global properties/styles using the `SetColumnConfig()` interface:
+- Alignment (horizontal & vertical)
+- Colorization
+- Transform individual cells based on the content
+- Visibility
+- Width (minimum & maximum)
+
+```golang
+ nameTransformer := text.Transformer(func(val interface{}) string {
+ return text.Bold.Sprint(val)
+ })
+
+ t.SetColumnConfigs([]ColumnConfig{
+ {
+ Name: "First Name",
+ Align: text.AlignLeft,
+ AlignFooter: text.AlignLeft,
+ AlignHeader: text.AlignLeft,
+ Colors: text.Colors{text.BgBlack, text.FgRed},
+ ColorsHeader: text.Colors{text.BgRed, text.FgBlack, text.Bold},
+ ColorsFooter: text.Colors{text.BgRed, text.FgBlack},
+ Hidden: false,
+ Transformer: nameTransformer,
+ TransformerFooter: nameTransformer,
+ TransformerHeader: nameTransformer,
+ VAlign: text.VAlignMiddle,
+ VAlignFooter: text.VAlignTop,
+ VAlignHeader: text.VAlignBottom,
+ WidthMin: 6,
+ WidthMax: 64,
+ }
+ })
+```
+
+## Render As ...
+
+Tables can be rendered in other common formats such as:
+
+### ... CSV
+
+```golang
+ t.RenderCSV()
+```
+to get:
+```
+,First Name,Last Name,Salary,
+1,Arya,Stark,3000,
+20,Jon,Snow,2000,"You know nothing\, Jon Snow!"
+300,Tyrion,Lannister,5000,
+,,Total,10000,
+```
+
+### ... HTML Table
+
+```golang
+ t.Style().HTML = table.HTMLOptions{
+ CSSClass: "game-of-thrones",
+ EmptyColumn: " ",
+ EscapeText: true,
+ Newline: "
",
+ }
+ t.RenderHTML()
+```
+to get:
+```html
+
+
+
+ # |
+ First Name |
+ Last Name |
+ Salary |
+ |
+
+
+
+
+ 1 |
+ Arya |
+ Stark |
+ 3000 |
+ |
+
+
+ 20 |
+ Jon |
+ Snow |
+ 2000 |
+ You know nothing, Jon Snow! |
+
+
+ 300 |
+ Tyrion |
+ Lannister |
+ 5000 |
+ |
+
+
+
+
+ |
+ |
+ Total |
+ 10000 |
+ |
+
+
+
+```
+
+### ... Markdown Table
+
+```golang
+ t.RenderMarkdown()
+```
+to get:
+```markdown
+| # | First Name | Last Name | Salary | |
+| ---:| --- | --- | ---:| --- |
+| 1 | Arya | Stark | 3000 | |
+| 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
+| 300 | Tyrion | Lannister | 5000 | |
+| | | Total | 10000 | |
+```
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/config.go b/vendor/github.com/jedib0t/go-pretty/v6/table/config.go
new file mode 100644
index 0000000000..fb96d70864
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/config.go
@@ -0,0 +1,103 @@
+package table
+
+import (
+ "github.com/jedib0t/go-pretty/v6/text"
+)
+
+// ColumnConfig contains configurations that determine and modify the way the
+// contents of the column get rendered.
+type ColumnConfig struct {
+ // Name is the name of the Column as it appears in the first Header row.
+ // If a Header is not provided, or the name is not found in the header, this
+ // will not work.
+ Name string
+ // Number is the Column # from left. When specified, it overrides the Name
+ // property. If you know the exact Column number, use this instead of Name.
+ Number int
+
+ // Align defines the horizontal alignment
+ Align text.Align
+ // AlignFooter defines the horizontal alignment of Footer rows
+ AlignFooter text.Align
+ // AlignHeader defines the horizontal alignment of Header rows
+ AlignHeader text.Align
+
+ // AutoMerge merges cells with similar values and prevents separators from
+ // being drawn. Caveats:
+ // * VAlign is applied on the individual cell and not on the merged cell
+ // * Does not work in CSV/HTML/Markdown render modes
+ // * Does not work well with horizontal auto-merge (RowConfig.AutoMerge)
+ //
+ // Works best when:
+ // * Style().Options.SeparateRows == true
+ // * Style().Color.Row == Style().Color.RowAlternate (or not set)
+ AutoMerge bool
+
+ // Colors defines the colors to be used on the column
+ Colors text.Colors
+ // ColorsFooter defines the colors to be used on the column in Footer rows
+ ColorsFooter text.Colors
+ // ColorsHeader defines the colors to be used on the column in Header rows
+ ColorsHeader text.Colors
+
+ // Hidden when set to true will prevent the column from being rendered.
+ // This is useful in cases like needing a column for sorting, but not for
+ // display.
+ Hidden bool
+
+ // Transformer is a custom-function that changes the way the value gets
+ // rendered to the console. Refer to text/transformer.go for ready-to-use
+ // Transformer functions.
+ Transformer text.Transformer
+ // TransformerFooter is like Transformer but for Footer rows
+ TransformerFooter text.Transformer
+ // TransformerHeader is like Transformer but for Header rows
+ TransformerHeader text.Transformer
+
+ // VAlign defines the vertical alignment
+ VAlign text.VAlign
+ // VAlignFooter defines the vertical alignment in Footer rows
+ VAlignFooter text.VAlign
+ // VAlignHeader defines the vertical alignment in Header rows
+ VAlignHeader text.VAlign
+
+ // WidthMax defines the maximum character length of the column
+ WidthMax int
+ // WidthEnforcer enforces the WidthMax value on the column contents;
+ // default: text.WrapText
+ WidthMaxEnforcer WidthEnforcer
+ // WidthMin defines the minimum character length of the column
+ WidthMin int
+}
+
+func (c ColumnConfig) getWidthMaxEnforcer() WidthEnforcer {
+ if c.WidthMax <= 0 {
+ return widthEnforcerNone
+ }
+ if c.WidthMaxEnforcer != nil {
+ return c.WidthMaxEnforcer
+ }
+ return text.WrapText
+}
+
+// RowConfig contains configurations that determine and modify the way the
+// contents of a row get rendered.
+type RowConfig struct {
+ // AutoMerge merges cells with similar values and prevents separators from
+ // being drawn. Caveats:
+ // * Align is overridden to text.AlignCenter on the merged cell (unless set
+ // by AutoMergeAlign value below)
+ // * Does not work in CSV/HTML/Markdown render modes
+ // * Does not work well with vertical auto-merge (ColumnConfig.AutoMerge)
+ AutoMerge bool
+
+ // Alignment to use on a merge (defaults to text.AlignCenter)
+ AutoMergeAlign text.Align
+}
+
+func (rc RowConfig) getAutoMergeAlign() text.Align {
+ if rc.AutoMergeAlign == text.AlignDefault {
+ return text.AlignCenter
+ }
+ return rc.AutoMergeAlign
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/pager.go b/vendor/github.com/jedib0t/go-pretty/v6/table/pager.go
new file mode 100644
index 0000000000..f4585b2972
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/pager.go
@@ -0,0 +1,70 @@
+package table
+
+import (
+ "io"
+)
+
+// Pager lets you interact with the table rendering in a paged manner.
+type Pager interface {
+ // GoTo moves to the given 1-indexed page number.
+ GoTo(pageNum int) string
+ // Location returns the current page number in 1-indexed form.
+ Location() int
+ // Next moves to the next available page and returns the same.
+ Next() string
+ // Prev moves to the previous available page and returns the same.
+ Prev() string
+ // Render returns the current page.
+ Render() string
+ // SetOutputMirror sets up the writer to which Render() will write the
+ // output other than returning.
+ SetOutputMirror(mirror io.Writer)
+}
+
+type pager struct {
+ index int // 0-indexed
+ pages []string
+ outputMirror io.Writer
+ size int
+}
+
+func (p *pager) GoTo(pageNum int) string {
+ if pageNum < 1 {
+ pageNum = 1
+ }
+ if pageNum > len(p.pages) {
+ pageNum = len(p.pages)
+ }
+ p.index = pageNum - 1
+ return p.pages[p.index]
+}
+
+func (p *pager) Location() int {
+ return p.index + 1
+}
+
+func (p *pager) Next() string {
+ if p.index < len(p.pages)-1 {
+ p.index++
+ }
+ return p.pages[p.index]
+}
+
+func (p *pager) Prev() string {
+ if p.index > 0 {
+ p.index--
+ }
+ return p.pages[p.index]
+}
+
+func (p *pager) Render() string {
+ pageToWrite := p.pages[p.index]
+ if p.outputMirror != nil {
+ _, _ = p.outputMirror.Write([]byte(pageToWrite))
+ }
+ return pageToWrite
+}
+
+func (p *pager) SetOutputMirror(mirror io.Writer) {
+ p.outputMirror = mirror
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/pager_options.go b/vendor/github.com/jedib0t/go-pretty/v6/table/pager_options.go
new file mode 100644
index 0000000000..f46ebc1280
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/pager_options.go
@@ -0,0 +1,11 @@
+package table
+
+// PagerOption helps control Paging.
+type PagerOption func(t *Table)
+
+// PageSize sets the size of each page rendered.
+func PageSize(pageSize int) PagerOption {
+ return func(t *Table) {
+ t.pager.size = pageSize
+ }
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render.go
new file mode 100644
index 0000000000..7641da3c14
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render.go
@@ -0,0 +1,428 @@
+package table
+
+import (
+ "fmt"
+ "strings"
+ "unicode/utf8"
+
+ "github.com/jedib0t/go-pretty/v6/text"
+)
+
+// Render renders the Table in a human-readable "pretty" format. Example:
+//
+// ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+// │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+// ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+// │ 1 │ Arya │ Stark │ 3000 │ │
+// │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+// │ 300 │ Tyrion │ Lannister │ 5000 │ │
+// ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+// │ │ │ TOTAL │ 10000 │ │
+// └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+func (t *Table) Render() string {
+ t.initForRender()
+
+ var out strings.Builder
+ if t.numColumns > 0 {
+ t.renderTitle(&out)
+
+ // top-most border
+ t.renderRowsBorderTop(&out)
+
+ // header rows
+ t.renderRowsHeader(&out)
+
+ // (data) rows
+ t.renderRows(&out, t.rows, renderHint{})
+
+ // footer rows
+ t.renderRowsFooter(&out)
+
+ // bottom-most border
+ t.renderRowsBorderBottom(&out)
+
+ // caption
+ if t.caption != "" {
+ out.WriteRune('\n')
+ out.WriteString(t.caption)
+ }
+ }
+ return t.render(&out)
+}
+
+func (t *Table) renderColumn(out *strings.Builder, row rowStr, colIdx int, maxColumnLength int, hint renderHint) int {
+ numColumnsRendered := 1
+
+ // when working on the first column, and autoIndex is true, insert a new
+ // column with the row number on it.
+ if colIdx == 0 && t.autoIndex {
+ hintAutoIndex := hint
+ hintAutoIndex.isAutoIndexColumn = true
+ t.renderColumnAutoIndex(out, hintAutoIndex)
+ }
+
+ // when working on column number 2 or more, render the column separator
+ if colIdx > 0 {
+ t.renderColumnSeparator(out, row, colIdx, hint)
+ }
+
+ // extract the text, convert-case if not-empty and align horizontally
+ mergeVertically := t.shouldMergeCellsVertically(colIdx, hint)
+ var colStr string
+ if mergeVertically {
+ // leave colStr empty; align will expand the column as necessary
+ } else if colIdx < len(row) {
+ colStr = t.getFormat(hint).Apply(row[colIdx])
+ }
+ align := t.getAlign(colIdx, hint)
+
+ // if horizontal cell merges are enabled, look ahead and see how many cells
+ // have the same content and merge them all until a cell with a different
+ // content is found; override alignment to Center in this case
+ rowConfig := t.getRowConfig(hint)
+ if rowConfig.AutoMerge && !hint.isSeparatorRow {
+ // get the real row to consider all lines in each column instead of just
+ // looking at the current "line"
+ rowUnwrapped := t.getRow(hint.rowNumber-1, hint)
+ for idx := colIdx + 1; idx < len(rowUnwrapped); idx++ {
+ if rowUnwrapped[colIdx] != rowUnwrapped[idx] {
+ break
+ }
+ align = rowConfig.getAutoMergeAlign()
+ maxColumnLength += t.getMaxColumnLengthForMerging(idx)
+ numColumnsRendered++
+ }
+ }
+ colStr = align.Apply(colStr, maxColumnLength)
+
+ // pad both sides of the column
+ if !hint.isSeparatorRow || (hint.isSeparatorRow && mergeVertically) {
+ colStr = t.style.Box.PaddingLeft + colStr + t.style.Box.PaddingRight
+ }
+
+ t.renderColumnColorized(out, colIdx, colStr, hint)
+
+ return colIdx + numColumnsRendered
+}
+
+func (t *Table) renderColumnAutoIndex(out *strings.Builder, hint renderHint) {
+ var outAutoIndex strings.Builder
+ outAutoIndex.Grow(t.maxColumnLengths[0])
+
+ if hint.isSeparatorRow {
+ numChars := t.autoIndexVIndexMaxLength + utf8.RuneCountInString(t.style.Box.PaddingLeft) +
+ utf8.RuneCountInString(t.style.Box.PaddingRight)
+ chars := t.style.Box.MiddleHorizontal
+ if hint.isAutoIndexColumn && hint.isHeaderOrFooterSeparator() {
+ chars = text.RepeatAndTrim(" ", len(t.style.Box.MiddleHorizontal))
+ }
+ outAutoIndex.WriteString(text.RepeatAndTrim(chars, numChars))
+ } else {
+ outAutoIndex.WriteString(t.style.Box.PaddingLeft)
+ rowNumStr := fmt.Sprint(hint.rowNumber)
+ if hint.isHeaderRow || hint.isFooterRow || hint.rowLineNumber > 1 {
+ rowNumStr = strings.Repeat(" ", t.autoIndexVIndexMaxLength)
+ }
+ outAutoIndex.WriteString(text.AlignRight.Apply(rowNumStr, t.autoIndexVIndexMaxLength))
+ outAutoIndex.WriteString(t.style.Box.PaddingRight)
+ }
+
+ if t.style.Color.IndexColumn != nil {
+ colors := t.style.Color.IndexColumn
+ if hint.isFooterRow {
+ colors = t.style.Color.Footer
+ }
+ out.WriteString(colors.Sprint(outAutoIndex.String()))
+ } else {
+ out.WriteString(outAutoIndex.String())
+ }
+ hint.isAutoIndexColumn = true
+ t.renderColumnSeparator(out, rowStr{}, 0, hint)
+}
+
+func (t *Table) renderColumnColorized(out *strings.Builder, colIdx int, colStr string, hint renderHint) {
+ colors := t.getColumnColors(colIdx, hint)
+ if colors != nil {
+ out.WriteString(colors.Sprint(colStr))
+ } else if hint.isHeaderRow && t.style.Color.Header != nil {
+ out.WriteString(t.style.Color.Header.Sprint(colStr))
+ } else if hint.isFooterRow && t.style.Color.Footer != nil {
+ out.WriteString(t.style.Color.Footer.Sprint(colStr))
+ } else if hint.isRegularRow() {
+ if colIdx == t.indexColumn-1 && t.style.Color.IndexColumn != nil {
+ out.WriteString(t.style.Color.IndexColumn.Sprint(colStr))
+ } else if hint.rowNumber%2 == 0 && t.style.Color.RowAlternate != nil {
+ out.WriteString(t.style.Color.RowAlternate.Sprint(colStr))
+ } else if t.style.Color.Row != nil {
+ out.WriteString(t.style.Color.Row.Sprint(colStr))
+ } else {
+ out.WriteString(colStr)
+ }
+ } else {
+ out.WriteString(colStr)
+ }
+}
+
+func (t *Table) renderColumnSeparator(out *strings.Builder, row rowStr, colIdx int, hint renderHint) {
+ if t.style.Options.SeparateColumns {
+ separator := t.getColumnSeparator(row, colIdx, hint)
+
+ colors := t.getSeparatorColors(hint)
+ if colors.EscapeSeq() != "" {
+ out.WriteString(colors.Sprint(separator))
+ } else {
+ out.WriteString(separator)
+ }
+ }
+}
+
+func (t *Table) renderLine(out *strings.Builder, row rowStr, hint renderHint) {
+ // if the output has content, it means that this call is working on line
+ // number 2 or more; separate them with a newline
+ if out.Len() > 0 {
+ out.WriteRune('\n')
+ }
+
+ // use a brand-new strings.Builder if a row length limit has been set
+ var outLine *strings.Builder
+ if t.style.Size.WidthMax > 0 {
+ outLine = &strings.Builder{}
+ } else {
+ outLine = out
+ }
+ // grow the strings.Builder to the maximum possible row length
+ outLine.Grow(t.maxRowLength)
+
+ nextColIdx := 0
+ t.renderMarginLeft(outLine, hint)
+ for colIdx, maxColumnLength := range t.maxColumnLengths {
+ if colIdx != nextColIdx {
+ continue
+ }
+ nextColIdx = t.renderColumn(outLine, row, colIdx, maxColumnLength, hint)
+ }
+ t.renderMarginRight(outLine, hint)
+
+ // merge the strings.Builder objects if a new one was created earlier
+ if outLine != out {
+ t.renderLineMergeOutputs(out, outLine)
+ }
+ t.firstRowOfPage = false
+
+ // if a page size has been set, and said number of lines has already
+ // been rendered, and the header is not being rendered right now, render
+ // the header all over again with a spacing line
+ if hint.isRegularNonSeparatorRow() {
+ t.numLinesRendered++
+ if t.pager.size > 0 && t.numLinesRendered%t.pager.size == 0 && !hint.isLastLineOfLastRow() {
+ t.renderRowsFooter(out)
+ t.renderRowsBorderBottom(out)
+ out.WriteString(t.style.Box.PageSeparator)
+ t.renderRowsBorderTop(out)
+ t.renderRowsHeader(out)
+ t.firstRowOfPage = true
+ }
+ }
+}
+
+func (t *Table) renderLineMergeOutputs(out *strings.Builder, outLine *strings.Builder) {
+ outLineStr := outLine.String()
+ if text.RuneWidthWithoutEscSequences(outLineStr) > t.style.Size.WidthMax {
+ trimLength := t.style.Size.WidthMax - utf8.RuneCountInString(t.style.Box.UnfinishedRow)
+ if trimLength > 0 {
+ out.WriteString(text.Trim(outLineStr, trimLength))
+ out.WriteString(t.style.Box.UnfinishedRow)
+ }
+ } else {
+ out.WriteString(outLineStr)
+ }
+}
+
+func (t *Table) renderMarginLeft(out *strings.Builder, hint renderHint) {
+ out.WriteString(t.style.Format.Direction.Modifier())
+ if t.style.Options.DrawBorder {
+ border := t.getBorderLeft(hint)
+ colors := t.getBorderColors(hint)
+ if colors.EscapeSeq() != "" {
+ out.WriteString(colors.Sprint(border))
+ } else {
+ out.WriteString(border)
+ }
+ }
+}
+
+func (t *Table) renderMarginRight(out *strings.Builder, hint renderHint) {
+ if t.style.Options.DrawBorder {
+ border := t.getBorderRight(hint)
+ colors := t.getBorderColors(hint)
+ if colors.EscapeSeq() != "" {
+ out.WriteString(colors.Sprint(border))
+ } else {
+ out.WriteString(border)
+ }
+ }
+}
+
+func (t *Table) renderRow(out *strings.Builder, row rowStr, hint renderHint) {
+ if len(row) > 0 {
+ // fit every column into the allowedColumnLength/maxColumnLength limit
+ // and in the process find the max. number of lines in any column in
+ // this row
+ colMaxLines, rowWrapped := t.wrapRow(row)
+
+ // if there is just 1 line in all columns, add the row as such; else
+ // split each column into individual lines and render them one-by-one
+ if colMaxLines == 1 {
+ hint.isLastLineOfRow = true
+ t.renderLine(out, rowWrapped, hint)
+ } else {
+ // convert one row into N # of rows based on colMaxLines
+ rowLines := make([]rowStr, len(row))
+ for colIdx, colStr := range rowWrapped {
+ rowLines[colIdx] = t.getVAlign(colIdx, hint).ApplyStr(colStr, colMaxLines)
+ }
+ for colLineIdx := 0; colLineIdx < colMaxLines; colLineIdx++ {
+ rowLine := make(rowStr, len(rowLines))
+ for colIdx, colLines := range rowLines {
+ rowLine[colIdx] = colLines[colLineIdx]
+ }
+ hint.isLastLineOfRow = colLineIdx == colMaxLines-1
+ hint.rowLineNumber = colLineIdx + 1
+ t.renderLine(out, rowLine, hint)
+ }
+ }
+ }
+}
+
+func (t *Table) renderRowSeparator(out *strings.Builder, hint renderHint) {
+ if hint.isBorderTop || hint.isBorderBottom {
+ if !t.style.Options.DrawBorder {
+ return
+ }
+ } else if hint.isHeaderRow && !t.style.Options.SeparateHeader {
+ return
+ } else if hint.isFooterRow && !t.style.Options.SeparateFooter {
+ return
+ }
+ hint.isSeparatorRow = true
+ t.renderLine(out, t.rowSeparator, hint)
+}
+
+func (t *Table) renderRows(out *strings.Builder, rows []rowStr, hint renderHint) {
+ for rowIdx, row := range rows {
+ hint.isFirstRow = rowIdx == 0
+ hint.isLastRow = rowIdx == len(rows)-1
+ hint.rowNumber = rowIdx + 1
+ t.renderRow(out, row, hint)
+
+ if t.shouldSeparateRows(rowIdx, len(rows)) {
+ hint.isFirstRow = false
+ t.renderRowSeparator(out, hint)
+ }
+ }
+}
+
+func (t *Table) renderRowsBorderBottom(out *strings.Builder) {
+ if len(t.rowsFooter) > 0 {
+ t.renderRowSeparator(out, renderHint{
+ isBorderBottom: true,
+ isFooterRow: true,
+ rowNumber: len(t.rowsFooter),
+ })
+ } else {
+ t.renderRowSeparator(out, renderHint{
+ isBorderBottom: true,
+ isFooterRow: false,
+ rowNumber: len(t.rows),
+ })
+ }
+}
+
+func (t *Table) renderRowsBorderTop(out *strings.Builder) {
+ if len(t.rowsHeader) > 0 || t.autoIndex {
+ t.renderRowSeparator(out, renderHint{
+ isBorderTop: true,
+ isHeaderRow: true,
+ rowNumber: 0,
+ })
+ } else {
+ t.renderRowSeparator(out, renderHint{
+ isBorderTop: true,
+ isHeaderRow: false,
+ rowNumber: 0,
+ })
+ }
+}
+
+func (t *Table) renderRowsFooter(out *strings.Builder) {
+ if len(t.rowsFooter) > 0 {
+ t.renderRowSeparator(out, renderHint{
+ isFooterRow: true,
+ isFirstRow: true,
+ isSeparatorRow: true,
+ })
+ t.renderRows(out, t.rowsFooter, renderHint{isFooterRow: true})
+ }
+}
+
+func (t *Table) renderRowsHeader(out *strings.Builder) {
+ if len(t.rowsHeader) > 0 || t.autoIndex {
+ hintSeparator := renderHint{isHeaderRow: true, isLastRow: true, isSeparatorRow: true}
+
+ if len(t.rowsHeader) > 0 {
+ t.renderRows(out, t.rowsHeader, renderHint{isHeaderRow: true})
+ hintSeparator.rowNumber = len(t.rowsHeader)
+ } else if t.autoIndex {
+ t.renderRow(out, t.getAutoIndexColumnIDs(), renderHint{isAutoIndexRow: true, isHeaderRow: true})
+ hintSeparator.rowNumber = 1
+ }
+ t.renderRowSeparator(out, hintSeparator)
+ }
+}
+
+func (t *Table) renderTitle(out *strings.Builder) {
+ if t.title != "" {
+ colors := t.style.Title.Colors
+ colorsBorder := t.getBorderColors(renderHint{isTitleRow: true})
+ rowLength := t.maxRowLength
+ if wm := t.style.Size.WidthMax; wm > 0 && wm < rowLength {
+ rowLength = wm
+ }
+ if wm := t.style.Size.WidthMin; wm > 0 && wm > rowLength {
+ rowLength = wm
+ }
+ if t.style.Options.DrawBorder {
+ lenBorder := rowLength - text.RuneWidthWithoutEscSequences(t.style.Box.TopLeft+t.style.Box.TopRight)
+ out.WriteString(colorsBorder.Sprint(t.style.Box.TopLeft))
+ out.WriteString(colorsBorder.Sprint(text.RepeatAndTrim(t.style.Box.MiddleHorizontal, lenBorder)))
+ out.WriteString(colorsBorder.Sprint(t.style.Box.TopRight))
+ }
+
+ lenText := rowLength - text.RuneWidthWithoutEscSequences(t.style.Box.PaddingLeft+t.style.Box.PaddingRight)
+ if t.style.Options.DrawBorder {
+ lenText -= text.RuneWidthWithoutEscSequences(t.style.Box.Left + t.style.Box.Right)
+ }
+ titleText := text.WrapText(t.title, lenText)
+ for _, titleLine := range strings.Split(titleText, "\n") {
+ t.renderTitleLine(out, lenText, titleLine, colors, colorsBorder)
+ }
+ }
+}
+
+func (t *Table) renderTitleLine(out *strings.Builder, lenText int, titleLine string, colors text.Colors, colorsBorder text.Colors) {
+ titleLine = strings.TrimSpace(titleLine)
+ titleLine = t.style.Title.Format.Apply(titleLine)
+ titleLine = t.style.Title.Align.Apply(titleLine, lenText)
+ titleLine = t.style.Box.PaddingLeft + titleLine + t.style.Box.PaddingRight
+
+ if out.Len() > 0 {
+ out.WriteRune('\n')
+ }
+ if t.style.Options.DrawBorder {
+ out.WriteString(colorsBorder.Sprint(t.style.Box.Left))
+ }
+ out.WriteString(colors.Sprint(titleLine))
+ if t.style.Options.DrawBorder {
+ out.WriteString(colorsBorder.Sprint(t.style.Box.Right))
+ }
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go
new file mode 100644
index 0000000000..831194ef33
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go
@@ -0,0 +1,82 @@
+package table
+
+import (
+ "fmt"
+ "strings"
+ "unicode/utf8"
+)
+
+// RenderCSV renders the Table in CSV format. Example:
+//
+// #,First Name,Last Name,Salary,
+// 1,Arya,Stark,3000,
+// 20,Jon,Snow,2000,"You know nothing\, Jon Snow!"
+// 300,Tyrion,Lannister,5000,
+// ,,Total,10000,
+func (t *Table) RenderCSV() string {
+ t.initForRender()
+
+ var out strings.Builder
+ if t.numColumns > 0 {
+ if t.title != "" {
+ out.WriteString(t.title)
+ }
+ if t.autoIndex && len(t.rowsHeader) == 0 {
+ t.csvRenderRow(&out, t.getAutoIndexColumnIDs(), renderHint{isAutoIndexRow: true, isHeaderRow: true})
+ }
+ t.csvRenderRows(&out, t.rowsHeader, renderHint{isHeaderRow: true})
+ t.csvRenderRows(&out, t.rows, renderHint{})
+ t.csvRenderRows(&out, t.rowsFooter, renderHint{isFooterRow: true})
+ if t.caption != "" {
+ out.WriteRune('\n')
+ out.WriteString(t.caption)
+ }
+ }
+ return t.render(&out)
+}
+
+func (t *Table) csvFixCommas(str string) string {
+ return strings.Replace(str, ",", "\\,", -1)
+}
+
+func (t *Table) csvFixDoubleQuotes(str string) string {
+ return strings.Replace(str, "\"", "\\\"", -1)
+}
+
+func (t *Table) csvRenderRow(out *strings.Builder, row rowStr, hint renderHint) {
+ // when working on line number 2 or more, insert a newline first
+ if out.Len() > 0 {
+ out.WriteRune('\n')
+ }
+
+ // generate the columns to render in CSV format and append to "out"
+ for colIdx, colStr := range row {
+ // auto-index column
+ if colIdx == 0 && t.autoIndex {
+ if hint.isRegularRow() {
+ out.WriteString(fmt.Sprint(hint.rowNumber))
+ }
+ out.WriteRune(',')
+ }
+ if colIdx > 0 {
+ out.WriteRune(',')
+ }
+ if strings.ContainsAny(colStr, "\",\n") {
+ out.WriteRune('"')
+ out.WriteString(t.csvFixCommas(t.csvFixDoubleQuotes(colStr)))
+ out.WriteRune('"')
+ } else if utf8.RuneCountInString(colStr) > 0 {
+ out.WriteString(colStr)
+ }
+ }
+ for colIdx := len(row); colIdx < t.numColumns; colIdx++ {
+ out.WriteRune(',')
+ }
+}
+
+func (t *Table) csvRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) {
+ for rowIdx, row := range rows {
+ hint.rowNumber = rowIdx + 1
+ t.csvRenderRow(out, row, hint)
+ }
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go
new file mode 100644
index 0000000000..e46cdc845b
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go
@@ -0,0 +1,39 @@
+package table
+
+// renderHint has hints for the Render*() logic
+type renderHint struct {
+ isAutoIndexColumn bool // auto-index column?
+ isAutoIndexRow bool // auto-index row?
+ isBorderBottom bool // bottom-border?
+ isBorderTop bool // top-border?
+ isFirstRow bool // first-row of header/footer/regular-rows?
+ isFooterRow bool // footer row?
+ isHeaderRow bool // header row?
+ isLastLineOfRow bool // last-line of the current row?
+ isLastRow bool // last-row of header/footer/regular-rows?
+ isSeparatorRow bool // separator row?
+ isTitleRow bool // title row?
+ rowLineNumber int // the line number for a multi-line row
+ rowNumber int // the row number/index
+}
+
+func (h *renderHint) isBorderOrSeparator() bool {
+ return h.isBorderTop || h.isSeparatorRow || h.isBorderBottom
+}
+
+func (h *renderHint) isRegularRow() bool {
+ return !h.isHeaderRow && !h.isFooterRow
+}
+
+func (h *renderHint) isRegularNonSeparatorRow() bool {
+ return !h.isHeaderRow && !h.isFooterRow && !h.isSeparatorRow
+}
+
+func (h *renderHint) isHeaderOrFooterSeparator() bool {
+ return h.isSeparatorRow && !h.isBorderBottom && !h.isBorderTop &&
+ ((h.isHeaderRow && !h.isLastRow) || (h.isFooterRow && (!h.isFirstRow || h.rowNumber > 0)))
+}
+
+func (h *renderHint) isLastLineOfLastRow() bool {
+ return h.isLastLineOfRow && h.isLastRow
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go
new file mode 100644
index 0000000000..decf4fa0db
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go
@@ -0,0 +1,268 @@
+package table
+
+import (
+ "fmt"
+ "html"
+ "strings"
+
+ "github.com/jedib0t/go-pretty/v6/text"
+)
+
+const (
+ // DefaultHTMLCSSClass stores the css-class to use when none-provided via
+ // SetHTMLCSSClass(cssClass string).
+ DefaultHTMLCSSClass = "go-pretty-table"
+)
+
+// RenderHTML renders the Table in HTML format. Example:
+//
+//
+//
+//
+// # |
+// First Name |
+// Last Name |
+// Salary |
+// |
+//
+//
+//
+//
+// 1 |
+// Arya |
+// Stark |
+// 3000 |
+// |
+//
+//
+// 20 |
+// Jon |
+// Snow |
+// 2000 |
+// You know nothing, Jon Snow! |
+//
+//
+// 300 |
+// Tyrion |
+// Lannister |
+// 5000 |
+// |
+//
+//
+//
+//
+// |
+// |
+// Total |
+// 10000 |
+// |
+//
+//
+//
+func (t *Table) RenderHTML() string {
+ t.initForRender()
+
+ var out strings.Builder
+ if t.numColumns > 0 {
+ out.WriteString("\n")
+ t.htmlRenderTitle(&out)
+ t.htmlRenderRowsHeader(&out)
+ t.htmlRenderRows(&out, t.rows, renderHint{})
+ t.htmlRenderRowsFooter(&out)
+ t.htmlRenderCaption(&out)
+ out.WriteString("
")
+ }
+ return t.render(&out)
+}
+
+func (t *Table) htmlGetColStrAndTag(row rowStr, colIdx int, hint renderHint) (string, string) {
+ // get the column contents
+ var colStr string
+ if colIdx < len(row) {
+ colStr = row[colIdx]
+ }
+
+ // header uses "th" instead of "td"
+ colTagName := "td"
+ if hint.isHeaderRow {
+ colTagName = "th"
+ }
+
+ return colStr, colTagName
+}
+
+func (t *Table) htmlRenderCaption(out *strings.Builder) {
+ if t.caption != "" {
+ out.WriteString(" ")
+ out.WriteString(t.caption)
+ out.WriteString("\n")
+ }
+}
+
+func (t *Table) htmlRenderColumn(out *strings.Builder, colStr string) {
+ if t.style.HTML.EscapeText {
+ colStr = html.EscapeString(colStr)
+ }
+ if t.style.HTML.Newline != "\n" {
+ colStr = strings.Replace(colStr, "\n", t.style.HTML.Newline, -1)
+ }
+ out.WriteString(colStr)
+}
+
+func (t *Table) htmlRenderColumnAttributes(out *strings.Builder, colIdx int, hint renderHint, alignOverride text.Align) {
+ // determine the HTML "align"/"valign" property values
+ align := alignOverride.HTMLProperty()
+ vAlign := t.getVAlign(colIdx, hint).HTMLProperty()
+ // determine the HTML "class" property values for the colors
+ class := t.getColumnColors(colIdx, hint).HTMLProperty()
+
+ if align != "" {
+ out.WriteRune(' ')
+ out.WriteString(align)
+ }
+ if class != "" {
+ out.WriteRune(' ')
+ out.WriteString(class)
+ }
+ if vAlign != "" {
+ out.WriteRune(' ')
+ out.WriteString(vAlign)
+ }
+}
+
+func (t *Table) htmlRenderColumnAutoIndex(out *strings.Builder, hint renderHint) {
+ if hint.isHeaderRow {
+ out.WriteString(" ")
+ out.WriteString(t.style.HTML.EmptyColumn)
+ out.WriteString(" | \n")
+ } else if hint.isFooterRow {
+ out.WriteString(" ")
+ out.WriteString(t.style.HTML.EmptyColumn)
+ out.WriteString(" | \n")
+ } else {
+ out.WriteString(" ")
+ out.WriteString(fmt.Sprint(hint.rowNumber))
+ out.WriteString(" | \n")
+ }
+}
+
+func (t *Table) htmlRenderRow(out *strings.Builder, row rowStr, hint renderHint) {
+ out.WriteString(" \n")
+ for colIdx := 0; colIdx < t.numColumns; colIdx++ {
+ // auto-index column
+ if colIdx == 0 && t.autoIndex {
+ t.htmlRenderColumnAutoIndex(out, hint)
+ }
+
+ align := t.getAlign(colIdx, hint)
+ rowConfig := t.getRowConfig(hint)
+ extraColumnsRendered := 0
+ if rowConfig.AutoMerge && !hint.isSeparatorRow {
+ // get the real row to consider all lines in each column instead of just
+ // looking at the current "line"
+ rowUnwrapped := t.getRow(hint.rowNumber-1, hint)
+ for idx := colIdx + 1; idx < len(rowUnwrapped); idx++ {
+ if rowUnwrapped[colIdx] != rowUnwrapped[idx] {
+ break
+ }
+ align = rowConfig.getAutoMergeAlign()
+ extraColumnsRendered++
+ }
+ }
+
+ colStr, colTagName := t.htmlGetColStrAndTag(row, colIdx, hint)
+ // write the row
+ out.WriteString(" <")
+ out.WriteString(colTagName)
+ t.htmlRenderColumnAttributes(out, colIdx, hint, align)
+ if extraColumnsRendered > 0 {
+ out.WriteString(" colspan=")
+ out.WriteString(fmt.Sprint(extraColumnsRendered + 1))
+ }
+ out.WriteString(">")
+ if len(colStr) == 0 {
+ out.WriteString(t.style.HTML.EmptyColumn)
+ } else {
+ t.htmlRenderColumn(out, colStr)
+ }
+ out.WriteString("")
+ out.WriteString(colTagName)
+ out.WriteString(">\n")
+ colIdx += extraColumnsRendered
+ }
+ out.WriteString("
\n")
+}
+
+func (t *Table) htmlRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) {
+ if len(rows) > 0 {
+ // determine that tag to use based on the type of the row
+ rowsTag := "tbody"
+ if hint.isHeaderRow {
+ rowsTag = "thead"
+ } else if hint.isFooterRow {
+ rowsTag = "tfoot"
+ }
+
+ var renderedTagOpen, shouldRenderTagClose bool
+ for idx, row := range rows {
+ hint.rowNumber = idx + 1
+ if len(row) > 0 {
+ if !renderedTagOpen {
+ out.WriteString(" <")
+ out.WriteString(rowsTag)
+ out.WriteString(">\n")
+ renderedTagOpen = true
+ }
+ t.htmlRenderRow(out, row, hint)
+ shouldRenderTagClose = true
+ }
+ }
+ if shouldRenderTagClose {
+ out.WriteString(" ")
+ out.WriteString(rowsTag)
+ out.WriteString(">\n")
+ }
+ }
+}
+
+func (t *Table) htmlRenderRowsFooter(out *strings.Builder) {
+ if len(t.rowsFooter) > 0 {
+ t.htmlRenderRows(out, t.rowsFooter, renderHint{isFooterRow: true})
+ }
+}
+
+func (t *Table) htmlRenderRowsHeader(out *strings.Builder) {
+ if len(t.rowsHeader) > 0 {
+ t.htmlRenderRows(out, t.rowsHeader, renderHint{isHeaderRow: true})
+ } else if t.autoIndex {
+ hint := renderHint{isAutoIndexRow: true, isHeaderRow: true}
+ t.htmlRenderRows(out, []rowStr{t.getAutoIndexColumnIDs()}, hint)
+ }
+}
+
+func (t *Table) htmlRenderTitle(out *strings.Builder) {
+ if t.title != "" {
+ align := t.style.Title.Align.HTMLProperty()
+ colors := t.style.Title.Colors.HTMLProperty()
+ title := t.style.Title.Format.Apply(t.title)
+
+ out.WriteString(" ')
+ out.WriteString(title)
+ out.WriteString("\n")
+ }
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go
new file mode 100644
index 0000000000..2627e012ba
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go
@@ -0,0 +1,330 @@
+package table
+
+import (
+ "fmt"
+ "strings"
+ "unicode"
+
+ "github.com/jedib0t/go-pretty/v6/text"
+)
+
+func (t *Table) analyzeAndStringify(row Row, hint renderHint) rowStr {
+ // update t.numColumns if this row is the longest seen till now
+ if len(row) > t.numColumns {
+ // init the slice for the first time; and pad it the rest of the time
+ if t.numColumns == 0 {
+ t.columnIsNonNumeric = make([]bool, len(row))
+ } else {
+ t.columnIsNonNumeric = append(t.columnIsNonNumeric, make([]bool, len(row)-t.numColumns)...)
+ }
+ // update t.numColumns
+ t.numColumns = len(row)
+ }
+
+ // convert each column to string and figure out if it has non-numeric data
+ rowOut := make(rowStr, len(row))
+ for colIdx, col := range row {
+ // if the column is not a number, keep track of it
+ if !hint.isHeaderRow && !hint.isFooterRow && !t.columnIsNonNumeric[colIdx] && !isNumber(col) {
+ t.columnIsNonNumeric[colIdx] = true
+ }
+
+ rowOut[colIdx] = t.analyzeAndStringifyColumn(colIdx, col, hint)
+ }
+ return rowOut
+}
+
+func (t *Table) analyzeAndStringifyColumn(colIdx int, col interface{}, hint renderHint) string {
+ // convert to a string and store it in the row
+ var colStr string
+ if transformer := t.getColumnTransformer(colIdx, hint); transformer != nil {
+ colStr = transformer(col)
+ } else if colStrVal, ok := col.(string); ok {
+ colStr = colStrVal
+ } else {
+ colStr = fmt.Sprint(col)
+ }
+ colStr = strings.ReplaceAll(colStr, "\t", " ")
+ colStr = text.ProcessCRLF(colStr)
+ return fmt.Sprintf("%s%s", t.style.Format.Direction.Modifier(), colStr)
+}
+
+func (t *Table) extractMaxColumnLengths(rows []rowStr, hint renderHint) {
+ for rowIdx, row := range rows {
+ hint.rowNumber = rowIdx + 1
+ t.extractMaxColumnLengthsFromRow(row, t.getMergedColumnIndices(row, hint))
+ }
+}
+
+func (t *Table) extractMaxColumnLengthsFromRow(row rowStr, mci mergedColumnIndices) {
+ for colIdx, colStr := range row {
+ longestLineLen := text.LongestLineLen(colStr)
+ maxColWidth := t.getColumnWidthMax(colIdx)
+ if maxColWidth > 0 && maxColWidth < longestLineLen {
+ longestLineLen = maxColWidth
+ }
+ mergedColumnsLength := mci.mergedLength(colIdx, t.maxColumnLengths)
+ if longestLineLen > mergedColumnsLength {
+ if mergedColumnsLength > 0 {
+ t.extractMaxColumnLengthsFromRowForMergedColumns(colIdx, longestLineLen, mci)
+ } else {
+ t.maxColumnLengths[colIdx] = longestLineLen
+ }
+ } else if maxColWidth == 0 && longestLineLen > t.maxColumnLengths[colIdx] {
+ t.maxColumnLengths[colIdx] = longestLineLen
+ }
+ }
+}
+
+func (t *Table) extractMaxColumnLengthsFromRowForMergedColumns(colIdx int, mergedColumnLength int, mci mergedColumnIndices) {
+ numMergedColumns := mci.len(colIdx)
+ mergedColumnLength -= (numMergedColumns - 1) * text.RuneWidthWithoutEscSequences(t.style.Box.MiddleSeparator)
+ maxLengthSplitAcrossColumns := mergedColumnLength / numMergedColumns
+ if maxLengthSplitAcrossColumns > t.maxColumnLengths[colIdx] {
+ t.maxColumnLengths[colIdx] = maxLengthSplitAcrossColumns
+ }
+ for otherColIdx := range mci[colIdx] {
+ if maxLengthSplitAcrossColumns > t.maxColumnLengths[otherColIdx] {
+ t.maxColumnLengths[otherColIdx] = maxLengthSplitAcrossColumns
+ }
+ }
+}
+
+func (t *Table) initForRender() {
+ // pick a default style if none was set until now
+ t.Style()
+
+ // reset rendering state
+ t.reset()
+
+ // initialize the column configs and normalize them
+ t.initForRenderColumnConfigs()
+
+ // initialize and stringify all the raw rows
+ t.initForRenderRows()
+
+ // find the longest continuous line in each column
+ t.initForRenderColumnLengths()
+ t.initForRenderMaxRowLength()
+ t.initForRenderPaddedColumns()
+
+ // generate a separator row and calculate maximum row length
+ t.initForRenderRowSeparator()
+
+ // reset the counter for the number of lines rendered
+ t.numLinesRendered = 0
+}
+
+func (t *Table) initForRenderColumnConfigs() {
+ t.columnConfigMap = map[int]ColumnConfig{}
+ for _, colCfg := range t.columnConfigs {
+ // find the column number if none provided; this logic can work only if
+ // a header row is present and has a column with the given name
+ if colCfg.Number == 0 {
+ for _, row := range t.rowsHeaderRaw {
+ colCfg.Number = row.findColumnNumber(colCfg.Name)
+ if colCfg.Number > 0 {
+ break
+ }
+ }
+ }
+ if colCfg.Number > 0 {
+ t.columnConfigMap[colCfg.Number-1] = colCfg
+ }
+ }
+}
+
+func (t *Table) initForRenderColumnLengths() {
+ t.maxColumnLengths = make([]int, t.numColumns)
+ t.extractMaxColumnLengths(t.rowsHeader, renderHint{isHeaderRow: true})
+ t.extractMaxColumnLengths(t.rows, renderHint{})
+ t.extractMaxColumnLengths(t.rowsFooter, renderHint{isFooterRow: true})
+
+ // increase the column lengths if any are under the limits
+ for colIdx := range t.maxColumnLengths {
+ minWidth := t.getColumnWidthMin(colIdx)
+ if minWidth > 0 && t.maxColumnLengths[colIdx] < minWidth {
+ t.maxColumnLengths[colIdx] = minWidth
+ }
+ }
+}
+
+func (t *Table) initForRenderHideColumns() {
+ if !t.hasHiddenColumns() {
+ return
+ }
+ colIdxMap := t.hideColumns()
+
+ // re-create columnIsNonNumeric with new column indices
+ columnIsNonNumeric := make([]bool, t.numColumns)
+ for oldColIdx, nonNumeric := range t.columnIsNonNumeric {
+ if newColIdx, ok := colIdxMap[oldColIdx]; ok {
+ columnIsNonNumeric[newColIdx] = nonNumeric
+ }
+ }
+ t.columnIsNonNumeric = columnIsNonNumeric
+
+ // re-create columnConfigMap with new column indices
+ columnConfigMap := make(map[int]ColumnConfig)
+ for oldColIdx, cc := range t.columnConfigMap {
+ if newColIdx, ok := colIdxMap[oldColIdx]; ok {
+ columnConfigMap[newColIdx] = cc
+ }
+ }
+ t.columnConfigMap = columnConfigMap
+}
+
+func (t *Table) initForRenderMaxRowLength() {
+ t.maxRowLength = 0
+ if t.autoIndex {
+ t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingLeft)
+ t.maxRowLength += len(fmt.Sprint(len(t.rows)))
+ t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingRight)
+ if t.style.Options.SeparateColumns {
+ t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.MiddleSeparator)
+ }
+ }
+ if t.style.Options.SeparateColumns {
+ t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.MiddleSeparator) * (t.numColumns - 1)
+ }
+ for _, maxColumnLength := range t.maxColumnLengths {
+ maxColumnLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingLeft + t.style.Box.PaddingRight)
+ t.maxRowLength += maxColumnLength
+ }
+ if t.style.Options.DrawBorder {
+ t.maxRowLength += text.RuneWidthWithoutEscSequences(t.style.Box.Left + t.style.Box.Right)
+ }
+}
+
+func (t *Table) initForRenderPaddedColumns() {
+ paddingSize := t.style.Size.WidthMin - t.maxRowLength
+ for paddingSize > 0 {
+ // distribute padding equally among all columns
+ numColumnsPadded := 0
+ for colIdx := 0; paddingSize > 0 && colIdx < t.numColumns; colIdx++ {
+ colWidthMax := t.getColumnWidthMax(colIdx)
+ if colWidthMax == 0 || t.maxColumnLengths[colIdx] < colWidthMax {
+ t.maxColumnLengths[colIdx]++
+ numColumnsPadded++
+ paddingSize--
+ }
+ }
+
+ // avoid endless looping because all columns are at max size and cannot
+ // be expanded any further
+ if numColumnsPadded == 0 {
+ break
+ }
+ }
+}
+
+func (t *Table) initForRenderRows() {
+ // auto-index: calc the index column's max length
+ t.autoIndexVIndexMaxLength = len(fmt.Sprint(len(t.rowsRaw)))
+
+ // stringify all the rows to make it easy to render
+ if t.rowPainter != nil {
+ t.rowsColors = make([]text.Colors, len(t.rowsRaw))
+ }
+ t.rows = t.initForRenderRowsStringify(t.rowsRaw, renderHint{})
+ t.rowsFooter = t.initForRenderRowsStringify(t.rowsFooterRaw, renderHint{isFooterRow: true})
+ t.rowsHeader = t.initForRenderRowsStringify(t.rowsHeaderRaw, renderHint{isHeaderRow: true})
+
+ // sort the rows as requested
+ t.initForRenderSortRows()
+
+ // suppress columns without any content
+ t.initForRenderSuppressColumns()
+
+ // strip out hidden columns
+ t.initForRenderHideColumns()
+}
+
+func (t *Table) initForRenderRowsStringify(rows []Row, hint renderHint) []rowStr {
+ rowsStr := make([]rowStr, len(rows))
+ for idx, row := range rows {
+ if t.rowPainter != nil && hint.isRegularRow() {
+ t.rowsColors[idx] = t.rowPainter(row)
+ }
+ rowsStr[idx] = t.analyzeAndStringify(row, hint)
+ }
+ return rowsStr
+}
+
+func (t *Table) initForRenderRowSeparator() {
+ t.rowSeparator = make(rowStr, t.numColumns)
+ for colIdx, maxColumnLength := range t.maxColumnLengths {
+ maxColumnLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingLeft + t.style.Box.PaddingRight)
+ t.rowSeparator[colIdx] = text.RepeatAndTrim(t.style.Box.MiddleHorizontal, maxColumnLength)
+ }
+}
+
+func (t *Table) initForRenderSortRows() {
+ if len(t.sortBy) == 0 {
+ return
+ }
+
+ // sort the rows
+ sortedRowIndices := t.getSortedRowIndices()
+ sortedRows := make([]rowStr, len(t.rows))
+ for idx := range t.rows {
+ sortedRows[idx] = t.rows[sortedRowIndices[idx]]
+ }
+ t.rows = sortedRows
+
+ // sort the rowsColors
+ if len(t.rowsColors) > 0 {
+ sortedRowsColors := make([]text.Colors, len(t.rows))
+ for idx := range t.rows {
+ sortedRowsColors[idx] = t.rowsColors[sortedRowIndices[idx]]
+ }
+ t.rowsColors = sortedRowsColors
+ }
+}
+
+func (t *Table) initForRenderSuppressColumns() {
+ shouldSuppressColumn := func(colIdx int) bool {
+ for _, row := range t.rows {
+ if colIdx < len(row) && row[colIdx] != "" {
+ // Columns may contain non-printable characters. For example
+ // the text.Direction modifiers. These should not be considered
+ // when deciding to suppress a column.
+ for _, r := range row[colIdx] {
+ if unicode.IsPrint(r) {
+ return false
+ }
+ }
+ return true
+ }
+ }
+ return true
+ }
+
+ if t.suppressEmptyColumns {
+ for colIdx := 0; colIdx < t.numColumns; colIdx++ {
+ if shouldSuppressColumn(colIdx) {
+ cc := t.columnConfigMap[colIdx]
+ cc.Hidden = true
+ t.columnConfigMap[colIdx] = cc
+ }
+ }
+ }
+}
+
+// reset initializes all the variables used to maintain rendering information
+// that are written to in this file
+func (t *Table) reset() {
+ t.autoIndexVIndexMaxLength = 0
+ t.columnConfigMap = nil
+ t.columnIsNonNumeric = nil
+ t.firstRowOfPage = true
+ t.maxColumnLengths = nil
+ t.maxRowLength = 0
+ t.numColumns = 0
+ t.numLinesRendered = 0
+ t.rowSeparator = nil
+ t.rows = nil
+ t.rowsColors = nil
+ t.rowsFooter = nil
+ t.rowsHeader = nil
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go
new file mode 100644
index 0000000000..adf573fc91
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go
@@ -0,0 +1,109 @@
+package table
+
+import (
+ "fmt"
+ "strings"
+)
+
+// RenderMarkdown renders the Table in Markdown format. Example:
+//
+// | # | First Name | Last Name | Salary | |
+// | ---:| --- | --- | ---:| --- |
+// | 1 | Arya | Stark | 3000 | |
+// | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
+// | 300 | Tyrion | Lannister | 5000 | |
+// | | | Total | 10000 | |
+func (t *Table) RenderMarkdown() string {
+ t.initForRender()
+
+ var out strings.Builder
+ if t.numColumns > 0 {
+ t.markdownRenderTitle(&out)
+ t.markdownRenderRowsHeader(&out)
+ t.markdownRenderRows(&out, t.rows, renderHint{})
+ t.markdownRenderRowsFooter(&out)
+ t.markdownRenderCaption(&out)
+ }
+ return t.render(&out)
+}
+
+func (t *Table) markdownRenderCaption(out *strings.Builder) {
+ if t.caption != "" {
+ out.WriteRune('\n')
+ out.WriteRune('_')
+ out.WriteString(t.caption)
+ out.WriteRune('_')
+ }
+}
+
+func (t *Table) markdownRenderRow(out *strings.Builder, row rowStr, hint renderHint) {
+ // when working on line number 2 or more, insert a newline first
+ if out.Len() > 0 {
+ out.WriteRune('\n')
+ }
+
+ // render each column up to the max. columns seen in all the rows
+ out.WriteRune('|')
+ for colIdx := 0; colIdx < t.numColumns; colIdx++ {
+ t.markdownRenderRowAutoIndex(out, colIdx, hint)
+
+ if hint.isSeparatorRow {
+ out.WriteString(t.getAlign(colIdx, hint).MarkdownProperty())
+ } else {
+ var colStr string
+ if colIdx < len(row) {
+ colStr = row[colIdx]
+ }
+ out.WriteRune(' ')
+ colStr = strings.ReplaceAll(colStr, "|", "\\|")
+ colStr = strings.ReplaceAll(colStr, "\n", "
")
+ out.WriteString(colStr)
+ out.WriteRune(' ')
+ }
+ out.WriteRune('|')
+ }
+}
+
+func (t *Table) markdownRenderRowAutoIndex(out *strings.Builder, colIdx int, hint renderHint) {
+ if colIdx == 0 && t.autoIndex {
+ out.WriteRune(' ')
+ if hint.isSeparatorRow {
+ out.WriteString("---:")
+ } else if hint.isRegularRow() {
+ out.WriteString(fmt.Sprintf("%d ", hint.rowNumber))
+ }
+ out.WriteRune('|')
+ }
+}
+
+func (t *Table) markdownRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) {
+ if len(rows) > 0 {
+ for idx, row := range rows {
+ hint.rowNumber = idx + 1
+ t.markdownRenderRow(out, row, hint)
+
+ if idx == len(rows)-1 && hint.isHeaderRow {
+ t.markdownRenderRow(out, t.rowSeparator, renderHint{isSeparatorRow: true})
+ }
+ }
+ }
+}
+
+func (t *Table) markdownRenderRowsFooter(out *strings.Builder) {
+ t.markdownRenderRows(out, t.rowsFooter, renderHint{isFooterRow: true})
+}
+
+func (t *Table) markdownRenderRowsHeader(out *strings.Builder) {
+ if len(t.rowsHeader) > 0 {
+ t.markdownRenderRows(out, t.rowsHeader, renderHint{isHeaderRow: true})
+ } else if t.autoIndex {
+ t.markdownRenderRows(out, []rowStr{t.getAutoIndexColumnIDs()}, renderHint{isAutoIndexRow: true, isHeaderRow: true})
+ }
+}
+
+func (t *Table) markdownRenderTitle(out *strings.Builder) {
+ if t.title != "" {
+ out.WriteString("# ")
+ out.WriteString(t.title)
+ }
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go b/vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go
new file mode 100644
index 0000000000..bb7397506f
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go
@@ -0,0 +1,73 @@
+package table
+
+import (
+ "fmt"
+ "strings"
+)
+
+func (t *Table) RenderTSV() string {
+ t.initForRender()
+
+ var out strings.Builder
+
+ if t.numColumns > 0 {
+ if t.title != "" {
+ out.WriteString(t.title)
+ }
+
+ if t.autoIndex && len(t.rowsHeader) == 0 {
+ t.tsvRenderRow(&out, t.getAutoIndexColumnIDs(), renderHint{isAutoIndexRow: true, isHeaderRow: true})
+ }
+
+ t.tsvRenderRows(&out, t.rowsHeader, renderHint{isHeaderRow: true})
+ t.tsvRenderRows(&out, t.rows, renderHint{})
+ t.tsvRenderRows(&out, t.rowsFooter, renderHint{isFooterRow: true})
+
+ if t.caption != "" {
+ out.WriteRune('\n')
+ out.WriteString(t.caption)
+ }
+ }
+
+ return t.render(&out)
+}
+
+func (t *Table) tsvRenderRow(out *strings.Builder, row rowStr, hint renderHint) {
+ if out.Len() > 0 {
+ out.WriteRune('\n')
+ }
+
+ for idx, col := range row {
+ if idx == 0 && t.autoIndex {
+ if hint.isRegularRow() {
+ out.WriteString(fmt.Sprint(hint.rowNumber))
+ }
+ out.WriteRune('\t')
+ }
+
+ if idx > 0 {
+ out.WriteRune('\t')
+ }
+
+ if strings.ContainsAny(col, "\t\n\"") || strings.Contains(col, " ") {
+ out.WriteString(fmt.Sprintf("\"%s\"", t.tsvFixDoubleQuotes(col)))
+ } else {
+ out.WriteString(col)
+ }
+ }
+
+ for colIdx := len(row); colIdx < t.numColumns; colIdx++ {
+ out.WriteRune('\t')
+ }
+}
+
+func (t *Table) tsvFixDoubleQuotes(str string) string {
+ return strings.Replace(str, "\"", "\"\"", -1)
+}
+
+func (t *Table) tsvRenderRows(out *strings.Builder, rows []rowStr, hint renderHint) {
+ for idx, row := range rows {
+ hint.rowNumber = idx + 1
+ t.tsvRenderRow(out, row, hint)
+ }
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/sort.go b/vendor/github.com/jedib0t/go-pretty/v6/table/sort.go
new file mode 100644
index 0000000000..7a47765aec
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/sort.go
@@ -0,0 +1,228 @@
+package table
+
+import (
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// SortBy defines What to sort (Column Name or Number), and How to sort (Mode).
+type SortBy struct {
+ // Name is the name of the Column as it appears in the first Header row.
+ // If a Header is not provided, or the name is not found in the header, this
+ // will not work.
+ Name string
+ // Number is the Column # from left. When specified, it overrides the Name
+ // property. If you know the exact Column number, use this instead of Name.
+ Number int
+
+ // Mode tells the Writer how to Sort. Asc/Dsc/etc.
+ Mode SortMode
+
+ // IgnoreCase makes sorting case-insensitive
+ IgnoreCase bool
+}
+
+// SortMode defines How to sort.
+type SortMode int
+
+const (
+ // Asc sorts the column in Ascending order alphabetically.
+ Asc SortMode = iota
+ // AscAlphaNumeric sorts the column in Ascending order alphabetically and
+ // then numerically.
+ AscAlphaNumeric
+ // AscNumeric sorts the column in Ascending order numerically.
+ AscNumeric
+ // AscNumericAlpha sorts the column in Ascending order numerically and
+ // then alphabetically.
+ AscNumericAlpha
+ // Dsc sorts the column in Descending order alphabetically.
+ Dsc
+ // DscAlphaNumeric sorts the column in Descending order alphabetically and
+ // then numerically.
+ DscAlphaNumeric
+ // DscNumeric sorts the column in Descending order numerically.
+ DscNumeric
+ // DscNumericAlpha sorts the column in Descending order numerically and
+ // then alphabetically.
+ DscNumericAlpha
+)
+
+type rowsSorter struct {
+ rows []rowStr
+ sortBy []SortBy
+ sortedIndices []int
+}
+
+// getSortedRowIndices sorts and returns the row indices in Sorted order as
+// directed by Table.sortBy which can be set using Table.SortBy(...)
+func (t *Table) getSortedRowIndices() []int {
+ sortedIndices := make([]int, len(t.rows))
+ for idx := range t.rows {
+ sortedIndices[idx] = idx
+ }
+
+ if t.sortBy != nil && len(t.sortBy) > 0 {
+ sort.Sort(rowsSorter{
+ rows: t.rows,
+ sortBy: t.parseSortBy(t.sortBy),
+ sortedIndices: sortedIndices,
+ })
+ }
+
+ return sortedIndices
+}
+
+func (t *Table) parseSortBy(sortBy []SortBy) []SortBy {
+ var resSortBy []SortBy
+ for _, col := range sortBy {
+ colNum := 0
+ if col.Number > 0 && col.Number <= t.numColumns {
+ colNum = col.Number
+ } else if col.Name != "" && len(t.rowsHeader) > 0 {
+ for idx, colName := range t.rowsHeader[0] {
+ if col.Name == colName {
+ colNum = idx + 1
+ break
+ }
+ }
+ }
+ if colNum > 0 {
+ resSortBy = append(resSortBy, SortBy{
+ Name: col.Name,
+ Number: colNum,
+ Mode: col.Mode,
+ IgnoreCase: col.IgnoreCase,
+ })
+ }
+ }
+ return resSortBy
+}
+
+func (rs rowsSorter) Len() int {
+ return len(rs.rows)
+}
+
+func (rs rowsSorter) Swap(i, j int) {
+ rs.sortedIndices[i], rs.sortedIndices[j] = rs.sortedIndices[j], rs.sortedIndices[i]
+}
+
+func (rs rowsSorter) Less(i, j int) bool {
+ shouldContinue, returnValue := false, false
+ realI, realJ := rs.sortedIndices[i], rs.sortedIndices[j]
+ for _, sortBy := range rs.sortBy {
+ // extract the values/cells from the rows for comparison
+ rowI, rowJ, colIdx := rs.rows[realI], rs.rows[realJ], sortBy.Number-1
+ iVal, jVal := "", ""
+ if colIdx < len(rowI) {
+ iVal = rowI[colIdx]
+ }
+ if colIdx < len(rowJ) {
+ jVal = rowJ[colIdx]
+ }
+
+ // compare and choose whether to continue
+ shouldContinue, returnValue = less(iVal, jVal, sortBy)
+ if !shouldContinue {
+ break
+ }
+ }
+ return returnValue
+}
+
+func less(iVal string, jVal string, sb SortBy) (bool, bool) {
+ if iVal == jVal {
+ return true, false
+ }
+
+ switch sb.Mode {
+ case Asc, Dsc:
+ return lessAlphabetic(iVal, jVal, sb)
+ case AscNumeric, DscNumeric:
+ return lessNumeric(iVal, jVal, sb)
+ default: // AscAlphaNumeric, AscNumericAlpha, DscAlphaNumeric, DscNumericAlpha
+ return lessMixedMode(iVal, jVal, sb)
+ }
+}
+
+func lessAlphabetic(iVal string, jVal string, sb SortBy) (bool, bool) {
+ if sb.IgnoreCase {
+ iLow := strings.ToLower(iVal)
+ jLow := strings.ToLower(jVal)
+ // when two strings are case-insensitive identical, compare them casesensitive.
+ // That makes sure to get a consistent sorting
+ identical := iLow == jLow
+ switch sb.Mode {
+ case Asc, AscAlphaNumeric, AscNumericAlpha:
+ return identical, (identical && iVal < jVal) || iLow < jLow
+ default: // Dsc, DscAlphaNumeric, DscNumericAlpha
+ return identical, (identical && iVal > jVal) || iLow > jLow
+ }
+ }
+ switch sb.Mode {
+ case Asc, AscAlphaNumeric, AscNumericAlpha:
+ return false, iVal < jVal
+ default: // Dsc, DscAlphaNumeric, DscNumericAlpha
+ return false, iVal > jVal
+ }
+}
+
+func lessAlphaNumericI(sb SortBy) (bool, bool) {
+ // i == "abc"; j == 5
+ switch sb.Mode {
+ case AscAlphaNumeric, DscAlphaNumeric:
+ return false, true
+ default: // AscNumericAlpha, DscNumericAlpha
+ return false, false
+ }
+}
+
+func lessAlphaNumericJ(sb SortBy) (bool, bool) {
+ // i == 5; j == "abc"
+ switch sb.Mode {
+ case AscAlphaNumeric, DscAlphaNumeric:
+ return false, false
+ default: // AscNumericAlpha, DscNumericAlpha:
+ return false, true
+ }
+}
+
+func lessMixedMode(iVal string, jVal string, sb SortBy) (bool, bool) {
+ iNumVal, iErr := strconv.ParseFloat(iVal, 64)
+ jNumVal, jErr := strconv.ParseFloat(jVal, 64)
+ if iErr != nil && jErr != nil { // both are alphanumeric
+ return lessAlphabetic(iVal, jVal, sb)
+ }
+ if iErr != nil { // iVal is alphabetic, jVal is numeric
+ return lessAlphaNumericI(sb)
+ }
+ if jErr != nil { // iVal is numeric, jVal is alphabetic
+ return lessAlphaNumericJ(sb)
+ }
+ // both values numeric
+ return lessNumericVal(iNumVal, jNumVal, sb)
+}
+
+func lessNumeric(iVal string, jVal string, sb SortBy) (bool, bool) {
+ iNumVal, iErr := strconv.ParseFloat(iVal, 64)
+ jNumVal, jErr := strconv.ParseFloat(jVal, 64)
+ if iErr != nil || jErr != nil {
+ return false, false
+ }
+
+ return lessNumericVal(iNumVal, jNumVal, sb)
+}
+
+func lessNumericVal(iVal float64, jVal float64, sb SortBy) (bool, bool) {
+ if iVal == jVal {
+ return true, false
+ }
+
+ switch sb.Mode {
+ case AscNumeric, AscAlphaNumeric, AscNumericAlpha:
+ return false, iVal < jVal
+ default: // DscNumeric, DscAlphaNumeric, DscNumericAlpha
+ return false, iVal > jVal
+ }
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/style.go b/vendor/github.com/jedib0t/go-pretty/v6/table/style.go
new file mode 100644
index 0000000000..23b32b1bd6
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/style.go
@@ -0,0 +1,918 @@
+package table
+
+import (
+ "github.com/jedib0t/go-pretty/v6/text"
+)
+
+// Style declares how to render the Table and provides very fine-grained control
+// on how the Table gets rendered on the Console.
+type Style struct {
+ Name string // name of the Style
+ Box BoxStyle // characters to use for the boxes
+ Color ColorOptions // colors to use for the rows and columns
+ Format FormatOptions // formatting options for the rows and columns
+ HTML HTMLOptions // rendering options for HTML mode
+ Options Options // misc. options for the table
+ Size SizeOptions // size (width) options for the table
+ Title TitleOptions // formation options for the title text
+}
+
+var (
+ // StyleDefault renders a Table like below:
+ // +-----+------------+-----------+--------+-----------------------------+
+ // | # | FIRST NAME | LAST NAME | SALARY | |
+ // +-----+------------+-----------+--------+-----------------------------+
+ // | 1 | Arya | Stark | 3000 | |
+ // | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
+ // | 300 | Tyrion | Lannister | 5000 | |
+ // +-----+------------+-----------+--------+-----------------------------+
+ // | | | TOTAL | 10000 | |
+ // +-----+------------+-----------+--------+-----------------------------+
+ StyleDefault = Style{
+ Name: "StyleDefault",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsDefault,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsDefault,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsDefault,
+ }
+
+ // StyleBold renders a Table like below:
+ // ┏━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ // ┃ # ┃ FIRST NAME ┃ LAST NAME ┃ SALARY ┃ ┃
+ // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
+ // ┃ 1 ┃ Arya ┃ Stark ┃ 3000 ┃ ┃
+ // ┃ 20 ┃ Jon ┃ Snow ┃ 2000 ┃ You know nothing, Jon Snow! ┃
+ // ┃ 300 ┃ Tyrion ┃ Lannister ┃ 5000 ┃ ┃
+ // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
+ // ┃ ┃ ┃ TOTAL ┃ 10000 ┃ ┃
+ // ┗━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ StyleBold = Style{
+ Name: "StyleBold",
+ Box: StyleBoxBold,
+ Color: ColorOptionsDefault,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsDefault,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsDefault,
+ }
+
+ // StyleColoredBright renders a Table without any borders or separators,
+ // and with Black text on Cyan background for Header/Footer and
+ // White background for other rows.
+ StyleColoredBright = Style{
+ Name: "StyleColoredBright",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsBright,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsDark,
+ }
+
+ // StyleColoredDark renders a Table without any borders or separators, and
+ // with Header/Footer in Cyan text and other rows with White text, all on
+ // Black background.
+ StyleColoredDark = Style{
+ Name: "StyleColoredDark",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsDark,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsBright,
+ }
+
+ // StyleColoredBlackOnBlueWhite renders a Table without any borders or
+ // separators, and with Black text on Blue background for Header/Footer and
+ // White background for other rows.
+ StyleColoredBlackOnBlueWhite = Style{
+ Name: "StyleColoredBlackOnBlueWhite",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsBlackOnBlueWhite,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsBlueOnBlack,
+ }
+
+ // StyleColoredBlackOnCyanWhite renders a Table without any borders or
+ // separators, and with Black text on Cyan background for Header/Footer and
+ // White background for other rows.
+ StyleColoredBlackOnCyanWhite = Style{
+ Name: "StyleColoredBlackOnCyanWhite",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsBlackOnCyanWhite,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsCyanOnBlack,
+ }
+
+ // StyleColoredBlackOnGreenWhite renders a Table without any borders or
+ // separators, and with Black text on Green background for Header/Footer and
+ // White background for other rows.
+ StyleColoredBlackOnGreenWhite = Style{
+ Name: "StyleColoredBlackOnGreenWhite",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsBlackOnGreenWhite,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsGreenOnBlack,
+ }
+
+ // StyleColoredBlackOnMagentaWhite renders a Table without any borders or
+ // separators, and with Black text on Magenta background for Header/Footer and
+ // White background for other rows.
+ StyleColoredBlackOnMagentaWhite = Style{
+ Name: "StyleColoredBlackOnMagentaWhite",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsBlackOnMagentaWhite,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsMagentaOnBlack,
+ }
+
+ // StyleColoredBlackOnYellowWhite renders a Table without any borders or
+ // separators, and with Black text on Yellow background for Header/Footer and
+ // White background for other rows.
+ StyleColoredBlackOnYellowWhite = Style{
+ Name: "StyleColoredBlackOnYellowWhite",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsBlackOnYellowWhite,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsYellowOnBlack,
+ }
+
+ // StyleColoredBlackOnRedWhite renders a Table without any borders or
+ // separators, and with Black text on Red background for Header/Footer and
+ // White background for other rows.
+ StyleColoredBlackOnRedWhite = Style{
+ Name: "StyleColoredBlackOnRedWhite",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsBlackOnRedWhite,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsRedOnBlack,
+ }
+
+ // StyleColoredBlueWhiteOnBlack renders a Table without any borders or
+ // separators, and with Header/Footer in Blue text and other rows with
+ // White text, all on Black background.
+ StyleColoredBlueWhiteOnBlack = Style{
+ Name: "StyleColoredBlueWhiteOnBlack",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsBlueWhiteOnBlack,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsBlackOnBlue,
+ }
+
+ // StyleColoredCyanWhiteOnBlack renders a Table without any borders or
+ // separators, and with Header/Footer in Cyan text and other rows with
+ // White text, all on Black background.
+ StyleColoredCyanWhiteOnBlack = Style{
+ Name: "StyleColoredCyanWhiteOnBlack",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsCyanWhiteOnBlack,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsBlackOnCyan,
+ }
+
+ // StyleColoredGreenWhiteOnBlack renders a Table without any borders or
+ // separators, and with Header/Footer in Green text and other rows with
+ // White text, all on Black background.
+ StyleColoredGreenWhiteOnBlack = Style{
+ Name: "StyleColoredGreenWhiteOnBlack",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsGreenWhiteOnBlack,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsBlackOnGreen,
+ }
+
+ // StyleColoredMagentaWhiteOnBlack renders a Table without any borders or
+ // separators, and with Header/Footer in Magenta text and other rows with
+ // White text, all on Black background.
+ StyleColoredMagentaWhiteOnBlack = Style{
+ Name: "StyleColoredMagentaWhiteOnBlack",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsMagentaWhiteOnBlack,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsBlackOnMagenta,
+ }
+
+ // StyleColoredRedWhiteOnBlack renders a Table without any borders or
+ // separators, and with Header/Footer in Red text and other rows with
+ // White text, all on Black background.
+ StyleColoredRedWhiteOnBlack = Style{
+ Name: "StyleColoredRedWhiteOnBlack",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsRedWhiteOnBlack,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsBlackOnRed,
+ }
+
+ // StyleColoredYellowWhiteOnBlack renders a Table without any borders or
+ // separators, and with Header/Footer in Yellow text and other rows with
+ // White text, all on Black background.
+ StyleColoredYellowWhiteOnBlack = Style{
+ Name: "StyleColoredYellowWhiteOnBlack",
+ Box: StyleBoxDefault,
+ Color: ColorOptionsYellowWhiteOnBlack,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsNoBordersAndSeparators,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsBlackOnYellow,
+ }
+
+ // StyleDouble renders a Table like below:
+ // ╔═════╦════════════╦═══════════╦════════╦═════════════════════════════╗
+ // ║ # ║ FIRST NAME ║ LAST NAME ║ SALARY ║ ║
+ // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣
+ // ║ 1 ║ Arya ║ Stark ║ 3000 ║ ║
+ // ║ 20 ║ Jon ║ Snow ║ 2000 ║ You know nothing, Jon Snow! ║
+ // ║ 300 ║ Tyrion ║ Lannister ║ 5000 ║ ║
+ // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣
+ // ║ ║ ║ TOTAL ║ 10000 ║ ║
+ // ╚═════╩════════════╩═══════════╩════════╩═════════════════════════════╝
+ StyleDouble = Style{
+ Name: "StyleDouble",
+ Box: StyleBoxDouble,
+ Color: ColorOptionsDefault,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsDefault,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsDefault,
+ }
+
+ // StyleLight renders a Table like below:
+ // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ │ │ TOTAL │ 10000 │ │
+ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+ StyleLight = Style{
+ Name: "StyleLight",
+ Box: StyleBoxLight,
+ Color: ColorOptionsDefault,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsDefault,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsDefault,
+ }
+
+ // StyleRounded renders a Table like below:
+ // ╭─────┬────────────┬───────────┬────────┬─────────────────────────────╮
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ │ │ TOTAL │ 10000 │ │
+ // ╰─────┴────────────┴───────────┴────────┴─────────────────────────────╯
+ StyleRounded = Style{
+ Name: "StyleRounded",
+ Box: StyleBoxRounded,
+ Color: ColorOptionsDefault,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsDefault,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsDefault,
+ }
+
+ // styleTest renders a Table like below:
+ // (-----^------------^-----------^--------^-----------------------------)
+ // [< #>||||< >]
+ // {-----+------------+-----------+--------+-----------------------------}
+ // [< 1>|||< 3000>|< >]
+ // [< 20>|||< 2000>|]
+ // [<300>|||< 5000>|< >]
+ // {-----+------------+-----------+--------+-----------------------------}
+ // [< >|< >||< 10000>|< >]
+ // \-----v------------v-----------v--------v-----------------------------/
+ styleTest = Style{
+ Name: "styleTest",
+ Box: styleBoxTest,
+ Color: ColorOptionsDefault,
+ Format: FormatOptionsDefault,
+ HTML: DefaultHTMLOptions,
+ Options: OptionsDefault,
+ Size: SizeOptionsDefault,
+ Title: TitleOptionsDefault,
+ }
+)
+
+// BoxStyle defines the characters/strings to use to render the borders and
+// separators for the Table.
+type BoxStyle struct {
+ BottomLeft string
+ BottomRight string
+ BottomSeparator string
+ EmptySeparator string
+ Left string
+ LeftSeparator string
+ MiddleHorizontal string
+ MiddleSeparator string
+ MiddleVertical string
+ PaddingLeft string
+ PaddingRight string
+ PageSeparator string
+ Right string
+ RightSeparator string
+ TopLeft string
+ TopRight string
+ TopSeparator string
+ UnfinishedRow string
+}
+
+var (
+ // StyleBoxDefault defines a Boxed-Table like below:
+ // +-----+------------+-----------+--------+-----------------------------+
+ // | # | FIRST NAME | LAST NAME | SALARY | |
+ // +-----+------------+-----------+--------+-----------------------------+
+ // | 1 | Arya | Stark | 3000 | |
+ // | 20 | Jon | Snow | 2000 | You know nothing, Jon Snow! |
+ // | 300 | Tyrion | Lannister | 5000 | |
+ // +-----+------------+-----------+--------+-----------------------------+
+ // | | | TOTAL | 10000 | |
+ // +-----+------------+-----------+--------+-----------------------------+
+ StyleBoxDefault = BoxStyle{
+ BottomLeft: "+",
+ BottomRight: "+",
+ BottomSeparator: "+",
+ EmptySeparator: " ",
+ Left: "|",
+ LeftSeparator: "+",
+ MiddleHorizontal: "-",
+ MiddleSeparator: "+",
+ MiddleVertical: "|",
+ PaddingLeft: " ",
+ PaddingRight: " ",
+ PageSeparator: "\n",
+ Right: "|",
+ RightSeparator: "+",
+ TopLeft: "+",
+ TopRight: "+",
+ TopSeparator: "+",
+ UnfinishedRow: " ~",
+ }
+
+ // StyleBoxBold defines a Boxed-Table like below:
+ // ┏━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
+ // ┃ # ┃ FIRST NAME ┃ LAST NAME ┃ SALARY ┃ ┃
+ // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
+ // ┃ 1 ┃ Arya ┃ Stark ┃ 3000 ┃ ┃
+ // ┃ 20 ┃ Jon ┃ Snow ┃ 2000 ┃ You know nothing, Jon Snow! ┃
+ // ┃ 300 ┃ Tyrion ┃ Lannister ┃ 5000 ┃ ┃
+ // ┣━━━━━╋━━━━━━━━━━━━╋━━━━━━━━━━━╋━━━━━━━━╋━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
+ // ┃ ┃ ┃ TOTAL ┃ 10000 ┃ ┃
+ // ┗━━━━━┻━━━━━━━━━━━━┻━━━━━━━━━━━┻━━━━━━━━┻━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
+ StyleBoxBold = BoxStyle{
+ BottomLeft: "┗",
+ BottomRight: "┛",
+ BottomSeparator: "┻",
+ EmptySeparator: " ",
+ Left: "┃",
+ LeftSeparator: "┣",
+ MiddleHorizontal: "━",
+ MiddleSeparator: "╋",
+ MiddleVertical: "┃",
+ PaddingLeft: " ",
+ PaddingRight: " ",
+ PageSeparator: "\n",
+ Right: "┃",
+ RightSeparator: "┫",
+ TopLeft: "┏",
+ TopRight: "┓",
+ TopSeparator: "┳",
+ UnfinishedRow: " ≈",
+ }
+
+ // StyleBoxDouble defines a Boxed-Table like below:
+ // ╔═════╦════════════╦═══════════╦════════╦═════════════════════════════╗
+ // ║ # ║ FIRST NAME ║ LAST NAME ║ SALARY ║ ║
+ // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣
+ // ║ 1 ║ Arya ║ Stark ║ 3000 ║ ║
+ // ║ 20 ║ Jon ║ Snow ║ 2000 ║ You know nothing, Jon Snow! ║
+ // ║ 300 ║ Tyrion ║ Lannister ║ 5000 ║ ║
+ // ╠═════╬════════════╬═══════════╬════════╬═════════════════════════════╣
+ // ║ ║ ║ TOTAL ║ 10000 ║ ║
+ // ╚═════╩════════════╩═══════════╩════════╩═════════════════════════════╝
+ StyleBoxDouble = BoxStyle{
+ BottomLeft: "╚",
+ BottomRight: "╝",
+ BottomSeparator: "╩",
+ EmptySeparator: " ",
+ Left: "║",
+ LeftSeparator: "╠",
+ MiddleHorizontal: "═",
+ MiddleSeparator: "╬",
+ MiddleVertical: "║",
+ PaddingLeft: " ",
+ PaddingRight: " ",
+ PageSeparator: "\n",
+ Right: "║",
+ RightSeparator: "╣",
+ TopLeft: "╔",
+ TopRight: "╗",
+ TopSeparator: "╦",
+ UnfinishedRow: " ≈",
+ }
+
+ // StyleBoxLight defines a Boxed-Table like below:
+ // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ │ │ TOTAL │ 10000 │ │
+ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+ StyleBoxLight = BoxStyle{
+ BottomLeft: "└",
+ BottomRight: "┘",
+ BottomSeparator: "┴",
+ EmptySeparator: " ",
+ Left: "│",
+ LeftSeparator: "├",
+ MiddleHorizontal: "─",
+ MiddleSeparator: "┼",
+ MiddleVertical: "│",
+ PaddingLeft: " ",
+ PaddingRight: " ",
+ PageSeparator: "\n",
+ Right: "│",
+ RightSeparator: "┤",
+ TopLeft: "┌",
+ TopRight: "┐",
+ TopSeparator: "┬",
+ UnfinishedRow: " ≈",
+ }
+
+ // StyleBoxRounded defines a Boxed-Table like below:
+ // ╭─────┬────────────┬───────────┬────────┬─────────────────────────────╮
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ │ │ TOTAL │ 10000 │ │
+ // ╰─────┴────────────┴───────────┴────────┴─────────────────────────────╯
+ StyleBoxRounded = BoxStyle{
+ BottomLeft: "╰",
+ BottomRight: "╯",
+ BottomSeparator: "┴",
+ EmptySeparator: " ",
+ Left: "│",
+ LeftSeparator: "├",
+ MiddleHorizontal: "─",
+ MiddleSeparator: "┼",
+ MiddleVertical: "│",
+ PaddingLeft: " ",
+ PaddingRight: " ",
+ PageSeparator: "\n",
+ Right: "│",
+ RightSeparator: "┤",
+ TopLeft: "╭",
+ TopRight: "╮",
+ TopSeparator: "┬",
+ UnfinishedRow: " ≈",
+ }
+
+ // styleBoxTest defines a Boxed-Table like below:
+ // (-----^------------^-----------^--------^-----------------------------)
+ // [< #>||||< >]
+ // {-----+------------+-----------+--------+-----------------------------}
+ // [< 1>|||< 3000>|< >]
+ // [< 20>|||< 2000>|]
+ // [<300>|||< 5000>|< >]
+ // {-----+------------+-----------+--------+-----------------------------}
+ // [< >|< >||< 10000>|< >]
+ // \-----v------------v-----------v--------v-----------------------------/
+ styleBoxTest = BoxStyle{
+ BottomLeft: "\\",
+ BottomRight: "/",
+ BottomSeparator: "v",
+ EmptySeparator: " ",
+ Left: "[",
+ LeftSeparator: "{",
+ MiddleHorizontal: "--",
+ MiddleSeparator: "+",
+ MiddleVertical: "|",
+ PaddingLeft: "<",
+ PaddingRight: ">",
+ PageSeparator: "\n",
+ Right: "]",
+ RightSeparator: "}",
+ TopLeft: "(",
+ TopRight: ")",
+ TopSeparator: "^",
+ UnfinishedRow: " ~~~",
+ }
+)
+
+// ColorOptions defines the ANSI colors to use for parts of the Table.
+type ColorOptions struct {
+ Border text.Colors // borders (if nil, uses one of the below)
+ Footer text.Colors // footer row(s) colors
+ Header text.Colors // header row(s) colors
+ IndexColumn text.Colors // index-column colors (row #, etc.)
+ Row text.Colors // regular row(s) colors
+ RowAlternate text.Colors // regular row(s) colors for the even-numbered rows
+ Separator text.Colors // separators (if nil, uses one of the above)
+}
+
+var (
+ // ColorOptionsDefault defines sensible ANSI color options - basically NONE.
+ ColorOptionsDefault = ColorOptions{}
+
+ // ColorOptionsBright renders dark text on bright background.
+ ColorOptionsBright = ColorOptionsBlackOnCyanWhite
+
+ // ColorOptionsDark renders bright text on dark background.
+ ColorOptionsDark = ColorOptionsCyanWhiteOnBlack
+
+ // ColorOptionsBlackOnBlueWhite renders Black text on Blue/White background.
+ ColorOptionsBlackOnBlueWhite = ColorOptions{
+ Footer: text.Colors{text.BgBlue, text.FgBlack},
+ Header: text.Colors{text.BgHiBlue, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiBlue, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlackOnCyanWhite renders Black text on Cyan/White background.
+ ColorOptionsBlackOnCyanWhite = ColorOptions{
+ Footer: text.Colors{text.BgCyan, text.FgBlack},
+ Header: text.Colors{text.BgHiCyan, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiCyan, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlackOnGreenWhite renders Black text on Green/White
+ // background.
+ ColorOptionsBlackOnGreenWhite = ColorOptions{
+ Footer: text.Colors{text.BgGreen, text.FgBlack},
+ Header: text.Colors{text.BgHiGreen, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiGreen, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlackOnMagentaWhite renders Black text on Magenta/White
+ // background.
+ ColorOptionsBlackOnMagentaWhite = ColorOptions{
+ Footer: text.Colors{text.BgMagenta, text.FgBlack},
+ Header: text.Colors{text.BgHiMagenta, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiMagenta, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlackOnRedWhite renders Black text on Red/White background.
+ ColorOptionsBlackOnRedWhite = ColorOptions{
+ Footer: text.Colors{text.BgRed, text.FgBlack},
+ Header: text.Colors{text.BgHiRed, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiRed, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlackOnYellowWhite renders Black text on Yellow/White
+ // background.
+ ColorOptionsBlackOnYellowWhite = ColorOptions{
+ Footer: text.Colors{text.BgYellow, text.FgBlack},
+ Header: text.Colors{text.BgHiYellow, text.FgBlack},
+ IndexColumn: text.Colors{text.BgHiYellow, text.FgBlack},
+ Row: text.Colors{text.BgHiWhite, text.FgBlack},
+ RowAlternate: text.Colors{text.BgWhite, text.FgBlack},
+ }
+
+ // ColorOptionsBlueWhiteOnBlack renders Blue/White text on Black background.
+ ColorOptionsBlueWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgBlue, text.BgHiBlack},
+ Header: text.Colors{text.FgHiBlue, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiBlue, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+
+ // ColorOptionsCyanWhiteOnBlack renders Cyan/White text on Black background.
+ ColorOptionsCyanWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgCyan, text.BgHiBlack},
+ Header: text.Colors{text.FgHiCyan, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiCyan, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+
+ // ColorOptionsGreenWhiteOnBlack renders Green/White text on Black
+ // background.
+ ColorOptionsGreenWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgGreen, text.BgHiBlack},
+ Header: text.Colors{text.FgHiGreen, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiGreen, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+
+ // ColorOptionsMagentaWhiteOnBlack renders Magenta/White text on Black
+ // background.
+ ColorOptionsMagentaWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgMagenta, text.BgHiBlack},
+ Header: text.Colors{text.FgHiMagenta, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiMagenta, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+
+ // ColorOptionsRedWhiteOnBlack renders Red/White text on Black background.
+ ColorOptionsRedWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgRed, text.BgHiBlack},
+ Header: text.Colors{text.FgHiRed, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiRed, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+
+ // ColorOptionsYellowWhiteOnBlack renders Yellow/White text on Black
+ // background.
+ ColorOptionsYellowWhiteOnBlack = ColorOptions{
+ Footer: text.Colors{text.FgYellow, text.BgHiBlack},
+ Header: text.Colors{text.FgHiYellow, text.BgHiBlack},
+ IndexColumn: text.Colors{text.FgHiYellow, text.BgHiBlack},
+ Row: text.Colors{text.FgHiWhite, text.BgBlack},
+ RowAlternate: text.Colors{text.FgWhite, text.BgBlack},
+ }
+)
+
+// FormatOptions defines the text-formatting to perform on parts of the Table.
+type FormatOptions struct {
+ Direction text.Direction // (forced) BiDi direction for each Column
+ Footer text.Format // footer row(s) text format
+ Header text.Format // header row(s) text format
+ Row text.Format // (data) row(s) text format
+}
+
+// FormatOptionsDefault defines sensible formatting options.
+var FormatOptionsDefault = FormatOptions{
+ Footer: text.FormatUpper,
+ Header: text.FormatUpper,
+ Row: text.FormatDefault,
+}
+
+// HTMLOptions defines the global options to control HTML rendering.
+type HTMLOptions struct {
+ CSSClass string // CSS class to set on the overall tag
+ EmptyColumn string // string to replace "" columns with (entire content being "")
+ EscapeText bool // escape text into HTML-safe content?
+ Newline string // string to replace "\n" characters with
+}
+
+// DefaultHTMLOptions defines sensible HTML rendering defaults.
+var DefaultHTMLOptions = HTMLOptions{
+ CSSClass: DefaultHTMLCSSClass,
+ EmptyColumn: " ",
+ EscapeText: true,
+ Newline: "
",
+}
+
+// Options defines the global options that determine how the Table is
+// rendered.
+type Options struct {
+ // DoNotColorBordersAndSeparators disables coloring all the borders and row
+ // or column separators.
+ DoNotColorBordersAndSeparators bool
+
+ // DrawBorder enables or disables drawing the border around the Table.
+ // Example of a table where it is disabled:
+ // # │ FIRST NAME │ LAST NAME │ SALARY │
+ // ─────┼────────────┼───────────┼────────┼─────────────────────────────
+ // 1 │ Arya │ Stark │ 3000 │
+ // 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow!
+ // 300 │ Tyrion │ Lannister │ 5000 │
+ // ─────┼────────────┼───────────┼────────┼─────────────────────────────
+ // │ │ TOTAL │ 10000 │
+ DrawBorder bool
+
+ // SeparateColumns enables or disable drawing border between columns.
+ // Example of a table where it is disabled:
+ // ┌─────────────────────────────────────────────────────────────────┐
+ // │ # FIRST NAME LAST NAME SALARY │
+ // ├─────────────────────────────────────────────────────────────────┤
+ // │ 1 Arya Stark 3000 │
+ // │ 20 Jon Snow 2000 You know nothing, Jon Snow! │
+ // │ 300 Tyrion Lannister 5000 │
+ // │ TOTAL 10000 │
+ // └─────────────────────────────────────────────────────────────────┘
+ SeparateColumns bool
+
+ // SeparateFooter enables or disable drawing border between the footer and
+ // the rows. Example of a table where it is disabled:
+ // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // │ │ │ TOTAL │ 10000 │ │
+ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+ SeparateFooter bool
+
+ // SeparateHeader enables or disable drawing border between the header and
+ // the rows. Example of a table where it is disabled:
+ // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ │ │ TOTAL │ 10000 │ │
+ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+ SeparateHeader bool
+
+ // SeparateRows enables or disables drawing separators between each row.
+ // Example of a table where it is enabled:
+ // ┌─────┬────────────┬───────────┬────────┬─────────────────────────────┐
+ // │ # │ FIRST NAME │ LAST NAME │ SALARY │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 1 │ Arya │ Stark │ 3000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 20 │ Jon │ Snow │ 2000 │ You know nothing, Jon Snow! │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ 300 │ Tyrion │ Lannister │ 5000 │ │
+ // ├─────┼────────────┼───────────┼────────┼─────────────────────────────┤
+ // │ │ │ TOTAL │ 10000 │ │
+ // └─────┴────────────┴───────────┴────────┴─────────────────────────────┘
+ SeparateRows bool
+}
+
+var (
+ // OptionsDefault defines sensible global options.
+ OptionsDefault = Options{
+ DrawBorder: true,
+ SeparateColumns: true,
+ SeparateFooter: true,
+ SeparateHeader: true,
+ SeparateRows: false,
+ }
+
+ // OptionsNoBorders sets up a table without any borders.
+ OptionsNoBorders = Options{
+ DrawBorder: false,
+ SeparateColumns: true,
+ SeparateFooter: true,
+ SeparateHeader: true,
+ SeparateRows: false,
+ }
+
+ // OptionsNoBordersAndSeparators sets up a table without any borders or
+ // separators.
+ OptionsNoBordersAndSeparators = Options{
+ DrawBorder: false,
+ SeparateColumns: false,
+ SeparateFooter: false,
+ SeparateHeader: false,
+ SeparateRows: false,
+ }
+)
+
+// SizeOptions defines the way to control the width of the table output.
+type SizeOptions struct {
+ // WidthMax is the maximum allotted width for the full row;
+ // any content beyond this will be truncated using the text
+ // in Style.Box.UnfinishedRow
+ WidthMax int
+ // WidthMin is the minimum allotted width for the full row;
+ // columns will be auto-expanded until the overall width
+ // is met
+ WidthMin int
+}
+
+var (
+ // SizeOptionsDefault defines sensible size options - basically NONE.
+ SizeOptionsDefault = SizeOptions{
+ WidthMax: 0,
+ WidthMin: 0,
+ }
+)
+
+// TitleOptions defines the way the title text is to be rendered.
+type TitleOptions struct {
+ Align text.Align
+ Colors text.Colors
+ Format text.Format
+}
+
+var (
+ // TitleOptionsDefault defines sensible title options - basically NONE.
+ TitleOptionsDefault = TitleOptions{}
+
+ // TitleOptionsBright renders Bright Bold text on Dark background.
+ TitleOptionsBright = TitleOptionsBlackOnCyan
+
+ // TitleOptionsDark renders Dark Bold text on Bright background.
+ TitleOptionsDark = TitleOptionsCyanOnBlack
+
+ // TitleOptionsBlackOnBlue renders Black text on Blue background.
+ TitleOptionsBlackOnBlue = TitleOptions{
+ Colors: append(ColorOptionsBlackOnBlueWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlackOnCyan renders Black Bold text on Cyan background.
+ TitleOptionsBlackOnCyan = TitleOptions{
+ Colors: append(ColorOptionsBlackOnCyanWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlackOnGreen renders Black Bold text onGreen background.
+ TitleOptionsBlackOnGreen = TitleOptions{
+ Colors: append(ColorOptionsBlackOnGreenWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlackOnMagenta renders Black Bold text on Magenta background.
+ TitleOptionsBlackOnMagenta = TitleOptions{
+ Colors: append(ColorOptionsBlackOnMagentaWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlackOnRed renders Black Bold text on Red background.
+ TitleOptionsBlackOnRed = TitleOptions{
+ Colors: append(ColorOptionsBlackOnRedWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlackOnYellow renders Black Bold text on Yellow background.
+ TitleOptionsBlackOnYellow = TitleOptions{
+ Colors: append(ColorOptionsBlackOnYellowWhite.Header, text.Bold),
+ }
+
+ // TitleOptionsBlueOnBlack renders Blue Bold text on Black background.
+ TitleOptionsBlueOnBlack = TitleOptions{
+ Colors: append(ColorOptionsBlueWhiteOnBlack.Header, text.Bold),
+ }
+
+ // TitleOptionsCyanOnBlack renders Cyan Bold text on Black background.
+ TitleOptionsCyanOnBlack = TitleOptions{
+ Colors: append(ColorOptionsCyanWhiteOnBlack.Header, text.Bold),
+ }
+
+ // TitleOptionsGreenOnBlack renders Green Bold text on Black background.
+ TitleOptionsGreenOnBlack = TitleOptions{
+ Colors: append(ColorOptionsGreenWhiteOnBlack.Header, text.Bold),
+ }
+
+ // TitleOptionsMagentaOnBlack renders Magenta Bold text on Black background.
+ TitleOptionsMagentaOnBlack = TitleOptions{
+ Colors: append(ColorOptionsMagentaWhiteOnBlack.Header, text.Bold),
+ }
+
+ // TitleOptionsRedOnBlack renders Red Bold text on Black background.
+ TitleOptionsRedOnBlack = TitleOptions{
+ Colors: append(ColorOptionsRedWhiteOnBlack.Header, text.Bold),
+ }
+
+ // TitleOptionsYellowOnBlack renders Yellow Bold text on Black background.
+ TitleOptionsYellowOnBlack = TitleOptions{
+ Colors: append(ColorOptionsYellowWhiteOnBlack.Header, text.Bold),
+ }
+)
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/table.go b/vendor/github.com/jedib0t/go-pretty/v6/table/table.go
new file mode 100644
index 0000000000..34e5a348d1
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/table.go
@@ -0,0 +1,850 @@
+package table
+
+import (
+ "fmt"
+ "io"
+ "strings"
+ "time"
+ "unicode"
+
+ "github.com/jedib0t/go-pretty/v6/text"
+)
+
+// Row defines a single row in the Table.
+type Row []interface{}
+
+func (r Row) findColumnNumber(colName string) int {
+ for colIdx, col := range r {
+ if fmt.Sprint(col) == colName {
+ return colIdx + 1
+ }
+ }
+ return 0
+}
+
+// RowPainter is a custom function that takes a Row as input and returns the
+// text.Colors{} to use on the entire row
+type RowPainter func(row Row) text.Colors
+
+// rowStr defines a single row in the Table comprised of just string objects.
+type rowStr []string
+
+// areEqual returns true if the contents of the 2 given columns are the same
+func (row rowStr) areEqual(colIdx1 int, colIdx2 int) bool {
+ return colIdx1 >= 0 && colIdx2 < len(row) && row[colIdx1] == row[colIdx2]
+}
+
+// Table helps print a 2-dimensional array in a human-readable pretty-table.
+type Table struct {
+ // allowedRowLength is the max allowed length for a row (or line of output)
+ allowedRowLength int
+ // enable automatic indexing of the rows and columns like a spreadsheet?
+ autoIndex bool
+ // autoIndexVIndexMaxLength denotes the length in chars for the last row
+ autoIndexVIndexMaxLength int
+ // caption stores the text to be rendered just below the table; and doesn't
+ // get used when rendered as a CSV
+ caption string
+ // columnIsNonNumeric stores if a column contains non-numbers in all rows
+ columnIsNonNumeric []bool
+ // columnConfigs stores the custom-configuration for 1 or more columns
+ columnConfigs []ColumnConfig
+ // columnConfigMap stores the custom-configuration by column
+ // number and is generated before rendering
+ columnConfigMap map[int]ColumnConfig
+ // firstRowOfPage tells if the renderer is on the first row of a page?
+ firstRowOfPage bool
+ // htmlCSSClass stores the HTML CSS Class to use on the node
+ htmlCSSClass string
+ // indexColumn stores the number of the column considered as the "index"
+ indexColumn int
+ // maxColumnLengths stores the length of the longest line in each column
+ maxColumnLengths []int
+ // maxRowLength stores the length of the longest row
+ maxRowLength int
+ // numColumns stores the (max.) number of columns seen
+ numColumns int
+ // numLinesRendered keeps track of the number of lines rendered and helps in
+ // paginating long tables
+ numLinesRendered int
+ // outputMirror stores an io.Writer where the "Render" functions would write
+ outputMirror io.Writer
+ // pager controls how the output is separated into pages
+ pager pager
+ // rows stores the rows that make up the body (in string form)
+ rows []rowStr
+ // rowsColors stores the text.Colors over-rides for each row as defined by
+ // rowPainter
+ rowsColors []text.Colors
+ // rowsConfigs stores RowConfig for each row
+ rowsConfigMap map[int]RowConfig
+ // rowsRaw stores the rows that make up the body
+ rowsRaw []Row
+ // rowsFooter stores the rows that make up the footer (in string form)
+ rowsFooter []rowStr
+ // rowsFooterConfigs stores RowConfig for each footer row
+ rowsFooterConfigMap map[int]RowConfig
+ // rowsFooterRaw stores the rows that make up the footer
+ rowsFooterRaw []Row
+ // rowsHeader stores the rows that make up the header (in string form)
+ rowsHeader []rowStr
+ // rowsHeaderConfigs stores RowConfig for each header row
+ rowsHeaderConfigMap map[int]RowConfig
+ // rowsHeaderRaw stores the rows that make up the header
+ rowsHeaderRaw []Row
+ // rowPainter is a custom function that given a Row, returns the colors to
+ // use on the entire row
+ rowPainter RowPainter
+ // rowSeparator is a dummy row that contains the separator columns (dashes
+ // that make up the separator between header/body/footer
+ rowSeparator rowStr
+ // separators is used to keep track of all rowIndices after which a
+ // separator has to be rendered
+ separators map[int]bool
+ // sortBy stores a map of Column
+ sortBy []SortBy
+ // style contains all the strings used to draw the table, and more
+ style *Style
+ // suppressEmptyColumns hides columns which have no content on all regular
+ // rows
+ suppressEmptyColumns bool
+ // suppressTrailingSpaces removes all trailing spaces from the end of the last column
+ suppressTrailingSpaces bool
+ // title contains the text to appear above the table
+ title string
+}
+
+// AppendFooter appends the row to the List of footers to render.
+//
+// Only the first item in the "config" will be tagged against this row.
+func (t *Table) AppendFooter(row Row, config ...RowConfig) {
+ t.rowsFooterRaw = append(t.rowsFooterRaw, row)
+ if len(config) > 0 {
+ if t.rowsFooterConfigMap == nil {
+ t.rowsFooterConfigMap = make(map[int]RowConfig)
+ }
+ t.rowsFooterConfigMap[len(t.rowsFooterRaw)-1] = config[0]
+ }
+}
+
+// AppendHeader appends the row to the List of headers to render.
+//
+// Only the first item in the "config" will be tagged against this row.
+func (t *Table) AppendHeader(row Row, config ...RowConfig) {
+ t.rowsHeaderRaw = append(t.rowsHeaderRaw, row)
+ if len(config) > 0 {
+ if t.rowsHeaderConfigMap == nil {
+ t.rowsHeaderConfigMap = make(map[int]RowConfig)
+ }
+ t.rowsHeaderConfigMap[len(t.rowsHeaderRaw)-1] = config[0]
+ }
+}
+
+// AppendRow appends the row to the List of rows to render.
+//
+// Only the first item in the "config" will be tagged against this row.
+func (t *Table) AppendRow(row Row, config ...RowConfig) {
+ t.rowsRaw = append(t.rowsRaw, row)
+ if len(config) > 0 {
+ if t.rowsConfigMap == nil {
+ t.rowsConfigMap = make(map[int]RowConfig)
+ }
+ t.rowsConfigMap[len(t.rowsRaw)-1] = config[0]
+ }
+}
+
+// AppendRows appends the rows to the List of rows to render.
+//
+// Only the first item in the "config" will be tagged against all the rows.
+func (t *Table) AppendRows(rows []Row, config ...RowConfig) {
+ for _, row := range rows {
+ t.AppendRow(row, config...)
+ }
+}
+
+// AppendSeparator helps render a separator row after the current last row. You
+// could call this function over and over, but it will be a no-op unless you
+// call AppendRow or AppendRows in between. Likewise, if the last thing you
+// append is a separator, it will not be rendered in addition to the usual table
+// separator.
+//
+// ******************************************************************************
+// Please note the following caveats:
+// 1. SetPageSize(): this may end up creating consecutive separator rows near
+// the end of a page or at the beginning of a page
+// 2. SortBy(): since SortBy could inherently alter the ordering of rows, the
+// separators may not appear after the row it was originally intended to
+// follow
+//
+// ******************************************************************************
+func (t *Table) AppendSeparator() {
+ if t.separators == nil {
+ t.separators = make(map[int]bool)
+ }
+ if len(t.rowsRaw) > 0 {
+ t.separators[len(t.rowsRaw)-1] = true
+ }
+}
+
+// Length returns the number of rows to be rendered.
+func (t *Table) Length() int {
+ return len(t.rowsRaw)
+}
+
+// Pager returns an object that splits the table output into pages and
+// lets you move back and forth through them.
+func (t *Table) Pager(opts ...PagerOption) Pager {
+ for _, opt := range opts {
+ opt(t)
+ }
+
+ // use a temporary page separator for splitting up the pages
+ tempPageSep := fmt.Sprintf("%p // page separator // %d", t.rows, time.Now().UnixNano())
+
+ // backup
+ origOutputMirror, origPageSep := t.outputMirror, t.Style().Box.PageSeparator
+ // restore on exit
+ defer func() {
+ t.outputMirror = origOutputMirror
+ t.Style().Box.PageSeparator = origPageSep
+ }()
+ // override
+ t.outputMirror = nil
+ t.Style().Box.PageSeparator = tempPageSep
+ // render
+ t.pager.pages = strings.Split(t.Render(), tempPageSep)
+
+ return &t.pager
+}
+
+// ResetFooters resets and clears all the Footer rows appended earlier.
+func (t *Table) ResetFooters() {
+ t.rowsFooterRaw = nil
+}
+
+// ResetHeaders resets and clears all the Header rows appended earlier.
+func (t *Table) ResetHeaders() {
+ t.rowsHeaderRaw = nil
+}
+
+// ResetRows resets and clears all the rows appended earlier.
+func (t *Table) ResetRows() {
+ t.rowsRaw = nil
+ t.separators = nil
+}
+
+// SetAllowedRowLength sets the maximum allowed length or a row (or line of
+// output) when rendered as a table. Rows that are longer than this limit will
+// be "snipped" to the length. Length has to be a positive value to take effect.
+//
+// Deprecated: in favor if Style().Size.WidthMax
+func (t *Table) SetAllowedRowLength(length int) {
+ t.allowedRowLength = length
+}
+
+// SetAutoIndex adds a generated header with columns such as "A", "B", "C", etc.
+// and a leading column with the row number similar to what you'd see on any
+// spreadsheet application. NOTE: Appending a Header will void this
+// functionality.
+func (t *Table) SetAutoIndex(autoIndex bool) {
+ t.autoIndex = autoIndex
+}
+
+// SetCaption sets the text to be rendered just below the table. This will not
+// show up when the Table is rendered as a CSV.
+func (t *Table) SetCaption(format string, a ...interface{}) {
+ t.caption = fmt.Sprintf(format, a...)
+}
+
+// SetColumnConfigs sets the configs for each Column.
+func (t *Table) SetColumnConfigs(configs []ColumnConfig) {
+ t.columnConfigs = configs
+}
+
+// SetHTMLCSSClass sets the HTML CSS Class to use on the node
+// when rendering the Table in HTML format.
+//
+// Deprecated: in favor of Style().HTML.CSSClass
+func (t *Table) SetHTMLCSSClass(cssClass string) {
+ t.htmlCSSClass = cssClass
+}
+
+// SetIndexColumn sets the given Column # as the column that has the row
+// "Number". Valid values range from 1 to N. Note that this is not 0-indexed.
+func (t *Table) SetIndexColumn(colNum int) {
+ t.indexColumn = colNum
+}
+
+// SetOutputMirror sets an io.Writer for all the Render functions to "Write" to
+// in addition to returning a string.
+func (t *Table) SetOutputMirror(mirror io.Writer) {
+ t.outputMirror = mirror
+ t.pager.SetOutputMirror(mirror)
+}
+
+// SetPageSize sets the maximum number of lines to render before rendering the
+// header rows again. This can be useful when dealing with tables containing a
+// long list of rows that can span pages. Please note that the pagination logic
+// will not consider Header/Footer lines for paging.
+func (t *Table) SetPageSize(numLines int) {
+ t.pager.size = numLines
+}
+
+// SetRowPainter sets the RowPainter function which determines the colors to use
+// on a row. Before rendering, this function is invoked on all rows and the
+// color of each row is determined. This color takes precedence over other ways
+// to set color (ColumnConfig.Color*, SetColor*()).
+func (t *Table) SetRowPainter(painter RowPainter) {
+ t.rowPainter = painter
+}
+
+// SetStyle overrides the DefaultStyle with the provided one.
+func (t *Table) SetStyle(style Style) {
+ t.style = &style
+}
+
+// SetTitle sets the title text to be rendered above the table.
+func (t *Table) SetTitle(format string, a ...interface{}) {
+ t.title = fmt.Sprintf(format, a...)
+}
+
+// SortBy sets the rules for sorting the Rows in the order specified. i.e., the
+// first SortBy instruction takes precedence over the second and so on. Any
+// duplicate instructions on the same column will be discarded while sorting.
+func (t *Table) SortBy(sortBy []SortBy) {
+ t.sortBy = sortBy
+}
+
+// Style returns the current style.
+func (t *Table) Style() *Style {
+ if t.style == nil {
+ tempStyle := StyleDefault
+ t.style = &tempStyle
+ }
+ // override WidthMax with allowedRowLength until allowedRowLength is
+ // removed from code
+ if t.allowedRowLength > 0 {
+ t.style.Size.WidthMax = t.allowedRowLength
+ }
+ return t.style
+}
+
+// SuppressEmptyColumns hides columns when the column is empty in ALL the
+// regular rows.
+func (t *Table) SuppressEmptyColumns() {
+ t.suppressEmptyColumns = true
+}
+
+// SuppressTrailingSpaces removes all trailing spaces from the output.
+func (t *Table) SuppressTrailingSpaces() {
+ t.suppressTrailingSpaces = true
+}
+
+func (t *Table) getAlign(colIdx int, hint renderHint) text.Align {
+ align := text.AlignDefault
+ if cfg, ok := t.columnConfigMap[colIdx]; ok {
+ if hint.isHeaderRow {
+ align = cfg.AlignHeader
+ } else if hint.isFooterRow {
+ align = cfg.AlignFooter
+ } else {
+ align = cfg.Align
+ }
+ }
+ if align == text.AlignDefault {
+ if !t.columnIsNonNumeric[colIdx] {
+ align = text.AlignRight
+ } else if hint.isAutoIndexRow {
+ align = text.AlignCenter
+ }
+ }
+ return align
+}
+
+func (t *Table) getAutoIndexColumnIDs() rowStr {
+ row := make(rowStr, t.numColumns)
+ for colIdx := range row {
+ row[colIdx] = AutoIndexColumnID(colIdx)
+ }
+ return row
+}
+
+func (t *Table) getBorderColors(hint renderHint) text.Colors {
+ if t.style.Options.DoNotColorBordersAndSeparators {
+ return nil
+ } else if t.style.Color.Border != nil {
+ return t.style.Color.Border
+ } else if hint.isTitleRow {
+ return t.style.Title.Colors
+ } else if hint.isHeaderRow {
+ return t.style.Color.Header
+ } else if hint.isFooterRow {
+ return t.style.Color.Footer
+ } else if t.autoIndex {
+ return t.style.Color.IndexColumn
+ } else if hint.rowNumber%2 == 0 && t.style.Color.RowAlternate != nil {
+ return t.style.Color.RowAlternate
+ }
+ return t.style.Color.Row
+}
+
+func (t *Table) getBorderLeft(hint renderHint) string {
+ border := t.style.Box.Left
+ if hint.isBorderTop {
+ if t.title != "" {
+ border = t.style.Box.LeftSeparator
+ } else {
+ border = t.style.Box.TopLeft
+ }
+ } else if hint.isBorderBottom {
+ border = t.style.Box.BottomLeft
+ } else if hint.isSeparatorRow {
+ if t.autoIndex && hint.isHeaderOrFooterSeparator() {
+ border = t.style.Box.Left
+ } else if !t.autoIndex && t.shouldMergeCellsVertically(0, hint) {
+ border = t.style.Box.Left
+ } else {
+ border = t.style.Box.LeftSeparator
+ }
+ }
+ return border
+}
+
+func (t *Table) getBorderRight(hint renderHint) string {
+ border := t.style.Box.Right
+ if hint.isBorderTop {
+ if t.title != "" {
+ border = t.style.Box.RightSeparator
+ } else {
+ border = t.style.Box.TopRight
+ }
+ } else if hint.isBorderBottom {
+ border = t.style.Box.BottomRight
+ } else if hint.isSeparatorRow {
+ if t.shouldMergeCellsVertically(t.numColumns-1, hint) {
+ border = t.style.Box.Right
+ } else {
+ border = t.style.Box.RightSeparator
+ }
+ }
+ return border
+}
+
+func (t *Table) getColumnColors(colIdx int, hint renderHint) text.Colors {
+ if hint.isBorderOrSeparator() {
+ if colors := t.getColumnColorsForBorderOrSeparator(hint); colors != nil {
+ return colors
+ }
+ }
+ if t.rowPainter != nil && hint.isRegularNonSeparatorRow() && !t.isIndexColumn(colIdx, hint) {
+ if colors := t.rowsColors[hint.rowNumber-1]; colors != nil {
+ return colors
+ }
+ }
+ if cfg, ok := t.columnConfigMap[colIdx]; ok {
+ if hint.isSeparatorRow {
+ return nil
+ } else if hint.isHeaderRow {
+ return cfg.ColorsHeader
+ } else if hint.isFooterRow {
+ return cfg.ColorsFooter
+ }
+ return cfg.Colors
+ }
+ return nil
+}
+
+func (t *Table) getColumnColorsForBorderOrSeparator(hint renderHint) text.Colors {
+ if t.style.Options.DoNotColorBordersAndSeparators {
+ return text.Colors{} // not nil to force caller to paint with no colors
+ }
+ if (hint.isBorderBottom || hint.isBorderTop) && t.style.Color.Border != nil {
+ return t.style.Color.Border
+ }
+ if hint.isSeparatorRow && t.style.Color.Separator != nil {
+ return t.style.Color.Separator
+ }
+ return nil
+}
+
+func (t *Table) getColumnSeparator(row rowStr, colIdx int, hint renderHint) string {
+ separator := t.style.Box.MiddleVertical
+ if hint.isSeparatorRow {
+ if hint.isBorderTop {
+ if t.shouldMergeCellsHorizontallyBelow(row, colIdx, hint) {
+ separator = t.style.Box.MiddleHorizontal
+ } else {
+ separator = t.style.Box.TopSeparator
+ }
+ } else if hint.isBorderBottom {
+ if t.shouldMergeCellsHorizontallyAbove(row, colIdx, hint) {
+ separator = t.style.Box.MiddleHorizontal
+ } else {
+ separator = t.style.Box.BottomSeparator
+ }
+ } else {
+ sm1 := t.shouldMergeCellsHorizontallyAbove(row, colIdx, hint)
+ sm2 := t.shouldMergeCellsHorizontallyBelow(row, colIdx, hint)
+ separator = t.getColumnSeparatorNonBorder(sm1, sm2, colIdx, hint)
+ }
+ }
+ return separator
+}
+
+func (t *Table) getColumnSeparatorNonBorder(mergeCellsAbove bool, mergeCellsBelow bool, colIdx int, hint renderHint) string {
+ mergeNextCol := t.shouldMergeCellsVertically(colIdx, hint)
+ if hint.isAutoIndexColumn {
+ return t.getColumnSeparatorNonBorderAutoIndex(mergeNextCol, hint)
+ }
+
+ mergeCurrCol := t.shouldMergeCellsVertically(colIdx-1, hint)
+ return t.getColumnSeparatorNonBorderNonAutoIndex(mergeCellsAbove, mergeCellsBelow, mergeCurrCol, mergeNextCol)
+}
+
+func (t *Table) getColumnSeparatorNonBorderAutoIndex(mergeNextCol bool, hint renderHint) string {
+ if hint.isHeaderOrFooterSeparator() {
+ if mergeNextCol {
+ return t.style.Box.MiddleVertical
+ }
+ return t.style.Box.LeftSeparator
+ } else if mergeNextCol {
+ return t.style.Box.RightSeparator
+ }
+ return t.style.Box.MiddleSeparator
+}
+
+func (t *Table) getColumnSeparatorNonBorderNonAutoIndex(mergeCellsAbove bool, mergeCellsBelow bool, mergeCurrCol bool, mergeNextCol bool) string {
+ if mergeCellsAbove && mergeCellsBelow && mergeCurrCol && mergeNextCol {
+ return t.style.Box.EmptySeparator
+ } else if mergeCellsAbove && mergeCellsBelow {
+ return t.style.Box.MiddleHorizontal
+ } else if mergeCellsAbove {
+ return t.style.Box.TopSeparator
+ } else if mergeCellsBelow {
+ return t.style.Box.BottomSeparator
+ } else if mergeCurrCol && mergeNextCol {
+ return t.style.Box.MiddleVertical
+ } else if mergeCurrCol {
+ return t.style.Box.LeftSeparator
+ } else if mergeNextCol {
+ return t.style.Box.RightSeparator
+ }
+ return t.style.Box.MiddleSeparator
+}
+
+func (t *Table) getColumnTransformer(colIdx int, hint renderHint) text.Transformer {
+ var transformer text.Transformer
+ if cfg, ok := t.columnConfigMap[colIdx]; ok {
+ if hint.isHeaderRow {
+ transformer = cfg.TransformerHeader
+ } else if hint.isFooterRow {
+ transformer = cfg.TransformerFooter
+ } else {
+ transformer = cfg.Transformer
+ }
+ }
+ return transformer
+}
+
+func (t *Table) getColumnWidthMax(colIdx int) int {
+ if cfg, ok := t.columnConfigMap[colIdx]; ok {
+ return cfg.WidthMax
+ }
+ return 0
+}
+
+func (t *Table) getColumnWidthMin(colIdx int) int {
+ if cfg, ok := t.columnConfigMap[colIdx]; ok {
+ return cfg.WidthMin
+ }
+ return 0
+}
+
+func (t *Table) getFormat(hint renderHint) text.Format {
+ if hint.isSeparatorRow {
+ return text.FormatDefault
+ } else if hint.isHeaderRow {
+ return t.style.Format.Header
+ } else if hint.isFooterRow {
+ return t.style.Format.Footer
+ }
+ return t.style.Format.Row
+}
+
+func (t *Table) getMaxColumnLengthForMerging(colIdx int) int {
+ maxColumnLength := t.maxColumnLengths[colIdx]
+ maxColumnLength += text.RuneWidthWithoutEscSequences(t.style.Box.PaddingRight + t.style.Box.PaddingLeft)
+ if t.style.Options.SeparateColumns {
+ maxColumnLength += text.RuneWidthWithoutEscSequences(t.style.Box.EmptySeparator)
+ }
+ return maxColumnLength
+}
+
+// getMergedColumnIndices returns a map of colIdx values to all the other colIdx
+// values (that are being merged) and their lengths.
+func (t *Table) getMergedColumnIndices(row rowStr, hint renderHint) mergedColumnIndices {
+ if !t.getRowConfig(hint).AutoMerge {
+ return nil
+ }
+
+ mci := make(mergedColumnIndices)
+ for colIdx := 0; colIdx < t.numColumns-1; colIdx++ {
+ // look backward
+ for otherColIdx := colIdx - 1; colIdx >= 0 && otherColIdx >= 0; otherColIdx-- {
+ if row[colIdx] != row[otherColIdx] {
+ break
+ }
+ mci.safeAppend(colIdx, otherColIdx)
+ }
+ // look forward
+ for otherColIdx := colIdx + 1; colIdx < len(row) && otherColIdx < len(row); otherColIdx++ {
+ if row[colIdx] != row[otherColIdx] {
+ break
+ }
+ mci.safeAppend(colIdx, otherColIdx)
+ }
+ }
+ return mci
+}
+
+func (t *Table) getRow(rowIdx int, hint renderHint) rowStr {
+ switch {
+ case hint.isHeaderRow:
+ if rowIdx >= 0 && rowIdx < len(t.rowsHeader) {
+ return t.rowsHeader[rowIdx]
+ }
+ case hint.isFooterRow:
+ if rowIdx >= 0 && rowIdx < len(t.rowsFooter) {
+ return t.rowsFooter[rowIdx]
+ }
+ default:
+ if rowIdx >= 0 && rowIdx < len(t.rows) {
+ return t.rows[rowIdx]
+ }
+ }
+ return rowStr{}
+}
+
+func (t *Table) getRowConfig(hint renderHint) RowConfig {
+ rowIdx := hint.rowNumber - 1
+ if rowIdx < 0 {
+ rowIdx = 0
+ }
+
+ switch {
+ case hint.isHeaderRow:
+ return t.rowsHeaderConfigMap[rowIdx]
+ case hint.isFooterRow:
+ return t.rowsFooterConfigMap[rowIdx]
+ default:
+ return t.rowsConfigMap[rowIdx]
+ }
+}
+
+func (t *Table) getSeparatorColors(hint renderHint) text.Colors {
+ if t.style.Options.DoNotColorBordersAndSeparators {
+ return nil
+ } else if (hint.isBorderBottom || hint.isBorderTop) && t.style.Color.Border != nil {
+ return t.style.Color.Border
+ } else if t.style.Color.Separator != nil {
+ return t.style.Color.Separator
+ } else if hint.isHeaderRow {
+ return t.style.Color.Header
+ } else if hint.isFooterRow {
+ return t.style.Color.Footer
+ } else if hint.isAutoIndexColumn {
+ return t.style.Color.IndexColumn
+ } else if hint.rowNumber > 0 && hint.rowNumber%2 == 0 {
+ return t.style.Color.RowAlternate
+ }
+ return t.style.Color.Row
+}
+
+func (t *Table) getVAlign(colIdx int, hint renderHint) text.VAlign {
+ vAlign := text.VAlignDefault
+ if cfg, ok := t.columnConfigMap[colIdx]; ok {
+ if hint.isHeaderRow {
+ vAlign = cfg.VAlignHeader
+ } else if hint.isFooterRow {
+ vAlign = cfg.VAlignFooter
+ } else {
+ vAlign = cfg.VAlign
+ }
+ }
+ return vAlign
+}
+
+func (t *Table) hasHiddenColumns() bool {
+ for _, cc := range t.columnConfigMap {
+ if cc.Hidden {
+ return true
+ }
+ }
+ return false
+}
+
+func (t *Table) hideColumns() map[int]int {
+ colIdxMap := make(map[int]int)
+ numColumns := 0
+ hideColumnsInRows := func(rows []rowStr) []rowStr {
+ var rsp []rowStr
+ for _, row := range rows {
+ var rowNew rowStr
+ for colIdx, col := range row {
+ cc := t.columnConfigMap[colIdx]
+ if !cc.Hidden {
+ rowNew = append(rowNew, col)
+ colIdxMap[colIdx] = len(rowNew) - 1
+ }
+ }
+ if len(rowNew) > numColumns {
+ numColumns = len(rowNew)
+ }
+ rsp = append(rsp, rowNew)
+ }
+ return rsp
+ }
+
+ // hide columns as directed
+ t.rows = hideColumnsInRows(t.rows)
+ t.rowsFooter = hideColumnsInRows(t.rowsFooter)
+ t.rowsHeader = hideColumnsInRows(t.rowsHeader)
+
+ // reset numColumns to the new number of columns
+ t.numColumns = numColumns
+
+ return colIdxMap
+}
+
+func (t *Table) isIndexColumn(colIdx int, hint renderHint) bool {
+ return t.indexColumn == colIdx+1 || hint.isAutoIndexColumn
+}
+
+func (t *Table) render(out *strings.Builder) string {
+ outStr := out.String()
+ if t.suppressTrailingSpaces {
+ var trimmed []string
+ for _, line := range strings.Split(outStr, "\n") {
+ trimmed = append(trimmed, strings.TrimRightFunc(line, unicode.IsSpace))
+ }
+ outStr = strings.Join(trimmed, "\n")
+ }
+ if t.outputMirror != nil && len(outStr) > 0 {
+ _, _ = t.outputMirror.Write([]byte(outStr))
+ _, _ = t.outputMirror.Write([]byte("\n"))
+ }
+ return outStr
+}
+
+func (t *Table) shouldMergeCellsHorizontallyAbove(row rowStr, colIdx int, hint renderHint) bool {
+ if hint.isAutoIndexColumn || hint.isAutoIndexRow {
+ return false
+ }
+
+ rowConfig := t.getRowConfig(hint)
+ if hint.isSeparatorRow {
+ if hint.isHeaderRow && hint.rowNumber == 1 {
+ rowConfig = t.getRowConfig(hint)
+ row = t.getRow(hint.rowNumber-1, hint)
+ } else if hint.isFooterRow && hint.isFirstRow {
+ rowConfig = t.getRowConfig(renderHint{isLastRow: true, rowNumber: len(t.rows)})
+ row = t.getRow(len(t.rows)-1, renderHint{})
+ } else if hint.isFooterRow && hint.isBorderBottom {
+ row = t.getRow(len(t.rowsFooter)-1, renderHint{isFooterRow: true})
+ } else {
+ row = t.getRow(hint.rowNumber-1, hint)
+ }
+ }
+
+ if rowConfig.AutoMerge {
+ return row.areEqual(colIdx-1, colIdx)
+ }
+ return false
+}
+
+func (t *Table) shouldMergeCellsHorizontallyBelow(row rowStr, colIdx int, hint renderHint) bool {
+ if hint.isAutoIndexColumn || hint.isAutoIndexRow {
+ return false
+ }
+
+ var rowConfig RowConfig
+ if hint.isSeparatorRow {
+ if hint.isRegularRow() {
+ rowConfig = t.getRowConfig(renderHint{rowNumber: hint.rowNumber + 1})
+ row = t.getRow(hint.rowNumber, renderHint{})
+ } else if hint.isHeaderRow && hint.rowNumber == 0 {
+ rowConfig = t.getRowConfig(renderHint{isHeaderRow: true, rowNumber: 1})
+ row = t.getRow(0, hint)
+ } else if hint.isHeaderRow && hint.isLastRow {
+ rowConfig = t.getRowConfig(renderHint{rowNumber: 1})
+ row = t.getRow(0, renderHint{})
+ } else if hint.isHeaderRow {
+ rowConfig = t.getRowConfig(renderHint{isHeaderRow: true, rowNumber: hint.rowNumber + 1})
+ row = t.getRow(hint.rowNumber, hint)
+ } else if hint.isFooterRow && hint.rowNumber >= 0 {
+ rowConfig = t.getRowConfig(renderHint{isFooterRow: true, rowNumber: 1})
+ row = t.getRow(hint.rowNumber, renderHint{isFooterRow: true})
+ }
+ }
+
+ if rowConfig.AutoMerge {
+ return row.areEqual(colIdx-1, colIdx)
+ }
+ return false
+}
+
+func (t *Table) shouldMergeCellsVertically(colIdx int, hint renderHint) bool {
+ if !t.firstRowOfPage && t.columnConfigMap[colIdx].AutoMerge && colIdx < t.numColumns {
+ if hint.isSeparatorRow {
+ rowPrev := t.getRow(hint.rowNumber-1, hint)
+ rowNext := t.getRow(hint.rowNumber, hint)
+ if colIdx < len(rowPrev) && colIdx < len(rowNext) {
+ return rowPrev[colIdx] == rowNext[colIdx]
+ }
+ } else {
+ rowPrev := t.getRow(hint.rowNumber-2, hint)
+ rowCurr := t.getRow(hint.rowNumber-1, hint)
+ if colIdx < len(rowPrev) && colIdx < len(rowCurr) {
+ return rowPrev[colIdx] == rowCurr[colIdx]
+ }
+ }
+ }
+ return false
+}
+
+func (t *Table) shouldSeparateRows(rowIdx int, numRows int) bool {
+ // not asked to separate rows and no manually added separator
+ if !t.style.Options.SeparateRows && !t.separators[rowIdx] {
+ return false
+ }
+
+ pageSize := numRows
+ if t.pager.size > 0 {
+ pageSize = t.pager.size
+ }
+ if rowIdx%pageSize == pageSize-1 { // last row of page
+ return false
+ }
+ if rowIdx == numRows-1 { // last row of table
+ return false
+ }
+ return true
+}
+
+func (t *Table) wrapRow(row rowStr) (int, rowStr) {
+ colMaxLines := 0
+ rowWrapped := make(rowStr, len(row))
+ for colIdx, colStr := range row {
+ widthEnforcer := t.columnConfigMap[colIdx].getWidthMaxEnforcer()
+ maxWidth := t.getColumnWidthMax(colIdx)
+ if maxWidth == 0 {
+ maxWidth = t.maxColumnLengths[colIdx]
+ }
+ rowWrapped[colIdx] = widthEnforcer(colStr, maxWidth)
+ colNumLines := strings.Count(rowWrapped[colIdx], "\n") + 1
+ if colNumLines > colMaxLines {
+ colMaxLines = colNumLines
+ }
+ }
+ return colMaxLines, rowWrapped
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/util.go b/vendor/github.com/jedib0t/go-pretty/v6/table/util.go
new file mode 100644
index 0000000000..aa710125eb
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/util.go
@@ -0,0 +1,69 @@
+package table
+
+import (
+ "reflect"
+)
+
+// AutoIndexColumnID returns a unique Column ID/Name for the given Column Number.
+// The functionality is similar to what you get in an Excel spreadsheet w.r.t.
+// the Column ID/Name.
+func AutoIndexColumnID(colIdx int) string {
+ charIdx := colIdx % 26
+ out := string(rune(65 + charIdx))
+ colIdx = colIdx / 26
+ if colIdx > 0 {
+ return AutoIndexColumnID(colIdx-1) + out
+ }
+ return out
+}
+
+// WidthEnforcer is a function that helps enforce a width condition on a string.
+type WidthEnforcer func(col string, maxLen int) string
+
+// widthEnforcerNone returns the input string as is without any modifications.
+func widthEnforcerNone(col string, _ int) string {
+ return col
+}
+
+// isNumber returns true if the argument is a numeric type; false otherwise.
+func isNumber(x interface{}) bool {
+ if x == nil {
+ return false
+ }
+
+ switch reflect.TypeOf(x).Kind() {
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
+ reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
+ reflect.Float32, reflect.Float64:
+ return true
+ }
+ return false
+}
+
+type mergedColumnIndices map[int]map[int]bool
+
+func (m mergedColumnIndices) mergedLength(colIdx int, maxColumnLengths []int) int {
+ mergedLength := maxColumnLengths[colIdx]
+ for otherColIdx := range m[colIdx] {
+ mergedLength += maxColumnLengths[otherColIdx]
+ }
+ return mergedLength
+}
+
+func (m mergedColumnIndices) len(colIdx int) int {
+ return len(m[colIdx]) + 1
+}
+
+func (m mergedColumnIndices) safeAppend(colIdx, otherColIdx int) {
+ // map
+ if m[colIdx] == nil {
+ m[colIdx] = make(map[int]bool)
+ }
+ m[colIdx][otherColIdx] = true
+
+ // reverse map
+ if m[otherColIdx] == nil {
+ m[otherColIdx] = make(map[int]bool)
+ }
+ m[otherColIdx][colIdx] = true
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/table/writer.go b/vendor/github.com/jedib0t/go-pretty/v6/table/writer.go
new file mode 100644
index 0000000000..ec7a53a7e6
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/table/writer.go
@@ -0,0 +1,48 @@
+package table
+
+import (
+ "io"
+)
+
+// Writer declares the interfaces that can be used to set up and render a table.
+type Writer interface {
+ AppendFooter(row Row, configs ...RowConfig)
+ AppendHeader(row Row, configs ...RowConfig)
+ AppendRow(row Row, configs ...RowConfig)
+ AppendRows(rows []Row, configs ...RowConfig)
+ AppendSeparator()
+ Length() int
+ Pager(opts ...PagerOption) Pager
+ Render() string
+ RenderCSV() string
+ RenderHTML() string
+ RenderMarkdown() string
+ RenderTSV() string
+ ResetFooters()
+ ResetHeaders()
+ ResetRows()
+ SetAutoIndex(autoIndex bool)
+ SetCaption(format string, a ...interface{})
+ SetColumnConfigs(configs []ColumnConfig)
+ SetIndexColumn(colNum int)
+ SetOutputMirror(mirror io.Writer)
+ SetRowPainter(painter RowPainter)
+ SetStyle(style Style)
+ SetTitle(format string, a ...interface{})
+ SortBy(sortBy []SortBy)
+ Style() *Style
+ SuppressEmptyColumns()
+ SuppressTrailingSpaces()
+
+ // deprecated; in favor if Style().Size.WidthMax
+ SetAllowedRowLength(length int)
+ // deprecated; in favor of Style().HTML.CSSClass
+ SetHTMLCSSClass(cssClass string)
+ // deprecated; in favor of Pager()
+ SetPageSize(numLines int)
+}
+
+// NewWriter initializes and returns a Writer.
+func NewWriter() Writer {
+ return &Table{}
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/README.md b/vendor/github.com/jedib0t/go-pretty/v6/text/README.md
new file mode 100644
index 0000000000..afd163aff1
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/README.md
@@ -0,0 +1,8 @@
+# text
+
+[![Go Reference](https://pkg.go.dev/badge/github.com/jedib0t/go-pretty/v6.svg)](https://pkg.go.dev/github.com/jedib0t/go-pretty/v6/text)
+
+Package with utility functions to manipulate strings/text.
+
+Used heavily in the other packages in this repo ([list](../list),
+[progress](../progress), and [table](../table)).
\ No newline at end of file
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/align.go b/vendor/github.com/jedib0t/go-pretty/v6/text/align.go
new file mode 100644
index 0000000000..2a1068a860
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/align.go
@@ -0,0 +1,149 @@
+package text
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+ "unicode/utf8"
+)
+
+// Align denotes how text is to be aligned horizontally.
+type Align int
+
+// Align enumerations
+const (
+ AlignDefault Align = iota // same as AlignLeft
+ AlignLeft // "left "
+ AlignCenter // " center "
+ AlignJustify // "justify it"
+ AlignRight // " right"
+ AlignAuto // AlignRight for numbers, AlignLeft for the rest
+)
+
+// Apply aligns the text as directed. For ex.:
+// - AlignDefault.Apply("Jon Snow", 12) returns "Jon Snow "
+// - AlignLeft.Apply("Jon Snow", 12) returns "Jon Snow "
+// - AlignCenter.Apply("Jon Snow", 12) returns " Jon Snow "
+// - AlignJustify.Apply("Jon Snow", 12) returns "Jon Snow"
+// - AlignRight.Apply("Jon Snow", 12) returns " Jon Snow"
+// - AlignAuto.Apply("Jon Snow", 12) returns "Jon Snow "
+func (a Align) Apply(text string, maxLength int) string {
+ aComputed := a
+ if aComputed == AlignAuto {
+ _, err := strconv.ParseFloat(text, 64)
+ if err == nil { // was able to parse a number out of the string
+ aComputed = AlignRight
+ } else {
+ aComputed = AlignLeft
+ }
+ }
+
+ text = aComputed.trimString(text)
+ sLen := utf8.RuneCountInString(text)
+ sLenWoE := RuneWidthWithoutEscSequences(text)
+ numEscChars := sLen - sLenWoE
+
+ // now, align the text
+ switch aComputed {
+ case AlignDefault, AlignLeft:
+ return fmt.Sprintf("%-"+strconv.Itoa(maxLength+numEscChars)+"s", text)
+ case AlignCenter:
+ if sLenWoE < maxLength {
+ // left pad with half the number of spaces needed before using %text
+ return fmt.Sprintf("%"+strconv.Itoa(maxLength+numEscChars)+"s",
+ text+strings.Repeat(" ", (maxLength-sLenWoE)/2))
+ }
+ case AlignJustify:
+ return justifyText(text, sLenWoE, maxLength)
+ }
+ return fmt.Sprintf("%"+strconv.Itoa(maxLength+numEscChars)+"s", text)
+}
+
+// HTMLProperty returns the equivalent HTML horizontal-align tag property.
+func (a Align) HTMLProperty() string {
+ switch a {
+ case AlignLeft:
+ return "align=\"left\""
+ case AlignCenter:
+ return "align=\"center\""
+ case AlignJustify:
+ return "align=\"justify\""
+ case AlignRight:
+ return "align=\"right\""
+ default:
+ return ""
+ }
+}
+
+// MarkdownProperty returns the equivalent Markdown horizontal-align separator.
+func (a Align) MarkdownProperty() string {
+ switch a {
+ case AlignLeft:
+ return ":--- "
+ case AlignCenter:
+ return ":---:"
+ case AlignRight:
+ return " ---:"
+ default:
+ return " --- "
+ }
+}
+
+func (a Align) trimString(text string) string {
+ switch a {
+ case AlignDefault, AlignLeft:
+ if strings.HasSuffix(text, " ") {
+ return strings.TrimRight(text, " ")
+ }
+ case AlignRight:
+ if strings.HasPrefix(text, " ") {
+ return strings.TrimLeft(text, " ")
+ }
+ default:
+ if strings.HasPrefix(text, " ") || strings.HasSuffix(text, " ") {
+ return strings.Trim(text, " ")
+ }
+ }
+ return text
+}
+
+func justifyText(text string, textLength int, maxLength int) string {
+ // split the text into individual words
+ words := Filter(strings.Split(text, " "), func(item string) bool {
+ return item != ""
+ })
+ // empty string implies result is just spaces for maxLength
+ if len(words) == 0 {
+ return strings.Repeat(" ", maxLength)
+ }
+
+ // get the number of spaces to insert into the text
+ numSpacesNeeded := maxLength - textLength + strings.Count(text, " ")
+ numSpacesNeededBetweenWords := 0
+ if len(words) > 1 {
+ numSpacesNeededBetweenWords = numSpacesNeeded / (len(words) - 1)
+ }
+ // create the output string word by word with spaces in between
+ var outText strings.Builder
+ outText.Grow(maxLength)
+ for idx, word := range words {
+ if idx > 0 {
+ // insert spaces only after the first word
+ if idx == len(words)-1 {
+ // insert all the remaining space before the last word
+ outText.WriteString(strings.Repeat(" ", numSpacesNeeded))
+ numSpacesNeeded = 0
+ } else {
+ // insert the determined number of spaces between each word
+ outText.WriteString(strings.Repeat(" ", numSpacesNeededBetweenWords))
+ // and reduce the number of spaces needed after this
+ numSpacesNeeded -= numSpacesNeededBetweenWords
+ }
+ }
+ outText.WriteString(word)
+ if idx == len(words)-1 && numSpacesNeeded > 0 {
+ outText.WriteString(strings.Repeat(" ", numSpacesNeeded))
+ }
+ }
+ return outText.String()
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/ansi.go b/vendor/github.com/jedib0t/go-pretty/v6/text/ansi.go
new file mode 100644
index 0000000000..6a396b1e6e
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/ansi.go
@@ -0,0 +1,57 @@
+package text
+
+import "strings"
+
+// ANSICodesSupported will be true on consoles where ANSI Escape Codes/Sequences
+// are supported.
+var ANSICodesSupported = areANSICodesSupported()
+
+// Escape encodes the string with the ANSI Escape Sequence.
+// For ex.:
+//
+// Escape("Ghost", "") == "Ghost"
+// Escape("Ghost", "\x1b[91m") == "\x1b[91mGhost\x1b[0m"
+// Escape("\x1b[94mGhost\x1b[0mLady", "\x1b[91m") == "\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m"
+// Escape("Nymeria\x1b[94mGhost\x1b[0mLady", "\x1b[91m") == "\x1b[91mNymeria\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m"
+// Escape("Nymeria \x1b[94mGhost\x1b[0m Lady", "\x1b[91m") == "\x1b[91mNymeria \x1b[94mGhost\x1b[0m\x1b[91m Lady\x1b[0m"
+func Escape(str string, escapeSeq string) string {
+ out := ""
+ if !strings.HasPrefix(str, EscapeStart) {
+ out += escapeSeq
+ }
+ out += strings.Replace(str, EscapeReset, EscapeReset+escapeSeq, -1)
+ if !strings.HasSuffix(out, EscapeReset) {
+ out += EscapeReset
+ }
+ if strings.Contains(out, escapeSeq+EscapeReset) {
+ out = strings.Replace(out, escapeSeq+EscapeReset, "", -1)
+ }
+ return out
+}
+
+// StripEscape strips all ANSI Escape Sequence from the string.
+// For ex.:
+//
+// StripEscape("Ghost") == "Ghost"
+// StripEscape("\x1b[91mGhost\x1b[0m") == "Ghost"
+// StripEscape("\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m") == "GhostLady"
+// StripEscape("\x1b[91mNymeria\x1b[94mGhost\x1b[0m\x1b[91mLady\x1b[0m") == "NymeriaGhostLady"
+// StripEscape("\x1b[91mNymeria \x1b[94mGhost\x1b[0m\x1b[91m Lady\x1b[0m") == "Nymeria Ghost Lady"
+func StripEscape(str string) string {
+ var out strings.Builder
+ out.Grow(RuneWidthWithoutEscSequences(str))
+
+ isEscSeq := false
+ for _, sChr := range str {
+ if sChr == EscapeStartRune {
+ isEscSeq = true
+ }
+ if !isEscSeq {
+ out.WriteRune(sChr)
+ }
+ if isEscSeq && sChr == EscapeStopRune {
+ isEscSeq = false
+ }
+ }
+ return out.String()
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go b/vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go
new file mode 100644
index 0000000000..635be79eed
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go
@@ -0,0 +1,8 @@
+//go:build !windows
+// +build !windows
+
+package text
+
+func areANSICodesSupported() bool {
+ return true
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/ansi_windows.go b/vendor/github.com/jedib0t/go-pretty/v6/text/ansi_windows.go
new file mode 100644
index 0000000000..4f8b42f068
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/ansi_windows.go
@@ -0,0 +1,30 @@
+//go:build windows
+// +build windows
+
+package text
+
+import (
+ "os"
+ "sync"
+
+ "golang.org/x/sys/windows"
+)
+
+var enableVTPMutex = sync.Mutex{}
+
+func areANSICodesSupported() bool {
+ enableVTPMutex.Lock()
+ defer enableVTPMutex.Unlock()
+
+ outHandle := windows.Handle(os.Stdout.Fd())
+ var outMode uint32
+ if err := windows.GetConsoleMode(outHandle, &outMode); err == nil {
+ if outMode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 {
+ return true
+ }
+ if err := windows.SetConsoleMode(outHandle, outMode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING); err == nil {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/color.go b/vendor/github.com/jedib0t/go-pretty/v6/text/color.go
new file mode 100644
index 0000000000..f42dfb385d
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/color.go
@@ -0,0 +1,179 @@
+package text
+
+import (
+ "fmt"
+ "sort"
+ "strconv"
+ "strings"
+ "sync"
+)
+
+var colorsEnabled = areANSICodesSupported()
+
+// DisableColors (forcefully) disables color coding globally.
+func DisableColors() {
+ colorsEnabled = false
+}
+
+// EnableColors (forcefully) enables color coding globally.
+func EnableColors() {
+ colorsEnabled = true
+}
+
+// The logic here is inspired from github.com/fatih/color; the following is
+// the bare minimum logic required to print Colored to the console.
+// The differences:
+// * This one caches the escape sequences for cases with multiple colors
+// * This one handles cases where the incoming already has colors in the
+// form of escape sequences; in which case, text that does not have any
+// escape sequences are colored/escaped
+
+// Color represents a single color to render with.
+type Color int
+
+// Base colors -- attributes in reality
+const (
+ Reset Color = iota
+ Bold
+ Faint
+ Italic
+ Underline
+ BlinkSlow
+ BlinkRapid
+ ReverseVideo
+ Concealed
+ CrossedOut
+)
+
+// Foreground colors
+const (
+ FgBlack Color = iota + 30
+ FgRed
+ FgGreen
+ FgYellow
+ FgBlue
+ FgMagenta
+ FgCyan
+ FgWhite
+)
+
+// Foreground Hi-Intensity colors
+const (
+ FgHiBlack Color = iota + 90
+ FgHiRed
+ FgHiGreen
+ FgHiYellow
+ FgHiBlue
+ FgHiMagenta
+ FgHiCyan
+ FgHiWhite
+)
+
+// Background colors
+const (
+ BgBlack Color = iota + 40
+ BgRed
+ BgGreen
+ BgYellow
+ BgBlue
+ BgMagenta
+ BgCyan
+ BgWhite
+)
+
+// Background Hi-Intensity colors
+const (
+ BgHiBlack Color = iota + 100
+ BgHiRed
+ BgHiGreen
+ BgHiYellow
+ BgHiBlue
+ BgHiMagenta
+ BgHiCyan
+ BgHiWhite
+)
+
+// EscapeSeq returns the ANSI escape sequence for the color.
+func (c Color) EscapeSeq() string {
+ return EscapeStart + strconv.Itoa(int(c)) + EscapeStop
+}
+
+// HTMLProperty returns the "class" attribute for the color.
+func (c Color) HTMLProperty() string {
+ out := ""
+ if class, ok := colorCSSClassMap[c]; ok {
+ out = fmt.Sprintf("class=\"%s\"", class)
+ }
+ return out
+}
+
+// Sprint colorizes and prints the given string(s).
+func (c Color) Sprint(a ...interface{}) string {
+ return colorize(fmt.Sprint(a...), c.EscapeSeq())
+}
+
+// Sprintf formats and colorizes and prints the given string(s).
+func (c Color) Sprintf(format string, a ...interface{}) string {
+ return colorize(fmt.Sprintf(format, a...), c.EscapeSeq())
+}
+
+// Colors represents an array of Color objects to render with.
+// Example: Colors{FgCyan, BgBlack}
+type Colors []Color
+
+// colorsSeqMap caches the escape sequence for a set of colors
+var colorsSeqMap = sync.Map{}
+
+// EscapeSeq returns the ANSI escape sequence for the colors set.
+func (c Colors) EscapeSeq() string {
+ if len(c) == 0 {
+ return ""
+ }
+
+ colorsKey := fmt.Sprintf("%#v", c)
+ escapeSeq, ok := colorsSeqMap.Load(colorsKey)
+ if !ok || escapeSeq == "" {
+ colorNums := make([]string, len(c))
+ for idx, color := range c {
+ colorNums[idx] = strconv.Itoa(int(color))
+ }
+ escapeSeq = EscapeStart + strings.Join(colorNums, ";") + EscapeStop
+ colorsSeqMap.Store(colorsKey, escapeSeq)
+ }
+ return escapeSeq.(string)
+}
+
+// HTMLProperty returns the "class" attribute for the colors.
+func (c Colors) HTMLProperty() string {
+ if len(c) == 0 {
+ return ""
+ }
+
+ var classes []string
+ for _, color := range c {
+ if class, ok := colorCSSClassMap[color]; ok {
+ classes = append(classes, class)
+ }
+ }
+ if len(classes) > 1 {
+ sort.Strings(classes)
+ }
+ return fmt.Sprintf("class=\"%s\"", strings.Join(classes, " "))
+}
+
+// Sprint colorizes and prints the given string(s).
+func (c Colors) Sprint(a ...interface{}) string {
+ return colorize(fmt.Sprint(a...), c.EscapeSeq())
+}
+
+// Sprintf formats and colorizes and prints the given string(s).
+func (c Colors) Sprintf(format string, a ...interface{}) string {
+ return colorize(fmt.Sprintf(format, a...), c.EscapeSeq())
+}
+
+func colorize(s string, escapeSeq string) string {
+ if !colorsEnabled || escapeSeq == "" {
+ return s
+ }
+ return Escape(s, escapeSeq)
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/color_html.go b/vendor/github.com/jedib0t/go-pretty/v6/text/color_html.go
new file mode 100644
index 0000000000..14fa17432e
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/color_html.go
@@ -0,0 +1,46 @@
+package text
+
+// colorCSSClassMap contains the equivalent CSS-class for all colors
+var colorCSSClassMap = map[Color]string{
+ Bold: "bold",
+ Faint: "faint",
+ Italic: "italic",
+ Underline: "underline",
+ BlinkSlow: "blink-slow",
+ BlinkRapid: "blink-rapid",
+ ReverseVideo: "reverse-video",
+ Concealed: "concealed",
+ CrossedOut: "crossed-out",
+ FgBlack: "fg-black",
+ FgRed: "fg-red",
+ FgGreen: "fg-green",
+ FgYellow: "fg-yellow",
+ FgBlue: "fg-blue",
+ FgMagenta: "fg-magenta",
+ FgCyan: "fg-cyan",
+ FgWhite: "fg-white",
+ FgHiBlack: "fg-hi-black",
+ FgHiRed: "fg-hi-red",
+ FgHiGreen: "fg-hi-green",
+ FgHiYellow: "fg-hi-yellow",
+ FgHiBlue: "fg-hi-blue",
+ FgHiMagenta: "fg-hi-magenta",
+ FgHiCyan: "fg-hi-cyan",
+ FgHiWhite: "fg-hi-white",
+ BgBlack: "bg-black",
+ BgRed: "bg-red",
+ BgGreen: "bg-green",
+ BgYellow: "bg-yellow",
+ BgBlue: "bg-blue",
+ BgMagenta: "bg-magenta",
+ BgCyan: "bg-cyan",
+ BgWhite: "bg-white",
+ BgHiBlack: "bg-hi-black",
+ BgHiRed: "bg-hi-red",
+ BgHiGreen: "bg-hi-green",
+ BgHiYellow: "bg-hi-yellow",
+ BgHiBlue: "bg-hi-blue",
+ BgHiMagenta: "bg-hi-magenta",
+ BgHiCyan: "bg-hi-cyan",
+ BgHiWhite: "bg-hi-white",
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/cursor.go b/vendor/github.com/jedib0t/go-pretty/v6/text/cursor.go
new file mode 100644
index 0000000000..6d54281dd6
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/cursor.go
@@ -0,0 +1,39 @@
+package text
+
+import (
+ "fmt"
+)
+
+// Cursor helps move the cursor on the console in multiple directions.
+type Cursor rune
+
+const (
+ // CursorDown helps move the Cursor Down X lines
+ CursorDown Cursor = 'B'
+
+ // CursorLeft helps move the Cursor Left X characters
+ CursorLeft Cursor = 'D'
+
+ // CursorRight helps move the Cursor Right X characters
+ CursorRight Cursor = 'C'
+
+ // CursorUp helps move the Cursor Up X lines
+ CursorUp Cursor = 'A'
+
+ // EraseLine helps erase all characters to the Right of the Cursor in the
+ // current line
+ EraseLine Cursor = 'K'
+)
+
+// Sprint prints the Escape Sequence to move the Cursor once.
+func (c Cursor) Sprint() string {
+ return fmt.Sprintf("%s%c", EscapeStart, c)
+}
+
+// Sprintn prints the Escape Sequence to move the Cursor "n" times.
+func (c Cursor) Sprintn(n int) string {
+ if c == EraseLine {
+ return c.Sprint()
+ }
+ return fmt.Sprintf("%s%d%c", EscapeStart, n, c)
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/direction.go b/vendor/github.com/jedib0t/go-pretty/v6/text/direction.go
new file mode 100644
index 0000000000..25eccc21f7
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/direction.go
@@ -0,0 +1,24 @@
+package text
+
+// Direction defines the overall flow of text. Similar to bidi.Direction, but
+// simplified and specific to this package.
+type Direction int
+
+// Available Directions.
+const (
+ Default Direction = iota
+ LeftToRight
+ RightToLeft
+)
+
+// Modifier returns a character to force the given direction for the text that
+// follows the modifier.
+func (d Direction) Modifier() string {
+ switch d {
+ case LeftToRight:
+ return "\u202a"
+ case RightToLeft:
+ return "\u202b"
+ }
+ return ""
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/escape_seq_parser.go b/vendor/github.com/jedib0t/go-pretty/v6/text/escape_seq_parser.go
new file mode 100644
index 0000000000..ab618acce6
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/escape_seq_parser.go
@@ -0,0 +1,201 @@
+package text
+
+import (
+ "fmt"
+ "sort"
+ "strconv"
+ "strings"
+)
+
+// Constants
+const (
+ EscapeReset = EscapeResetCSI
+ EscapeResetCSI = EscapeStartCSI + "0" + EscapeStopCSI
+ EscapeResetOSI = EscapeStartOSI + "0" + EscapeStopOSI
+ EscapeStart = EscapeStartCSI
+ EscapeStartCSI = "\x1b["
+ EscapeStartOSI = "\x1b]"
+ EscapeStartRune = rune(27) // \x1b
+ EscapeStartRuneCSI = '[' // [
+ EscapeStartRuneOSI = ']' // ]
+ EscapeStop = EscapeStopCSI
+ EscapeStopCSI = "m"
+ EscapeStopOSI = "\\"
+ EscapeStopRune = EscapeStopRuneCSI
+ EscapeStopRuneCSI = 'm'
+ EscapeStopRuneOSI = '\\'
+)
+
+// Deprecated Constants
+const (
+ CSIStartRune = EscapeStartRuneCSI
+ CSIStopRune = EscapeStopRuneCSI
+ OSIStartRune = EscapeStartRuneOSI
+ OSIStopRune = EscapeStopRuneOSI
+)
+
+type escSeqKind int
+
+const (
+ escSeqKindUnknown escSeqKind = iota
+ escSeqKindCSI
+ escSeqKindOSI
+)
+
+type escSeqParser struct {
+ codes map[int]bool
+
+ // consume specific
+ inEscSeq bool
+ escSeqKind escSeqKind
+ escapeSeq string
+}
+
+func (s *escSeqParser) Codes() []int {
+ codes := make([]int, 0)
+ for code, val := range s.codes {
+ if val {
+ codes = append(codes, code)
+ }
+ }
+ sort.Ints(codes)
+ return codes
+}
+
+func (s *escSeqParser) Consume(char rune) {
+ if !s.inEscSeq && char == EscapeStartRune {
+ s.inEscSeq = true
+ s.escSeqKind = escSeqKindUnknown
+ s.escapeSeq = ""
+ } else if s.inEscSeq && s.escSeqKind == escSeqKindUnknown {
+ if char == EscapeStartRuneCSI {
+ s.escSeqKind = escSeqKindCSI
+ } else if char == EscapeStartRuneOSI {
+ s.escSeqKind = escSeqKindOSI
+ }
+ }
+
+ if s.inEscSeq {
+ s.escapeSeq += string(char)
+
+ if s.isEscapeStopRune(char) {
+ s.ParseSeq(s.escapeSeq, s.escSeqKind)
+ s.Reset()
+ }
+ }
+}
+
+func (s *escSeqParser) InSequence() bool {
+ return s.inEscSeq
+}
+
+func (s *escSeqParser) IsOpen() bool {
+ return len(s.codes) > 0
+}
+
+func (s *escSeqParser) Reset() {
+ s.inEscSeq = false
+ s.escSeqKind = escSeqKindUnknown
+ s.escapeSeq = ""
+}
+
+const (
+ escCodeResetAll = 0
+ escCodeResetIntensity = 22
+ escCodeResetItalic = 23
+ escCodeResetUnderline = 24
+ escCodeResetBlink = 25
+ escCodeResetReverse = 27
+ escCodeResetCrossedOut = 29
+ escCodeBold = 1
+ escCodeDim = 2
+ escCodeItalic = 3
+ escCodeUnderline = 4
+ escCodeBlinkSlow = 5
+ escCodeBlinkRapid = 6
+ escCodeReverse = 7
+ escCodeConceal = 8
+ escCodeCrossedOut = 9
+)
+
+func (s *escSeqParser) ParseSeq(seq string, seqKind escSeqKind) {
+ if s.codes == nil {
+ s.codes = make(map[int]bool)
+ }
+
+ if seqKind == escSeqKindOSI {
+ seq = strings.Replace(seq, EscapeStartOSI, "", 1)
+ seq = strings.Replace(seq, EscapeStopOSI, "", 1)
+ } else { // escSeqKindCSI
+ seq = strings.Replace(seq, EscapeStartCSI, "", 1)
+ seq = strings.Replace(seq, EscapeStopCSI, "", 1)
+ }
+
+ codes := strings.Split(seq, ";")
+ for _, code := range codes {
+ code = strings.TrimSpace(code)
+ if codeNum, err := strconv.Atoi(code); err == nil {
+ switch codeNum {
+ case escCodeResetAll:
+ s.codes = make(map[int]bool) // clear everything
+ case escCodeResetIntensity:
+ delete(s.codes, escCodeBold)
+ delete(s.codes, escCodeDim)
+ case escCodeResetItalic:
+ delete(s.codes, escCodeItalic)
+ case escCodeResetUnderline:
+ delete(s.codes, escCodeUnderline)
+ case escCodeResetBlink:
+ delete(s.codes, escCodeBlinkSlow)
+ delete(s.codes, escCodeBlinkRapid)
+ case escCodeResetReverse:
+ delete(s.codes, escCodeReverse)
+ case escCodeResetCrossedOut:
+ delete(s.codes, escCodeCrossedOut)
+ default:
+ s.codes[codeNum] = true
+ }
+ }
+ }
+}
+
+func (s *escSeqParser) ParseString(str string) string {
+ s.escapeSeq, s.inEscSeq, s.escSeqKind = "", false, escSeqKindUnknown
+ for _, char := range str {
+ s.Consume(char)
+ }
+ return s.Sequence()
+}
+
+func (s *escSeqParser) Sequence() string {
+ out := strings.Builder{}
+ if s.IsOpen() {
+ out.WriteString(EscapeStart)
+ for idx, code := range s.Codes() {
+ if idx > 0 {
+ out.WriteRune(';')
+ }
+ out.WriteString(fmt.Sprint(code))
+ }
+ out.WriteString(EscapeStop)
+ }
+
+ return out.String()
+}
+
+const (
+ escapeStartConcealOSI = "\x1b]8;"
+ escapeStopConcealOSI = "\x1b\\"
+)
+
+func (s *escSeqParser) isEscapeStopRune(char rune) bool {
+ if strings.HasPrefix(s.escapeSeq, escapeStartConcealOSI) {
+ if strings.HasSuffix(s.escapeSeq, escapeStopConcealOSI) {
+ return true
+ }
+ } else if (s.escSeqKind == escSeqKindCSI && char == EscapeStopRuneCSI) ||
+ (s.escSeqKind == escSeqKindOSI && char == EscapeStopRuneOSI) {
+ return true
+ }
+ return false
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/filter.go b/vendor/github.com/jedib0t/go-pretty/v6/text/filter.go
new file mode 100644
index 0000000000..765c29d45e
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/filter.go
@@ -0,0 +1,12 @@
+package text
+
+// Filter filters the slice 's' to items which return truth when passed to 'f'.
+func Filter(s []string, f func(string) bool) []string {
+ var out []string
+ for _, item := range s {
+ if f(item) {
+ out = append(out, item)
+ }
+ }
+ return out
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/format.go b/vendor/github.com/jedib0t/go-pretty/v6/text/format.go
new file mode 100644
index 0000000000..458620d747
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/format.go
@@ -0,0 +1,100 @@
+package text
+
+import (
+ "strings"
+ "unicode"
+)
+
+// Format lets you transform the text in supported methods while keeping escape
+// sequences in the string intact and untouched.
+type Format int
+
+// Format enumerations
+const (
+ FormatDefault Format = iota // default_Case
+ FormatLower // lower
+ FormatTitle // Title
+ FormatUpper // UPPER
+)
+
+// Apply converts the text as directed.
+func (tc Format) Apply(text string) string {
+ switch tc {
+ case FormatLower:
+ return strings.ToLower(text)
+ case FormatTitle:
+ return toTitle(text)
+ case FormatUpper:
+ return toUpper(text)
+ default:
+ return text
+ }
+}
+
+func toTitle(text string) string {
+ prev, inEscSeq := ' ', false
+ return strings.Map(
+ func(r rune) rune {
+ if r == EscapeStartRune {
+ inEscSeq = true
+ }
+ if !inEscSeq {
+ if isSeparator(prev) {
+ prev = r
+ r = unicode.ToUpper(r)
+ } else {
+ prev = r
+ }
+ }
+ if inEscSeq && r == EscapeStopRune {
+ inEscSeq = false
+ }
+ return r
+ },
+ text,
+ )
+}
+
+func toUpper(text string) string {
+ inEscSeq := false
+ return strings.Map(
+ func(r rune) rune {
+ if r == EscapeStartRune {
+ inEscSeq = true
+ }
+ if !inEscSeq {
+ r = unicode.ToUpper(r)
+ }
+ if inEscSeq && r == EscapeStopRune {
+ inEscSeq = false
+ }
+ return r
+ },
+ text,
+ )
+}
+
+// isSeparator returns true if the given rune is a separator. This function is
+// lifted straight out of the standard library @ strings/strings.go.
+func isSeparator(r rune) bool {
+ // ASCII alphanumerics and underscore are not separators
+ if r <= 0x7F {
+ switch {
+ case '0' <= r && r <= '9':
+ return false
+ case 'a' <= r && r <= 'z':
+ return false
+ case 'A' <= r && r <= 'Z':
+ return false
+ case r == '_':
+ return false
+ }
+ return true
+ }
+ // Letters and digits are not separators
+ if unicode.IsLetter(r) || unicode.IsDigit(r) {
+ return false
+ }
+ // Otherwise, all we can do for now is treat spaces as separators.
+ return unicode.IsSpace(r)
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/hyperlink.go b/vendor/github.com/jedib0t/go-pretty/v6/text/hyperlink.go
new file mode 100644
index 0000000000..00a551ae27
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/hyperlink.go
@@ -0,0 +1,14 @@
+package text
+
+import "fmt"
+
+func Hyperlink(url, text string) string {
+ if url == "" {
+ return text
+ }
+ if text == "" {
+ return url
+ }
+ // source https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda
+ return fmt.Sprintf("\x1b]8;;%s\x1b\\%s\x1b]8;;\x1b\\", url, text)
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/string.go b/vendor/github.com/jedib0t/go-pretty/v6/text/string.go
new file mode 100644
index 0000000000..6d3e0ee5e6
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/string.go
@@ -0,0 +1,274 @@
+package text
+
+import (
+ "strings"
+ "unicode/utf8"
+
+ "github.com/mattn/go-runewidth"
+)
+
+// RuneWidth stuff
+var (
+ rwCondition = runewidth.NewCondition()
+)
+
+// InsertEveryN inserts the rune every N characters in the string. For ex.:
+//
+// InsertEveryN("Ghost", '-', 1) == "G-h-o-s-t"
+// InsertEveryN("Ghost", '-', 2) == "Gh-os-t"
+// InsertEveryN("Ghost", '-', 3) == "Gho-st"
+// InsertEveryN("Ghost", '-', 4) == "Ghos-t"
+// InsertEveryN("Ghost", '-', 5) == "Ghost"
+func InsertEveryN(str string, runeToInsert rune, n int) string {
+ if n <= 0 {
+ return str
+ }
+
+ sLen := RuneWidthWithoutEscSequences(str)
+ var out strings.Builder
+ out.Grow(sLen + (sLen / n))
+ outLen, esp := 0, escSeqParser{}
+ for idx, c := range str {
+ if esp.InSequence() {
+ esp.Consume(c)
+ out.WriteRune(c)
+ continue
+ }
+ esp.Consume(c)
+ if !esp.InSequence() && outLen > 0 && (outLen%n) == 0 && idx != sLen {
+ out.WriteRune(runeToInsert)
+ }
+ out.WriteRune(c)
+ if !esp.InSequence() {
+ outLen += RuneWidth(c)
+ }
+ }
+ return out.String()
+}
+
+// LongestLineLen returns the length of the longest "line" within the
+// argument string. For ex.:
+//
+// LongestLineLen("Ghost!\nCome back here!\nRight now!") == 15
+func LongestLineLen(str string) int {
+ maxLength, currLength, esp := 0, 0, escSeqParser{}
+ //fmt.Println(str)
+ for _, c := range str {
+ //fmt.Printf("%03d | %03d | %c | %5v | %v | %#v\n", idx, c, c, esp.inEscSeq, esp.Codes(), esp.escapeSeq)
+ if esp.InSequence() {
+ esp.Consume(c)
+ continue
+ }
+ esp.Consume(c)
+ if c == '\n' {
+ if currLength > maxLength {
+ maxLength = currLength
+ }
+ currLength = 0
+ } else if !esp.InSequence() {
+ currLength += RuneWidth(c)
+ }
+ }
+ if currLength > maxLength {
+ maxLength = currLength
+ }
+ return maxLength
+}
+
+// OverrideRuneWidthEastAsianWidth can *probably* help with alignment, and
+// length calculation issues when dealing with Unicode character-set and a
+// non-English language set in the LANG variable.
+//
+// Set this to 'false' to force the "runewidth" library to pretend to deal with
+// English character-set. Be warned that if the text/content you are dealing
+// with contains East Asian character-set, this may result in unexpected
+// behavior.
+//
+// References:
+// * https://github.com/mattn/go-runewidth/issues/64#issuecomment-1221642154
+// * https://github.com/jedib0t/go-pretty/issues/220
+// * https://github.com/jedib0t/go-pretty/issues/204
+func OverrideRuneWidthEastAsianWidth(val bool) {
+ rwCondition.EastAsianWidth = val
+}
+
+// Pad pads the given string with as many characters as needed to make it as
+// long as specified (maxLen). This function does not count escape sequences
+// while calculating length of the string. Ex.:
+//
+// Pad("Ghost", 0, ' ') == "Ghost"
+// Pad("Ghost", 3, ' ') == "Ghost"
+// Pad("Ghost", 5, ' ') == "Ghost"
+// Pad("Ghost", 7, ' ') == "Ghost "
+// Pad("Ghost", 10, '.') == "Ghost....."
+func Pad(str string, maxLen int, paddingChar rune) string {
+ strLen := RuneWidthWithoutEscSequences(str)
+ if strLen < maxLen {
+ str += strings.Repeat(string(paddingChar), maxLen-strLen)
+ }
+ return str
+}
+
+// ProcessCRLF converts "\r\n" to "\n", and processes lone "\r" by moving the
+// cursor/carriage to the start of the line and overwrites the contents
+// accordingly. Ex.:
+//
+// ProcessCRLF("abc") == "abc"
+// ProcessCRLF("abc\r\ndef") == "abc\ndef"
+// ProcessCRLF("abc\r\ndef\rghi") == "abc\nghi"
+// ProcessCRLF("abc\r\ndef\rghi\njkl") == "abc\nghi\njkl"
+// ProcessCRLF("abc\r\ndef\rghi\njkl\r") == "abc\nghi\njkl"
+// ProcessCRLF("abc\r\ndef\rghi\rjkl\rmn") == "abc\nmnl"
+func ProcessCRLF(str string) string {
+ str = strings.ReplaceAll(str, "\r\n", "\n")
+ if !strings.Contains(str, "\r") {
+ return str
+ }
+
+ lines := strings.Split(str, "\n")
+ for lineIdx, line := range lines {
+ if !strings.Contains(line, "\r") {
+ continue
+ }
+
+ lineRunes, newLineRunes := []rune(line), make([]rune, 0)
+ for idx, realIdx := 0, 0; idx < len(lineRunes); idx++ {
+ // if a CR, move "cursor" back to beginning of line
+ if lineRunes[idx] == '\r' {
+ realIdx = 0
+ continue
+ }
+
+ // if cursor is not at end, overwrite
+ if realIdx < len(newLineRunes) {
+ newLineRunes[realIdx] = lineRunes[idx]
+ } else { // else append
+ newLineRunes = append(newLineRunes, lineRunes[idx])
+ }
+ realIdx++
+ }
+ lines[lineIdx] = string(newLineRunes)
+ }
+ return strings.Join(lines, "\n")
+}
+
+// RepeatAndTrim repeats the given string until it is as long as maxRunes.
+// For ex.:
+//
+// RepeatAndTrim("", 5) == ""
+// RepeatAndTrim("Ghost", 0) == ""
+// RepeatAndTrim("Ghost", 5) == "Ghost"
+// RepeatAndTrim("Ghost", 7) == "GhostGh"
+// RepeatAndTrim("Ghost", 10) == "GhostGhost"
+func RepeatAndTrim(str string, maxRunes int) string {
+ if str == "" || maxRunes == 0 {
+ return ""
+ } else if maxRunes == utf8.RuneCountInString(str) {
+ return str
+ }
+ repeatedS := strings.Repeat(str, int(maxRunes/utf8.RuneCountInString(str))+1)
+ return Trim(repeatedS, maxRunes)
+}
+
+// RuneCount is similar to utf8.RuneCountInString, except for the fact that it
+// ignores escape sequences while counting. For ex.:
+//
+// RuneCount("") == 0
+// RuneCount("Ghost") == 5
+// RuneCount("\x1b[33mGhost\x1b[0m") == 5
+// RuneCount("\x1b[33mGhost\x1b[0") == 5
+//
+// Deprecated: in favor of RuneWidthWithoutEscSequences
+func RuneCount(str string) int {
+ return RuneWidthWithoutEscSequences(str)
+}
+
+// RuneWidth returns the mostly accurate character-width of the rune. This is
+// not 100% accurate as the character width is usually dependent on the
+// typeface (font) used in the console/terminal. For ex.:
+//
+// RuneWidth('A') == 1
+// RuneWidth('ツ') == 2
+// RuneWidth('⊙') == 1
+// RuneWidth('︿') == 2
+// RuneWidth(0x27) == 0
+func RuneWidth(r rune) int {
+ return rwCondition.RuneWidth(r)
+}
+
+// RuneWidthWithoutEscSequences is similar to RuneWidth, except for the fact
+// that it ignores escape sequences while counting. For ex.:
+//
+// RuneWidthWithoutEscSequences("") == 0
+// RuneWidthWithoutEscSequences("Ghost") == 5
+// RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0m") == 5
+// RuneWidthWithoutEscSequences("\x1b[33mGhost\x1b[0") == 5
+func RuneWidthWithoutEscSequences(str string) int {
+ count, esp := 0, escSeqParser{}
+ for _, c := range str {
+ if esp.InSequence() {
+ esp.Consume(c)
+ continue
+ }
+ esp.Consume(c)
+ if !esp.InSequence() {
+ count += RuneWidth(c)
+ }
+ }
+ return count
+}
+
+// Snip returns the given string with a fixed length. For ex.:
+//
+// Snip("Ghost", 0, "~") == "Ghost"
+// Snip("Ghost", 1, "~") == "~"
+// Snip("Ghost", 3, "~") == "Gh~"
+// Snip("Ghost", 5, "~") == "Ghost"
+// Snip("Ghost", 7, "~") == "Ghost "
+// Snip("\x1b[33mGhost\x1b[0m", 7, "~") == "\x1b[33mGhost\x1b[0m "
+func Snip(str string, length int, snipIndicator string) string {
+ if length > 0 {
+ lenStr := RuneWidthWithoutEscSequences(str)
+ if lenStr > length {
+ lenStrFinal := length - RuneWidthWithoutEscSequences(snipIndicator)
+ return Trim(str, lenStrFinal) + snipIndicator
+ }
+ }
+ return str
+}
+
+// Trim trims a string to the given length while ignoring escape sequences. For
+// ex.:
+//
+// Trim("Ghost", 3) == "Gho"
+// Trim("Ghost", 6) == "Ghost"
+// Trim("\x1b[33mGhost\x1b[0m", 3) == "\x1b[33mGho\x1b[0m"
+// Trim("\x1b[33mGhost\x1b[0m", 6) == "\x1b[33mGhost\x1b[0m"
+func Trim(str string, maxLen int) string {
+ if maxLen <= 0 {
+ return ""
+ }
+
+ var out strings.Builder
+ out.Grow(maxLen)
+
+ outLen, esp := 0, escSeqParser{}
+ for _, sChr := range str {
+ if esp.InSequence() {
+ esp.Consume(sChr)
+ out.WriteRune(sChr)
+ continue
+ }
+ esp.Consume(sChr)
+ if esp.InSequence() {
+ out.WriteRune(sChr)
+ continue
+ }
+ if outLen < maxLen {
+ outLen++
+ out.WriteRune(sChr)
+ continue
+ }
+ }
+ return out.String()
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go b/vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go
new file mode 100644
index 0000000000..193a721cce
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go
@@ -0,0 +1,228 @@
+package text
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "strconv"
+ "strings"
+ "time"
+)
+
+// Transformer related constants
+const (
+ unixTimeMinMilliseconds = int64(10000000000)
+ unixTimeMinMicroseconds = unixTimeMinMilliseconds * 1000
+ unixTimeMinNanoSeconds = unixTimeMinMicroseconds * 1000
+)
+
+// Transformer related variables
+var (
+ colorsNumberPositive = Colors{FgHiGreen}
+ colorsNumberNegative = Colors{FgHiRed}
+ colorsNumberZero = Colors{}
+ colorsURL = Colors{Underline, FgBlue}
+ rfc3339Milli = "2006-01-02T15:04:05.000Z07:00"
+ rfc3339Micro = "2006-01-02T15:04:05.000000Z07:00"
+
+ possibleTimeLayouts = []string{
+ time.RFC3339,
+ rfc3339Milli, // strfmt.DateTime.String()'s default layout
+ rfc3339Micro,
+ time.RFC3339Nano,
+ }
+)
+
+// Transformer helps format the contents of an object to the user's liking.
+type Transformer func(val interface{}) string
+
+// NewNumberTransformer returns a number Transformer that:
+// - transforms the number as directed by 'format' (ex.: %.2f)
+// - colors negative values Red
+// - colors positive values Green
+func NewNumberTransformer(format string) Transformer {
+ return func(val interface{}) string {
+ if valStr := transformInt(format, val); valStr != "" {
+ return valStr
+ }
+ if valStr := transformUint(format, val); valStr != "" {
+ return valStr
+ }
+ if valStr := transformFloat(format, val); valStr != "" {
+ return valStr
+ }
+ return fmt.Sprint(val)
+ }
+}
+
+func transformInt(format string, val interface{}) string {
+ transform := func(val int64) string {
+ if val < 0 {
+ return colorsNumberNegative.Sprintf("-"+format, -val)
+ }
+ if val > 0 {
+ return colorsNumberPositive.Sprintf(format, val)
+ }
+ return colorsNumberZero.Sprintf(format, val)
+ }
+
+ if number, ok := val.(int); ok {
+ return transform(int64(number))
+ }
+ if number, ok := val.(int8); ok {
+ return transform(int64(number))
+ }
+ if number, ok := val.(int16); ok {
+ return transform(int64(number))
+ }
+ if number, ok := val.(int32); ok {
+ return transform(int64(number))
+ }
+ if number, ok := val.(int64); ok {
+ return transform(number)
+ }
+ return ""
+}
+
+func transformUint(format string, val interface{}) string {
+ transform := func(val uint64) string {
+ if val > 0 {
+ return colorsNumberPositive.Sprintf(format, val)
+ }
+ return colorsNumberZero.Sprintf(format, val)
+ }
+
+ if number, ok := val.(uint); ok {
+ return transform(uint64(number))
+ }
+ if number, ok := val.(uint8); ok {
+ return transform(uint64(number))
+ }
+ if number, ok := val.(uint16); ok {
+ return transform(uint64(number))
+ }
+ if number, ok := val.(uint32); ok {
+ return transform(uint64(number))
+ }
+ if number, ok := val.(uint64); ok {
+ return transform(number)
+ }
+ return ""
+}
+
+func transformFloat(format string, val interface{}) string {
+ transform := func(val float64) string {
+ if val < 0 {
+ return colorsNumberNegative.Sprintf("-"+format, -val)
+ }
+ if val > 0 {
+ return colorsNumberPositive.Sprintf(format, val)
+ }
+ return colorsNumberZero.Sprintf(format, val)
+ }
+
+ if number, ok := val.(float32); ok {
+ return transform(float64(number))
+ }
+ if number, ok := val.(float64); ok {
+ return transform(number)
+ }
+ return ""
+}
+
+// NewJSONTransformer returns a Transformer that can format a JSON string or an
+// object into pretty-indented JSON-strings.
+func NewJSONTransformer(prefix string, indent string) Transformer {
+ return func(val interface{}) string {
+ if valStr, ok := val.(string); ok {
+ var b bytes.Buffer
+ if err := json.Indent(&b, []byte(strings.TrimSpace(valStr)), prefix, indent); err == nil {
+ return b.String()
+ }
+ } else if b, err := json.MarshalIndent(val, prefix, indent); err == nil {
+ return string(b)
+ }
+ return fmt.Sprintf("%#v", val)
+ }
+}
+
+// NewTimeTransformer returns a Transformer that can format a timestamp (a
+// time.Time) into a well-defined time format defined using the provided layout
+// (ex.: time.RFC3339).
+//
+// If a non-nil location value is provided, the time will be localized to that
+// location (use time.Local to get localized timestamps).
+func NewTimeTransformer(layout string, location *time.Location) Transformer {
+ return func(val interface{}) string {
+ rsp := fmt.Sprint(val)
+ if valTime, ok := val.(time.Time); ok {
+ rsp = formatTime(valTime, layout, location)
+ } else {
+ // cycle through some supported layouts to see if the string form
+ // of the object matches any of these layouts
+ for _, possibleTimeLayout := range possibleTimeLayouts {
+ if valTime, err := time.Parse(possibleTimeLayout, rsp); err == nil {
+ rsp = formatTime(valTime, layout, location)
+ break
+ }
+ }
+ }
+ return rsp
+ }
+}
+
+// NewUnixTimeTransformer returns a Transformer that can format a unix-timestamp
+// into a well-defined time format as defined by 'layout'. This can handle
+// unix-time in Seconds, MilliSeconds, Microseconds and Nanoseconds.
+//
+// If a non-nil location value is provided, the time will be localized to that
+// location (use time.Local to get localized timestamps).
+func NewUnixTimeTransformer(layout string, location *time.Location) Transformer {
+ transformer := NewTimeTransformer(layout, location)
+
+ return func(val interface{}) string {
+ if unixTime, ok := val.(int64); ok {
+ return formatTimeUnix(unixTime, transformer)
+ } else if unixTimeStr, ok := val.(string); ok {
+ if unixTime, err := strconv.ParseInt(unixTimeStr, 10, 64); err == nil {
+ return formatTimeUnix(unixTime, transformer)
+ }
+ }
+ return fmt.Sprint(val)
+ }
+}
+
+// NewURLTransformer returns a Transformer that can format and pretty print a string
+// that contains a URL (the text is underlined and colored Blue).
+func NewURLTransformer(colors ...Color) Transformer {
+ colorsToUse := colorsURL
+ if len(colors) > 0 {
+ colorsToUse = colors
+ }
+
+ return func(val interface{}) string {
+ return colorsToUse.Sprint(val)
+ }
+}
+
+func formatTime(t time.Time, layout string, location *time.Location) string {
+ rsp := ""
+ if t.Unix() > 0 {
+ if location != nil {
+ t = t.In(location)
+ }
+ rsp = t.Format(layout)
+ }
+ return rsp
+}
+
+func formatTimeUnix(unixTime int64, timeTransformer Transformer) string {
+ if unixTime >= unixTimeMinNanoSeconds {
+ unixTime = unixTime / time.Second.Nanoseconds()
+ } else if unixTime >= unixTimeMinMicroseconds {
+ unixTime = unixTime / (time.Second.Nanoseconds() / 1000)
+ } else if unixTime >= unixTimeMinMilliseconds {
+ unixTime = unixTime / (time.Second.Nanoseconds() / 1000000)
+ }
+ return timeTransformer(time.Unix(unixTime, 0))
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/valign.go b/vendor/github.com/jedib0t/go-pretty/v6/text/valign.go
new file mode 100644
index 0000000000..f1a75e96d5
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/valign.go
@@ -0,0 +1,67 @@
+package text
+
+import "strings"
+
+// VAlign denotes how text is to be aligned vertically.
+type VAlign int
+
+// VAlign enumerations
+const (
+ VAlignDefault VAlign = iota // same as VAlignTop
+ VAlignTop // "top\n\n"
+ VAlignMiddle // "\nmiddle\n"
+ VAlignBottom // "\n\nbottom"
+)
+
+// Apply aligns the lines vertically. For ex.:
+// - VAlignTop.Apply({"Game", "Of", "Thrones"}, 5)
+// returns {"Game", "Of", "Thrones", "", ""}
+// - VAlignMiddle.Apply({"Game", "Of", "Thrones"}, 5)
+// returns {"", "Game", "Of", "Thrones", ""}
+// - VAlignBottom.Apply({"Game", "Of", "Thrones"}, 5)
+// returns {"", "", "Game", "Of", "Thrones"}
+func (va VAlign) Apply(lines []string, maxLines int) []string {
+ if len(lines) == maxLines {
+ return lines
+ } else if len(lines) > maxLines {
+ maxLines = len(lines)
+ }
+
+ insertIdx := 0
+ if va == VAlignMiddle {
+ insertIdx = int(maxLines-len(lines)) / 2
+ } else if va == VAlignBottom {
+ insertIdx = maxLines - len(lines)
+ }
+
+ linesOut := strings.Split(strings.Repeat("\n", maxLines-1), "\n")
+ for idx, line := range lines {
+ linesOut[idx+insertIdx] = line
+ }
+ return linesOut
+}
+
+// ApplyStr aligns the string (of 1 or more lines) vertically. For ex.:
+// - VAlignTop.ApplyStr("Game\nOf\nThrones", 5)
+// returns {"Game", "Of", "Thrones", "", ""}
+// - VAlignMiddle.ApplyStr("Game\nOf\nThrones", 5)
+// returns {"", "Game", "Of", "Thrones", ""}
+// - VAlignBottom.ApplyStr("Game\nOf\nThrones", 5)
+// returns {"", "", "Game", "Of", "Thrones"}
+func (va VAlign) ApplyStr(text string, maxLines int) []string {
+ return va.Apply(strings.Split(text, "\n"), maxLines)
+}
+
+// HTMLProperty returns the equivalent HTML vertical-align tag property.
+func (va VAlign) HTMLProperty() string {
+ switch va {
+ case VAlignTop:
+ return "valign=\"top\""
+ case VAlignMiddle:
+ return "valign=\"middle\""
+ case VAlignBottom:
+ return "valign=\"bottom\""
+ default:
+ return ""
+ }
+}
diff --git a/vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go b/vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go
new file mode 100644
index 0000000000..0f6d7e9e02
--- /dev/null
+++ b/vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go
@@ -0,0 +1,235 @@
+package text
+
+import (
+ "strings"
+ "unicode/utf8"
+)
+
+// WrapHard wraps a string to the given length using a newline. Handles strings
+// with ANSI escape sequences (such as text color) without breaking the text
+// formatting. Breaks all words that go beyond the line boundary.
+//
+// For examples, refer to the unit-tests or GoDoc examples.
+func WrapHard(str string, wrapLen int) string {
+ if wrapLen <= 0 {
+ return ""
+ }
+ str = strings.Replace(str, "\t", " ", -1)
+ sLen := utf8.RuneCountInString(str)
+ if sLen <= wrapLen {
+ return str
+ }
+
+ out := &strings.Builder{}
+ out.Grow(sLen + (sLen / wrapLen))
+ for idx, paragraph := range strings.Split(str, "\n\n") {
+ if idx > 0 {
+ out.WriteString("\n\n")
+ }
+ wrapHard(paragraph, wrapLen, out)
+ }
+
+ return out.String()
+}
+
+// WrapSoft wraps a string to the given length using a newline. Handles strings
+// with ANSI escape sequences (such as text color) without breaking the text
+// formatting. Tries to move words that go beyond the line boundary to the next
+// line.
+//
+// For examples, refer to the unit-tests or GoDoc examples.
+func WrapSoft(str string, wrapLen int) string {
+ if wrapLen <= 0 {
+ return ""
+ }
+ str = strings.Replace(str, "\t", " ", -1)
+ sLen := utf8.RuneCountInString(str)
+ if sLen <= wrapLen {
+ return str
+ }
+
+ out := &strings.Builder{}
+ out.Grow(sLen + (sLen / wrapLen))
+ for idx, paragraph := range strings.Split(str, "\n\n") {
+ if idx > 0 {
+ out.WriteString("\n\n")
+ }
+ wrapSoft(paragraph, wrapLen, out)
+ }
+
+ return out.String()
+}
+
+// WrapText is very similar to WrapHard except for one minor difference. Unlike
+// WrapHard which discards line-breaks and respects only paragraph-breaks, this
+// function respects line-breaks too.
+//
+// For examples, refer to the unit-tests or GoDoc examples.
+func WrapText(str string, wrapLen int) string {
+ if wrapLen <= 0 {
+ return ""
+ }
+ str = strings.Replace(str, "\t", " ", -1)
+ sLen := utf8.RuneCountInString(str)
+ if sLen <= wrapLen {
+ return str
+ }
+
+ out := &strings.Builder{}
+ out.Grow(sLen + (sLen / wrapLen))
+ for idx, line := range strings.Split(str, "\n") {
+ if idx > 0 {
+ out.WriteString("\n")
+ }
+ wrapHard(line, wrapLen, out)
+ }
+
+ return out.String()
+}
+
+func appendChar(char rune, wrapLen int, lineLen *int, inEscSeq bool, lastSeenEscSeq string, out *strings.Builder) {
+ // handle reaching the end of the line as dictated by wrapLen or by finding
+ // a newline character
+ if (*lineLen == wrapLen && !inEscSeq && char != '\n') || (char == '\n') {
+ if lastSeenEscSeq != "" {
+ // terminate escape sequence and the line; and restart the escape
+ // sequence in the next line
+ out.WriteString(EscapeReset)
+ out.WriteRune('\n')
+ out.WriteString(lastSeenEscSeq)
+ } else {
+ // just start a new line
+ out.WriteRune('\n')
+ }
+ // reset line index to 0th character
+ *lineLen = 0
+ }
+
+ // if the rune is not a new line, output it
+ if char != '\n' {
+ out.WriteRune(char)
+
+ // increment the line index if not in the middle of an escape sequence
+ if !inEscSeq {
+ *lineLen++
+ }
+ }
+}
+
+func appendWord(word string, lineIdx *int, lastSeenEscSeq string, wrapLen int, out *strings.Builder) {
+ inEscSeq := false
+ for _, char := range word {
+ if char == EscapeStartRune {
+ inEscSeq = true
+ lastSeenEscSeq = ""
+ }
+ if inEscSeq {
+ lastSeenEscSeq += string(char)
+ }
+
+ appendChar(char, wrapLen, lineIdx, inEscSeq, lastSeenEscSeq, out)
+
+ if inEscSeq && char == EscapeStopRune {
+ inEscSeq = false
+ }
+ if lastSeenEscSeq == EscapeReset {
+ lastSeenEscSeq = ""
+ }
+ }
+}
+
+func terminateLine(wrapLen int, lineLen *int, lastSeenEscSeq string, out *strings.Builder) {
+ if *lineLen < wrapLen {
+ out.WriteString(strings.Repeat(" ", wrapLen-*lineLen))
+ }
+ // something is already on the line; terminate it
+ if lastSeenEscSeq != "" {
+ out.WriteString(EscapeReset)
+ }
+ out.WriteRune('\n')
+ out.WriteString(lastSeenEscSeq)
+ *lineLen = 0
+}
+
+func terminateOutput(lastSeenEscSeq string, out *strings.Builder) {
+ if lastSeenEscSeq != "" && lastSeenEscSeq != EscapeReset && !strings.HasSuffix(out.String(), EscapeReset) {
+ out.WriteString(EscapeReset)
+ }
+}
+
+func wrapHard(paragraph string, wrapLen int, out *strings.Builder) {
+ esp := escSeqParser{}
+ lineLen, lastSeenEscSeq := 0, ""
+ words := strings.Fields(paragraph)
+ for wordIdx, word := range words {
+ if openEscSeq := esp.ParseString(word); openEscSeq != "" {
+ lastSeenEscSeq = openEscSeq
+ }
+ if lineLen > 0 {
+ out.WriteRune(' ')
+ lineLen++
+ }
+
+ wordLen := RuneWidthWithoutEscSequences(word)
+ if lineLen+wordLen <= wrapLen { // word fits within the line
+ out.WriteString(word)
+ lineLen += wordLen
+ } else { // word doesn't fit within the line; hard-wrap
+ appendWord(word, &lineLen, lastSeenEscSeq, wrapLen, out)
+ }
+
+ // end of line; but more words incoming
+ if lineLen == wrapLen && wordIdx < len(words)-1 {
+ terminateLine(wrapLen, &lineLen, lastSeenEscSeq, out)
+ }
+ }
+ terminateOutput(lastSeenEscSeq, out)
+}
+
+func wrapSoft(paragraph string, wrapLen int, out *strings.Builder) {
+ esp := escSeqParser{}
+ lineLen, lastSeenEscSeq := 0, ""
+ words := strings.Fields(paragraph)
+ for wordIdx, word := range words {
+ if openEscSeq := esp.ParseString(word); openEscSeq != "" {
+ lastSeenEscSeq = openEscSeq
+ }
+
+ spacing, spacingLen := wrapSoftSpacing(lineLen)
+ wordLen := RuneWidthWithoutEscSequences(word)
+ if lineLen+spacingLen+wordLen <= wrapLen { // word fits within the line
+ out.WriteString(spacing)
+ out.WriteString(word)
+ lineLen += spacingLen + wordLen
+ } else { // word doesn't fit within the line
+ lineLen = wrapSoftLastWordInLine(wrapLen, lineLen, lastSeenEscSeq, wordLen, word, out)
+ }
+
+ // end of line; but more words incoming
+ if lineLen == wrapLen && wordIdx < len(words)-1 {
+ terminateLine(wrapLen, &lineLen, lastSeenEscSeq, out)
+ }
+ }
+ terminateOutput(lastSeenEscSeq, out)
+}
+
+func wrapSoftLastWordInLine(wrapLen int, lineLen int, lastSeenEscSeq string, wordLen int, word string, out *strings.Builder) int {
+ if lineLen > 0 { // something is already on the line; terminate it
+ terminateLine(wrapLen, &lineLen, lastSeenEscSeq, out)
+ }
+ if wordLen <= wrapLen { // word fits within a single line
+ out.WriteString(word)
+ lineLen = wordLen
+ } else { // word doesn't fit within a single line; hard-wrap
+ appendWord(word, &lineLen, lastSeenEscSeq, wrapLen, out)
+ }
+ return lineLen
+}
+
+func wrapSoftSpacing(lineLen int) (string, int) {
+ spacing, spacingLen := "", 0
+ if lineLen > 0 {
+ spacing, spacingLen = " ", 1
+ }
+ return spacing, spacingLen
+}
diff --git a/vendor/github.com/mattn/go-runewidth/LICENSE b/vendor/github.com/mattn/go-runewidth/LICENSE
new file mode 100644
index 0000000000..91b5cef30e
--- /dev/null
+++ b/vendor/github.com/mattn/go-runewidth/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2016 Yasuhiro Matsumoto
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/mattn/go-runewidth/README.md b/vendor/github.com/mattn/go-runewidth/README.md
new file mode 100644
index 0000000000..5e2cfd98cb
--- /dev/null
+++ b/vendor/github.com/mattn/go-runewidth/README.md
@@ -0,0 +1,27 @@
+go-runewidth
+============
+
+[![Build Status](https://github.com/mattn/go-runewidth/workflows/test/badge.svg?branch=master)](https://github.com/mattn/go-runewidth/actions?query=workflow%3Atest)
+[![Codecov](https://codecov.io/gh/mattn/go-runewidth/branch/master/graph/badge.svg)](https://codecov.io/gh/mattn/go-runewidth)
+[![GoDoc](https://godoc.org/github.com/mattn/go-runewidth?status.svg)](http://godoc.org/github.com/mattn/go-runewidth)
+[![Go Report Card](https://goreportcard.com/badge/github.com/mattn/go-runewidth)](https://goreportcard.com/report/github.com/mattn/go-runewidth)
+
+Provides functions to get fixed width of the character or string.
+
+Usage
+-----
+
+```go
+runewidth.StringWidth("つのだ☆HIRO") == 12
+```
+
+
+Author
+------
+
+Yasuhiro Matsumoto
+
+License
+-------
+
+under the MIT License: http://mattn.mit-license.org/2013
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth.go b/vendor/github.com/mattn/go-runewidth/runewidth.go
new file mode 100644
index 0000000000..7dfbb3be91
--- /dev/null
+++ b/vendor/github.com/mattn/go-runewidth/runewidth.go
@@ -0,0 +1,358 @@
+package runewidth
+
+import (
+ "os"
+ "strings"
+
+ "github.com/rivo/uniseg"
+)
+
+//go:generate go run script/generate.go
+
+var (
+ // EastAsianWidth will be set true if the current locale is CJK
+ EastAsianWidth bool
+
+ // StrictEmojiNeutral should be set false if handle broken fonts
+ StrictEmojiNeutral bool = true
+
+ // DefaultCondition is a condition in current locale
+ DefaultCondition = &Condition{
+ EastAsianWidth: false,
+ StrictEmojiNeutral: true,
+ }
+)
+
+func init() {
+ handleEnv()
+}
+
+func handleEnv() {
+ env := os.Getenv("RUNEWIDTH_EASTASIAN")
+ if env == "" {
+ EastAsianWidth = IsEastAsian()
+ } else {
+ EastAsianWidth = env == "1"
+ }
+ // update DefaultCondition
+ if DefaultCondition.EastAsianWidth != EastAsianWidth {
+ DefaultCondition.EastAsianWidth = EastAsianWidth
+ if len(DefaultCondition.combinedLut) > 0 {
+ DefaultCondition.combinedLut = DefaultCondition.combinedLut[:0]
+ CreateLUT()
+ }
+ }
+}
+
+type interval struct {
+ first rune
+ last rune
+}
+
+type table []interval
+
+func inTables(r rune, ts ...table) bool {
+ for _, t := range ts {
+ if inTable(r, t) {
+ return true
+ }
+ }
+ return false
+}
+
+func inTable(r rune, t table) bool {
+ if r < t[0].first {
+ return false
+ }
+
+ bot := 0
+ top := len(t) - 1
+ for top >= bot {
+ mid := (bot + top) >> 1
+
+ switch {
+ case t[mid].last < r:
+ bot = mid + 1
+ case t[mid].first > r:
+ top = mid - 1
+ default:
+ return true
+ }
+ }
+
+ return false
+}
+
+var private = table{
+ {0x00E000, 0x00F8FF}, {0x0F0000, 0x0FFFFD}, {0x100000, 0x10FFFD},
+}
+
+var nonprint = table{
+ {0x0000, 0x001F}, {0x007F, 0x009F}, {0x00AD, 0x00AD},
+ {0x070F, 0x070F}, {0x180B, 0x180E}, {0x200B, 0x200F},
+ {0x2028, 0x202E}, {0x206A, 0x206F}, {0xD800, 0xDFFF},
+ {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFB}, {0xFFFE, 0xFFFF},
+}
+
+// Condition have flag EastAsianWidth whether the current locale is CJK or not.
+type Condition struct {
+ combinedLut []byte
+ EastAsianWidth bool
+ StrictEmojiNeutral bool
+}
+
+// NewCondition return new instance of Condition which is current locale.
+func NewCondition() *Condition {
+ return &Condition{
+ EastAsianWidth: EastAsianWidth,
+ StrictEmojiNeutral: StrictEmojiNeutral,
+ }
+}
+
+// RuneWidth returns the number of cells in r.
+// See http://www.unicode.org/reports/tr11/
+func (c *Condition) RuneWidth(r rune) int {
+ if r < 0 || r > 0x10FFFF {
+ return 0
+ }
+ if len(c.combinedLut) > 0 {
+ return int(c.combinedLut[r>>1]>>(uint(r&1)*4)) & 3
+ }
+ // optimized version, verified by TestRuneWidthChecksums()
+ if !c.EastAsianWidth {
+ switch {
+ case r < 0x20:
+ return 0
+ case (r >= 0x7F && r <= 0x9F) || r == 0xAD: // nonprint
+ return 0
+ case r < 0x300:
+ return 1
+ case inTable(r, narrow):
+ return 1
+ case inTables(r, nonprint, combining):
+ return 0
+ case inTable(r, doublewidth):
+ return 2
+ default:
+ return 1
+ }
+ } else {
+ switch {
+ case inTables(r, nonprint, combining):
+ return 0
+ case inTable(r, narrow):
+ return 1
+ case inTables(r, ambiguous, doublewidth):
+ return 2
+ case !c.StrictEmojiNeutral && inTables(r, ambiguous, emoji, narrow):
+ return 2
+ default:
+ return 1
+ }
+ }
+}
+
+// CreateLUT will create an in-memory lookup table of 557056 bytes for faster operation.
+// This should not be called concurrently with other operations on c.
+// If options in c is changed, CreateLUT should be called again.
+func (c *Condition) CreateLUT() {
+ const max = 0x110000
+ lut := c.combinedLut
+ if len(c.combinedLut) != 0 {
+ // Remove so we don't use it.
+ c.combinedLut = nil
+ } else {
+ lut = make([]byte, max/2)
+ }
+ for i := range lut {
+ i32 := int32(i * 2)
+ x0 := c.RuneWidth(i32)
+ x1 := c.RuneWidth(i32 + 1)
+ lut[i] = uint8(x0) | uint8(x1)<<4
+ }
+ c.combinedLut = lut
+}
+
+// StringWidth return width as you can see
+func (c *Condition) StringWidth(s string) (width int) {
+ g := uniseg.NewGraphemes(s)
+ for g.Next() {
+ var chWidth int
+ for _, r := range g.Runes() {
+ chWidth = c.RuneWidth(r)
+ if chWidth > 0 {
+ break // Our best guess at this point is to use the width of the first non-zero-width rune.
+ }
+ }
+ width += chWidth
+ }
+ return
+}
+
+// Truncate return string truncated with w cells
+func (c *Condition) Truncate(s string, w int, tail string) string {
+ if c.StringWidth(s) <= w {
+ return s
+ }
+ w -= c.StringWidth(tail)
+ var width int
+ pos := len(s)
+ g := uniseg.NewGraphemes(s)
+ for g.Next() {
+ var chWidth int
+ for _, r := range g.Runes() {
+ chWidth = c.RuneWidth(r)
+ if chWidth > 0 {
+ break // See StringWidth() for details.
+ }
+ }
+ if width+chWidth > w {
+ pos, _ = g.Positions()
+ break
+ }
+ width += chWidth
+ }
+ return s[:pos] + tail
+}
+
+// TruncateLeft cuts w cells from the beginning of the `s`.
+func (c *Condition) TruncateLeft(s string, w int, prefix string) string {
+ if c.StringWidth(s) <= w {
+ return prefix
+ }
+
+ var width int
+ pos := len(s)
+
+ g := uniseg.NewGraphemes(s)
+ for g.Next() {
+ var chWidth int
+ for _, r := range g.Runes() {
+ chWidth = c.RuneWidth(r)
+ if chWidth > 0 {
+ break // See StringWidth() for details.
+ }
+ }
+
+ if width+chWidth > w {
+ if width < w {
+ _, pos = g.Positions()
+ prefix += strings.Repeat(" ", width+chWidth-w)
+ } else {
+ pos, _ = g.Positions()
+ }
+
+ break
+ }
+
+ width += chWidth
+ }
+
+ return prefix + s[pos:]
+}
+
+// Wrap return string wrapped with w cells
+func (c *Condition) Wrap(s string, w int) string {
+ width := 0
+ out := ""
+ for _, r := range s {
+ cw := c.RuneWidth(r)
+ if r == '\n' {
+ out += string(r)
+ width = 0
+ continue
+ } else if width+cw > w {
+ out += "\n"
+ width = 0
+ out += string(r)
+ width += cw
+ continue
+ }
+ out += string(r)
+ width += cw
+ }
+ return out
+}
+
+// FillLeft return string filled in left by spaces in w cells
+func (c *Condition) FillLeft(s string, w int) string {
+ width := c.StringWidth(s)
+ count := w - width
+ if count > 0 {
+ b := make([]byte, count)
+ for i := range b {
+ b[i] = ' '
+ }
+ return string(b) + s
+ }
+ return s
+}
+
+// FillRight return string filled in left by spaces in w cells
+func (c *Condition) FillRight(s string, w int) string {
+ width := c.StringWidth(s)
+ count := w - width
+ if count > 0 {
+ b := make([]byte, count)
+ for i := range b {
+ b[i] = ' '
+ }
+ return s + string(b)
+ }
+ return s
+}
+
+// RuneWidth returns the number of cells in r.
+// See http://www.unicode.org/reports/tr11/
+func RuneWidth(r rune) int {
+ return DefaultCondition.RuneWidth(r)
+}
+
+// IsAmbiguousWidth returns whether is ambiguous width or not.
+func IsAmbiguousWidth(r rune) bool {
+ return inTables(r, private, ambiguous)
+}
+
+// IsNeutralWidth returns whether is neutral width or not.
+func IsNeutralWidth(r rune) bool {
+ return inTable(r, neutral)
+}
+
+// StringWidth return width as you can see
+func StringWidth(s string) (width int) {
+ return DefaultCondition.StringWidth(s)
+}
+
+// Truncate return string truncated with w cells
+func Truncate(s string, w int, tail string) string {
+ return DefaultCondition.Truncate(s, w, tail)
+}
+
+// TruncateLeft cuts w cells from the beginning of the `s`.
+func TruncateLeft(s string, w int, prefix string) string {
+ return DefaultCondition.TruncateLeft(s, w, prefix)
+}
+
+// Wrap return string wrapped with w cells
+func Wrap(s string, w int) string {
+ return DefaultCondition.Wrap(s, w)
+}
+
+// FillLeft return string filled in left by spaces in w cells
+func FillLeft(s string, w int) string {
+ return DefaultCondition.FillLeft(s, w)
+}
+
+// FillRight return string filled in left by spaces in w cells
+func FillRight(s string, w int) string {
+ return DefaultCondition.FillRight(s, w)
+}
+
+// CreateLUT will create an in-memory lookup table of 557055 bytes for faster operation.
+// This should not be called concurrently with other operations.
+func CreateLUT() {
+ if len(DefaultCondition.combinedLut) > 0 {
+ return
+ }
+ DefaultCondition.CreateLUT()
+}
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go
new file mode 100644
index 0000000000..84b6528dfe
--- /dev/null
+++ b/vendor/github.com/mattn/go-runewidth/runewidth_appengine.go
@@ -0,0 +1,9 @@
+//go:build appengine
+// +build appengine
+
+package runewidth
+
+// IsEastAsian return true if the current locale is CJK
+func IsEastAsian() bool {
+ return false
+}
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_js.go b/vendor/github.com/mattn/go-runewidth/runewidth_js.go
new file mode 100644
index 0000000000..c2abbc2db3
--- /dev/null
+++ b/vendor/github.com/mattn/go-runewidth/runewidth_js.go
@@ -0,0 +1,9 @@
+//go:build js && !appengine
+// +build js,!appengine
+
+package runewidth
+
+func IsEastAsian() bool {
+ // TODO: Implement this for the web. Detect east asian in a compatible way, and return true.
+ return false
+}
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_posix.go b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go
new file mode 100644
index 0000000000..5a31d738ec
--- /dev/null
+++ b/vendor/github.com/mattn/go-runewidth/runewidth_posix.go
@@ -0,0 +1,81 @@
+//go:build !windows && !js && !appengine
+// +build !windows,!js,!appengine
+
+package runewidth
+
+import (
+ "os"
+ "regexp"
+ "strings"
+)
+
+var reLoc = regexp.MustCompile(`^[a-z][a-z][a-z]?(?:_[A-Z][A-Z])?\.(.+)`)
+
+var mblenTable = map[string]int{
+ "utf-8": 6,
+ "utf8": 6,
+ "jis": 8,
+ "eucjp": 3,
+ "euckr": 2,
+ "euccn": 2,
+ "sjis": 2,
+ "cp932": 2,
+ "cp51932": 2,
+ "cp936": 2,
+ "cp949": 2,
+ "cp950": 2,
+ "big5": 2,
+ "gbk": 2,
+ "gb2312": 2,
+}
+
+func isEastAsian(locale string) bool {
+ charset := strings.ToLower(locale)
+ r := reLoc.FindStringSubmatch(locale)
+ if len(r) == 2 {
+ charset = strings.ToLower(r[1])
+ }
+
+ if strings.HasSuffix(charset, "@cjk_narrow") {
+ return false
+ }
+
+ for pos, b := range []byte(charset) {
+ if b == '@' {
+ charset = charset[:pos]
+ break
+ }
+ }
+ max := 1
+ if m, ok := mblenTable[charset]; ok {
+ max = m
+ }
+ if max > 1 && (charset[0] != 'u' ||
+ strings.HasPrefix(locale, "ja") ||
+ strings.HasPrefix(locale, "ko") ||
+ strings.HasPrefix(locale, "zh")) {
+ return true
+ }
+ return false
+}
+
+// IsEastAsian return true if the current locale is CJK
+func IsEastAsian() bool {
+ locale := os.Getenv("LC_ALL")
+ if locale == "" {
+ locale = os.Getenv("LC_CTYPE")
+ }
+ if locale == "" {
+ locale = os.Getenv("LANG")
+ }
+
+ // ignore C locale
+ if locale == "POSIX" || locale == "C" {
+ return false
+ }
+ if len(locale) > 1 && locale[0] == 'C' && (locale[1] == '.' || locale[1] == '-') {
+ return false
+ }
+
+ return isEastAsian(locale)
+}
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_table.go b/vendor/github.com/mattn/go-runewidth/runewidth_table.go
new file mode 100644
index 0000000000..e5d890c266
--- /dev/null
+++ b/vendor/github.com/mattn/go-runewidth/runewidth_table.go
@@ -0,0 +1,439 @@
+// Code generated by script/generate.go. DO NOT EDIT.
+
+package runewidth
+
+var combining = table{
+ {0x0300, 0x036F}, {0x0483, 0x0489}, {0x07EB, 0x07F3},
+ {0x0C00, 0x0C00}, {0x0C04, 0x0C04}, {0x0D00, 0x0D01},
+ {0x135D, 0x135F}, {0x1A7F, 0x1A7F}, {0x1AB0, 0x1AC0},
+ {0x1B6B, 0x1B73}, {0x1DC0, 0x1DF9}, {0x1DFB, 0x1DFF},
+ {0x20D0, 0x20F0}, {0x2CEF, 0x2CF1}, {0x2DE0, 0x2DFF},
+ {0x3099, 0x309A}, {0xA66F, 0xA672}, {0xA674, 0xA67D},
+ {0xA69E, 0xA69F}, {0xA6F0, 0xA6F1}, {0xA8E0, 0xA8F1},
+ {0xFE20, 0xFE2F}, {0x101FD, 0x101FD}, {0x10376, 0x1037A},
+ {0x10EAB, 0x10EAC}, {0x10F46, 0x10F50}, {0x11300, 0x11301},
+ {0x1133B, 0x1133C}, {0x11366, 0x1136C}, {0x11370, 0x11374},
+ {0x16AF0, 0x16AF4}, {0x1D165, 0x1D169}, {0x1D16D, 0x1D172},
+ {0x1D17B, 0x1D182}, {0x1D185, 0x1D18B}, {0x1D1AA, 0x1D1AD},
+ {0x1D242, 0x1D244}, {0x1E000, 0x1E006}, {0x1E008, 0x1E018},
+ {0x1E01B, 0x1E021}, {0x1E023, 0x1E024}, {0x1E026, 0x1E02A},
+ {0x1E8D0, 0x1E8D6},
+}
+
+var doublewidth = table{
+ {0x1100, 0x115F}, {0x231A, 0x231B}, {0x2329, 0x232A},
+ {0x23E9, 0x23EC}, {0x23F0, 0x23F0}, {0x23F3, 0x23F3},
+ {0x25FD, 0x25FE}, {0x2614, 0x2615}, {0x2648, 0x2653},
+ {0x267F, 0x267F}, {0x2693, 0x2693}, {0x26A1, 0x26A1},
+ {0x26AA, 0x26AB}, {0x26BD, 0x26BE}, {0x26C4, 0x26C5},
+ {0x26CE, 0x26CE}, {0x26D4, 0x26D4}, {0x26EA, 0x26EA},
+ {0x26F2, 0x26F3}, {0x26F5, 0x26F5}, {0x26FA, 0x26FA},
+ {0x26FD, 0x26FD}, {0x2705, 0x2705}, {0x270A, 0x270B},
+ {0x2728, 0x2728}, {0x274C, 0x274C}, {0x274E, 0x274E},
+ {0x2753, 0x2755}, {0x2757, 0x2757}, {0x2795, 0x2797},
+ {0x27B0, 0x27B0}, {0x27BF, 0x27BF}, {0x2B1B, 0x2B1C},
+ {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x2E80, 0x2E99},
+ {0x2E9B, 0x2EF3}, {0x2F00, 0x2FD5}, {0x2FF0, 0x2FFB},
+ {0x3000, 0x303E}, {0x3041, 0x3096}, {0x3099, 0x30FF},
+ {0x3105, 0x312F}, {0x3131, 0x318E}, {0x3190, 0x31E3},
+ {0x31F0, 0x321E}, {0x3220, 0x3247}, {0x3250, 0x4DBF},
+ {0x4E00, 0xA48C}, {0xA490, 0xA4C6}, {0xA960, 0xA97C},
+ {0xAC00, 0xD7A3}, {0xF900, 0xFAFF}, {0xFE10, 0xFE19},
+ {0xFE30, 0xFE52}, {0xFE54, 0xFE66}, {0xFE68, 0xFE6B},
+ {0xFF01, 0xFF60}, {0xFFE0, 0xFFE6}, {0x16FE0, 0x16FE4},
+ {0x16FF0, 0x16FF1}, {0x17000, 0x187F7}, {0x18800, 0x18CD5},
+ {0x18D00, 0x18D08}, {0x1B000, 0x1B11E}, {0x1B150, 0x1B152},
+ {0x1B164, 0x1B167}, {0x1B170, 0x1B2FB}, {0x1F004, 0x1F004},
+ {0x1F0CF, 0x1F0CF}, {0x1F18E, 0x1F18E}, {0x1F191, 0x1F19A},
+ {0x1F200, 0x1F202}, {0x1F210, 0x1F23B}, {0x1F240, 0x1F248},
+ {0x1F250, 0x1F251}, {0x1F260, 0x1F265}, {0x1F300, 0x1F320},
+ {0x1F32D, 0x1F335}, {0x1F337, 0x1F37C}, {0x1F37E, 0x1F393},
+ {0x1F3A0, 0x1F3CA}, {0x1F3CF, 0x1F3D3}, {0x1F3E0, 0x1F3F0},
+ {0x1F3F4, 0x1F3F4}, {0x1F3F8, 0x1F43E}, {0x1F440, 0x1F440},
+ {0x1F442, 0x1F4FC}, {0x1F4FF, 0x1F53D}, {0x1F54B, 0x1F54E},
+ {0x1F550, 0x1F567}, {0x1F57A, 0x1F57A}, {0x1F595, 0x1F596},
+ {0x1F5A4, 0x1F5A4}, {0x1F5FB, 0x1F64F}, {0x1F680, 0x1F6C5},
+ {0x1F6CC, 0x1F6CC}, {0x1F6D0, 0x1F6D2}, {0x1F6D5, 0x1F6D7},
+ {0x1F6EB, 0x1F6EC}, {0x1F6F4, 0x1F6FC}, {0x1F7E0, 0x1F7EB},
+ {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1F978},
+ {0x1F97A, 0x1F9CB}, {0x1F9CD, 0x1F9FF}, {0x1FA70, 0x1FA74},
+ {0x1FA78, 0x1FA7A}, {0x1FA80, 0x1FA86}, {0x1FA90, 0x1FAA8},
+ {0x1FAB0, 0x1FAB6}, {0x1FAC0, 0x1FAC2}, {0x1FAD0, 0x1FAD6},
+ {0x20000, 0x2FFFD}, {0x30000, 0x3FFFD},
+}
+
+var ambiguous = table{
+ {0x00A1, 0x00A1}, {0x00A4, 0x00A4}, {0x00A7, 0x00A8},
+ {0x00AA, 0x00AA}, {0x00AD, 0x00AE}, {0x00B0, 0x00B4},
+ {0x00B6, 0x00BA}, {0x00BC, 0x00BF}, {0x00C6, 0x00C6},
+ {0x00D0, 0x00D0}, {0x00D7, 0x00D8}, {0x00DE, 0x00E1},
+ {0x00E6, 0x00E6}, {0x00E8, 0x00EA}, {0x00EC, 0x00ED},
+ {0x00F0, 0x00F0}, {0x00F2, 0x00F3}, {0x00F7, 0x00FA},
+ {0x00FC, 0x00FC}, {0x00FE, 0x00FE}, {0x0101, 0x0101},
+ {0x0111, 0x0111}, {0x0113, 0x0113}, {0x011B, 0x011B},
+ {0x0126, 0x0127}, {0x012B, 0x012B}, {0x0131, 0x0133},
+ {0x0138, 0x0138}, {0x013F, 0x0142}, {0x0144, 0x0144},
+ {0x0148, 0x014B}, {0x014D, 0x014D}, {0x0152, 0x0153},
+ {0x0166, 0x0167}, {0x016B, 0x016B}, {0x01CE, 0x01CE},
+ {0x01D0, 0x01D0}, {0x01D2, 0x01D2}, {0x01D4, 0x01D4},
+ {0x01D6, 0x01D6}, {0x01D8, 0x01D8}, {0x01DA, 0x01DA},
+ {0x01DC, 0x01DC}, {0x0251, 0x0251}, {0x0261, 0x0261},
+ {0x02C4, 0x02C4}, {0x02C7, 0x02C7}, {0x02C9, 0x02CB},
+ {0x02CD, 0x02CD}, {0x02D0, 0x02D0}, {0x02D8, 0x02DB},
+ {0x02DD, 0x02DD}, {0x02DF, 0x02DF}, {0x0300, 0x036F},
+ {0x0391, 0x03A1}, {0x03A3, 0x03A9}, {0x03B1, 0x03C1},
+ {0x03C3, 0x03C9}, {0x0401, 0x0401}, {0x0410, 0x044F},
+ {0x0451, 0x0451}, {0x2010, 0x2010}, {0x2013, 0x2016},
+ {0x2018, 0x2019}, {0x201C, 0x201D}, {0x2020, 0x2022},
+ {0x2024, 0x2027}, {0x2030, 0x2030}, {0x2032, 0x2033},
+ {0x2035, 0x2035}, {0x203B, 0x203B}, {0x203E, 0x203E},
+ {0x2074, 0x2074}, {0x207F, 0x207F}, {0x2081, 0x2084},
+ {0x20AC, 0x20AC}, {0x2103, 0x2103}, {0x2105, 0x2105},
+ {0x2109, 0x2109}, {0x2113, 0x2113}, {0x2116, 0x2116},
+ {0x2121, 0x2122}, {0x2126, 0x2126}, {0x212B, 0x212B},
+ {0x2153, 0x2154}, {0x215B, 0x215E}, {0x2160, 0x216B},
+ {0x2170, 0x2179}, {0x2189, 0x2189}, {0x2190, 0x2199},
+ {0x21B8, 0x21B9}, {0x21D2, 0x21D2}, {0x21D4, 0x21D4},
+ {0x21E7, 0x21E7}, {0x2200, 0x2200}, {0x2202, 0x2203},
+ {0x2207, 0x2208}, {0x220B, 0x220B}, {0x220F, 0x220F},
+ {0x2211, 0x2211}, {0x2215, 0x2215}, {0x221A, 0x221A},
+ {0x221D, 0x2220}, {0x2223, 0x2223}, {0x2225, 0x2225},
+ {0x2227, 0x222C}, {0x222E, 0x222E}, {0x2234, 0x2237},
+ {0x223C, 0x223D}, {0x2248, 0x2248}, {0x224C, 0x224C},
+ {0x2252, 0x2252}, {0x2260, 0x2261}, {0x2264, 0x2267},
+ {0x226A, 0x226B}, {0x226E, 0x226F}, {0x2282, 0x2283},
+ {0x2286, 0x2287}, {0x2295, 0x2295}, {0x2299, 0x2299},
+ {0x22A5, 0x22A5}, {0x22BF, 0x22BF}, {0x2312, 0x2312},
+ {0x2460, 0x24E9}, {0x24EB, 0x254B}, {0x2550, 0x2573},
+ {0x2580, 0x258F}, {0x2592, 0x2595}, {0x25A0, 0x25A1},
+ {0x25A3, 0x25A9}, {0x25B2, 0x25B3}, {0x25B6, 0x25B7},
+ {0x25BC, 0x25BD}, {0x25C0, 0x25C1}, {0x25C6, 0x25C8},
+ {0x25CB, 0x25CB}, {0x25CE, 0x25D1}, {0x25E2, 0x25E5},
+ {0x25EF, 0x25EF}, {0x2605, 0x2606}, {0x2609, 0x2609},
+ {0x260E, 0x260F}, {0x261C, 0x261C}, {0x261E, 0x261E},
+ {0x2640, 0x2640}, {0x2642, 0x2642}, {0x2660, 0x2661},
+ {0x2663, 0x2665}, {0x2667, 0x266A}, {0x266C, 0x266D},
+ {0x266F, 0x266F}, {0x269E, 0x269F}, {0x26BF, 0x26BF},
+ {0x26C6, 0x26CD}, {0x26CF, 0x26D3}, {0x26D5, 0x26E1},
+ {0x26E3, 0x26E3}, {0x26E8, 0x26E9}, {0x26EB, 0x26F1},
+ {0x26F4, 0x26F4}, {0x26F6, 0x26F9}, {0x26FB, 0x26FC},
+ {0x26FE, 0x26FF}, {0x273D, 0x273D}, {0x2776, 0x277F},
+ {0x2B56, 0x2B59}, {0x3248, 0x324F}, {0xE000, 0xF8FF},
+ {0xFE00, 0xFE0F}, {0xFFFD, 0xFFFD}, {0x1F100, 0x1F10A},
+ {0x1F110, 0x1F12D}, {0x1F130, 0x1F169}, {0x1F170, 0x1F18D},
+ {0x1F18F, 0x1F190}, {0x1F19B, 0x1F1AC}, {0xE0100, 0xE01EF},
+ {0xF0000, 0xFFFFD}, {0x100000, 0x10FFFD},
+}
+var narrow = table{
+ {0x0020, 0x007E}, {0x00A2, 0x00A3}, {0x00A5, 0x00A6},
+ {0x00AC, 0x00AC}, {0x00AF, 0x00AF}, {0x27E6, 0x27ED},
+ {0x2985, 0x2986},
+}
+
+var neutral = table{
+ {0x0000, 0x001F}, {0x007F, 0x00A0}, {0x00A9, 0x00A9},
+ {0x00AB, 0x00AB}, {0x00B5, 0x00B5}, {0x00BB, 0x00BB},
+ {0x00C0, 0x00C5}, {0x00C7, 0x00CF}, {0x00D1, 0x00D6},
+ {0x00D9, 0x00DD}, {0x00E2, 0x00E5}, {0x00E7, 0x00E7},
+ {0x00EB, 0x00EB}, {0x00EE, 0x00EF}, {0x00F1, 0x00F1},
+ {0x00F4, 0x00F6}, {0x00FB, 0x00FB}, {0x00FD, 0x00FD},
+ {0x00FF, 0x0100}, {0x0102, 0x0110}, {0x0112, 0x0112},
+ {0x0114, 0x011A}, {0x011C, 0x0125}, {0x0128, 0x012A},
+ {0x012C, 0x0130}, {0x0134, 0x0137}, {0x0139, 0x013E},
+ {0x0143, 0x0143}, {0x0145, 0x0147}, {0x014C, 0x014C},
+ {0x014E, 0x0151}, {0x0154, 0x0165}, {0x0168, 0x016A},
+ {0x016C, 0x01CD}, {0x01CF, 0x01CF}, {0x01D1, 0x01D1},
+ {0x01D3, 0x01D3}, {0x01D5, 0x01D5}, {0x01D7, 0x01D7},
+ {0x01D9, 0x01D9}, {0x01DB, 0x01DB}, {0x01DD, 0x0250},
+ {0x0252, 0x0260}, {0x0262, 0x02C3}, {0x02C5, 0x02C6},
+ {0x02C8, 0x02C8}, {0x02CC, 0x02CC}, {0x02CE, 0x02CF},
+ {0x02D1, 0x02D7}, {0x02DC, 0x02DC}, {0x02DE, 0x02DE},
+ {0x02E0, 0x02FF}, {0x0370, 0x0377}, {0x037A, 0x037F},
+ {0x0384, 0x038A}, {0x038C, 0x038C}, {0x038E, 0x0390},
+ {0x03AA, 0x03B0}, {0x03C2, 0x03C2}, {0x03CA, 0x0400},
+ {0x0402, 0x040F}, {0x0450, 0x0450}, {0x0452, 0x052F},
+ {0x0531, 0x0556}, {0x0559, 0x058A}, {0x058D, 0x058F},
+ {0x0591, 0x05C7}, {0x05D0, 0x05EA}, {0x05EF, 0x05F4},
+ {0x0600, 0x061C}, {0x061E, 0x070D}, {0x070F, 0x074A},
+ {0x074D, 0x07B1}, {0x07C0, 0x07FA}, {0x07FD, 0x082D},
+ {0x0830, 0x083E}, {0x0840, 0x085B}, {0x085E, 0x085E},
+ {0x0860, 0x086A}, {0x08A0, 0x08B4}, {0x08B6, 0x08C7},
+ {0x08D3, 0x0983}, {0x0985, 0x098C}, {0x098F, 0x0990},
+ {0x0993, 0x09A8}, {0x09AA, 0x09B0}, {0x09B2, 0x09B2},
+ {0x09B6, 0x09B9}, {0x09BC, 0x09C4}, {0x09C7, 0x09C8},
+ {0x09CB, 0x09CE}, {0x09D7, 0x09D7}, {0x09DC, 0x09DD},
+ {0x09DF, 0x09E3}, {0x09E6, 0x09FE}, {0x0A01, 0x0A03},
+ {0x0A05, 0x0A0A}, {0x0A0F, 0x0A10}, {0x0A13, 0x0A28},
+ {0x0A2A, 0x0A30}, {0x0A32, 0x0A33}, {0x0A35, 0x0A36},
+ {0x0A38, 0x0A39}, {0x0A3C, 0x0A3C}, {0x0A3E, 0x0A42},
+ {0x0A47, 0x0A48}, {0x0A4B, 0x0A4D}, {0x0A51, 0x0A51},
+ {0x0A59, 0x0A5C}, {0x0A5E, 0x0A5E}, {0x0A66, 0x0A76},
+ {0x0A81, 0x0A83}, {0x0A85, 0x0A8D}, {0x0A8F, 0x0A91},
+ {0x0A93, 0x0AA8}, {0x0AAA, 0x0AB0}, {0x0AB2, 0x0AB3},
+ {0x0AB5, 0x0AB9}, {0x0ABC, 0x0AC5}, {0x0AC7, 0x0AC9},
+ {0x0ACB, 0x0ACD}, {0x0AD0, 0x0AD0}, {0x0AE0, 0x0AE3},
+ {0x0AE6, 0x0AF1}, {0x0AF9, 0x0AFF}, {0x0B01, 0x0B03},
+ {0x0B05, 0x0B0C}, {0x0B0F, 0x0B10}, {0x0B13, 0x0B28},
+ {0x0B2A, 0x0B30}, {0x0B32, 0x0B33}, {0x0B35, 0x0B39},
+ {0x0B3C, 0x0B44}, {0x0B47, 0x0B48}, {0x0B4B, 0x0B4D},
+ {0x0B55, 0x0B57}, {0x0B5C, 0x0B5D}, {0x0B5F, 0x0B63},
+ {0x0B66, 0x0B77}, {0x0B82, 0x0B83}, {0x0B85, 0x0B8A},
+ {0x0B8E, 0x0B90}, {0x0B92, 0x0B95}, {0x0B99, 0x0B9A},
+ {0x0B9C, 0x0B9C}, {0x0B9E, 0x0B9F}, {0x0BA3, 0x0BA4},
+ {0x0BA8, 0x0BAA}, {0x0BAE, 0x0BB9}, {0x0BBE, 0x0BC2},
+ {0x0BC6, 0x0BC8}, {0x0BCA, 0x0BCD}, {0x0BD0, 0x0BD0},
+ {0x0BD7, 0x0BD7}, {0x0BE6, 0x0BFA}, {0x0C00, 0x0C0C},
+ {0x0C0E, 0x0C10}, {0x0C12, 0x0C28}, {0x0C2A, 0x0C39},
+ {0x0C3D, 0x0C44}, {0x0C46, 0x0C48}, {0x0C4A, 0x0C4D},
+ {0x0C55, 0x0C56}, {0x0C58, 0x0C5A}, {0x0C60, 0x0C63},
+ {0x0C66, 0x0C6F}, {0x0C77, 0x0C8C}, {0x0C8E, 0x0C90},
+ {0x0C92, 0x0CA8}, {0x0CAA, 0x0CB3}, {0x0CB5, 0x0CB9},
+ {0x0CBC, 0x0CC4}, {0x0CC6, 0x0CC8}, {0x0CCA, 0x0CCD},
+ {0x0CD5, 0x0CD6}, {0x0CDE, 0x0CDE}, {0x0CE0, 0x0CE3},
+ {0x0CE6, 0x0CEF}, {0x0CF1, 0x0CF2}, {0x0D00, 0x0D0C},
+ {0x0D0E, 0x0D10}, {0x0D12, 0x0D44}, {0x0D46, 0x0D48},
+ {0x0D4A, 0x0D4F}, {0x0D54, 0x0D63}, {0x0D66, 0x0D7F},
+ {0x0D81, 0x0D83}, {0x0D85, 0x0D96}, {0x0D9A, 0x0DB1},
+ {0x0DB3, 0x0DBB}, {0x0DBD, 0x0DBD}, {0x0DC0, 0x0DC6},
+ {0x0DCA, 0x0DCA}, {0x0DCF, 0x0DD4}, {0x0DD6, 0x0DD6},
+ {0x0DD8, 0x0DDF}, {0x0DE6, 0x0DEF}, {0x0DF2, 0x0DF4},
+ {0x0E01, 0x0E3A}, {0x0E3F, 0x0E5B}, {0x0E81, 0x0E82},
+ {0x0E84, 0x0E84}, {0x0E86, 0x0E8A}, {0x0E8C, 0x0EA3},
+ {0x0EA5, 0x0EA5}, {0x0EA7, 0x0EBD}, {0x0EC0, 0x0EC4},
+ {0x0EC6, 0x0EC6}, {0x0EC8, 0x0ECD}, {0x0ED0, 0x0ED9},
+ {0x0EDC, 0x0EDF}, {0x0F00, 0x0F47}, {0x0F49, 0x0F6C},
+ {0x0F71, 0x0F97}, {0x0F99, 0x0FBC}, {0x0FBE, 0x0FCC},
+ {0x0FCE, 0x0FDA}, {0x1000, 0x10C5}, {0x10C7, 0x10C7},
+ {0x10CD, 0x10CD}, {0x10D0, 0x10FF}, {0x1160, 0x1248},
+ {0x124A, 0x124D}, {0x1250, 0x1256}, {0x1258, 0x1258},
+ {0x125A, 0x125D}, {0x1260, 0x1288}, {0x128A, 0x128D},
+ {0x1290, 0x12B0}, {0x12B2, 0x12B5}, {0x12B8, 0x12BE},
+ {0x12C0, 0x12C0}, {0x12C2, 0x12C5}, {0x12C8, 0x12D6},
+ {0x12D8, 0x1310}, {0x1312, 0x1315}, {0x1318, 0x135A},
+ {0x135D, 0x137C}, {0x1380, 0x1399}, {0x13A0, 0x13F5},
+ {0x13F8, 0x13FD}, {0x1400, 0x169C}, {0x16A0, 0x16F8},
+ {0x1700, 0x170C}, {0x170E, 0x1714}, {0x1720, 0x1736},
+ {0x1740, 0x1753}, {0x1760, 0x176C}, {0x176E, 0x1770},
+ {0x1772, 0x1773}, {0x1780, 0x17DD}, {0x17E0, 0x17E9},
+ {0x17F0, 0x17F9}, {0x1800, 0x180E}, {0x1810, 0x1819},
+ {0x1820, 0x1878}, {0x1880, 0x18AA}, {0x18B0, 0x18F5},
+ {0x1900, 0x191E}, {0x1920, 0x192B}, {0x1930, 0x193B},
+ {0x1940, 0x1940}, {0x1944, 0x196D}, {0x1970, 0x1974},
+ {0x1980, 0x19AB}, {0x19B0, 0x19C9}, {0x19D0, 0x19DA},
+ {0x19DE, 0x1A1B}, {0x1A1E, 0x1A5E}, {0x1A60, 0x1A7C},
+ {0x1A7F, 0x1A89}, {0x1A90, 0x1A99}, {0x1AA0, 0x1AAD},
+ {0x1AB0, 0x1AC0}, {0x1B00, 0x1B4B}, {0x1B50, 0x1B7C},
+ {0x1B80, 0x1BF3}, {0x1BFC, 0x1C37}, {0x1C3B, 0x1C49},
+ {0x1C4D, 0x1C88}, {0x1C90, 0x1CBA}, {0x1CBD, 0x1CC7},
+ {0x1CD0, 0x1CFA}, {0x1D00, 0x1DF9}, {0x1DFB, 0x1F15},
+ {0x1F18, 0x1F1D}, {0x1F20, 0x1F45}, {0x1F48, 0x1F4D},
+ {0x1F50, 0x1F57}, {0x1F59, 0x1F59}, {0x1F5B, 0x1F5B},
+ {0x1F5D, 0x1F5D}, {0x1F5F, 0x1F7D}, {0x1F80, 0x1FB4},
+ {0x1FB6, 0x1FC4}, {0x1FC6, 0x1FD3}, {0x1FD6, 0x1FDB},
+ {0x1FDD, 0x1FEF}, {0x1FF2, 0x1FF4}, {0x1FF6, 0x1FFE},
+ {0x2000, 0x200F}, {0x2011, 0x2012}, {0x2017, 0x2017},
+ {0x201A, 0x201B}, {0x201E, 0x201F}, {0x2023, 0x2023},
+ {0x2028, 0x202F}, {0x2031, 0x2031}, {0x2034, 0x2034},
+ {0x2036, 0x203A}, {0x203C, 0x203D}, {0x203F, 0x2064},
+ {0x2066, 0x2071}, {0x2075, 0x207E}, {0x2080, 0x2080},
+ {0x2085, 0x208E}, {0x2090, 0x209C}, {0x20A0, 0x20A8},
+ {0x20AA, 0x20AB}, {0x20AD, 0x20BF}, {0x20D0, 0x20F0},
+ {0x2100, 0x2102}, {0x2104, 0x2104}, {0x2106, 0x2108},
+ {0x210A, 0x2112}, {0x2114, 0x2115}, {0x2117, 0x2120},
+ {0x2123, 0x2125}, {0x2127, 0x212A}, {0x212C, 0x2152},
+ {0x2155, 0x215A}, {0x215F, 0x215F}, {0x216C, 0x216F},
+ {0x217A, 0x2188}, {0x218A, 0x218B}, {0x219A, 0x21B7},
+ {0x21BA, 0x21D1}, {0x21D3, 0x21D3}, {0x21D5, 0x21E6},
+ {0x21E8, 0x21FF}, {0x2201, 0x2201}, {0x2204, 0x2206},
+ {0x2209, 0x220A}, {0x220C, 0x220E}, {0x2210, 0x2210},
+ {0x2212, 0x2214}, {0x2216, 0x2219}, {0x221B, 0x221C},
+ {0x2221, 0x2222}, {0x2224, 0x2224}, {0x2226, 0x2226},
+ {0x222D, 0x222D}, {0x222F, 0x2233}, {0x2238, 0x223B},
+ {0x223E, 0x2247}, {0x2249, 0x224B}, {0x224D, 0x2251},
+ {0x2253, 0x225F}, {0x2262, 0x2263}, {0x2268, 0x2269},
+ {0x226C, 0x226D}, {0x2270, 0x2281}, {0x2284, 0x2285},
+ {0x2288, 0x2294}, {0x2296, 0x2298}, {0x229A, 0x22A4},
+ {0x22A6, 0x22BE}, {0x22C0, 0x2311}, {0x2313, 0x2319},
+ {0x231C, 0x2328}, {0x232B, 0x23E8}, {0x23ED, 0x23EF},
+ {0x23F1, 0x23F2}, {0x23F4, 0x2426}, {0x2440, 0x244A},
+ {0x24EA, 0x24EA}, {0x254C, 0x254F}, {0x2574, 0x257F},
+ {0x2590, 0x2591}, {0x2596, 0x259F}, {0x25A2, 0x25A2},
+ {0x25AA, 0x25B1}, {0x25B4, 0x25B5}, {0x25B8, 0x25BB},
+ {0x25BE, 0x25BF}, {0x25C2, 0x25C5}, {0x25C9, 0x25CA},
+ {0x25CC, 0x25CD}, {0x25D2, 0x25E1}, {0x25E6, 0x25EE},
+ {0x25F0, 0x25FC}, {0x25FF, 0x2604}, {0x2607, 0x2608},
+ {0x260A, 0x260D}, {0x2610, 0x2613}, {0x2616, 0x261B},
+ {0x261D, 0x261D}, {0x261F, 0x263F}, {0x2641, 0x2641},
+ {0x2643, 0x2647}, {0x2654, 0x265F}, {0x2662, 0x2662},
+ {0x2666, 0x2666}, {0x266B, 0x266B}, {0x266E, 0x266E},
+ {0x2670, 0x267E}, {0x2680, 0x2692}, {0x2694, 0x269D},
+ {0x26A0, 0x26A0}, {0x26A2, 0x26A9}, {0x26AC, 0x26BC},
+ {0x26C0, 0x26C3}, {0x26E2, 0x26E2}, {0x26E4, 0x26E7},
+ {0x2700, 0x2704}, {0x2706, 0x2709}, {0x270C, 0x2727},
+ {0x2729, 0x273C}, {0x273E, 0x274B}, {0x274D, 0x274D},
+ {0x274F, 0x2752}, {0x2756, 0x2756}, {0x2758, 0x2775},
+ {0x2780, 0x2794}, {0x2798, 0x27AF}, {0x27B1, 0x27BE},
+ {0x27C0, 0x27E5}, {0x27EE, 0x2984}, {0x2987, 0x2B1A},
+ {0x2B1D, 0x2B4F}, {0x2B51, 0x2B54}, {0x2B5A, 0x2B73},
+ {0x2B76, 0x2B95}, {0x2B97, 0x2C2E}, {0x2C30, 0x2C5E},
+ {0x2C60, 0x2CF3}, {0x2CF9, 0x2D25}, {0x2D27, 0x2D27},
+ {0x2D2D, 0x2D2D}, {0x2D30, 0x2D67}, {0x2D6F, 0x2D70},
+ {0x2D7F, 0x2D96}, {0x2DA0, 0x2DA6}, {0x2DA8, 0x2DAE},
+ {0x2DB0, 0x2DB6}, {0x2DB8, 0x2DBE}, {0x2DC0, 0x2DC6},
+ {0x2DC8, 0x2DCE}, {0x2DD0, 0x2DD6}, {0x2DD8, 0x2DDE},
+ {0x2DE0, 0x2E52}, {0x303F, 0x303F}, {0x4DC0, 0x4DFF},
+ {0xA4D0, 0xA62B}, {0xA640, 0xA6F7}, {0xA700, 0xA7BF},
+ {0xA7C2, 0xA7CA}, {0xA7F5, 0xA82C}, {0xA830, 0xA839},
+ {0xA840, 0xA877}, {0xA880, 0xA8C5}, {0xA8CE, 0xA8D9},
+ {0xA8E0, 0xA953}, {0xA95F, 0xA95F}, {0xA980, 0xA9CD},
+ {0xA9CF, 0xA9D9}, {0xA9DE, 0xA9FE}, {0xAA00, 0xAA36},
+ {0xAA40, 0xAA4D}, {0xAA50, 0xAA59}, {0xAA5C, 0xAAC2},
+ {0xAADB, 0xAAF6}, {0xAB01, 0xAB06}, {0xAB09, 0xAB0E},
+ {0xAB11, 0xAB16}, {0xAB20, 0xAB26}, {0xAB28, 0xAB2E},
+ {0xAB30, 0xAB6B}, {0xAB70, 0xABED}, {0xABF0, 0xABF9},
+ {0xD7B0, 0xD7C6}, {0xD7CB, 0xD7FB}, {0xD800, 0xDFFF},
+ {0xFB00, 0xFB06}, {0xFB13, 0xFB17}, {0xFB1D, 0xFB36},
+ {0xFB38, 0xFB3C}, {0xFB3E, 0xFB3E}, {0xFB40, 0xFB41},
+ {0xFB43, 0xFB44}, {0xFB46, 0xFBC1}, {0xFBD3, 0xFD3F},
+ {0xFD50, 0xFD8F}, {0xFD92, 0xFDC7}, {0xFDF0, 0xFDFD},
+ {0xFE20, 0xFE2F}, {0xFE70, 0xFE74}, {0xFE76, 0xFEFC},
+ {0xFEFF, 0xFEFF}, {0xFFF9, 0xFFFC}, {0x10000, 0x1000B},
+ {0x1000D, 0x10026}, {0x10028, 0x1003A}, {0x1003C, 0x1003D},
+ {0x1003F, 0x1004D}, {0x10050, 0x1005D}, {0x10080, 0x100FA},
+ {0x10100, 0x10102}, {0x10107, 0x10133}, {0x10137, 0x1018E},
+ {0x10190, 0x1019C}, {0x101A0, 0x101A0}, {0x101D0, 0x101FD},
+ {0x10280, 0x1029C}, {0x102A0, 0x102D0}, {0x102E0, 0x102FB},
+ {0x10300, 0x10323}, {0x1032D, 0x1034A}, {0x10350, 0x1037A},
+ {0x10380, 0x1039D}, {0x1039F, 0x103C3}, {0x103C8, 0x103D5},
+ {0x10400, 0x1049D}, {0x104A0, 0x104A9}, {0x104B0, 0x104D3},
+ {0x104D8, 0x104FB}, {0x10500, 0x10527}, {0x10530, 0x10563},
+ {0x1056F, 0x1056F}, {0x10600, 0x10736}, {0x10740, 0x10755},
+ {0x10760, 0x10767}, {0x10800, 0x10805}, {0x10808, 0x10808},
+ {0x1080A, 0x10835}, {0x10837, 0x10838}, {0x1083C, 0x1083C},
+ {0x1083F, 0x10855}, {0x10857, 0x1089E}, {0x108A7, 0x108AF},
+ {0x108E0, 0x108F2}, {0x108F4, 0x108F5}, {0x108FB, 0x1091B},
+ {0x1091F, 0x10939}, {0x1093F, 0x1093F}, {0x10980, 0x109B7},
+ {0x109BC, 0x109CF}, {0x109D2, 0x10A03}, {0x10A05, 0x10A06},
+ {0x10A0C, 0x10A13}, {0x10A15, 0x10A17}, {0x10A19, 0x10A35},
+ {0x10A38, 0x10A3A}, {0x10A3F, 0x10A48}, {0x10A50, 0x10A58},
+ {0x10A60, 0x10A9F}, {0x10AC0, 0x10AE6}, {0x10AEB, 0x10AF6},
+ {0x10B00, 0x10B35}, {0x10B39, 0x10B55}, {0x10B58, 0x10B72},
+ {0x10B78, 0x10B91}, {0x10B99, 0x10B9C}, {0x10BA9, 0x10BAF},
+ {0x10C00, 0x10C48}, {0x10C80, 0x10CB2}, {0x10CC0, 0x10CF2},
+ {0x10CFA, 0x10D27}, {0x10D30, 0x10D39}, {0x10E60, 0x10E7E},
+ {0x10E80, 0x10EA9}, {0x10EAB, 0x10EAD}, {0x10EB0, 0x10EB1},
+ {0x10F00, 0x10F27}, {0x10F30, 0x10F59}, {0x10FB0, 0x10FCB},
+ {0x10FE0, 0x10FF6}, {0x11000, 0x1104D}, {0x11052, 0x1106F},
+ {0x1107F, 0x110C1}, {0x110CD, 0x110CD}, {0x110D0, 0x110E8},
+ {0x110F0, 0x110F9}, {0x11100, 0x11134}, {0x11136, 0x11147},
+ {0x11150, 0x11176}, {0x11180, 0x111DF}, {0x111E1, 0x111F4},
+ {0x11200, 0x11211}, {0x11213, 0x1123E}, {0x11280, 0x11286},
+ {0x11288, 0x11288}, {0x1128A, 0x1128D}, {0x1128F, 0x1129D},
+ {0x1129F, 0x112A9}, {0x112B0, 0x112EA}, {0x112F0, 0x112F9},
+ {0x11300, 0x11303}, {0x11305, 0x1130C}, {0x1130F, 0x11310},
+ {0x11313, 0x11328}, {0x1132A, 0x11330}, {0x11332, 0x11333},
+ {0x11335, 0x11339}, {0x1133B, 0x11344}, {0x11347, 0x11348},
+ {0x1134B, 0x1134D}, {0x11350, 0x11350}, {0x11357, 0x11357},
+ {0x1135D, 0x11363}, {0x11366, 0x1136C}, {0x11370, 0x11374},
+ {0x11400, 0x1145B}, {0x1145D, 0x11461}, {0x11480, 0x114C7},
+ {0x114D0, 0x114D9}, {0x11580, 0x115B5}, {0x115B8, 0x115DD},
+ {0x11600, 0x11644}, {0x11650, 0x11659}, {0x11660, 0x1166C},
+ {0x11680, 0x116B8}, {0x116C0, 0x116C9}, {0x11700, 0x1171A},
+ {0x1171D, 0x1172B}, {0x11730, 0x1173F}, {0x11800, 0x1183B},
+ {0x118A0, 0x118F2}, {0x118FF, 0x11906}, {0x11909, 0x11909},
+ {0x1190C, 0x11913}, {0x11915, 0x11916}, {0x11918, 0x11935},
+ {0x11937, 0x11938}, {0x1193B, 0x11946}, {0x11950, 0x11959},
+ {0x119A0, 0x119A7}, {0x119AA, 0x119D7}, {0x119DA, 0x119E4},
+ {0x11A00, 0x11A47}, {0x11A50, 0x11AA2}, {0x11AC0, 0x11AF8},
+ {0x11C00, 0x11C08}, {0x11C0A, 0x11C36}, {0x11C38, 0x11C45},
+ {0x11C50, 0x11C6C}, {0x11C70, 0x11C8F}, {0x11C92, 0x11CA7},
+ {0x11CA9, 0x11CB6}, {0x11D00, 0x11D06}, {0x11D08, 0x11D09},
+ {0x11D0B, 0x11D36}, {0x11D3A, 0x11D3A}, {0x11D3C, 0x11D3D},
+ {0x11D3F, 0x11D47}, {0x11D50, 0x11D59}, {0x11D60, 0x11D65},
+ {0x11D67, 0x11D68}, {0x11D6A, 0x11D8E}, {0x11D90, 0x11D91},
+ {0x11D93, 0x11D98}, {0x11DA0, 0x11DA9}, {0x11EE0, 0x11EF8},
+ {0x11FB0, 0x11FB0}, {0x11FC0, 0x11FF1}, {0x11FFF, 0x12399},
+ {0x12400, 0x1246E}, {0x12470, 0x12474}, {0x12480, 0x12543},
+ {0x13000, 0x1342E}, {0x13430, 0x13438}, {0x14400, 0x14646},
+ {0x16800, 0x16A38}, {0x16A40, 0x16A5E}, {0x16A60, 0x16A69},
+ {0x16A6E, 0x16A6F}, {0x16AD0, 0x16AED}, {0x16AF0, 0x16AF5},
+ {0x16B00, 0x16B45}, {0x16B50, 0x16B59}, {0x16B5B, 0x16B61},
+ {0x16B63, 0x16B77}, {0x16B7D, 0x16B8F}, {0x16E40, 0x16E9A},
+ {0x16F00, 0x16F4A}, {0x16F4F, 0x16F87}, {0x16F8F, 0x16F9F},
+ {0x1BC00, 0x1BC6A}, {0x1BC70, 0x1BC7C}, {0x1BC80, 0x1BC88},
+ {0x1BC90, 0x1BC99}, {0x1BC9C, 0x1BCA3}, {0x1D000, 0x1D0F5},
+ {0x1D100, 0x1D126}, {0x1D129, 0x1D1E8}, {0x1D200, 0x1D245},
+ {0x1D2E0, 0x1D2F3}, {0x1D300, 0x1D356}, {0x1D360, 0x1D378},
+ {0x1D400, 0x1D454}, {0x1D456, 0x1D49C}, {0x1D49E, 0x1D49F},
+ {0x1D4A2, 0x1D4A2}, {0x1D4A5, 0x1D4A6}, {0x1D4A9, 0x1D4AC},
+ {0x1D4AE, 0x1D4B9}, {0x1D4BB, 0x1D4BB}, {0x1D4BD, 0x1D4C3},
+ {0x1D4C5, 0x1D505}, {0x1D507, 0x1D50A}, {0x1D50D, 0x1D514},
+ {0x1D516, 0x1D51C}, {0x1D51E, 0x1D539}, {0x1D53B, 0x1D53E},
+ {0x1D540, 0x1D544}, {0x1D546, 0x1D546}, {0x1D54A, 0x1D550},
+ {0x1D552, 0x1D6A5}, {0x1D6A8, 0x1D7CB}, {0x1D7CE, 0x1DA8B},
+ {0x1DA9B, 0x1DA9F}, {0x1DAA1, 0x1DAAF}, {0x1E000, 0x1E006},
+ {0x1E008, 0x1E018}, {0x1E01B, 0x1E021}, {0x1E023, 0x1E024},
+ {0x1E026, 0x1E02A}, {0x1E100, 0x1E12C}, {0x1E130, 0x1E13D},
+ {0x1E140, 0x1E149}, {0x1E14E, 0x1E14F}, {0x1E2C0, 0x1E2F9},
+ {0x1E2FF, 0x1E2FF}, {0x1E800, 0x1E8C4}, {0x1E8C7, 0x1E8D6},
+ {0x1E900, 0x1E94B}, {0x1E950, 0x1E959}, {0x1E95E, 0x1E95F},
+ {0x1EC71, 0x1ECB4}, {0x1ED01, 0x1ED3D}, {0x1EE00, 0x1EE03},
+ {0x1EE05, 0x1EE1F}, {0x1EE21, 0x1EE22}, {0x1EE24, 0x1EE24},
+ {0x1EE27, 0x1EE27}, {0x1EE29, 0x1EE32}, {0x1EE34, 0x1EE37},
+ {0x1EE39, 0x1EE39}, {0x1EE3B, 0x1EE3B}, {0x1EE42, 0x1EE42},
+ {0x1EE47, 0x1EE47}, {0x1EE49, 0x1EE49}, {0x1EE4B, 0x1EE4B},
+ {0x1EE4D, 0x1EE4F}, {0x1EE51, 0x1EE52}, {0x1EE54, 0x1EE54},
+ {0x1EE57, 0x1EE57}, {0x1EE59, 0x1EE59}, {0x1EE5B, 0x1EE5B},
+ {0x1EE5D, 0x1EE5D}, {0x1EE5F, 0x1EE5F}, {0x1EE61, 0x1EE62},
+ {0x1EE64, 0x1EE64}, {0x1EE67, 0x1EE6A}, {0x1EE6C, 0x1EE72},
+ {0x1EE74, 0x1EE77}, {0x1EE79, 0x1EE7C}, {0x1EE7E, 0x1EE7E},
+ {0x1EE80, 0x1EE89}, {0x1EE8B, 0x1EE9B}, {0x1EEA1, 0x1EEA3},
+ {0x1EEA5, 0x1EEA9}, {0x1EEAB, 0x1EEBB}, {0x1EEF0, 0x1EEF1},
+ {0x1F000, 0x1F003}, {0x1F005, 0x1F02B}, {0x1F030, 0x1F093},
+ {0x1F0A0, 0x1F0AE}, {0x1F0B1, 0x1F0BF}, {0x1F0C1, 0x1F0CE},
+ {0x1F0D1, 0x1F0F5}, {0x1F10B, 0x1F10F}, {0x1F12E, 0x1F12F},
+ {0x1F16A, 0x1F16F}, {0x1F1AD, 0x1F1AD}, {0x1F1E6, 0x1F1FF},
+ {0x1F321, 0x1F32C}, {0x1F336, 0x1F336}, {0x1F37D, 0x1F37D},
+ {0x1F394, 0x1F39F}, {0x1F3CB, 0x1F3CE}, {0x1F3D4, 0x1F3DF},
+ {0x1F3F1, 0x1F3F3}, {0x1F3F5, 0x1F3F7}, {0x1F43F, 0x1F43F},
+ {0x1F441, 0x1F441}, {0x1F4FD, 0x1F4FE}, {0x1F53E, 0x1F54A},
+ {0x1F54F, 0x1F54F}, {0x1F568, 0x1F579}, {0x1F57B, 0x1F594},
+ {0x1F597, 0x1F5A3}, {0x1F5A5, 0x1F5FA}, {0x1F650, 0x1F67F},
+ {0x1F6C6, 0x1F6CB}, {0x1F6CD, 0x1F6CF}, {0x1F6D3, 0x1F6D4},
+ {0x1F6E0, 0x1F6EA}, {0x1F6F0, 0x1F6F3}, {0x1F700, 0x1F773},
+ {0x1F780, 0x1F7D8}, {0x1F800, 0x1F80B}, {0x1F810, 0x1F847},
+ {0x1F850, 0x1F859}, {0x1F860, 0x1F887}, {0x1F890, 0x1F8AD},
+ {0x1F8B0, 0x1F8B1}, {0x1F900, 0x1F90B}, {0x1F93B, 0x1F93B},
+ {0x1F946, 0x1F946}, {0x1FA00, 0x1FA53}, {0x1FA60, 0x1FA6D},
+ {0x1FB00, 0x1FB92}, {0x1FB94, 0x1FBCA}, {0x1FBF0, 0x1FBF9},
+ {0xE0001, 0xE0001}, {0xE0020, 0xE007F},
+}
+
+var emoji = table{
+ {0x203C, 0x203C}, {0x2049, 0x2049}, {0x2122, 0x2122},
+ {0x2139, 0x2139}, {0x2194, 0x2199}, {0x21A9, 0x21AA},
+ {0x231A, 0x231B}, {0x2328, 0x2328}, {0x2388, 0x2388},
+ {0x23CF, 0x23CF}, {0x23E9, 0x23F3}, {0x23F8, 0x23FA},
+ {0x24C2, 0x24C2}, {0x25AA, 0x25AB}, {0x25B6, 0x25B6},
+ {0x25C0, 0x25C0}, {0x25FB, 0x25FE}, {0x2600, 0x2605},
+ {0x2607, 0x2612}, {0x2614, 0x2685}, {0x2690, 0x2705},
+ {0x2708, 0x2712}, {0x2714, 0x2714}, {0x2716, 0x2716},
+ {0x271D, 0x271D}, {0x2721, 0x2721}, {0x2728, 0x2728},
+ {0x2733, 0x2734}, {0x2744, 0x2744}, {0x2747, 0x2747},
+ {0x274C, 0x274C}, {0x274E, 0x274E}, {0x2753, 0x2755},
+ {0x2757, 0x2757}, {0x2763, 0x2767}, {0x2795, 0x2797},
+ {0x27A1, 0x27A1}, {0x27B0, 0x27B0}, {0x27BF, 0x27BF},
+ {0x2934, 0x2935}, {0x2B05, 0x2B07}, {0x2B1B, 0x2B1C},
+ {0x2B50, 0x2B50}, {0x2B55, 0x2B55}, {0x3030, 0x3030},
+ {0x303D, 0x303D}, {0x3297, 0x3297}, {0x3299, 0x3299},
+ {0x1F000, 0x1F0FF}, {0x1F10D, 0x1F10F}, {0x1F12F, 0x1F12F},
+ {0x1F16C, 0x1F171}, {0x1F17E, 0x1F17F}, {0x1F18E, 0x1F18E},
+ {0x1F191, 0x1F19A}, {0x1F1AD, 0x1F1E5}, {0x1F201, 0x1F20F},
+ {0x1F21A, 0x1F21A}, {0x1F22F, 0x1F22F}, {0x1F232, 0x1F23A},
+ {0x1F23C, 0x1F23F}, {0x1F249, 0x1F3FA}, {0x1F400, 0x1F53D},
+ {0x1F546, 0x1F64F}, {0x1F680, 0x1F6FF}, {0x1F774, 0x1F77F},
+ {0x1F7D5, 0x1F7FF}, {0x1F80C, 0x1F80F}, {0x1F848, 0x1F84F},
+ {0x1F85A, 0x1F85F}, {0x1F888, 0x1F88F}, {0x1F8AE, 0x1F8FF},
+ {0x1F90C, 0x1F93A}, {0x1F93C, 0x1F945}, {0x1F947, 0x1FAFF},
+ {0x1FC00, 0x1FFFD},
+}
diff --git a/vendor/github.com/mattn/go-runewidth/runewidth_windows.go b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go
new file mode 100644
index 0000000000..5f987a310f
--- /dev/null
+++ b/vendor/github.com/mattn/go-runewidth/runewidth_windows.go
@@ -0,0 +1,28 @@
+//go:build windows && !appengine
+// +build windows,!appengine
+
+package runewidth
+
+import (
+ "syscall"
+)
+
+var (
+ kernel32 = syscall.NewLazyDLL("kernel32")
+ procGetConsoleOutputCP = kernel32.NewProc("GetConsoleOutputCP")
+)
+
+// IsEastAsian return true if the current locale is CJK
+func IsEastAsian() bool {
+ r1, _, _ := procGetConsoleOutputCP.Call()
+ if r1 == 0 {
+ return false
+ }
+
+ switch int(r1) {
+ case 932, 51932, 936, 949, 950:
+ return true
+ }
+
+ return false
+}
diff --git a/vendor/github.com/rivo/uniseg/LICENSE.txt b/vendor/github.com/rivo/uniseg/LICENSE.txt
new file mode 100644
index 0000000000..5040f1ef80
--- /dev/null
+++ b/vendor/github.com/rivo/uniseg/LICENSE.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2019 Oliver Kuederle
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/github.com/rivo/uniseg/README.md b/vendor/github.com/rivo/uniseg/README.md
new file mode 100644
index 0000000000..f8da293e15
--- /dev/null
+++ b/vendor/github.com/rivo/uniseg/README.md
@@ -0,0 +1,62 @@
+# Unicode Text Segmentation for Go
+
+[![Godoc Reference](https://img.shields.io/badge/godoc-reference-blue.svg)](https://godoc.org/github.com/rivo/uniseg)
+[![Go Report](https://img.shields.io/badge/go%20report-A%2B-brightgreen.svg)](https://goreportcard.com/report/github.com/rivo/uniseg)
+
+This Go package implements Unicode Text Segmentation according to [Unicode Standard Annex #29](http://unicode.org/reports/tr29/) (Unicode version 12.0.0).
+
+At this point, only the determination of grapheme cluster boundaries is implemented.
+
+## Background
+
+In Go, [strings are read-only slices of bytes](https://blog.golang.org/strings). They can be turned into Unicode code points using the `for` loop or by casting: `[]rune(str)`. However, multiple code points may be combined into one user-perceived character or what the Unicode specification calls "grapheme cluster". Here are some examples:
+
+|String|Bytes (UTF-8)|Code points (runes)|Grapheme clusters|
+|-|-|-|-|
+|Käse|6 bytes: `4b 61 cc 88 73 65`|5 code points: `4b 61 308 73 65`|4 clusters: `[4b],[61 308],[73],[65]`|
+|🏳️🌈|14 bytes: `f0 9f 8f b3 ef b8 8f e2 80 8d f0 9f 8c 88`|4 code points: `1f3f3 fe0f 200d 1f308`|1 cluster: `[1f3f3 fe0f 200d 1f308]`|
+|🇩🇪|8 bytes: `f0 9f 87 a9 f0 9f 87 aa`|2 code points: `1f1e9 1f1ea`|1 cluster: `[1f1e9 1f1ea]`|
+
+This package provides a tool to iterate over these grapheme clusters. This may be used to determine the number of user-perceived characters, to split strings in their intended places, or to extract individual characters which form a unit.
+
+## Installation
+
+```bash
+go get github.com/rivo/uniseg
+```
+
+## Basic Example
+
+```go
+package uniseg
+
+import (
+ "fmt"
+
+ "github.com/rivo/uniseg"
+)
+
+func main() {
+ gr := uniseg.NewGraphemes("👍🏼!")
+ for gr.Next() {
+ fmt.Printf("%x ", gr.Runes())
+ }
+ // Output: [1f44d 1f3fc] [21]
+}
+```
+
+## Documentation
+
+Refer to https://godoc.org/github.com/rivo/uniseg for the package's documentation.
+
+## Dependencies
+
+This package does not depend on any packages outside the standard library.
+
+## Your Feedback
+
+Add your issue here on GitHub. Feel free to get in touch if you have any questions.
+
+## Version
+
+Version tags will be introduced once Golang modules are official. Consider this version 0.1.
diff --git a/vendor/github.com/rivo/uniseg/doc.go b/vendor/github.com/rivo/uniseg/doc.go
new file mode 100644
index 0000000000..60c737d7b3
--- /dev/null
+++ b/vendor/github.com/rivo/uniseg/doc.go
@@ -0,0 +1,8 @@
+/*
+Package uniseg implements Unicode Text Segmentation according to Unicode
+Standard Annex #29 (http://unicode.org/reports/tr29/).
+
+At this point, only the determination of grapheme cluster boundaries is
+implemented.
+*/
+package uniseg
diff --git a/vendor/github.com/rivo/uniseg/grapheme.go b/vendor/github.com/rivo/uniseg/grapheme.go
new file mode 100644
index 0000000000..207157f5e4
--- /dev/null
+++ b/vendor/github.com/rivo/uniseg/grapheme.go
@@ -0,0 +1,268 @@
+package uniseg
+
+import "unicode/utf8"
+
+// The states of the grapheme cluster parser.
+const (
+ grAny = iota
+ grCR
+ grControlLF
+ grL
+ grLVV
+ grLVTT
+ grPrepend
+ grExtendedPictographic
+ grExtendedPictographicZWJ
+ grRIOdd
+ grRIEven
+)
+
+// The grapheme cluster parser's breaking instructions.
+const (
+ grNoBoundary = iota
+ grBoundary
+)
+
+// The grapheme cluster parser's state transitions. Maps (state, property) to
+// (new state, breaking instruction, rule number). The breaking instruction
+// always refers to the boundary between the last and next code point.
+//
+// This map is queried as follows:
+//
+// 1. Find specific state + specific property. Stop if found.
+// 2. Find specific state + any property.
+// 3. Find any state + specific property.
+// 4. If only (2) or (3) (but not both) was found, stop.
+// 5. If both (2) and (3) were found, use state and breaking instruction from
+// the transition with the lower rule number, prefer (3) if rule numbers
+// are equal. Stop.
+// 6. Assume grAny and grBoundary.
+var grTransitions = map[[2]int][3]int{
+ // GB5
+ {grAny, prCR}: {grCR, grBoundary, 50},
+ {grAny, prLF}: {grControlLF, grBoundary, 50},
+ {grAny, prControl}: {grControlLF, grBoundary, 50},
+
+ // GB4
+ {grCR, prAny}: {grAny, grBoundary, 40},
+ {grControlLF, prAny}: {grAny, grBoundary, 40},
+
+ // GB3.
+ {grCR, prLF}: {grAny, grNoBoundary, 30},
+
+ // GB6.
+ {grAny, prL}: {grL, grBoundary, 9990},
+ {grL, prL}: {grL, grNoBoundary, 60},
+ {grL, prV}: {grLVV, grNoBoundary, 60},
+ {grL, prLV}: {grLVV, grNoBoundary, 60},
+ {grL, prLVT}: {grLVTT, grNoBoundary, 60},
+
+ // GB7.
+ {grAny, prLV}: {grLVV, grBoundary, 9990},
+ {grAny, prV}: {grLVV, grBoundary, 9990},
+ {grLVV, prV}: {grLVV, grNoBoundary, 70},
+ {grLVV, prT}: {grLVTT, grNoBoundary, 70},
+
+ // GB8.
+ {grAny, prLVT}: {grLVTT, grBoundary, 9990},
+ {grAny, prT}: {grLVTT, grBoundary, 9990},
+ {grLVTT, prT}: {grLVTT, grNoBoundary, 80},
+
+ // GB9.
+ {grAny, prExtend}: {grAny, grNoBoundary, 90},
+ {grAny, prZWJ}: {grAny, grNoBoundary, 90},
+
+ // GB9a.
+ {grAny, prSpacingMark}: {grAny, grNoBoundary, 91},
+
+ // GB9b.
+ {grAny, prPreprend}: {grPrepend, grBoundary, 9990},
+ {grPrepend, prAny}: {grAny, grNoBoundary, 92},
+
+ // GB11.
+ {grAny, prExtendedPictographic}: {grExtendedPictographic, grBoundary, 9990},
+ {grExtendedPictographic, prExtend}: {grExtendedPictographic, grNoBoundary, 110},
+ {grExtendedPictographic, prZWJ}: {grExtendedPictographicZWJ, grNoBoundary, 110},
+ {grExtendedPictographicZWJ, prExtendedPictographic}: {grExtendedPictographic, grNoBoundary, 110},
+
+ // GB12 / GB13.
+ {grAny, prRegionalIndicator}: {grRIOdd, grBoundary, 9990},
+ {grRIOdd, prRegionalIndicator}: {grRIEven, grNoBoundary, 120},
+ {grRIEven, prRegionalIndicator}: {grRIOdd, grBoundary, 120},
+}
+
+// Graphemes implements an iterator over Unicode extended grapheme clusters,
+// specified in the Unicode Standard Annex #29. Grapheme clusters correspond to
+// "user-perceived characters". These characters often consist of multiple
+// code points (e.g. the "woman kissing woman" emoji consists of 8 code points:
+// woman + ZWJ + heavy black heart (2 code points) + ZWJ + kiss mark + ZWJ +
+// woman) and the rules described in Annex #29 must be applied to group those
+// code points into clusters perceived by the user as one character.
+type Graphemes struct {
+ // The code points over which this class iterates.
+ codePoints []rune
+
+ // The (byte-based) indices of the code points into the original string plus
+ // len(original string). Thus, len(indices) = len(codePoints) + 1.
+ indices []int
+
+ // The current grapheme cluster to be returned. These are indices into
+ // codePoints/indices. If start == end, we either haven't started iterating
+ // yet (0) or the iteration has already completed (1).
+ start, end int
+
+ // The index of the next code point to be parsed.
+ pos int
+
+ // The current state of the code point parser.
+ state int
+}
+
+// NewGraphemes returns a new grapheme cluster iterator.
+func NewGraphemes(s string) *Graphemes {
+ l := utf8.RuneCountInString(s)
+ codePoints := make([]rune, l)
+ indices := make([]int, l+1)
+ i := 0
+ for pos, r := range s {
+ codePoints[i] = r
+ indices[i] = pos
+ i++
+ }
+ indices[l] = len(s)
+ g := &Graphemes{
+ codePoints: codePoints,
+ indices: indices,
+ }
+ g.Next() // Parse ahead.
+ return g
+}
+
+// Next advances the iterator by one grapheme cluster and returns false if no
+// clusters are left. This function must be called before the first cluster is
+// accessed.
+func (g *Graphemes) Next() bool {
+ g.start = g.end
+
+ // The state transition gives us a boundary instruction BEFORE the next code
+ // point so we always need to stay ahead by one code point.
+
+ // Parse the next code point.
+ for g.pos <= len(g.codePoints) {
+ // GB2.
+ if g.pos == len(g.codePoints) {
+ g.end = g.pos
+ g.pos++
+ break
+ }
+
+ // Determine the property of the next character.
+ nextProperty := property(g.codePoints[g.pos])
+ g.pos++
+
+ // Find the applicable transition.
+ var boundary bool
+ transition, ok := grTransitions[[2]int{g.state, nextProperty}]
+ if ok {
+ // We have a specific transition. We'll use it.
+ g.state = transition[0]
+ boundary = transition[1] == grBoundary
+ } else {
+ // No specific transition found. Try the less specific ones.
+ transAnyProp, okAnyProp := grTransitions[[2]int{g.state, prAny}]
+ transAnyState, okAnyState := grTransitions[[2]int{grAny, nextProperty}]
+ if okAnyProp && okAnyState {
+ // Both apply. We'll use a mix (see comments for grTransitions).
+ g.state = transAnyState[0]
+ boundary = transAnyState[1] == grBoundary
+ if transAnyProp[2] < transAnyState[2] {
+ g.state = transAnyProp[0]
+ boundary = transAnyProp[1] == grBoundary
+ }
+ } else if okAnyProp {
+ // We only have a specific state.
+ g.state = transAnyProp[0]
+ boundary = transAnyProp[1] == grBoundary
+ // This branch will probably never be reached because okAnyState will
+ // always be true given the current transition map. But we keep it here
+ // for future modifications to the transition map where this may not be
+ // true anymore.
+ } else if okAnyState {
+ // We only have a specific property.
+ g.state = transAnyState[0]
+ boundary = transAnyState[1] == grBoundary
+ } else {
+ // No known transition. GB999: Any x Any.
+ g.state = grAny
+ boundary = true
+ }
+ }
+
+ // If we found a cluster boundary, let's stop here. The current cluster will
+ // be the one that just ended.
+ if g.pos-1 == 0 /* GB1 */ || boundary {
+ g.end = g.pos - 1
+ break
+ }
+ }
+
+ return g.start != g.end
+}
+
+// Runes returns a slice of runes (code points) which corresponds to the current
+// grapheme cluster. If the iterator is already past the end or Next() has not
+// yet been called, nil is returned.
+func (g *Graphemes) Runes() []rune {
+ if g.start == g.end {
+ return nil
+ }
+ return g.codePoints[g.start:g.end]
+}
+
+// Str returns a substring of the original string which corresponds to the
+// current grapheme cluster. If the iterator is already past the end or Next()
+// has not yet been called, an empty string is returned.
+func (g *Graphemes) Str() string {
+ if g.start == g.end {
+ return ""
+ }
+ return string(g.codePoints[g.start:g.end])
+}
+
+// Bytes returns a byte slice which corresponds to the current grapheme cluster.
+// If the iterator is already past the end or Next() has not yet been called,
+// nil is returned.
+func (g *Graphemes) Bytes() []byte {
+ if g.start == g.end {
+ return nil
+ }
+ return []byte(string(g.codePoints[g.start:g.end]))
+}
+
+// Positions returns the interval of the current grapheme cluster as byte
+// positions into the original string. The first returned value "from" indexes
+// the first byte and the second returned value "to" indexes the first byte that
+// is not included anymore, i.e. str[from:to] is the current grapheme cluster of
+// the original string "str". If Next() has not yet been called, both values are
+// 0. If the iterator is already past the end, both values are 1.
+func (g *Graphemes) Positions() (int, int) {
+ return g.indices[g.start], g.indices[g.end]
+}
+
+// Reset puts the iterator into its initial state such that the next call to
+// Next() sets it to the first grapheme cluster again.
+func (g *Graphemes) Reset() {
+ g.start, g.end, g.pos, g.state = 0, 0, 0, grAny
+ g.Next() // Parse ahead again.
+}
+
+// GraphemeClusterCount returns the number of user-perceived characters
+// (grapheme clusters) for the given string. To calculate this number, it
+// iterates through the string using the Graphemes iterator.
+func GraphemeClusterCount(s string) (n int) {
+ g := NewGraphemes(s)
+ for g.Next() {
+ n++
+ }
+ return
+}
diff --git a/vendor/github.com/rivo/uniseg/properties.go b/vendor/github.com/rivo/uniseg/properties.go
new file mode 100644
index 0000000000..a75ab58839
--- /dev/null
+++ b/vendor/github.com/rivo/uniseg/properties.go
@@ -0,0 +1,1658 @@
+package uniseg
+
+// The unicode properties. Only the ones needed in the context of this package
+// are included.
+const (
+ prAny = iota
+ prPreprend
+ prCR
+ prLF
+ prControl
+ prExtend
+ prRegionalIndicator
+ prSpacingMark
+ prL
+ prV
+ prT
+ prLV
+ prLVT
+ prZWJ
+ prExtendedPictographic
+)
+
+// Maps code point ranges to their properties. In the context of this package,
+// any code point that is not contained may map to "prAny". The code point
+// ranges in this slice are numerically sorted.
+//
+// These ranges were taken from
+// http://www.unicode.org/Public/UCD/latest/ucd/auxiliary/GraphemeBreakProperty.txt
+// as well as
+// https://unicode.org/Public/emoji/latest/emoji-data.txt
+// ("Extended_Pictographic" only) on March 11, 2019. See
+// https://www.unicode.org/license.html for the Unicode license agreement.
+var codePoints = [][3]int{
+ {0x0000, 0x0009, prControl}, // Cc [10] ..
+ {0x000A, 0x000A, prLF}, // Cc
+ {0x000B, 0x000C, prControl}, // Cc [2] ..
+ {0x000D, 0x000D, prCR}, // Cc
+ {0x000E, 0x001F, prControl}, // Cc [18] ..
+ {0x007F, 0x009F, prControl}, // Cc [33] ..
+ {0x00A9, 0x00A9, prExtendedPictographic}, // 1.1 [1] (©️) copyright
+ {0x00AD, 0x00AD, prControl}, // Cf SOFT HYPHEN
+ {0x00AE, 0x00AE, prExtendedPictographic}, // 1.1 [1] (®️) registered
+ {0x0300, 0x036F, prExtend}, // Mn [112] COMBINING GRAVE ACCENT..COMBINING LATIN SMALL LETTER X
+ {0x0483, 0x0487, prExtend}, // Mn [5] COMBINING CYRILLIC TITLO..COMBINING CYRILLIC POKRYTIE
+ {0x0488, 0x0489, prExtend}, // Me [2] COMBINING CYRILLIC HUNDRED THOUSANDS SIGN..COMBINING CYRILLIC MILLIONS SIGN
+ {0x0591, 0x05BD, prExtend}, // Mn [45] HEBREW ACCENT ETNAHTA..HEBREW POINT METEG
+ {0x05BF, 0x05BF, prExtend}, // Mn HEBREW POINT RAFE
+ {0x05C1, 0x05C2, prExtend}, // Mn [2] HEBREW POINT SHIN DOT..HEBREW POINT SIN DOT
+ {0x05C4, 0x05C5, prExtend}, // Mn [2] HEBREW MARK UPPER DOT..HEBREW MARK LOWER DOT
+ {0x05C7, 0x05C7, prExtend}, // Mn HEBREW POINT QAMATS QATAN
+ {0x0600, 0x0605, prPreprend}, // Cf [6] ARABIC NUMBER SIGN..ARABIC NUMBER MARK ABOVE
+ {0x0610, 0x061A, prExtend}, // Mn [11] ARABIC SIGN SALLALLAHOU ALAYHE WASSALLAM..ARABIC SMALL KASRA
+ {0x061C, 0x061C, prControl}, // Cf ARABIC LETTER MARK
+ {0x064B, 0x065F, prExtend}, // Mn [21] ARABIC FATHATAN..ARABIC WAVY HAMZA BELOW
+ {0x0670, 0x0670, prExtend}, // Mn ARABIC LETTER SUPERSCRIPT ALEF
+ {0x06D6, 0x06DC, prExtend}, // Mn [7] ARABIC SMALL HIGH LIGATURE SAD WITH LAM WITH ALEF MAKSURA..ARABIC SMALL HIGH SEEN
+ {0x06DD, 0x06DD, prPreprend}, // Cf ARABIC END OF AYAH
+ {0x06DF, 0x06E4, prExtend}, // Mn [6] ARABIC SMALL HIGH ROUNDED ZERO..ARABIC SMALL HIGH MADDA
+ {0x06E7, 0x06E8, prExtend}, // Mn [2] ARABIC SMALL HIGH YEH..ARABIC SMALL HIGH NOON
+ {0x06EA, 0x06ED, prExtend}, // Mn [4] ARABIC EMPTY CENTRE LOW STOP..ARABIC SMALL LOW MEEM
+ {0x070F, 0x070F, prPreprend}, // Cf SYRIAC ABBREVIATION MARK
+ {0x0711, 0x0711, prExtend}, // Mn SYRIAC LETTER SUPERSCRIPT ALAPH
+ {0x0730, 0x074A, prExtend}, // Mn [27] SYRIAC PTHAHA ABOVE..SYRIAC BARREKH
+ {0x07A6, 0x07B0, prExtend}, // Mn [11] THAANA ABAFILI..THAANA SUKUN
+ {0x07EB, 0x07F3, prExtend}, // Mn [9] NKO COMBINING SHORT HIGH TONE..NKO COMBINING DOUBLE DOT ABOVE
+ {0x07FD, 0x07FD, prExtend}, // Mn NKO DANTAYALAN
+ {0x0816, 0x0819, prExtend}, // Mn [4] SAMARITAN MARK IN..SAMARITAN MARK DAGESH
+ {0x081B, 0x0823, prExtend}, // Mn [9] SAMARITAN MARK EPENTHETIC YUT..SAMARITAN VOWEL SIGN A
+ {0x0825, 0x0827, prExtend}, // Mn [3] SAMARITAN VOWEL SIGN SHORT A..SAMARITAN VOWEL SIGN U
+ {0x0829, 0x082D, prExtend}, // Mn [5] SAMARITAN VOWEL SIGN LONG I..SAMARITAN MARK NEQUDAA
+ {0x0859, 0x085B, prExtend}, // Mn [3] MANDAIC AFFRICATION MARK..MANDAIC GEMINATION MARK
+ {0x08D3, 0x08E1, prExtend}, // Mn [15] ARABIC SMALL LOW WAW..ARABIC SMALL HIGH SIGN SAFHA
+ {0x08E2, 0x08E2, prPreprend}, // Cf ARABIC DISPUTED END OF AYAH
+ {0x08E3, 0x0902, prExtend}, // Mn [32] ARABIC TURNED DAMMA BELOW..DEVANAGARI SIGN ANUSVARA
+ {0x0903, 0x0903, prSpacingMark}, // Mc DEVANAGARI SIGN VISARGA
+ {0x093A, 0x093A, prExtend}, // Mn DEVANAGARI VOWEL SIGN OE
+ {0x093B, 0x093B, prSpacingMark}, // Mc DEVANAGARI VOWEL SIGN OOE
+ {0x093C, 0x093C, prExtend}, // Mn DEVANAGARI SIGN NUKTA
+ {0x093E, 0x0940, prSpacingMark}, // Mc [3] DEVANAGARI VOWEL SIGN AA..DEVANAGARI VOWEL SIGN II
+ {0x0941, 0x0948, prExtend}, // Mn [8] DEVANAGARI VOWEL SIGN U..DEVANAGARI VOWEL SIGN AI
+ {0x0949, 0x094C, prSpacingMark}, // Mc [4] DEVANAGARI VOWEL SIGN CANDRA O..DEVANAGARI VOWEL SIGN AU
+ {0x094D, 0x094D, prExtend}, // Mn DEVANAGARI SIGN VIRAMA
+ {0x094E, 0x094F, prSpacingMark}, // Mc [2] DEVANAGARI VOWEL SIGN PRISHTHAMATRA E..DEVANAGARI VOWEL SIGN AW
+ {0x0951, 0x0957, prExtend}, // Mn [7] DEVANAGARI STRESS SIGN UDATTA..DEVANAGARI VOWEL SIGN UUE
+ {0x0962, 0x0963, prExtend}, // Mn [2] DEVANAGARI VOWEL SIGN VOCALIC L..DEVANAGARI VOWEL SIGN VOCALIC LL
+ {0x0981, 0x0981, prExtend}, // Mn BENGALI SIGN CANDRABINDU
+ {0x0982, 0x0983, prSpacingMark}, // Mc [2] BENGALI SIGN ANUSVARA..BENGALI SIGN VISARGA
+ {0x09BC, 0x09BC, prExtend}, // Mn BENGALI SIGN NUKTA
+ {0x09BE, 0x09BE, prExtend}, // Mc BENGALI VOWEL SIGN AA
+ {0x09BF, 0x09C0, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN I..BENGALI VOWEL SIGN II
+ {0x09C1, 0x09C4, prExtend}, // Mn [4] BENGALI VOWEL SIGN U..BENGALI VOWEL SIGN VOCALIC RR
+ {0x09C7, 0x09C8, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN E..BENGALI VOWEL SIGN AI
+ {0x09CB, 0x09CC, prSpacingMark}, // Mc [2] BENGALI VOWEL SIGN O..BENGALI VOWEL SIGN AU
+ {0x09CD, 0x09CD, prExtend}, // Mn BENGALI SIGN VIRAMA
+ {0x09D7, 0x09D7, prExtend}, // Mc BENGALI AU LENGTH MARK
+ {0x09E2, 0x09E3, prExtend}, // Mn [2] BENGALI VOWEL SIGN VOCALIC L..BENGALI VOWEL SIGN VOCALIC LL
+ {0x09FE, 0x09FE, prExtend}, // Mn BENGALI SANDHI MARK
+ {0x0A01, 0x0A02, prExtend}, // Mn [2] GURMUKHI SIGN ADAK BINDI..GURMUKHI SIGN BINDI
+ {0x0A03, 0x0A03, prSpacingMark}, // Mc GURMUKHI SIGN VISARGA
+ {0x0A3C, 0x0A3C, prExtend}, // Mn GURMUKHI SIGN NUKTA
+ {0x0A3E, 0x0A40, prSpacingMark}, // Mc [3] GURMUKHI VOWEL SIGN AA..GURMUKHI VOWEL SIGN II
+ {0x0A41, 0x0A42, prExtend}, // Mn [2] GURMUKHI VOWEL SIGN U..GURMUKHI VOWEL SIGN UU
+ {0x0A47, 0x0A48, prExtend}, // Mn [2] GURMUKHI VOWEL SIGN EE..GURMUKHI VOWEL SIGN AI
+ {0x0A4B, 0x0A4D, prExtend}, // Mn [3] GURMUKHI VOWEL SIGN OO..GURMUKHI SIGN VIRAMA
+ {0x0A51, 0x0A51, prExtend}, // Mn GURMUKHI SIGN UDAAT
+ {0x0A70, 0x0A71, prExtend}, // Mn [2] GURMUKHI TIPPI..GURMUKHI ADDAK
+ {0x0A75, 0x0A75, prExtend}, // Mn GURMUKHI SIGN YAKASH
+ {0x0A81, 0x0A82, prExtend}, // Mn [2] GUJARATI SIGN CANDRABINDU..GUJARATI SIGN ANUSVARA
+ {0x0A83, 0x0A83, prSpacingMark}, // Mc GUJARATI SIGN VISARGA
+ {0x0ABC, 0x0ABC, prExtend}, // Mn GUJARATI SIGN NUKTA
+ {0x0ABE, 0x0AC0, prSpacingMark}, // Mc [3] GUJARATI VOWEL SIGN AA..GUJARATI VOWEL SIGN II
+ {0x0AC1, 0x0AC5, prExtend}, // Mn [5] GUJARATI VOWEL SIGN U..GUJARATI VOWEL SIGN CANDRA E
+ {0x0AC7, 0x0AC8, prExtend}, // Mn [2] GUJARATI VOWEL SIGN E..GUJARATI VOWEL SIGN AI
+ {0x0AC9, 0x0AC9, prSpacingMark}, // Mc GUJARATI VOWEL SIGN CANDRA O
+ {0x0ACB, 0x0ACC, prSpacingMark}, // Mc [2] GUJARATI VOWEL SIGN O..GUJARATI VOWEL SIGN AU
+ {0x0ACD, 0x0ACD, prExtend}, // Mn GUJARATI SIGN VIRAMA
+ {0x0AE2, 0x0AE3, prExtend}, // Mn [2] GUJARATI VOWEL SIGN VOCALIC L..GUJARATI VOWEL SIGN VOCALIC LL
+ {0x0AFA, 0x0AFF, prExtend}, // Mn [6] GUJARATI SIGN SUKUN..GUJARATI SIGN TWO-CIRCLE NUKTA ABOVE
+ {0x0B01, 0x0B01, prExtend}, // Mn ORIYA SIGN CANDRABINDU
+ {0x0B02, 0x0B03, prSpacingMark}, // Mc [2] ORIYA SIGN ANUSVARA..ORIYA SIGN VISARGA
+ {0x0B3C, 0x0B3C, prExtend}, // Mn ORIYA SIGN NUKTA
+ {0x0B3E, 0x0B3E, prExtend}, // Mc ORIYA VOWEL SIGN AA
+ {0x0B3F, 0x0B3F, prExtend}, // Mn ORIYA VOWEL SIGN I
+ {0x0B40, 0x0B40, prSpacingMark}, // Mc ORIYA VOWEL SIGN II
+ {0x0B41, 0x0B44, prExtend}, // Mn [4] ORIYA VOWEL SIGN U..ORIYA VOWEL SIGN VOCALIC RR
+ {0x0B47, 0x0B48, prSpacingMark}, // Mc [2] ORIYA VOWEL SIGN E..ORIYA VOWEL SIGN AI
+ {0x0B4B, 0x0B4C, prSpacingMark}, // Mc [2] ORIYA VOWEL SIGN O..ORIYA VOWEL SIGN AU
+ {0x0B4D, 0x0B4D, prExtend}, // Mn ORIYA SIGN VIRAMA
+ {0x0B56, 0x0B56, prExtend}, // Mn ORIYA AI LENGTH MARK
+ {0x0B57, 0x0B57, prExtend}, // Mc ORIYA AU LENGTH MARK
+ {0x0B62, 0x0B63, prExtend}, // Mn [2] ORIYA VOWEL SIGN VOCALIC L..ORIYA VOWEL SIGN VOCALIC LL
+ {0x0B82, 0x0B82, prExtend}, // Mn TAMIL SIGN ANUSVARA
+ {0x0BBE, 0x0BBE, prExtend}, // Mc TAMIL VOWEL SIGN AA
+ {0x0BBF, 0x0BBF, prSpacingMark}, // Mc TAMIL VOWEL SIGN I
+ {0x0BC0, 0x0BC0, prExtend}, // Mn TAMIL VOWEL SIGN II
+ {0x0BC1, 0x0BC2, prSpacingMark}, // Mc [2] TAMIL VOWEL SIGN U..TAMIL VOWEL SIGN UU
+ {0x0BC6, 0x0BC8, prSpacingMark}, // Mc [3] TAMIL VOWEL SIGN E..TAMIL VOWEL SIGN AI
+ {0x0BCA, 0x0BCC, prSpacingMark}, // Mc [3] TAMIL VOWEL SIGN O..TAMIL VOWEL SIGN AU
+ {0x0BCD, 0x0BCD, prExtend}, // Mn TAMIL SIGN VIRAMA
+ {0x0BD7, 0x0BD7, prExtend}, // Mc TAMIL AU LENGTH MARK
+ {0x0C00, 0x0C00, prExtend}, // Mn TELUGU SIGN COMBINING CANDRABINDU ABOVE
+ {0x0C01, 0x0C03, prSpacingMark}, // Mc [3] TELUGU SIGN CANDRABINDU..TELUGU SIGN VISARGA
+ {0x0C04, 0x0C04, prExtend}, // Mn TELUGU SIGN COMBINING ANUSVARA ABOVE
+ {0x0C3E, 0x0C40, prExtend}, // Mn [3] TELUGU VOWEL SIGN AA..TELUGU VOWEL SIGN II
+ {0x0C41, 0x0C44, prSpacingMark}, // Mc [4] TELUGU VOWEL SIGN U..TELUGU VOWEL SIGN VOCALIC RR
+ {0x0C46, 0x0C48, prExtend}, // Mn [3] TELUGU VOWEL SIGN E..TELUGU VOWEL SIGN AI
+ {0x0C4A, 0x0C4D, prExtend}, // Mn [4] TELUGU VOWEL SIGN O..TELUGU SIGN VIRAMA
+ {0x0C55, 0x0C56, prExtend}, // Mn [2] TELUGU LENGTH MARK..TELUGU AI LENGTH MARK
+ {0x0C62, 0x0C63, prExtend}, // Mn [2] TELUGU VOWEL SIGN VOCALIC L..TELUGU VOWEL SIGN VOCALIC LL
+ {0x0C81, 0x0C81, prExtend}, // Mn KANNADA SIGN CANDRABINDU
+ {0x0C82, 0x0C83, prSpacingMark}, // Mc [2] KANNADA SIGN ANUSVARA..KANNADA SIGN VISARGA
+ {0x0CBC, 0x0CBC, prExtend}, // Mn KANNADA SIGN NUKTA
+ {0x0CBE, 0x0CBE, prSpacingMark}, // Mc KANNADA VOWEL SIGN AA
+ {0x0CBF, 0x0CBF, prExtend}, // Mn KANNADA VOWEL SIGN I
+ {0x0CC0, 0x0CC1, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN II..KANNADA VOWEL SIGN U
+ {0x0CC2, 0x0CC2, prExtend}, // Mc KANNADA VOWEL SIGN UU
+ {0x0CC3, 0x0CC4, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN VOCALIC R..KANNADA VOWEL SIGN VOCALIC RR
+ {0x0CC6, 0x0CC6, prExtend}, // Mn KANNADA VOWEL SIGN E
+ {0x0CC7, 0x0CC8, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN EE..KANNADA VOWEL SIGN AI
+ {0x0CCA, 0x0CCB, prSpacingMark}, // Mc [2] KANNADA VOWEL SIGN O..KANNADA VOWEL SIGN OO
+ {0x0CCC, 0x0CCD, prExtend}, // Mn [2] KANNADA VOWEL SIGN AU..KANNADA SIGN VIRAMA
+ {0x0CD5, 0x0CD6, prExtend}, // Mc [2] KANNADA LENGTH MARK..KANNADA AI LENGTH MARK
+ {0x0CE2, 0x0CE3, prExtend}, // Mn [2] KANNADA VOWEL SIGN VOCALIC L..KANNADA VOWEL SIGN VOCALIC LL
+ {0x0D00, 0x0D01, prExtend}, // Mn [2] MALAYALAM SIGN COMBINING ANUSVARA ABOVE..MALAYALAM SIGN CANDRABINDU
+ {0x0D02, 0x0D03, prSpacingMark}, // Mc [2] MALAYALAM SIGN ANUSVARA..MALAYALAM SIGN VISARGA
+ {0x0D3B, 0x0D3C, prExtend}, // Mn [2] MALAYALAM SIGN VERTICAL BAR VIRAMA..MALAYALAM SIGN CIRCULAR VIRAMA
+ {0x0D3E, 0x0D3E, prExtend}, // Mc MALAYALAM VOWEL SIGN AA
+ {0x0D3F, 0x0D40, prSpacingMark}, // Mc [2] MALAYALAM VOWEL SIGN I..MALAYALAM VOWEL SIGN II
+ {0x0D41, 0x0D44, prExtend}, // Mn [4] MALAYALAM VOWEL SIGN U..MALAYALAM VOWEL SIGN VOCALIC RR
+ {0x0D46, 0x0D48, prSpacingMark}, // Mc [3] MALAYALAM VOWEL SIGN E..MALAYALAM VOWEL SIGN AI
+ {0x0D4A, 0x0D4C, prSpacingMark}, // Mc [3] MALAYALAM VOWEL SIGN O..MALAYALAM VOWEL SIGN AU
+ {0x0D4D, 0x0D4D, prExtend}, // Mn MALAYALAM SIGN VIRAMA
+ {0x0D4E, 0x0D4E, prPreprend}, // Lo MALAYALAM LETTER DOT REPH
+ {0x0D57, 0x0D57, prExtend}, // Mc MALAYALAM AU LENGTH MARK
+ {0x0D62, 0x0D63, prExtend}, // Mn [2] MALAYALAM VOWEL SIGN VOCALIC L..MALAYALAM VOWEL SIGN VOCALIC LL
+ {0x0D82, 0x0D83, prSpacingMark}, // Mc [2] SINHALA SIGN ANUSVARAYA..SINHALA SIGN VISARGAYA
+ {0x0DCA, 0x0DCA, prExtend}, // Mn SINHALA SIGN AL-LAKUNA
+ {0x0DCF, 0x0DCF, prExtend}, // Mc SINHALA VOWEL SIGN AELA-PILLA
+ {0x0DD0, 0x0DD1, prSpacingMark}, // Mc [2] SINHALA VOWEL SIGN KETTI AEDA-PILLA..SINHALA VOWEL SIGN DIGA AEDA-PILLA
+ {0x0DD2, 0x0DD4, prExtend}, // Mn [3] SINHALA VOWEL SIGN KETTI IS-PILLA..SINHALA VOWEL SIGN KETTI PAA-PILLA
+ {0x0DD6, 0x0DD6, prExtend}, // Mn SINHALA VOWEL SIGN DIGA PAA-PILLA
+ {0x0DD8, 0x0DDE, prSpacingMark}, // Mc [7] SINHALA VOWEL SIGN GAETTA-PILLA..SINHALA VOWEL SIGN KOMBUVA HAA GAYANUKITTA
+ {0x0DDF, 0x0DDF, prExtend}, // Mc SINHALA VOWEL SIGN GAYANUKITTA
+ {0x0DF2, 0x0DF3, prSpacingMark}, // Mc [2] SINHALA VOWEL SIGN DIGA GAETTA-PILLA..SINHALA VOWEL SIGN DIGA GAYANUKITTA
+ {0x0E31, 0x0E31, prExtend}, // Mn THAI CHARACTER MAI HAN-AKAT
+ {0x0E33, 0x0E33, prSpacingMark}, // Lo THAI CHARACTER SARA AM
+ {0x0E34, 0x0E3A, prExtend}, // Mn [7] THAI CHARACTER SARA I..THAI CHARACTER PHINTHU
+ {0x0E47, 0x0E4E, prExtend}, // Mn [8] THAI CHARACTER MAITAIKHU..THAI CHARACTER YAMAKKAN
+ {0x0EB1, 0x0EB1, prExtend}, // Mn LAO VOWEL SIGN MAI KAN
+ {0x0EB3, 0x0EB3, prSpacingMark}, // Lo LAO VOWEL SIGN AM
+ {0x0EB4, 0x0EBC, prExtend}, // Mn [9] LAO VOWEL SIGN I..LAO SEMIVOWEL SIGN LO
+ {0x0EC8, 0x0ECD, prExtend}, // Mn [6] LAO TONE MAI EK..LAO NIGGAHITA
+ {0x0F18, 0x0F19, prExtend}, // Mn [2] TIBETAN ASTROLOGICAL SIGN -KHYUD PA..TIBETAN ASTROLOGICAL SIGN SDONG TSHUGS
+ {0x0F35, 0x0F35, prExtend}, // Mn TIBETAN MARK NGAS BZUNG NYI ZLA
+ {0x0F37, 0x0F37, prExtend}, // Mn TIBETAN MARK NGAS BZUNG SGOR RTAGS
+ {0x0F39, 0x0F39, prExtend}, // Mn TIBETAN MARK TSA -PHRU
+ {0x0F3E, 0x0F3F, prSpacingMark}, // Mc [2] TIBETAN SIGN YAR TSHES..TIBETAN SIGN MAR TSHES
+ {0x0F71, 0x0F7E, prExtend}, // Mn [14] TIBETAN VOWEL SIGN AA..TIBETAN SIGN RJES SU NGA RO
+ {0x0F7F, 0x0F7F, prSpacingMark}, // Mc TIBETAN SIGN RNAM BCAD
+ {0x0F80, 0x0F84, prExtend}, // Mn [5] TIBETAN VOWEL SIGN REVERSED I..TIBETAN MARK HALANTA
+ {0x0F86, 0x0F87, prExtend}, // Mn [2] TIBETAN SIGN LCI RTAGS..TIBETAN SIGN YANG RTAGS
+ {0x0F8D, 0x0F97, prExtend}, // Mn [11] TIBETAN SUBJOINED SIGN LCE TSA CAN..TIBETAN SUBJOINED LETTER JA
+ {0x0F99, 0x0FBC, prExtend}, // Mn [36] TIBETAN SUBJOINED LETTER NYA..TIBETAN SUBJOINED LETTER FIXED-FORM RA
+ {0x0FC6, 0x0FC6, prExtend}, // Mn TIBETAN SYMBOL PADMA GDAN
+ {0x102D, 0x1030, prExtend}, // Mn [4] MYANMAR VOWEL SIGN I..MYANMAR VOWEL SIGN UU
+ {0x1031, 0x1031, prSpacingMark}, // Mc MYANMAR VOWEL SIGN E
+ {0x1032, 0x1037, prExtend}, // Mn [6] MYANMAR VOWEL SIGN AI..MYANMAR SIGN DOT BELOW
+ {0x1039, 0x103A, prExtend}, // Mn [2] MYANMAR SIGN VIRAMA..MYANMAR SIGN ASAT
+ {0x103B, 0x103C, prSpacingMark}, // Mc [2] MYANMAR CONSONANT SIGN MEDIAL YA..MYANMAR CONSONANT SIGN MEDIAL RA
+ {0x103D, 0x103E, prExtend}, // Mn [2] MYANMAR CONSONANT SIGN MEDIAL WA..MYANMAR CONSONANT SIGN MEDIAL HA
+ {0x1056, 0x1057, prSpacingMark}, // Mc [2] MYANMAR VOWEL SIGN VOCALIC R..MYANMAR VOWEL SIGN VOCALIC RR
+ {0x1058, 0x1059, prExtend}, // Mn [2] MYANMAR VOWEL SIGN VOCALIC L..MYANMAR VOWEL SIGN VOCALIC LL
+ {0x105E, 0x1060, prExtend}, // Mn [3] MYANMAR CONSONANT SIGN MON MEDIAL NA..MYANMAR CONSONANT SIGN MON MEDIAL LA
+ {0x1071, 0x1074, prExtend}, // Mn [4] MYANMAR VOWEL SIGN GEBA KAREN I..MYANMAR VOWEL SIGN KAYAH EE
+ {0x1082, 0x1082, prExtend}, // Mn MYANMAR CONSONANT SIGN SHAN MEDIAL WA
+ {0x1084, 0x1084, prSpacingMark}, // Mc MYANMAR VOWEL SIGN SHAN E
+ {0x1085, 0x1086, prExtend}, // Mn [2] MYANMAR VOWEL SIGN SHAN E ABOVE..MYANMAR VOWEL SIGN SHAN FINAL Y
+ {0x108D, 0x108D, prExtend}, // Mn MYANMAR SIGN SHAN COUNCIL EMPHATIC TONE
+ {0x109D, 0x109D, prExtend}, // Mn MYANMAR VOWEL SIGN AITON AI
+ {0x1100, 0x115F, prL}, // Lo [96] HANGUL CHOSEONG KIYEOK..HANGUL CHOSEONG FILLER
+ {0x1160, 0x11A7, prV}, // Lo [72] HANGUL JUNGSEONG FILLER..HANGUL JUNGSEONG O-YAE
+ {0x11A8, 0x11FF, prT}, // Lo [88] HANGUL JONGSEONG KIYEOK..HANGUL JONGSEONG SSANGNIEUN
+ {0x135D, 0x135F, prExtend}, // Mn [3] ETHIOPIC COMBINING GEMINATION AND VOWEL LENGTH MARK..ETHIOPIC COMBINING GEMINATION MARK
+ {0x1712, 0x1714, prExtend}, // Mn [3] TAGALOG VOWEL SIGN I..TAGALOG SIGN VIRAMA
+ {0x1732, 0x1734, prExtend}, // Mn [3] HANUNOO VOWEL SIGN I..HANUNOO SIGN PAMUDPOD
+ {0x1752, 0x1753, prExtend}, // Mn [2] BUHID VOWEL SIGN I..BUHID VOWEL SIGN U
+ {0x1772, 0x1773, prExtend}, // Mn [2] TAGBANWA VOWEL SIGN I..TAGBANWA VOWEL SIGN U
+ {0x17B4, 0x17B5, prExtend}, // Mn [2] KHMER VOWEL INHERENT AQ..KHMER VOWEL INHERENT AA
+ {0x17B6, 0x17B6, prSpacingMark}, // Mc KHMER VOWEL SIGN AA
+ {0x17B7, 0x17BD, prExtend}, // Mn [7] KHMER VOWEL SIGN I..KHMER VOWEL SIGN UA
+ {0x17BE, 0x17C5, prSpacingMark}, // Mc [8] KHMER VOWEL SIGN OE..KHMER VOWEL SIGN AU
+ {0x17C6, 0x17C6, prExtend}, // Mn KHMER SIGN NIKAHIT
+ {0x17C7, 0x17C8, prSpacingMark}, // Mc [2] KHMER SIGN REAHMUK..KHMER SIGN YUUKALEAPINTU
+ {0x17C9, 0x17D3, prExtend}, // Mn [11] KHMER SIGN MUUSIKATOAN..KHMER SIGN BATHAMASAT
+ {0x17DD, 0x17DD, prExtend}, // Mn KHMER SIGN ATTHACAN
+ {0x180B, 0x180D, prExtend}, // Mn [3] MONGOLIAN FREE VARIATION SELECTOR ONE..MONGOLIAN FREE VARIATION SELECTOR THREE
+ {0x180E, 0x180E, prControl}, // Cf MONGOLIAN VOWEL SEPARATOR
+ {0x1885, 0x1886, prExtend}, // Mn [2] MONGOLIAN LETTER ALI GALI BALUDA..MONGOLIAN LETTER ALI GALI THREE BALUDA
+ {0x18A9, 0x18A9, prExtend}, // Mn MONGOLIAN LETTER ALI GALI DAGALGA
+ {0x1920, 0x1922, prExtend}, // Mn [3] LIMBU VOWEL SIGN A..LIMBU VOWEL SIGN U
+ {0x1923, 0x1926, prSpacingMark}, // Mc [4] LIMBU VOWEL SIGN EE..LIMBU VOWEL SIGN AU
+ {0x1927, 0x1928, prExtend}, // Mn [2] LIMBU VOWEL SIGN E..LIMBU VOWEL SIGN O
+ {0x1929, 0x192B, prSpacingMark}, // Mc [3] LIMBU SUBJOINED LETTER YA..LIMBU SUBJOINED LETTER WA
+ {0x1930, 0x1931, prSpacingMark}, // Mc [2] LIMBU SMALL LETTER KA..LIMBU SMALL LETTER NGA
+ {0x1932, 0x1932, prExtend}, // Mn LIMBU SMALL LETTER ANUSVARA
+ {0x1933, 0x1938, prSpacingMark}, // Mc [6] LIMBU SMALL LETTER TA..LIMBU SMALL LETTER LA
+ {0x1939, 0x193B, prExtend}, // Mn [3] LIMBU SIGN MUKPHRENG..LIMBU SIGN SA-I
+ {0x1A17, 0x1A18, prExtend}, // Mn [2] BUGINESE VOWEL SIGN I..BUGINESE VOWEL SIGN U
+ {0x1A19, 0x1A1A, prSpacingMark}, // Mc [2] BUGINESE VOWEL SIGN E..BUGINESE VOWEL SIGN O
+ {0x1A1B, 0x1A1B, prExtend}, // Mn BUGINESE VOWEL SIGN AE
+ {0x1A55, 0x1A55, prSpacingMark}, // Mc TAI THAM CONSONANT SIGN MEDIAL RA
+ {0x1A56, 0x1A56, prExtend}, // Mn TAI THAM CONSONANT SIGN MEDIAL LA
+ {0x1A57, 0x1A57, prSpacingMark}, // Mc TAI THAM CONSONANT SIGN LA TANG LAI
+ {0x1A58, 0x1A5E, prExtend}, // Mn [7] TAI THAM SIGN MAI KANG LAI..TAI THAM CONSONANT SIGN SA
+ {0x1A60, 0x1A60, prExtend}, // Mn TAI THAM SIGN SAKOT
+ {0x1A62, 0x1A62, prExtend}, // Mn TAI THAM VOWEL SIGN MAI SAT
+ {0x1A65, 0x1A6C, prExtend}, // Mn [8] TAI THAM VOWEL SIGN I..TAI THAM VOWEL SIGN OA BELOW
+ {0x1A6D, 0x1A72, prSpacingMark}, // Mc [6] TAI THAM VOWEL SIGN OY..TAI THAM VOWEL SIGN THAM AI
+ {0x1A73, 0x1A7C, prExtend}, // Mn [10] TAI THAM VOWEL SIGN OA ABOVE..TAI THAM SIGN KHUEN-LUE KARAN
+ {0x1A7F, 0x1A7F, prExtend}, // Mn TAI THAM COMBINING CRYPTOGRAMMIC DOT
+ {0x1AB0, 0x1ABD, prExtend}, // Mn [14] COMBINING DOUBLED CIRCUMFLEX ACCENT..COMBINING PARENTHESES BELOW
+ {0x1ABE, 0x1ABE, prExtend}, // Me COMBINING PARENTHESES OVERLAY
+ {0x1B00, 0x1B03, prExtend}, // Mn [4] BALINESE SIGN ULU RICEM..BALINESE SIGN SURANG
+ {0x1B04, 0x1B04, prSpacingMark}, // Mc BALINESE SIGN BISAH
+ {0x1B34, 0x1B34, prExtend}, // Mn BALINESE SIGN REREKAN
+ {0x1B35, 0x1B35, prExtend}, // Mc BALINESE VOWEL SIGN TEDUNG
+ {0x1B36, 0x1B3A, prExtend}, // Mn [5] BALINESE VOWEL SIGN ULU..BALINESE VOWEL SIGN RA REPA
+ {0x1B3B, 0x1B3B, prSpacingMark}, // Mc BALINESE VOWEL SIGN RA REPA TEDUNG
+ {0x1B3C, 0x1B3C, prExtend}, // Mn BALINESE VOWEL SIGN LA LENGA
+ {0x1B3D, 0x1B41, prSpacingMark}, // Mc [5] BALINESE VOWEL SIGN LA LENGA TEDUNG..BALINESE VOWEL SIGN TALING REPA TEDUNG
+ {0x1B42, 0x1B42, prExtend}, // Mn BALINESE VOWEL SIGN PEPET
+ {0x1B43, 0x1B44, prSpacingMark}, // Mc [2] BALINESE VOWEL SIGN PEPET TEDUNG..BALINESE ADEG ADEG
+ {0x1B6B, 0x1B73, prExtend}, // Mn [9] BALINESE MUSICAL SYMBOL COMBINING TEGEH..BALINESE MUSICAL SYMBOL COMBINING GONG
+ {0x1B80, 0x1B81, prExtend}, // Mn [2] SUNDANESE SIGN PANYECEK..SUNDANESE SIGN PANGLAYAR
+ {0x1B82, 0x1B82, prSpacingMark}, // Mc SUNDANESE SIGN PANGWISAD
+ {0x1BA1, 0x1BA1, prSpacingMark}, // Mc SUNDANESE CONSONANT SIGN PAMINGKAL
+ {0x1BA2, 0x1BA5, prExtend}, // Mn [4] SUNDANESE CONSONANT SIGN PANYAKRA..SUNDANESE VOWEL SIGN PANYUKU
+ {0x1BA6, 0x1BA7, prSpacingMark}, // Mc [2] SUNDANESE VOWEL SIGN PANAELAENG..SUNDANESE VOWEL SIGN PANOLONG
+ {0x1BA8, 0x1BA9, prExtend}, // Mn [2] SUNDANESE VOWEL SIGN PAMEPET..SUNDANESE VOWEL SIGN PANEULEUNG
+ {0x1BAA, 0x1BAA, prSpacingMark}, // Mc SUNDANESE SIGN PAMAAEH
+ {0x1BAB, 0x1BAD, prExtend}, // Mn [3] SUNDANESE SIGN VIRAMA..SUNDANESE CONSONANT SIGN PASANGAN WA
+ {0x1BE6, 0x1BE6, prExtend}, // Mn BATAK SIGN TOMPI
+ {0x1BE7, 0x1BE7, prSpacingMark}, // Mc BATAK VOWEL SIGN E
+ {0x1BE8, 0x1BE9, prExtend}, // Mn [2] BATAK VOWEL SIGN PAKPAK E..BATAK VOWEL SIGN EE
+ {0x1BEA, 0x1BEC, prSpacingMark}, // Mc [3] BATAK VOWEL SIGN I..BATAK VOWEL SIGN O
+ {0x1BED, 0x1BED, prExtend}, // Mn BATAK VOWEL SIGN KARO O
+ {0x1BEE, 0x1BEE, prSpacingMark}, // Mc BATAK VOWEL SIGN U
+ {0x1BEF, 0x1BF1, prExtend}, // Mn [3] BATAK VOWEL SIGN U FOR SIMALUNGUN SA..BATAK CONSONANT SIGN H
+ {0x1BF2, 0x1BF3, prSpacingMark}, // Mc [2] BATAK PANGOLAT..BATAK PANONGONAN
+ {0x1C24, 0x1C2B, prSpacingMark}, // Mc [8] LEPCHA SUBJOINED LETTER YA..LEPCHA VOWEL SIGN UU
+ {0x1C2C, 0x1C33, prExtend}, // Mn [8] LEPCHA VOWEL SIGN E..LEPCHA CONSONANT SIGN T
+ {0x1C34, 0x1C35, prSpacingMark}, // Mc [2] LEPCHA CONSONANT SIGN NYIN-DO..LEPCHA CONSONANT SIGN KANG
+ {0x1C36, 0x1C37, prExtend}, // Mn [2] LEPCHA SIGN RAN..LEPCHA SIGN NUKTA
+ {0x1CD0, 0x1CD2, prExtend}, // Mn [3] VEDIC TONE KARSHANA..VEDIC TONE PRENKHA
+ {0x1CD4, 0x1CE0, prExtend}, // Mn [13] VEDIC SIGN YAJURVEDIC MIDLINE SVARITA..VEDIC TONE RIGVEDIC KASHMIRI INDEPENDENT SVARITA
+ {0x1CE1, 0x1CE1, prSpacingMark}, // Mc VEDIC TONE ATHARVAVEDIC INDEPENDENT SVARITA
+ {0x1CE2, 0x1CE8, prExtend}, // Mn [7] VEDIC SIGN VISARGA SVARITA..VEDIC SIGN VISARGA ANUDATTA WITH TAIL
+ {0x1CED, 0x1CED, prExtend}, // Mn VEDIC SIGN TIRYAK
+ {0x1CF4, 0x1CF4, prExtend}, // Mn VEDIC TONE CANDRA ABOVE
+ {0x1CF7, 0x1CF7, prSpacingMark}, // Mc VEDIC SIGN ATIKRAMA
+ {0x1CF8, 0x1CF9, prExtend}, // Mn [2] VEDIC TONE RING ABOVE..VEDIC TONE DOUBLE RING ABOVE
+ {0x1DC0, 0x1DF9, prExtend}, // Mn [58] COMBINING DOTTED GRAVE ACCENT..COMBINING WIDE INVERTED BRIDGE BELOW
+ {0x1DFB, 0x1DFF, prExtend}, // Mn [5] COMBINING DELETION MARK..COMBINING RIGHT ARROWHEAD AND DOWN ARROWHEAD BELOW
+ {0x200B, 0x200B, prControl}, // Cf ZERO WIDTH SPACE
+ {0x200C, 0x200C, prExtend}, // Cf ZERO WIDTH NON-JOINER
+ {0x200D, 0x200D, prZWJ}, // Cf ZERO WIDTH JOINER
+ {0x200E, 0x200F, prControl}, // Cf [2] LEFT-TO-RIGHT MARK..RIGHT-TO-LEFT MARK
+ {0x2028, 0x2028, prControl}, // Zl LINE SEPARATOR
+ {0x2029, 0x2029, prControl}, // Zp PARAGRAPH SEPARATOR
+ {0x202A, 0x202E, prControl}, // Cf [5] LEFT-TO-RIGHT EMBEDDING..RIGHT-TO-LEFT OVERRIDE
+ {0x203C, 0x203C, prExtendedPictographic}, // 1.1 [1] (‼️) double exclamation mark
+ {0x2049, 0x2049, prExtendedPictographic}, // 3.0 [1] (⁉️) exclamation question mark
+ {0x2060, 0x2064, prControl}, // Cf [5] WORD JOINER..INVISIBLE PLUS
+ {0x2065, 0x2065, prControl}, // Cn
+ {0x2066, 0x206F, prControl}, // Cf [10] LEFT-TO-RIGHT ISOLATE..NOMINAL DIGIT SHAPES
+ {0x20D0, 0x20DC, prExtend}, // Mn [13] COMBINING LEFT HARPOON ABOVE..COMBINING FOUR DOTS ABOVE
+ {0x20DD, 0x20E0, prExtend}, // Me [4] COMBINING ENCLOSING CIRCLE..COMBINING ENCLOSING CIRCLE BACKSLASH
+ {0x20E1, 0x20E1, prExtend}, // Mn COMBINING LEFT RIGHT ARROW ABOVE
+ {0x20E2, 0x20E4, prExtend}, // Me [3] COMBINING ENCLOSING SCREEN..COMBINING ENCLOSING UPWARD POINTING TRIANGLE
+ {0x20E5, 0x20F0, prExtend}, // Mn [12] COMBINING REVERSE SOLIDUS OVERLAY..COMBINING ASTERISK ABOVE
+ {0x2122, 0x2122, prExtendedPictographic}, // 1.1 [1] (™️) trade mark
+ {0x2139, 0x2139, prExtendedPictographic}, // 3.0 [1] (ℹ️) information
+ {0x2194, 0x2199, prExtendedPictographic}, // 1.1 [6] (↔️..↙️) left-right arrow..down-left arrow
+ {0x21A9, 0x21AA, prExtendedPictographic}, // 1.1 [2] (↩️..↪️) right arrow curving left..left arrow curving right
+ {0x231A, 0x231B, prExtendedPictographic}, // 1.1 [2] (⌚..⌛) watch..hourglass done
+ {0x2328, 0x2328, prExtendedPictographic}, // 1.1 [1] (⌨️) keyboard
+ {0x2388, 0x2388, prExtendedPictographic}, // 3.0 [1] (⎈) HELM SYMBOL
+ {0x23CF, 0x23CF, prExtendedPictographic}, // 4.0 [1] (⏏️) eject button
+ {0x23E9, 0x23F3, prExtendedPictographic}, // 6.0 [11] (⏩..⏳) fast-forward button..hourglass not done
+ {0x23F8, 0x23FA, prExtendedPictographic}, // 7.0 [3] (⏸️..⏺️) pause button..record button
+ {0x24C2, 0x24C2, prExtendedPictographic}, // 1.1 [1] (Ⓜ️) circled M
+ {0x25AA, 0x25AB, prExtendedPictographic}, // 1.1 [2] (▪️..▫️) black small square..white small square
+ {0x25B6, 0x25B6, prExtendedPictographic}, // 1.1 [1] (▶️) play button
+ {0x25C0, 0x25C0, prExtendedPictographic}, // 1.1 [1] (◀️) reverse button
+ {0x25FB, 0x25FE, prExtendedPictographic}, // 3.2 [4] (◻️..◾) white medium square..black medium-small square
+ {0x2600, 0x2605, prExtendedPictographic}, // 1.1 [6] (☀️..★) sun..BLACK STAR
+ {0x2607, 0x2612, prExtendedPictographic}, // 1.1 [12] (☇..☒) LIGHTNING..BALLOT BOX WITH X
+ {0x2614, 0x2615, prExtendedPictographic}, // 4.0 [2] (☔..☕) umbrella with rain drops..hot beverage
+ {0x2616, 0x2617, prExtendedPictographic}, // 3.2 [2] (☖..☗) WHITE SHOGI PIECE..BLACK SHOGI PIECE
+ {0x2618, 0x2618, prExtendedPictographic}, // 4.1 [1] (☘️) shamrock
+ {0x2619, 0x2619, prExtendedPictographic}, // 3.0 [1] (☙) REVERSED ROTATED FLORAL HEART BULLET
+ {0x261A, 0x266F, prExtendedPictographic}, // 1.1 [86] (☚..♯) BLACK LEFT POINTING INDEX..MUSIC SHARP SIGN
+ {0x2670, 0x2671, prExtendedPictographic}, // 3.0 [2] (♰..♱) WEST SYRIAC CROSS..EAST SYRIAC CROSS
+ {0x2672, 0x267D, prExtendedPictographic}, // 3.2 [12] (♲..♽) UNIVERSAL RECYCLING SYMBOL..PARTIALLY-RECYCLED PAPER SYMBOL
+ {0x267E, 0x267F, prExtendedPictographic}, // 4.1 [2] (♾️..♿) infinity..wheelchair symbol
+ {0x2680, 0x2685, prExtendedPictographic}, // 3.2 [6] (⚀..⚅) DIE FACE-1..DIE FACE-6
+ {0x2690, 0x2691, prExtendedPictographic}, // 4.0 [2] (⚐..⚑) WHITE FLAG..BLACK FLAG
+ {0x2692, 0x269C, prExtendedPictographic}, // 4.1 [11] (⚒️..⚜️) hammer and pick..fleur-de-lis
+ {0x269D, 0x269D, prExtendedPictographic}, // 5.1 [1] (⚝) OUTLINED WHITE STAR
+ {0x269E, 0x269F, prExtendedPictographic}, // 5.2 [2] (⚞..⚟) THREE LINES CONVERGING RIGHT..THREE LINES CONVERGING LEFT
+ {0x26A0, 0x26A1, prExtendedPictographic}, // 4.0 [2] (⚠️..⚡) warning..high voltage
+ {0x26A2, 0x26B1, prExtendedPictographic}, // 4.1 [16] (⚢..⚱️) DOUBLED FEMALE SIGN..funeral urn
+ {0x26B2, 0x26B2, prExtendedPictographic}, // 5.0 [1] (⚲) NEUTER
+ {0x26B3, 0x26BC, prExtendedPictographic}, // 5.1 [10] (⚳..⚼) CERES..SESQUIQUADRATE
+ {0x26BD, 0x26BF, prExtendedPictographic}, // 5.2 [3] (⚽..⚿) soccer ball..SQUARED KEY
+ {0x26C0, 0x26C3, prExtendedPictographic}, // 5.1 [4] (⛀..⛃) WHITE DRAUGHTS MAN..BLACK DRAUGHTS KING
+ {0x26C4, 0x26CD, prExtendedPictographic}, // 5.2 [10] (⛄..⛍) snowman without snow..DISABLED CAR
+ {0x26CE, 0x26CE, prExtendedPictographic}, // 6.0 [1] (⛎) Ophiuchus
+ {0x26CF, 0x26E1, prExtendedPictographic}, // 5.2 [19] (⛏️..⛡) pick..RESTRICTED LEFT ENTRY-2
+ {0x26E2, 0x26E2, prExtendedPictographic}, // 6.0 [1] (⛢) ASTRONOMICAL SYMBOL FOR URANUS
+ {0x26E3, 0x26E3, prExtendedPictographic}, // 5.2 [1] (⛣) HEAVY CIRCLE WITH STROKE AND TWO DOTS ABOVE
+ {0x26E4, 0x26E7, prExtendedPictographic}, // 6.0 [4] (⛤..⛧) PENTAGRAM..INVERTED PENTAGRAM
+ {0x26E8, 0x26FF, prExtendedPictographic}, // 5.2 [24] (⛨..⛿) BLACK CROSS ON SHIELD..WHITE FLAG WITH HORIZONTAL MIDDLE BLACK STRIPE
+ {0x2700, 0x2700, prExtendedPictographic}, // 7.0 [1] (✀) BLACK SAFETY SCISSORS
+ {0x2701, 0x2704, prExtendedPictographic}, // 1.1 [4] (✁..✄) UPPER BLADE SCISSORS..WHITE SCISSORS
+ {0x2705, 0x2705, prExtendedPictographic}, // 6.0 [1] (✅) check mark button
+ {0x2708, 0x2709, prExtendedPictographic}, // 1.1 [2] (✈️..✉️) airplane..envelope
+ {0x270A, 0x270B, prExtendedPictographic}, // 6.0 [2] (✊..✋) raised fist..raised hand
+ {0x270C, 0x2712, prExtendedPictographic}, // 1.1 [7] (✌️..✒️) victory hand..black nib
+ {0x2714, 0x2714, prExtendedPictographic}, // 1.1 [1] (✔️) check mark
+ {0x2716, 0x2716, prExtendedPictographic}, // 1.1 [1] (✖️) multiplication sign
+ {0x271D, 0x271D, prExtendedPictographic}, // 1.1 [1] (✝️) latin cross
+ {0x2721, 0x2721, prExtendedPictographic}, // 1.1 [1] (✡️) star of David
+ {0x2728, 0x2728, prExtendedPictographic}, // 6.0 [1] (✨) sparkles
+ {0x2733, 0x2734, prExtendedPictographic}, // 1.1 [2] (✳️..✴️) eight-spoked asterisk..eight-pointed star
+ {0x2744, 0x2744, prExtendedPictographic}, // 1.1 [1] (❄️) snowflake
+ {0x2747, 0x2747, prExtendedPictographic}, // 1.1 [1] (❇️) sparkle
+ {0x274C, 0x274C, prExtendedPictographic}, // 6.0 [1] (❌) cross mark
+ {0x274E, 0x274E, prExtendedPictographic}, // 6.0 [1] (❎) cross mark button
+ {0x2753, 0x2755, prExtendedPictographic}, // 6.0 [3] (❓..❕) question mark..white exclamation mark
+ {0x2757, 0x2757, prExtendedPictographic}, // 5.2 [1] (❗) exclamation mark
+ {0x2763, 0x2767, prExtendedPictographic}, // 1.1 [5] (❣️..❧) heart exclamation..ROTATED FLORAL HEART BULLET
+ {0x2795, 0x2797, prExtendedPictographic}, // 6.0 [3] (➕..➗) plus sign..division sign
+ {0x27A1, 0x27A1, prExtendedPictographic}, // 1.1 [1] (➡️) right arrow
+ {0x27B0, 0x27B0, prExtendedPictographic}, // 6.0 [1] (➰) curly loop
+ {0x27BF, 0x27BF, prExtendedPictographic}, // 6.0 [1] (➿) double curly loop
+ {0x2934, 0x2935, prExtendedPictographic}, // 3.2 [2] (⤴️..⤵️) right arrow curving up..right arrow curving down
+ {0x2B05, 0x2B07, prExtendedPictographic}, // 4.0 [3] (⬅️..⬇️) left arrow..down arrow
+ {0x2B1B, 0x2B1C, prExtendedPictographic}, // 5.1 [2] (⬛..⬜) black large square..white large square
+ {0x2B50, 0x2B50, prExtendedPictographic}, // 5.1 [1] (⭐) star
+ {0x2B55, 0x2B55, prExtendedPictographic}, // 5.2 [1] (⭕) hollow red circle
+ {0x2CEF, 0x2CF1, prExtend}, // Mn [3] COPTIC COMBINING NI ABOVE..COPTIC COMBINING SPIRITUS LENIS
+ {0x2D7F, 0x2D7F, prExtend}, // Mn TIFINAGH CONSONANT JOINER
+ {0x2DE0, 0x2DFF, prExtend}, // Mn [32] COMBINING CYRILLIC LETTER BE..COMBINING CYRILLIC LETTER IOTIFIED BIG YUS
+ {0x302A, 0x302D, prExtend}, // Mn [4] IDEOGRAPHIC LEVEL TONE MARK..IDEOGRAPHIC ENTERING TONE MARK
+ {0x302E, 0x302F, prExtend}, // Mc [2] HANGUL SINGLE DOT TONE MARK..HANGUL DOUBLE DOT TONE MARK
+ {0x3030, 0x3030, prExtendedPictographic}, // 1.1 [1] (〰️) wavy dash
+ {0x303D, 0x303D, prExtendedPictographic}, // 3.2 [1] (〽️) part alternation mark
+ {0x3099, 0x309A, prExtend}, // Mn [2] COMBINING KATAKANA-HIRAGANA VOICED SOUND MARK..COMBINING KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK
+ {0x3297, 0x3297, prExtendedPictographic}, // 1.1 [1] (㊗️) Japanese “congratulations” button
+ {0x3299, 0x3299, prExtendedPictographic}, // 1.1 [1] (㊙️) Japanese “secret” button
+ {0xA66F, 0xA66F, prExtend}, // Mn COMBINING CYRILLIC VZMET
+ {0xA670, 0xA672, prExtend}, // Me [3] COMBINING CYRILLIC TEN MILLIONS SIGN..COMBINING CYRILLIC THOUSAND MILLIONS SIGN
+ {0xA674, 0xA67D, prExtend}, // Mn [10] COMBINING CYRILLIC LETTER UKRAINIAN IE..COMBINING CYRILLIC PAYEROK
+ {0xA69E, 0xA69F, prExtend}, // Mn [2] COMBINING CYRILLIC LETTER EF..COMBINING CYRILLIC LETTER IOTIFIED E
+ {0xA6F0, 0xA6F1, prExtend}, // Mn [2] BAMUM COMBINING MARK KOQNDON..BAMUM COMBINING MARK TUKWENTIS
+ {0xA802, 0xA802, prExtend}, // Mn SYLOTI NAGRI SIGN DVISVARA
+ {0xA806, 0xA806, prExtend}, // Mn SYLOTI NAGRI SIGN HASANTA
+ {0xA80B, 0xA80B, prExtend}, // Mn SYLOTI NAGRI SIGN ANUSVARA
+ {0xA823, 0xA824, prSpacingMark}, // Mc [2] SYLOTI NAGRI VOWEL SIGN A..SYLOTI NAGRI VOWEL SIGN I
+ {0xA825, 0xA826, prExtend}, // Mn [2] SYLOTI NAGRI VOWEL SIGN U..SYLOTI NAGRI VOWEL SIGN E
+ {0xA827, 0xA827, prSpacingMark}, // Mc SYLOTI NAGRI VOWEL SIGN OO
+ {0xA880, 0xA881, prSpacingMark}, // Mc [2] SAURASHTRA SIGN ANUSVARA..SAURASHTRA SIGN VISARGA
+ {0xA8B4, 0xA8C3, prSpacingMark}, // Mc [16] SAURASHTRA CONSONANT SIGN HAARU..SAURASHTRA VOWEL SIGN AU
+ {0xA8C4, 0xA8C5, prExtend}, // Mn [2] SAURASHTRA SIGN VIRAMA..SAURASHTRA SIGN CANDRABINDU
+ {0xA8E0, 0xA8F1, prExtend}, // Mn [18] COMBINING DEVANAGARI DIGIT ZERO..COMBINING DEVANAGARI SIGN AVAGRAHA
+ {0xA8FF, 0xA8FF, prExtend}, // Mn DEVANAGARI VOWEL SIGN AY
+ {0xA926, 0xA92D, prExtend}, // Mn [8] KAYAH LI VOWEL UE..KAYAH LI TONE CALYA PLOPHU
+ {0xA947, 0xA951, prExtend}, // Mn [11] REJANG VOWEL SIGN I..REJANG CONSONANT SIGN R
+ {0xA952, 0xA953, prSpacingMark}, // Mc [2] REJANG CONSONANT SIGN H..REJANG VIRAMA
+ {0xA960, 0xA97C, prL}, // Lo [29] HANGUL CHOSEONG TIKEUT-MIEUM..HANGUL CHOSEONG SSANGYEORINHIEUH
+ {0xA980, 0xA982, prExtend}, // Mn [3] JAVANESE SIGN PANYANGGA..JAVANESE SIGN LAYAR
+ {0xA983, 0xA983, prSpacingMark}, // Mc JAVANESE SIGN WIGNYAN
+ {0xA9B3, 0xA9B3, prExtend}, // Mn JAVANESE SIGN CECAK TELU
+ {0xA9B4, 0xA9B5, prSpacingMark}, // Mc [2] JAVANESE VOWEL SIGN TARUNG..JAVANESE VOWEL SIGN TOLONG
+ {0xA9B6, 0xA9B9, prExtend}, // Mn [4] JAVANESE VOWEL SIGN WULU..JAVANESE VOWEL SIGN SUKU MENDUT
+ {0xA9BA, 0xA9BB, prSpacingMark}, // Mc [2] JAVANESE VOWEL SIGN TALING..JAVANESE VOWEL SIGN DIRGA MURE
+ {0xA9BC, 0xA9BD, prExtend}, // Mn [2] JAVANESE VOWEL SIGN PEPET..JAVANESE CONSONANT SIGN KERET
+ {0xA9BE, 0xA9C0, prSpacingMark}, // Mc [3] JAVANESE CONSONANT SIGN PENGKAL..JAVANESE PANGKON
+ {0xA9E5, 0xA9E5, prExtend}, // Mn MYANMAR SIGN SHAN SAW
+ {0xAA29, 0xAA2E, prExtend}, // Mn [6] CHAM VOWEL SIGN AA..CHAM VOWEL SIGN OE
+ {0xAA2F, 0xAA30, prSpacingMark}, // Mc [2] CHAM VOWEL SIGN O..CHAM VOWEL SIGN AI
+ {0xAA31, 0xAA32, prExtend}, // Mn [2] CHAM VOWEL SIGN AU..CHAM VOWEL SIGN UE
+ {0xAA33, 0xAA34, prSpacingMark}, // Mc [2] CHAM CONSONANT SIGN YA..CHAM CONSONANT SIGN RA
+ {0xAA35, 0xAA36, prExtend}, // Mn [2] CHAM CONSONANT SIGN LA..CHAM CONSONANT SIGN WA
+ {0xAA43, 0xAA43, prExtend}, // Mn CHAM CONSONANT SIGN FINAL NG
+ {0xAA4C, 0xAA4C, prExtend}, // Mn CHAM CONSONANT SIGN FINAL M
+ {0xAA4D, 0xAA4D, prSpacingMark}, // Mc CHAM CONSONANT SIGN FINAL H
+ {0xAA7C, 0xAA7C, prExtend}, // Mn MYANMAR SIGN TAI LAING TONE-2
+ {0xAAB0, 0xAAB0, prExtend}, // Mn TAI VIET MAI KANG
+ {0xAAB2, 0xAAB4, prExtend}, // Mn [3] TAI VIET VOWEL I..TAI VIET VOWEL U
+ {0xAAB7, 0xAAB8, prExtend}, // Mn [2] TAI VIET MAI KHIT..TAI VIET VOWEL IA
+ {0xAABE, 0xAABF, prExtend}, // Mn [2] TAI VIET VOWEL AM..TAI VIET TONE MAI EK
+ {0xAAC1, 0xAAC1, prExtend}, // Mn TAI VIET TONE MAI THO
+ {0xAAEB, 0xAAEB, prSpacingMark}, // Mc MEETEI MAYEK VOWEL SIGN II
+ {0xAAEC, 0xAAED, prExtend}, // Mn [2] MEETEI MAYEK VOWEL SIGN UU..MEETEI MAYEK VOWEL SIGN AAI
+ {0xAAEE, 0xAAEF, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN AU..MEETEI MAYEK VOWEL SIGN AAU
+ {0xAAF5, 0xAAF5, prSpacingMark}, // Mc MEETEI MAYEK VOWEL SIGN VISARGA
+ {0xAAF6, 0xAAF6, prExtend}, // Mn MEETEI MAYEK VIRAMA
+ {0xABE3, 0xABE4, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN ONAP..MEETEI MAYEK VOWEL SIGN INAP
+ {0xABE5, 0xABE5, prExtend}, // Mn MEETEI MAYEK VOWEL SIGN ANAP
+ {0xABE6, 0xABE7, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN YENAP..MEETEI MAYEK VOWEL SIGN SOUNAP
+ {0xABE8, 0xABE8, prExtend}, // Mn MEETEI MAYEK VOWEL SIGN UNAP
+ {0xABE9, 0xABEA, prSpacingMark}, // Mc [2] MEETEI MAYEK VOWEL SIGN CHEINAP..MEETEI MAYEK VOWEL SIGN NUNG
+ {0xABEC, 0xABEC, prSpacingMark}, // Mc MEETEI MAYEK LUM IYEK
+ {0xABED, 0xABED, prExtend}, // Mn MEETEI MAYEK APUN IYEK
+ {0xAC00, 0xAC00, prLV}, // Lo HANGUL SYLLABLE GA
+ {0xAC01, 0xAC1B, prLVT}, // Lo [27] HANGUL SYLLABLE GAG..HANGUL SYLLABLE GAH
+ {0xAC1C, 0xAC1C, prLV}, // Lo HANGUL SYLLABLE GAE
+ {0xAC1D, 0xAC37, prLVT}, // Lo [27] HANGUL SYLLABLE GAEG..HANGUL SYLLABLE GAEH
+ {0xAC38, 0xAC38, prLV}, // Lo HANGUL SYLLABLE GYA
+ {0xAC39, 0xAC53, prLVT}, // Lo [27] HANGUL SYLLABLE GYAG..HANGUL SYLLABLE GYAH
+ {0xAC54, 0xAC54, prLV}, // Lo HANGUL SYLLABLE GYAE
+ {0xAC55, 0xAC6F, prLVT}, // Lo [27] HANGUL SYLLABLE GYAEG..HANGUL SYLLABLE GYAEH
+ {0xAC70, 0xAC70, prLV}, // Lo HANGUL SYLLABLE GEO
+ {0xAC71, 0xAC8B, prLVT}, // Lo [27] HANGUL SYLLABLE GEOG..HANGUL SYLLABLE GEOH
+ {0xAC8C, 0xAC8C, prLV}, // Lo HANGUL SYLLABLE GE
+ {0xAC8D, 0xACA7, prLVT}, // Lo [27] HANGUL SYLLABLE GEG..HANGUL SYLLABLE GEH
+ {0xACA8, 0xACA8, prLV}, // Lo HANGUL SYLLABLE GYEO
+ {0xACA9, 0xACC3, prLVT}, // Lo [27] HANGUL SYLLABLE GYEOG..HANGUL SYLLABLE GYEOH
+ {0xACC4, 0xACC4, prLV}, // Lo HANGUL SYLLABLE GYE
+ {0xACC5, 0xACDF, prLVT}, // Lo [27] HANGUL SYLLABLE GYEG..HANGUL SYLLABLE GYEH
+ {0xACE0, 0xACE0, prLV}, // Lo HANGUL SYLLABLE GO
+ {0xACE1, 0xACFB, prLVT}, // Lo [27] HANGUL SYLLABLE GOG..HANGUL SYLLABLE GOH
+ {0xACFC, 0xACFC, prLV}, // Lo HANGUL SYLLABLE GWA
+ {0xACFD, 0xAD17, prLVT}, // Lo [27] HANGUL SYLLABLE GWAG..HANGUL SYLLABLE GWAH
+ {0xAD18, 0xAD18, prLV}, // Lo HANGUL SYLLABLE GWAE
+ {0xAD19, 0xAD33, prLVT}, // Lo [27] HANGUL SYLLABLE GWAEG..HANGUL SYLLABLE GWAEH
+ {0xAD34, 0xAD34, prLV}, // Lo HANGUL SYLLABLE GOE
+ {0xAD35, 0xAD4F, prLVT}, // Lo [27] HANGUL SYLLABLE GOEG..HANGUL SYLLABLE GOEH
+ {0xAD50, 0xAD50, prLV}, // Lo HANGUL SYLLABLE GYO
+ {0xAD51, 0xAD6B, prLVT}, // Lo [27] HANGUL SYLLABLE GYOG..HANGUL SYLLABLE GYOH
+ {0xAD6C, 0xAD6C, prLV}, // Lo HANGUL SYLLABLE GU
+ {0xAD6D, 0xAD87, prLVT}, // Lo [27] HANGUL SYLLABLE GUG..HANGUL SYLLABLE GUH
+ {0xAD88, 0xAD88, prLV}, // Lo HANGUL SYLLABLE GWEO
+ {0xAD89, 0xADA3, prLVT}, // Lo [27] HANGUL SYLLABLE GWEOG..HANGUL SYLLABLE GWEOH
+ {0xADA4, 0xADA4, prLV}, // Lo HANGUL SYLLABLE GWE
+ {0xADA5, 0xADBF, prLVT}, // Lo [27] HANGUL SYLLABLE GWEG..HANGUL SYLLABLE GWEH
+ {0xADC0, 0xADC0, prLV}, // Lo HANGUL SYLLABLE GWI
+ {0xADC1, 0xADDB, prLVT}, // Lo [27] HANGUL SYLLABLE GWIG..HANGUL SYLLABLE GWIH
+ {0xADDC, 0xADDC, prLV}, // Lo HANGUL SYLLABLE GYU
+ {0xADDD, 0xADF7, prLVT}, // Lo [27] HANGUL SYLLABLE GYUG..HANGUL SYLLABLE GYUH
+ {0xADF8, 0xADF8, prLV}, // Lo HANGUL SYLLABLE GEU
+ {0xADF9, 0xAE13, prLVT}, // Lo [27] HANGUL SYLLABLE GEUG..HANGUL SYLLABLE GEUH
+ {0xAE14, 0xAE14, prLV}, // Lo HANGUL SYLLABLE GYI
+ {0xAE15, 0xAE2F, prLVT}, // Lo [27] HANGUL SYLLABLE GYIG..HANGUL SYLLABLE GYIH
+ {0xAE30, 0xAE30, prLV}, // Lo HANGUL SYLLABLE GI
+ {0xAE31, 0xAE4B, prLVT}, // Lo [27] HANGUL SYLLABLE GIG..HANGUL SYLLABLE GIH
+ {0xAE4C, 0xAE4C, prLV}, // Lo HANGUL SYLLABLE GGA
+ {0xAE4D, 0xAE67, prLVT}, // Lo [27] HANGUL SYLLABLE GGAG..HANGUL SYLLABLE GGAH
+ {0xAE68, 0xAE68, prLV}, // Lo HANGUL SYLLABLE GGAE
+ {0xAE69, 0xAE83, prLVT}, // Lo [27] HANGUL SYLLABLE GGAEG..HANGUL SYLLABLE GGAEH
+ {0xAE84, 0xAE84, prLV}, // Lo HANGUL SYLLABLE GGYA
+ {0xAE85, 0xAE9F, prLVT}, // Lo [27] HANGUL SYLLABLE GGYAG..HANGUL SYLLABLE GGYAH
+ {0xAEA0, 0xAEA0, prLV}, // Lo HANGUL SYLLABLE GGYAE
+ {0xAEA1, 0xAEBB, prLVT}, // Lo [27] HANGUL SYLLABLE GGYAEG..HANGUL SYLLABLE GGYAEH
+ {0xAEBC, 0xAEBC, prLV}, // Lo HANGUL SYLLABLE GGEO
+ {0xAEBD, 0xAED7, prLVT}, // Lo [27] HANGUL SYLLABLE GGEOG..HANGUL SYLLABLE GGEOH
+ {0xAED8, 0xAED8, prLV}, // Lo HANGUL SYLLABLE GGE
+ {0xAED9, 0xAEF3, prLVT}, // Lo [27] HANGUL SYLLABLE GGEG..HANGUL SYLLABLE GGEH
+ {0xAEF4, 0xAEF4, prLV}, // Lo HANGUL SYLLABLE GGYEO
+ {0xAEF5, 0xAF0F, prLVT}, // Lo [27] HANGUL SYLLABLE GGYEOG..HANGUL SYLLABLE GGYEOH
+ {0xAF10, 0xAF10, prLV}, // Lo HANGUL SYLLABLE GGYE
+ {0xAF11, 0xAF2B, prLVT}, // Lo [27] HANGUL SYLLABLE GGYEG..HANGUL SYLLABLE GGYEH
+ {0xAF2C, 0xAF2C, prLV}, // Lo HANGUL SYLLABLE GGO
+ {0xAF2D, 0xAF47, prLVT}, // Lo [27] HANGUL SYLLABLE GGOG..HANGUL SYLLABLE GGOH
+ {0xAF48, 0xAF48, prLV}, // Lo HANGUL SYLLABLE GGWA
+ {0xAF49, 0xAF63, prLVT}, // Lo [27] HANGUL SYLLABLE GGWAG..HANGUL SYLLABLE GGWAH
+ {0xAF64, 0xAF64, prLV}, // Lo HANGUL SYLLABLE GGWAE
+ {0xAF65, 0xAF7F, prLVT}, // Lo [27] HANGUL SYLLABLE GGWAEG..HANGUL SYLLABLE GGWAEH
+ {0xAF80, 0xAF80, prLV}, // Lo HANGUL SYLLABLE GGOE
+ {0xAF81, 0xAF9B, prLVT}, // Lo [27] HANGUL SYLLABLE GGOEG..HANGUL SYLLABLE GGOEH
+ {0xAF9C, 0xAF9C, prLV}, // Lo HANGUL SYLLABLE GGYO
+ {0xAF9D, 0xAFB7, prLVT}, // Lo [27] HANGUL SYLLABLE GGYOG..HANGUL SYLLABLE GGYOH
+ {0xAFB8, 0xAFB8, prLV}, // Lo HANGUL SYLLABLE GGU
+ {0xAFB9, 0xAFD3, prLVT}, // Lo [27] HANGUL SYLLABLE GGUG..HANGUL SYLLABLE GGUH
+ {0xAFD4, 0xAFD4, prLV}, // Lo HANGUL SYLLABLE GGWEO
+ {0xAFD5, 0xAFEF, prLVT}, // Lo [27] HANGUL SYLLABLE GGWEOG..HANGUL SYLLABLE GGWEOH
+ {0xAFF0, 0xAFF0, prLV}, // Lo HANGUL SYLLABLE GGWE
+ {0xAFF1, 0xB00B, prLVT}, // Lo [27] HANGUL SYLLABLE GGWEG..HANGUL SYLLABLE GGWEH
+ {0xB00C, 0xB00C, prLV}, // Lo HANGUL SYLLABLE GGWI
+ {0xB00D, 0xB027, prLVT}, // Lo [27] HANGUL SYLLABLE GGWIG..HANGUL SYLLABLE GGWIH
+ {0xB028, 0xB028, prLV}, // Lo HANGUL SYLLABLE GGYU
+ {0xB029, 0xB043, prLVT}, // Lo [27] HANGUL SYLLABLE GGYUG..HANGUL SYLLABLE GGYUH
+ {0xB044, 0xB044, prLV}, // Lo HANGUL SYLLABLE GGEU
+ {0xB045, 0xB05F, prLVT}, // Lo [27] HANGUL SYLLABLE GGEUG..HANGUL SYLLABLE GGEUH
+ {0xB060, 0xB060, prLV}, // Lo HANGUL SYLLABLE GGYI
+ {0xB061, 0xB07B, prLVT}, // Lo [27] HANGUL SYLLABLE GGYIG..HANGUL SYLLABLE GGYIH
+ {0xB07C, 0xB07C, prLV}, // Lo HANGUL SYLLABLE GGI
+ {0xB07D, 0xB097, prLVT}, // Lo [27] HANGUL SYLLABLE GGIG..HANGUL SYLLABLE GGIH
+ {0xB098, 0xB098, prLV}, // Lo HANGUL SYLLABLE NA
+ {0xB099, 0xB0B3, prLVT}, // Lo [27] HANGUL SYLLABLE NAG..HANGUL SYLLABLE NAH
+ {0xB0B4, 0xB0B4, prLV}, // Lo HANGUL SYLLABLE NAE
+ {0xB0B5, 0xB0CF, prLVT}, // Lo [27] HANGUL SYLLABLE NAEG..HANGUL SYLLABLE NAEH
+ {0xB0D0, 0xB0D0, prLV}, // Lo HANGUL SYLLABLE NYA
+ {0xB0D1, 0xB0EB, prLVT}, // Lo [27] HANGUL SYLLABLE NYAG..HANGUL SYLLABLE NYAH
+ {0xB0EC, 0xB0EC, prLV}, // Lo HANGUL SYLLABLE NYAE
+ {0xB0ED, 0xB107, prLVT}, // Lo [27] HANGUL SYLLABLE NYAEG..HANGUL SYLLABLE NYAEH
+ {0xB108, 0xB108, prLV}, // Lo HANGUL SYLLABLE NEO
+ {0xB109, 0xB123, prLVT}, // Lo [27] HANGUL SYLLABLE NEOG..HANGUL SYLLABLE NEOH
+ {0xB124, 0xB124, prLV}, // Lo HANGUL SYLLABLE NE
+ {0xB125, 0xB13F, prLVT}, // Lo [27] HANGUL SYLLABLE NEG..HANGUL SYLLABLE NEH
+ {0xB140, 0xB140, prLV}, // Lo HANGUL SYLLABLE NYEO
+ {0xB141, 0xB15B, prLVT}, // Lo [27] HANGUL SYLLABLE NYEOG..HANGUL SYLLABLE NYEOH
+ {0xB15C, 0xB15C, prLV}, // Lo HANGUL SYLLABLE NYE
+ {0xB15D, 0xB177, prLVT}, // Lo [27] HANGUL SYLLABLE NYEG..HANGUL SYLLABLE NYEH
+ {0xB178, 0xB178, prLV}, // Lo HANGUL SYLLABLE NO
+ {0xB179, 0xB193, prLVT}, // Lo [27] HANGUL SYLLABLE NOG..HANGUL SYLLABLE NOH
+ {0xB194, 0xB194, prLV}, // Lo HANGUL SYLLABLE NWA
+ {0xB195, 0xB1AF, prLVT}, // Lo [27] HANGUL SYLLABLE NWAG..HANGUL SYLLABLE NWAH
+ {0xB1B0, 0xB1B0, prLV}, // Lo HANGUL SYLLABLE NWAE
+ {0xB1B1, 0xB1CB, prLVT}, // Lo [27] HANGUL SYLLABLE NWAEG..HANGUL SYLLABLE NWAEH
+ {0xB1CC, 0xB1CC, prLV}, // Lo HANGUL SYLLABLE NOE
+ {0xB1CD, 0xB1E7, prLVT}, // Lo [27] HANGUL SYLLABLE NOEG..HANGUL SYLLABLE NOEH
+ {0xB1E8, 0xB1E8, prLV}, // Lo HANGUL SYLLABLE NYO
+ {0xB1E9, 0xB203, prLVT}, // Lo [27] HANGUL SYLLABLE NYOG..HANGUL SYLLABLE NYOH
+ {0xB204, 0xB204, prLV}, // Lo HANGUL SYLLABLE NU
+ {0xB205, 0xB21F, prLVT}, // Lo [27] HANGUL SYLLABLE NUG..HANGUL SYLLABLE NUH
+ {0xB220, 0xB220, prLV}, // Lo HANGUL SYLLABLE NWEO
+ {0xB221, 0xB23B, prLVT}, // Lo [27] HANGUL SYLLABLE NWEOG..HANGUL SYLLABLE NWEOH
+ {0xB23C, 0xB23C, prLV}, // Lo HANGUL SYLLABLE NWE
+ {0xB23D, 0xB257, prLVT}, // Lo [27] HANGUL SYLLABLE NWEG..HANGUL SYLLABLE NWEH
+ {0xB258, 0xB258, prLV}, // Lo HANGUL SYLLABLE NWI
+ {0xB259, 0xB273, prLVT}, // Lo [27] HANGUL SYLLABLE NWIG..HANGUL SYLLABLE NWIH
+ {0xB274, 0xB274, prLV}, // Lo HANGUL SYLLABLE NYU
+ {0xB275, 0xB28F, prLVT}, // Lo [27] HANGUL SYLLABLE NYUG..HANGUL SYLLABLE NYUH
+ {0xB290, 0xB290, prLV}, // Lo HANGUL SYLLABLE NEU
+ {0xB291, 0xB2AB, prLVT}, // Lo [27] HANGUL SYLLABLE NEUG..HANGUL SYLLABLE NEUH
+ {0xB2AC, 0xB2AC, prLV}, // Lo HANGUL SYLLABLE NYI
+ {0xB2AD, 0xB2C7, prLVT}, // Lo [27] HANGUL SYLLABLE NYIG..HANGUL SYLLABLE NYIH
+ {0xB2C8, 0xB2C8, prLV}, // Lo HANGUL SYLLABLE NI
+ {0xB2C9, 0xB2E3, prLVT}, // Lo [27] HANGUL SYLLABLE NIG..HANGUL SYLLABLE NIH
+ {0xB2E4, 0xB2E4, prLV}, // Lo HANGUL SYLLABLE DA
+ {0xB2E5, 0xB2FF, prLVT}, // Lo [27] HANGUL SYLLABLE DAG..HANGUL SYLLABLE DAH
+ {0xB300, 0xB300, prLV}, // Lo HANGUL SYLLABLE DAE
+ {0xB301, 0xB31B, prLVT}, // Lo [27] HANGUL SYLLABLE DAEG..HANGUL SYLLABLE DAEH
+ {0xB31C, 0xB31C, prLV}, // Lo HANGUL SYLLABLE DYA
+ {0xB31D, 0xB337, prLVT}, // Lo [27] HANGUL SYLLABLE DYAG..HANGUL SYLLABLE DYAH
+ {0xB338, 0xB338, prLV}, // Lo HANGUL SYLLABLE DYAE
+ {0xB339, 0xB353, prLVT}, // Lo [27] HANGUL SYLLABLE DYAEG..HANGUL SYLLABLE DYAEH
+ {0xB354, 0xB354, prLV}, // Lo HANGUL SYLLABLE DEO
+ {0xB355, 0xB36F, prLVT}, // Lo [27] HANGUL SYLLABLE DEOG..HANGUL SYLLABLE DEOH
+ {0xB370, 0xB370, prLV}, // Lo HANGUL SYLLABLE DE
+ {0xB371, 0xB38B, prLVT}, // Lo [27] HANGUL SYLLABLE DEG..HANGUL SYLLABLE DEH
+ {0xB38C, 0xB38C, prLV}, // Lo HANGUL SYLLABLE DYEO
+ {0xB38D, 0xB3A7, prLVT}, // Lo [27] HANGUL SYLLABLE DYEOG..HANGUL SYLLABLE DYEOH
+ {0xB3A8, 0xB3A8, prLV}, // Lo HANGUL SYLLABLE DYE
+ {0xB3A9, 0xB3C3, prLVT}, // Lo [27] HANGUL SYLLABLE DYEG..HANGUL SYLLABLE DYEH
+ {0xB3C4, 0xB3C4, prLV}, // Lo HANGUL SYLLABLE DO
+ {0xB3C5, 0xB3DF, prLVT}, // Lo [27] HANGUL SYLLABLE DOG..HANGUL SYLLABLE DOH
+ {0xB3E0, 0xB3E0, prLV}, // Lo HANGUL SYLLABLE DWA
+ {0xB3E1, 0xB3FB, prLVT}, // Lo [27] HANGUL SYLLABLE DWAG..HANGUL SYLLABLE DWAH
+ {0xB3FC, 0xB3FC, prLV}, // Lo HANGUL SYLLABLE DWAE
+ {0xB3FD, 0xB417, prLVT}, // Lo [27] HANGUL SYLLABLE DWAEG..HANGUL SYLLABLE DWAEH
+ {0xB418, 0xB418, prLV}, // Lo HANGUL SYLLABLE DOE
+ {0xB419, 0xB433, prLVT}, // Lo [27] HANGUL SYLLABLE DOEG..HANGUL SYLLABLE DOEH
+ {0xB434, 0xB434, prLV}, // Lo HANGUL SYLLABLE DYO
+ {0xB435, 0xB44F, prLVT}, // Lo [27] HANGUL SYLLABLE DYOG..HANGUL SYLLABLE DYOH
+ {0xB450, 0xB450, prLV}, // Lo HANGUL SYLLABLE DU
+ {0xB451, 0xB46B, prLVT}, // Lo [27] HANGUL SYLLABLE DUG..HANGUL SYLLABLE DUH
+ {0xB46C, 0xB46C, prLV}, // Lo HANGUL SYLLABLE DWEO
+ {0xB46D, 0xB487, prLVT}, // Lo [27] HANGUL SYLLABLE DWEOG..HANGUL SYLLABLE DWEOH
+ {0xB488, 0xB488, prLV}, // Lo HANGUL SYLLABLE DWE
+ {0xB489, 0xB4A3, prLVT}, // Lo [27] HANGUL SYLLABLE DWEG..HANGUL SYLLABLE DWEH
+ {0xB4A4, 0xB4A4, prLV}, // Lo HANGUL SYLLABLE DWI
+ {0xB4A5, 0xB4BF, prLVT}, // Lo [27] HANGUL SYLLABLE DWIG..HANGUL SYLLABLE DWIH
+ {0xB4C0, 0xB4C0, prLV}, // Lo HANGUL SYLLABLE DYU
+ {0xB4C1, 0xB4DB, prLVT}, // Lo [27] HANGUL SYLLABLE DYUG..HANGUL SYLLABLE DYUH
+ {0xB4DC, 0xB4DC, prLV}, // Lo HANGUL SYLLABLE DEU
+ {0xB4DD, 0xB4F7, prLVT}, // Lo [27] HANGUL SYLLABLE DEUG..HANGUL SYLLABLE DEUH
+ {0xB4F8, 0xB4F8, prLV}, // Lo HANGUL SYLLABLE DYI
+ {0xB4F9, 0xB513, prLVT}, // Lo [27] HANGUL SYLLABLE DYIG..HANGUL SYLLABLE DYIH
+ {0xB514, 0xB514, prLV}, // Lo HANGUL SYLLABLE DI
+ {0xB515, 0xB52F, prLVT}, // Lo [27] HANGUL SYLLABLE DIG..HANGUL SYLLABLE DIH
+ {0xB530, 0xB530, prLV}, // Lo HANGUL SYLLABLE DDA
+ {0xB531, 0xB54B, prLVT}, // Lo [27] HANGUL SYLLABLE DDAG..HANGUL SYLLABLE DDAH
+ {0xB54C, 0xB54C, prLV}, // Lo HANGUL SYLLABLE DDAE
+ {0xB54D, 0xB567, prLVT}, // Lo [27] HANGUL SYLLABLE DDAEG..HANGUL SYLLABLE DDAEH
+ {0xB568, 0xB568, prLV}, // Lo HANGUL SYLLABLE DDYA
+ {0xB569, 0xB583, prLVT}, // Lo [27] HANGUL SYLLABLE DDYAG..HANGUL SYLLABLE DDYAH
+ {0xB584, 0xB584, prLV}, // Lo HANGUL SYLLABLE DDYAE
+ {0xB585, 0xB59F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYAEG..HANGUL SYLLABLE DDYAEH
+ {0xB5A0, 0xB5A0, prLV}, // Lo HANGUL SYLLABLE DDEO
+ {0xB5A1, 0xB5BB, prLVT}, // Lo [27] HANGUL SYLLABLE DDEOG..HANGUL SYLLABLE DDEOH
+ {0xB5BC, 0xB5BC, prLV}, // Lo HANGUL SYLLABLE DDE
+ {0xB5BD, 0xB5D7, prLVT}, // Lo [27] HANGUL SYLLABLE DDEG..HANGUL SYLLABLE DDEH
+ {0xB5D8, 0xB5D8, prLV}, // Lo HANGUL SYLLABLE DDYEO
+ {0xB5D9, 0xB5F3, prLVT}, // Lo [27] HANGUL SYLLABLE DDYEOG..HANGUL SYLLABLE DDYEOH
+ {0xB5F4, 0xB5F4, prLV}, // Lo HANGUL SYLLABLE DDYE
+ {0xB5F5, 0xB60F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYEG..HANGUL SYLLABLE DDYEH
+ {0xB610, 0xB610, prLV}, // Lo HANGUL SYLLABLE DDO
+ {0xB611, 0xB62B, prLVT}, // Lo [27] HANGUL SYLLABLE DDOG..HANGUL SYLLABLE DDOH
+ {0xB62C, 0xB62C, prLV}, // Lo HANGUL SYLLABLE DDWA
+ {0xB62D, 0xB647, prLVT}, // Lo [27] HANGUL SYLLABLE DDWAG..HANGUL SYLLABLE DDWAH
+ {0xB648, 0xB648, prLV}, // Lo HANGUL SYLLABLE DDWAE
+ {0xB649, 0xB663, prLVT}, // Lo [27] HANGUL SYLLABLE DDWAEG..HANGUL SYLLABLE DDWAEH
+ {0xB664, 0xB664, prLV}, // Lo HANGUL SYLLABLE DDOE
+ {0xB665, 0xB67F, prLVT}, // Lo [27] HANGUL SYLLABLE DDOEG..HANGUL SYLLABLE DDOEH
+ {0xB680, 0xB680, prLV}, // Lo HANGUL SYLLABLE DDYO
+ {0xB681, 0xB69B, prLVT}, // Lo [27] HANGUL SYLLABLE DDYOG..HANGUL SYLLABLE DDYOH
+ {0xB69C, 0xB69C, prLV}, // Lo HANGUL SYLLABLE DDU
+ {0xB69D, 0xB6B7, prLVT}, // Lo [27] HANGUL SYLLABLE DDUG..HANGUL SYLLABLE DDUH
+ {0xB6B8, 0xB6B8, prLV}, // Lo HANGUL SYLLABLE DDWEO
+ {0xB6B9, 0xB6D3, prLVT}, // Lo [27] HANGUL SYLLABLE DDWEOG..HANGUL SYLLABLE DDWEOH
+ {0xB6D4, 0xB6D4, prLV}, // Lo HANGUL SYLLABLE DDWE
+ {0xB6D5, 0xB6EF, prLVT}, // Lo [27] HANGUL SYLLABLE DDWEG..HANGUL SYLLABLE DDWEH
+ {0xB6F0, 0xB6F0, prLV}, // Lo HANGUL SYLLABLE DDWI
+ {0xB6F1, 0xB70B, prLVT}, // Lo [27] HANGUL SYLLABLE DDWIG..HANGUL SYLLABLE DDWIH
+ {0xB70C, 0xB70C, prLV}, // Lo HANGUL SYLLABLE DDYU
+ {0xB70D, 0xB727, prLVT}, // Lo [27] HANGUL SYLLABLE DDYUG..HANGUL SYLLABLE DDYUH
+ {0xB728, 0xB728, prLV}, // Lo HANGUL SYLLABLE DDEU
+ {0xB729, 0xB743, prLVT}, // Lo [27] HANGUL SYLLABLE DDEUG..HANGUL SYLLABLE DDEUH
+ {0xB744, 0xB744, prLV}, // Lo HANGUL SYLLABLE DDYI
+ {0xB745, 0xB75F, prLVT}, // Lo [27] HANGUL SYLLABLE DDYIG..HANGUL SYLLABLE DDYIH
+ {0xB760, 0xB760, prLV}, // Lo HANGUL SYLLABLE DDI
+ {0xB761, 0xB77B, prLVT}, // Lo [27] HANGUL SYLLABLE DDIG..HANGUL SYLLABLE DDIH
+ {0xB77C, 0xB77C, prLV}, // Lo HANGUL SYLLABLE RA
+ {0xB77D, 0xB797, prLVT}, // Lo [27] HANGUL SYLLABLE RAG..HANGUL SYLLABLE RAH
+ {0xB798, 0xB798, prLV}, // Lo HANGUL SYLLABLE RAE
+ {0xB799, 0xB7B3, prLVT}, // Lo [27] HANGUL SYLLABLE RAEG..HANGUL SYLLABLE RAEH
+ {0xB7B4, 0xB7B4, prLV}, // Lo HANGUL SYLLABLE RYA
+ {0xB7B5, 0xB7CF, prLVT}, // Lo [27] HANGUL SYLLABLE RYAG..HANGUL SYLLABLE RYAH
+ {0xB7D0, 0xB7D0, prLV}, // Lo HANGUL SYLLABLE RYAE
+ {0xB7D1, 0xB7EB, prLVT}, // Lo [27] HANGUL SYLLABLE RYAEG..HANGUL SYLLABLE RYAEH
+ {0xB7EC, 0xB7EC, prLV}, // Lo HANGUL SYLLABLE REO
+ {0xB7ED, 0xB807, prLVT}, // Lo [27] HANGUL SYLLABLE REOG..HANGUL SYLLABLE REOH
+ {0xB808, 0xB808, prLV}, // Lo HANGUL SYLLABLE RE
+ {0xB809, 0xB823, prLVT}, // Lo [27] HANGUL SYLLABLE REG..HANGUL SYLLABLE REH
+ {0xB824, 0xB824, prLV}, // Lo HANGUL SYLLABLE RYEO
+ {0xB825, 0xB83F, prLVT}, // Lo [27] HANGUL SYLLABLE RYEOG..HANGUL SYLLABLE RYEOH
+ {0xB840, 0xB840, prLV}, // Lo HANGUL SYLLABLE RYE
+ {0xB841, 0xB85B, prLVT}, // Lo [27] HANGUL SYLLABLE RYEG..HANGUL SYLLABLE RYEH
+ {0xB85C, 0xB85C, prLV}, // Lo HANGUL SYLLABLE RO
+ {0xB85D, 0xB877, prLVT}, // Lo [27] HANGUL SYLLABLE ROG..HANGUL SYLLABLE ROH
+ {0xB878, 0xB878, prLV}, // Lo HANGUL SYLLABLE RWA
+ {0xB879, 0xB893, prLVT}, // Lo [27] HANGUL SYLLABLE RWAG..HANGUL SYLLABLE RWAH
+ {0xB894, 0xB894, prLV}, // Lo HANGUL SYLLABLE RWAE
+ {0xB895, 0xB8AF, prLVT}, // Lo [27] HANGUL SYLLABLE RWAEG..HANGUL SYLLABLE RWAEH
+ {0xB8B0, 0xB8B0, prLV}, // Lo HANGUL SYLLABLE ROE
+ {0xB8B1, 0xB8CB, prLVT}, // Lo [27] HANGUL SYLLABLE ROEG..HANGUL SYLLABLE ROEH
+ {0xB8CC, 0xB8CC, prLV}, // Lo HANGUL SYLLABLE RYO
+ {0xB8CD, 0xB8E7, prLVT}, // Lo [27] HANGUL SYLLABLE RYOG..HANGUL SYLLABLE RYOH
+ {0xB8E8, 0xB8E8, prLV}, // Lo HANGUL SYLLABLE RU
+ {0xB8E9, 0xB903, prLVT}, // Lo [27] HANGUL SYLLABLE RUG..HANGUL SYLLABLE RUH
+ {0xB904, 0xB904, prLV}, // Lo HANGUL SYLLABLE RWEO
+ {0xB905, 0xB91F, prLVT}, // Lo [27] HANGUL SYLLABLE RWEOG..HANGUL SYLLABLE RWEOH
+ {0xB920, 0xB920, prLV}, // Lo HANGUL SYLLABLE RWE
+ {0xB921, 0xB93B, prLVT}, // Lo [27] HANGUL SYLLABLE RWEG..HANGUL SYLLABLE RWEH
+ {0xB93C, 0xB93C, prLV}, // Lo HANGUL SYLLABLE RWI
+ {0xB93D, 0xB957, prLVT}, // Lo [27] HANGUL SYLLABLE RWIG..HANGUL SYLLABLE RWIH
+ {0xB958, 0xB958, prLV}, // Lo HANGUL SYLLABLE RYU
+ {0xB959, 0xB973, prLVT}, // Lo [27] HANGUL SYLLABLE RYUG..HANGUL SYLLABLE RYUH
+ {0xB974, 0xB974, prLV}, // Lo HANGUL SYLLABLE REU
+ {0xB975, 0xB98F, prLVT}, // Lo [27] HANGUL SYLLABLE REUG..HANGUL SYLLABLE REUH
+ {0xB990, 0xB990, prLV}, // Lo HANGUL SYLLABLE RYI
+ {0xB991, 0xB9AB, prLVT}, // Lo [27] HANGUL SYLLABLE RYIG..HANGUL SYLLABLE RYIH
+ {0xB9AC, 0xB9AC, prLV}, // Lo HANGUL SYLLABLE RI
+ {0xB9AD, 0xB9C7, prLVT}, // Lo [27] HANGUL SYLLABLE RIG..HANGUL SYLLABLE RIH
+ {0xB9C8, 0xB9C8, prLV}, // Lo HANGUL SYLLABLE MA
+ {0xB9C9, 0xB9E3, prLVT}, // Lo [27] HANGUL SYLLABLE MAG..HANGUL SYLLABLE MAH
+ {0xB9E4, 0xB9E4, prLV}, // Lo HANGUL SYLLABLE MAE
+ {0xB9E5, 0xB9FF, prLVT}, // Lo [27] HANGUL SYLLABLE MAEG..HANGUL SYLLABLE MAEH
+ {0xBA00, 0xBA00, prLV}, // Lo HANGUL SYLLABLE MYA
+ {0xBA01, 0xBA1B, prLVT}, // Lo [27] HANGUL SYLLABLE MYAG..HANGUL SYLLABLE MYAH
+ {0xBA1C, 0xBA1C, prLV}, // Lo HANGUL SYLLABLE MYAE
+ {0xBA1D, 0xBA37, prLVT}, // Lo [27] HANGUL SYLLABLE MYAEG..HANGUL SYLLABLE MYAEH
+ {0xBA38, 0xBA38, prLV}, // Lo HANGUL SYLLABLE MEO
+ {0xBA39, 0xBA53, prLVT}, // Lo [27] HANGUL SYLLABLE MEOG..HANGUL SYLLABLE MEOH
+ {0xBA54, 0xBA54, prLV}, // Lo HANGUL SYLLABLE ME
+ {0xBA55, 0xBA6F, prLVT}, // Lo [27] HANGUL SYLLABLE MEG..HANGUL SYLLABLE MEH
+ {0xBA70, 0xBA70, prLV}, // Lo HANGUL SYLLABLE MYEO
+ {0xBA71, 0xBA8B, prLVT}, // Lo [27] HANGUL SYLLABLE MYEOG..HANGUL SYLLABLE MYEOH
+ {0xBA8C, 0xBA8C, prLV}, // Lo HANGUL SYLLABLE MYE
+ {0xBA8D, 0xBAA7, prLVT}, // Lo [27] HANGUL SYLLABLE MYEG..HANGUL SYLLABLE MYEH
+ {0xBAA8, 0xBAA8, prLV}, // Lo HANGUL SYLLABLE MO
+ {0xBAA9, 0xBAC3, prLVT}, // Lo [27] HANGUL SYLLABLE MOG..HANGUL SYLLABLE MOH
+ {0xBAC4, 0xBAC4, prLV}, // Lo HANGUL SYLLABLE MWA
+ {0xBAC5, 0xBADF, prLVT}, // Lo [27] HANGUL SYLLABLE MWAG..HANGUL SYLLABLE MWAH
+ {0xBAE0, 0xBAE0, prLV}, // Lo HANGUL SYLLABLE MWAE
+ {0xBAE1, 0xBAFB, prLVT}, // Lo [27] HANGUL SYLLABLE MWAEG..HANGUL SYLLABLE MWAEH
+ {0xBAFC, 0xBAFC, prLV}, // Lo HANGUL SYLLABLE MOE
+ {0xBAFD, 0xBB17, prLVT}, // Lo [27] HANGUL SYLLABLE MOEG..HANGUL SYLLABLE MOEH
+ {0xBB18, 0xBB18, prLV}, // Lo HANGUL SYLLABLE MYO
+ {0xBB19, 0xBB33, prLVT}, // Lo [27] HANGUL SYLLABLE MYOG..HANGUL SYLLABLE MYOH
+ {0xBB34, 0xBB34, prLV}, // Lo HANGUL SYLLABLE MU
+ {0xBB35, 0xBB4F, prLVT}, // Lo [27] HANGUL SYLLABLE MUG..HANGUL SYLLABLE MUH
+ {0xBB50, 0xBB50, prLV}, // Lo HANGUL SYLLABLE MWEO
+ {0xBB51, 0xBB6B, prLVT}, // Lo [27] HANGUL SYLLABLE MWEOG..HANGUL SYLLABLE MWEOH
+ {0xBB6C, 0xBB6C, prLV}, // Lo HANGUL SYLLABLE MWE
+ {0xBB6D, 0xBB87, prLVT}, // Lo [27] HANGUL SYLLABLE MWEG..HANGUL SYLLABLE MWEH
+ {0xBB88, 0xBB88, prLV}, // Lo HANGUL SYLLABLE MWI
+ {0xBB89, 0xBBA3, prLVT}, // Lo [27] HANGUL SYLLABLE MWIG..HANGUL SYLLABLE MWIH
+ {0xBBA4, 0xBBA4, prLV}, // Lo HANGUL SYLLABLE MYU
+ {0xBBA5, 0xBBBF, prLVT}, // Lo [27] HANGUL SYLLABLE MYUG..HANGUL SYLLABLE MYUH
+ {0xBBC0, 0xBBC0, prLV}, // Lo HANGUL SYLLABLE MEU
+ {0xBBC1, 0xBBDB, prLVT}, // Lo [27] HANGUL SYLLABLE MEUG..HANGUL SYLLABLE MEUH
+ {0xBBDC, 0xBBDC, prLV}, // Lo HANGUL SYLLABLE MYI
+ {0xBBDD, 0xBBF7, prLVT}, // Lo [27] HANGUL SYLLABLE MYIG..HANGUL SYLLABLE MYIH
+ {0xBBF8, 0xBBF8, prLV}, // Lo HANGUL SYLLABLE MI
+ {0xBBF9, 0xBC13, prLVT}, // Lo [27] HANGUL SYLLABLE MIG..HANGUL SYLLABLE MIH
+ {0xBC14, 0xBC14, prLV}, // Lo HANGUL SYLLABLE BA
+ {0xBC15, 0xBC2F, prLVT}, // Lo [27] HANGUL SYLLABLE BAG..HANGUL SYLLABLE BAH
+ {0xBC30, 0xBC30, prLV}, // Lo HANGUL SYLLABLE BAE
+ {0xBC31, 0xBC4B, prLVT}, // Lo [27] HANGUL SYLLABLE BAEG..HANGUL SYLLABLE BAEH
+ {0xBC4C, 0xBC4C, prLV}, // Lo HANGUL SYLLABLE BYA
+ {0xBC4D, 0xBC67, prLVT}, // Lo [27] HANGUL SYLLABLE BYAG..HANGUL SYLLABLE BYAH
+ {0xBC68, 0xBC68, prLV}, // Lo HANGUL SYLLABLE BYAE
+ {0xBC69, 0xBC83, prLVT}, // Lo [27] HANGUL SYLLABLE BYAEG..HANGUL SYLLABLE BYAEH
+ {0xBC84, 0xBC84, prLV}, // Lo HANGUL SYLLABLE BEO
+ {0xBC85, 0xBC9F, prLVT}, // Lo [27] HANGUL SYLLABLE BEOG..HANGUL SYLLABLE BEOH
+ {0xBCA0, 0xBCA0, prLV}, // Lo HANGUL SYLLABLE BE
+ {0xBCA1, 0xBCBB, prLVT}, // Lo [27] HANGUL SYLLABLE BEG..HANGUL SYLLABLE BEH
+ {0xBCBC, 0xBCBC, prLV}, // Lo HANGUL SYLLABLE BYEO
+ {0xBCBD, 0xBCD7, prLVT}, // Lo [27] HANGUL SYLLABLE BYEOG..HANGUL SYLLABLE BYEOH
+ {0xBCD8, 0xBCD8, prLV}, // Lo HANGUL SYLLABLE BYE
+ {0xBCD9, 0xBCF3, prLVT}, // Lo [27] HANGUL SYLLABLE BYEG..HANGUL SYLLABLE BYEH
+ {0xBCF4, 0xBCF4, prLV}, // Lo HANGUL SYLLABLE BO
+ {0xBCF5, 0xBD0F, prLVT}, // Lo [27] HANGUL SYLLABLE BOG..HANGUL SYLLABLE BOH
+ {0xBD10, 0xBD10, prLV}, // Lo HANGUL SYLLABLE BWA
+ {0xBD11, 0xBD2B, prLVT}, // Lo [27] HANGUL SYLLABLE BWAG..HANGUL SYLLABLE BWAH
+ {0xBD2C, 0xBD2C, prLV}, // Lo HANGUL SYLLABLE BWAE
+ {0xBD2D, 0xBD47, prLVT}, // Lo [27] HANGUL SYLLABLE BWAEG..HANGUL SYLLABLE BWAEH
+ {0xBD48, 0xBD48, prLV}, // Lo HANGUL SYLLABLE BOE
+ {0xBD49, 0xBD63, prLVT}, // Lo [27] HANGUL SYLLABLE BOEG..HANGUL SYLLABLE BOEH
+ {0xBD64, 0xBD64, prLV}, // Lo HANGUL SYLLABLE BYO
+ {0xBD65, 0xBD7F, prLVT}, // Lo [27] HANGUL SYLLABLE BYOG..HANGUL SYLLABLE BYOH
+ {0xBD80, 0xBD80, prLV}, // Lo HANGUL SYLLABLE BU
+ {0xBD81, 0xBD9B, prLVT}, // Lo [27] HANGUL SYLLABLE BUG..HANGUL SYLLABLE BUH
+ {0xBD9C, 0xBD9C, prLV}, // Lo HANGUL SYLLABLE BWEO
+ {0xBD9D, 0xBDB7, prLVT}, // Lo [27] HANGUL SYLLABLE BWEOG..HANGUL SYLLABLE BWEOH
+ {0xBDB8, 0xBDB8, prLV}, // Lo HANGUL SYLLABLE BWE
+ {0xBDB9, 0xBDD3, prLVT}, // Lo [27] HANGUL SYLLABLE BWEG..HANGUL SYLLABLE BWEH
+ {0xBDD4, 0xBDD4, prLV}, // Lo HANGUL SYLLABLE BWI
+ {0xBDD5, 0xBDEF, prLVT}, // Lo [27] HANGUL SYLLABLE BWIG..HANGUL SYLLABLE BWIH
+ {0xBDF0, 0xBDF0, prLV}, // Lo HANGUL SYLLABLE BYU
+ {0xBDF1, 0xBE0B, prLVT}, // Lo [27] HANGUL SYLLABLE BYUG..HANGUL SYLLABLE BYUH
+ {0xBE0C, 0xBE0C, prLV}, // Lo HANGUL SYLLABLE BEU
+ {0xBE0D, 0xBE27, prLVT}, // Lo [27] HANGUL SYLLABLE BEUG..HANGUL SYLLABLE BEUH
+ {0xBE28, 0xBE28, prLV}, // Lo HANGUL SYLLABLE BYI
+ {0xBE29, 0xBE43, prLVT}, // Lo [27] HANGUL SYLLABLE BYIG..HANGUL SYLLABLE BYIH
+ {0xBE44, 0xBE44, prLV}, // Lo HANGUL SYLLABLE BI
+ {0xBE45, 0xBE5F, prLVT}, // Lo [27] HANGUL SYLLABLE BIG..HANGUL SYLLABLE BIH
+ {0xBE60, 0xBE60, prLV}, // Lo HANGUL SYLLABLE BBA
+ {0xBE61, 0xBE7B, prLVT}, // Lo [27] HANGUL SYLLABLE BBAG..HANGUL SYLLABLE BBAH
+ {0xBE7C, 0xBE7C, prLV}, // Lo HANGUL SYLLABLE BBAE
+ {0xBE7D, 0xBE97, prLVT}, // Lo [27] HANGUL SYLLABLE BBAEG..HANGUL SYLLABLE BBAEH
+ {0xBE98, 0xBE98, prLV}, // Lo HANGUL SYLLABLE BBYA
+ {0xBE99, 0xBEB3, prLVT}, // Lo [27] HANGUL SYLLABLE BBYAG..HANGUL SYLLABLE BBYAH
+ {0xBEB4, 0xBEB4, prLV}, // Lo HANGUL SYLLABLE BBYAE
+ {0xBEB5, 0xBECF, prLVT}, // Lo [27] HANGUL SYLLABLE BBYAEG..HANGUL SYLLABLE BBYAEH
+ {0xBED0, 0xBED0, prLV}, // Lo HANGUL SYLLABLE BBEO
+ {0xBED1, 0xBEEB, prLVT}, // Lo [27] HANGUL SYLLABLE BBEOG..HANGUL SYLLABLE BBEOH
+ {0xBEEC, 0xBEEC, prLV}, // Lo HANGUL SYLLABLE BBE
+ {0xBEED, 0xBF07, prLVT}, // Lo [27] HANGUL SYLLABLE BBEG..HANGUL SYLLABLE BBEH
+ {0xBF08, 0xBF08, prLV}, // Lo HANGUL SYLLABLE BBYEO
+ {0xBF09, 0xBF23, prLVT}, // Lo [27] HANGUL SYLLABLE BBYEOG..HANGUL SYLLABLE BBYEOH
+ {0xBF24, 0xBF24, prLV}, // Lo HANGUL SYLLABLE BBYE
+ {0xBF25, 0xBF3F, prLVT}, // Lo [27] HANGUL SYLLABLE BBYEG..HANGUL SYLLABLE BBYEH
+ {0xBF40, 0xBF40, prLV}, // Lo HANGUL SYLLABLE BBO
+ {0xBF41, 0xBF5B, prLVT}, // Lo [27] HANGUL SYLLABLE BBOG..HANGUL SYLLABLE BBOH
+ {0xBF5C, 0xBF5C, prLV}, // Lo HANGUL SYLLABLE BBWA
+ {0xBF5D, 0xBF77, prLVT}, // Lo [27] HANGUL SYLLABLE BBWAG..HANGUL SYLLABLE BBWAH
+ {0xBF78, 0xBF78, prLV}, // Lo HANGUL SYLLABLE BBWAE
+ {0xBF79, 0xBF93, prLVT}, // Lo [27] HANGUL SYLLABLE BBWAEG..HANGUL SYLLABLE BBWAEH
+ {0xBF94, 0xBF94, prLV}, // Lo HANGUL SYLLABLE BBOE
+ {0xBF95, 0xBFAF, prLVT}, // Lo [27] HANGUL SYLLABLE BBOEG..HANGUL SYLLABLE BBOEH
+ {0xBFB0, 0xBFB0, prLV}, // Lo HANGUL SYLLABLE BBYO
+ {0xBFB1, 0xBFCB, prLVT}, // Lo [27] HANGUL SYLLABLE BBYOG..HANGUL SYLLABLE BBYOH
+ {0xBFCC, 0xBFCC, prLV}, // Lo HANGUL SYLLABLE BBU
+ {0xBFCD, 0xBFE7, prLVT}, // Lo [27] HANGUL SYLLABLE BBUG..HANGUL SYLLABLE BBUH
+ {0xBFE8, 0xBFE8, prLV}, // Lo HANGUL SYLLABLE BBWEO
+ {0xBFE9, 0xC003, prLVT}, // Lo [27] HANGUL SYLLABLE BBWEOG..HANGUL SYLLABLE BBWEOH
+ {0xC004, 0xC004, prLV}, // Lo HANGUL SYLLABLE BBWE
+ {0xC005, 0xC01F, prLVT}, // Lo [27] HANGUL SYLLABLE BBWEG..HANGUL SYLLABLE BBWEH
+ {0xC020, 0xC020, prLV}, // Lo HANGUL SYLLABLE BBWI
+ {0xC021, 0xC03B, prLVT}, // Lo [27] HANGUL SYLLABLE BBWIG..HANGUL SYLLABLE BBWIH
+ {0xC03C, 0xC03C, prLV}, // Lo HANGUL SYLLABLE BBYU
+ {0xC03D, 0xC057, prLVT}, // Lo [27] HANGUL SYLLABLE BBYUG..HANGUL SYLLABLE BBYUH
+ {0xC058, 0xC058, prLV}, // Lo HANGUL SYLLABLE BBEU
+ {0xC059, 0xC073, prLVT}, // Lo [27] HANGUL SYLLABLE BBEUG..HANGUL SYLLABLE BBEUH
+ {0xC074, 0xC074, prLV}, // Lo HANGUL SYLLABLE BBYI
+ {0xC075, 0xC08F, prLVT}, // Lo [27] HANGUL SYLLABLE BBYIG..HANGUL SYLLABLE BBYIH
+ {0xC090, 0xC090, prLV}, // Lo HANGUL SYLLABLE BBI
+ {0xC091, 0xC0AB, prLVT}, // Lo [27] HANGUL SYLLABLE BBIG..HANGUL SYLLABLE BBIH
+ {0xC0AC, 0xC0AC, prLV}, // Lo HANGUL SYLLABLE SA
+ {0xC0AD, 0xC0C7, prLVT}, // Lo [27] HANGUL SYLLABLE SAG..HANGUL SYLLABLE SAH
+ {0xC0C8, 0xC0C8, prLV}, // Lo HANGUL SYLLABLE SAE
+ {0xC0C9, 0xC0E3, prLVT}, // Lo [27] HANGUL SYLLABLE SAEG..HANGUL SYLLABLE SAEH
+ {0xC0E4, 0xC0E4, prLV}, // Lo HANGUL SYLLABLE SYA
+ {0xC0E5, 0xC0FF, prLVT}, // Lo [27] HANGUL SYLLABLE SYAG..HANGUL SYLLABLE SYAH
+ {0xC100, 0xC100, prLV}, // Lo HANGUL SYLLABLE SYAE
+ {0xC101, 0xC11B, prLVT}, // Lo [27] HANGUL SYLLABLE SYAEG..HANGUL SYLLABLE SYAEH
+ {0xC11C, 0xC11C, prLV}, // Lo HANGUL SYLLABLE SEO
+ {0xC11D, 0xC137, prLVT}, // Lo [27] HANGUL SYLLABLE SEOG..HANGUL SYLLABLE SEOH
+ {0xC138, 0xC138, prLV}, // Lo HANGUL SYLLABLE SE
+ {0xC139, 0xC153, prLVT}, // Lo [27] HANGUL SYLLABLE SEG..HANGUL SYLLABLE SEH
+ {0xC154, 0xC154, prLV}, // Lo HANGUL SYLLABLE SYEO
+ {0xC155, 0xC16F, prLVT}, // Lo [27] HANGUL SYLLABLE SYEOG..HANGUL SYLLABLE SYEOH
+ {0xC170, 0xC170, prLV}, // Lo HANGUL SYLLABLE SYE
+ {0xC171, 0xC18B, prLVT}, // Lo [27] HANGUL SYLLABLE SYEG..HANGUL SYLLABLE SYEH
+ {0xC18C, 0xC18C, prLV}, // Lo HANGUL SYLLABLE SO
+ {0xC18D, 0xC1A7, prLVT}, // Lo [27] HANGUL SYLLABLE SOG..HANGUL SYLLABLE SOH
+ {0xC1A8, 0xC1A8, prLV}, // Lo HANGUL SYLLABLE SWA
+ {0xC1A9, 0xC1C3, prLVT}, // Lo [27] HANGUL SYLLABLE SWAG..HANGUL SYLLABLE SWAH
+ {0xC1C4, 0xC1C4, prLV}, // Lo HANGUL SYLLABLE SWAE
+ {0xC1C5, 0xC1DF, prLVT}, // Lo [27] HANGUL SYLLABLE SWAEG..HANGUL SYLLABLE SWAEH
+ {0xC1E0, 0xC1E0, prLV}, // Lo HANGUL SYLLABLE SOE
+ {0xC1E1, 0xC1FB, prLVT}, // Lo [27] HANGUL SYLLABLE SOEG..HANGUL SYLLABLE SOEH
+ {0xC1FC, 0xC1FC, prLV}, // Lo HANGUL SYLLABLE SYO
+ {0xC1FD, 0xC217, prLVT}, // Lo [27] HANGUL SYLLABLE SYOG..HANGUL SYLLABLE SYOH
+ {0xC218, 0xC218, prLV}, // Lo HANGUL SYLLABLE SU
+ {0xC219, 0xC233, prLVT}, // Lo [27] HANGUL SYLLABLE SUG..HANGUL SYLLABLE SUH
+ {0xC234, 0xC234, prLV}, // Lo HANGUL SYLLABLE SWEO
+ {0xC235, 0xC24F, prLVT}, // Lo [27] HANGUL SYLLABLE SWEOG..HANGUL SYLLABLE SWEOH
+ {0xC250, 0xC250, prLV}, // Lo HANGUL SYLLABLE SWE
+ {0xC251, 0xC26B, prLVT}, // Lo [27] HANGUL SYLLABLE SWEG..HANGUL SYLLABLE SWEH
+ {0xC26C, 0xC26C, prLV}, // Lo HANGUL SYLLABLE SWI
+ {0xC26D, 0xC287, prLVT}, // Lo [27] HANGUL SYLLABLE SWIG..HANGUL SYLLABLE SWIH
+ {0xC288, 0xC288, prLV}, // Lo HANGUL SYLLABLE SYU
+ {0xC289, 0xC2A3, prLVT}, // Lo [27] HANGUL SYLLABLE SYUG..HANGUL SYLLABLE SYUH
+ {0xC2A4, 0xC2A4, prLV}, // Lo HANGUL SYLLABLE SEU
+ {0xC2A5, 0xC2BF, prLVT}, // Lo [27] HANGUL SYLLABLE SEUG..HANGUL SYLLABLE SEUH
+ {0xC2C0, 0xC2C0, prLV}, // Lo HANGUL SYLLABLE SYI
+ {0xC2C1, 0xC2DB, prLVT}, // Lo [27] HANGUL SYLLABLE SYIG..HANGUL SYLLABLE SYIH
+ {0xC2DC, 0xC2DC, prLV}, // Lo HANGUL SYLLABLE SI
+ {0xC2DD, 0xC2F7, prLVT}, // Lo [27] HANGUL SYLLABLE SIG..HANGUL SYLLABLE SIH
+ {0xC2F8, 0xC2F8, prLV}, // Lo HANGUL SYLLABLE SSA
+ {0xC2F9, 0xC313, prLVT}, // Lo [27] HANGUL SYLLABLE SSAG..HANGUL SYLLABLE SSAH
+ {0xC314, 0xC314, prLV}, // Lo HANGUL SYLLABLE SSAE
+ {0xC315, 0xC32F, prLVT}, // Lo [27] HANGUL SYLLABLE SSAEG..HANGUL SYLLABLE SSAEH
+ {0xC330, 0xC330, prLV}, // Lo HANGUL SYLLABLE SSYA
+ {0xC331, 0xC34B, prLVT}, // Lo [27] HANGUL SYLLABLE SSYAG..HANGUL SYLLABLE SSYAH
+ {0xC34C, 0xC34C, prLV}, // Lo HANGUL SYLLABLE SSYAE
+ {0xC34D, 0xC367, prLVT}, // Lo [27] HANGUL SYLLABLE SSYAEG..HANGUL SYLLABLE SSYAEH
+ {0xC368, 0xC368, prLV}, // Lo HANGUL SYLLABLE SSEO
+ {0xC369, 0xC383, prLVT}, // Lo [27] HANGUL SYLLABLE SSEOG..HANGUL SYLLABLE SSEOH
+ {0xC384, 0xC384, prLV}, // Lo HANGUL SYLLABLE SSE
+ {0xC385, 0xC39F, prLVT}, // Lo [27] HANGUL SYLLABLE SSEG..HANGUL SYLLABLE SSEH
+ {0xC3A0, 0xC3A0, prLV}, // Lo HANGUL SYLLABLE SSYEO
+ {0xC3A1, 0xC3BB, prLVT}, // Lo [27] HANGUL SYLLABLE SSYEOG..HANGUL SYLLABLE SSYEOH
+ {0xC3BC, 0xC3BC, prLV}, // Lo HANGUL SYLLABLE SSYE
+ {0xC3BD, 0xC3D7, prLVT}, // Lo [27] HANGUL SYLLABLE SSYEG..HANGUL SYLLABLE SSYEH
+ {0xC3D8, 0xC3D8, prLV}, // Lo HANGUL SYLLABLE SSO
+ {0xC3D9, 0xC3F3, prLVT}, // Lo [27] HANGUL SYLLABLE SSOG..HANGUL SYLLABLE SSOH
+ {0xC3F4, 0xC3F4, prLV}, // Lo HANGUL SYLLABLE SSWA
+ {0xC3F5, 0xC40F, prLVT}, // Lo [27] HANGUL SYLLABLE SSWAG..HANGUL SYLLABLE SSWAH
+ {0xC410, 0xC410, prLV}, // Lo HANGUL SYLLABLE SSWAE
+ {0xC411, 0xC42B, prLVT}, // Lo [27] HANGUL SYLLABLE SSWAEG..HANGUL SYLLABLE SSWAEH
+ {0xC42C, 0xC42C, prLV}, // Lo HANGUL SYLLABLE SSOE
+ {0xC42D, 0xC447, prLVT}, // Lo [27] HANGUL SYLLABLE SSOEG..HANGUL SYLLABLE SSOEH
+ {0xC448, 0xC448, prLV}, // Lo HANGUL SYLLABLE SSYO
+ {0xC449, 0xC463, prLVT}, // Lo [27] HANGUL SYLLABLE SSYOG..HANGUL SYLLABLE SSYOH
+ {0xC464, 0xC464, prLV}, // Lo HANGUL SYLLABLE SSU
+ {0xC465, 0xC47F, prLVT}, // Lo [27] HANGUL SYLLABLE SSUG..HANGUL SYLLABLE SSUH
+ {0xC480, 0xC480, prLV}, // Lo HANGUL SYLLABLE SSWEO
+ {0xC481, 0xC49B, prLVT}, // Lo [27] HANGUL SYLLABLE SSWEOG..HANGUL SYLLABLE SSWEOH
+ {0xC49C, 0xC49C, prLV}, // Lo HANGUL SYLLABLE SSWE
+ {0xC49D, 0xC4B7, prLVT}, // Lo [27] HANGUL SYLLABLE SSWEG..HANGUL SYLLABLE SSWEH
+ {0xC4B8, 0xC4B8, prLV}, // Lo HANGUL SYLLABLE SSWI
+ {0xC4B9, 0xC4D3, prLVT}, // Lo [27] HANGUL SYLLABLE SSWIG..HANGUL SYLLABLE SSWIH
+ {0xC4D4, 0xC4D4, prLV}, // Lo HANGUL SYLLABLE SSYU
+ {0xC4D5, 0xC4EF, prLVT}, // Lo [27] HANGUL SYLLABLE SSYUG..HANGUL SYLLABLE SSYUH
+ {0xC4F0, 0xC4F0, prLV}, // Lo HANGUL SYLLABLE SSEU
+ {0xC4F1, 0xC50B, prLVT}, // Lo [27] HANGUL SYLLABLE SSEUG..HANGUL SYLLABLE SSEUH
+ {0xC50C, 0xC50C, prLV}, // Lo HANGUL SYLLABLE SSYI
+ {0xC50D, 0xC527, prLVT}, // Lo [27] HANGUL SYLLABLE SSYIG..HANGUL SYLLABLE SSYIH
+ {0xC528, 0xC528, prLV}, // Lo HANGUL SYLLABLE SSI
+ {0xC529, 0xC543, prLVT}, // Lo [27] HANGUL SYLLABLE SSIG..HANGUL SYLLABLE SSIH
+ {0xC544, 0xC544, prLV}, // Lo HANGUL SYLLABLE A
+ {0xC545, 0xC55F, prLVT}, // Lo [27] HANGUL SYLLABLE AG..HANGUL SYLLABLE AH
+ {0xC560, 0xC560, prLV}, // Lo HANGUL SYLLABLE AE
+ {0xC561, 0xC57B, prLVT}, // Lo [27] HANGUL SYLLABLE AEG..HANGUL SYLLABLE AEH
+ {0xC57C, 0xC57C, prLV}, // Lo HANGUL SYLLABLE YA
+ {0xC57D, 0xC597, prLVT}, // Lo [27] HANGUL SYLLABLE YAG..HANGUL SYLLABLE YAH
+ {0xC598, 0xC598, prLV}, // Lo HANGUL SYLLABLE YAE
+ {0xC599, 0xC5B3, prLVT}, // Lo [27] HANGUL SYLLABLE YAEG..HANGUL SYLLABLE YAEH
+ {0xC5B4, 0xC5B4, prLV}, // Lo HANGUL SYLLABLE EO
+ {0xC5B5, 0xC5CF, prLVT}, // Lo [27] HANGUL SYLLABLE EOG..HANGUL SYLLABLE EOH
+ {0xC5D0, 0xC5D0, prLV}, // Lo HANGUL SYLLABLE E
+ {0xC5D1, 0xC5EB, prLVT}, // Lo [27] HANGUL SYLLABLE EG..HANGUL SYLLABLE EH
+ {0xC5EC, 0xC5EC, prLV}, // Lo HANGUL SYLLABLE YEO
+ {0xC5ED, 0xC607, prLVT}, // Lo [27] HANGUL SYLLABLE YEOG..HANGUL SYLLABLE YEOH
+ {0xC608, 0xC608, prLV}, // Lo HANGUL SYLLABLE YE
+ {0xC609, 0xC623, prLVT}, // Lo [27] HANGUL SYLLABLE YEG..HANGUL SYLLABLE YEH
+ {0xC624, 0xC624, prLV}, // Lo HANGUL SYLLABLE O
+ {0xC625, 0xC63F, prLVT}, // Lo [27] HANGUL SYLLABLE OG..HANGUL SYLLABLE OH
+ {0xC640, 0xC640, prLV}, // Lo HANGUL SYLLABLE WA
+ {0xC641, 0xC65B, prLVT}, // Lo [27] HANGUL SYLLABLE WAG..HANGUL SYLLABLE WAH
+ {0xC65C, 0xC65C, prLV}, // Lo HANGUL SYLLABLE WAE
+ {0xC65D, 0xC677, prLVT}, // Lo [27] HANGUL SYLLABLE WAEG..HANGUL SYLLABLE WAEH
+ {0xC678, 0xC678, prLV}, // Lo HANGUL SYLLABLE OE
+ {0xC679, 0xC693, prLVT}, // Lo [27] HANGUL SYLLABLE OEG..HANGUL SYLLABLE OEH
+ {0xC694, 0xC694, prLV}, // Lo HANGUL SYLLABLE YO
+ {0xC695, 0xC6AF, prLVT}, // Lo [27] HANGUL SYLLABLE YOG..HANGUL SYLLABLE YOH
+ {0xC6B0, 0xC6B0, prLV}, // Lo HANGUL SYLLABLE U
+ {0xC6B1, 0xC6CB, prLVT}, // Lo [27] HANGUL SYLLABLE UG..HANGUL SYLLABLE UH
+ {0xC6CC, 0xC6CC, prLV}, // Lo HANGUL SYLLABLE WEO
+ {0xC6CD, 0xC6E7, prLVT}, // Lo [27] HANGUL SYLLABLE WEOG..HANGUL SYLLABLE WEOH
+ {0xC6E8, 0xC6E8, prLV}, // Lo HANGUL SYLLABLE WE
+ {0xC6E9, 0xC703, prLVT}, // Lo [27] HANGUL SYLLABLE WEG..HANGUL SYLLABLE WEH
+ {0xC704, 0xC704, prLV}, // Lo HANGUL SYLLABLE WI
+ {0xC705, 0xC71F, prLVT}, // Lo [27] HANGUL SYLLABLE WIG..HANGUL SYLLABLE WIH
+ {0xC720, 0xC720, prLV}, // Lo HANGUL SYLLABLE YU
+ {0xC721, 0xC73B, prLVT}, // Lo [27] HANGUL SYLLABLE YUG..HANGUL SYLLABLE YUH
+ {0xC73C, 0xC73C, prLV}, // Lo HANGUL SYLLABLE EU
+ {0xC73D, 0xC757, prLVT}, // Lo [27] HANGUL SYLLABLE EUG..HANGUL SYLLABLE EUH
+ {0xC758, 0xC758, prLV}, // Lo HANGUL SYLLABLE YI
+ {0xC759, 0xC773, prLVT}, // Lo [27] HANGUL SYLLABLE YIG..HANGUL SYLLABLE YIH
+ {0xC774, 0xC774, prLV}, // Lo HANGUL SYLLABLE I
+ {0xC775, 0xC78F, prLVT}, // Lo [27] HANGUL SYLLABLE IG..HANGUL SYLLABLE IH
+ {0xC790, 0xC790, prLV}, // Lo HANGUL SYLLABLE JA
+ {0xC791, 0xC7AB, prLVT}, // Lo [27] HANGUL SYLLABLE JAG..HANGUL SYLLABLE JAH
+ {0xC7AC, 0xC7AC, prLV}, // Lo HANGUL SYLLABLE JAE
+ {0xC7AD, 0xC7C7, prLVT}, // Lo [27] HANGUL SYLLABLE JAEG..HANGUL SYLLABLE JAEH
+ {0xC7C8, 0xC7C8, prLV}, // Lo HANGUL SYLLABLE JYA
+ {0xC7C9, 0xC7E3, prLVT}, // Lo [27] HANGUL SYLLABLE JYAG..HANGUL SYLLABLE JYAH
+ {0xC7E4, 0xC7E4, prLV}, // Lo HANGUL SYLLABLE JYAE
+ {0xC7E5, 0xC7FF, prLVT}, // Lo [27] HANGUL SYLLABLE JYAEG..HANGUL SYLLABLE JYAEH
+ {0xC800, 0xC800, prLV}, // Lo HANGUL SYLLABLE JEO
+ {0xC801, 0xC81B, prLVT}, // Lo [27] HANGUL SYLLABLE JEOG..HANGUL SYLLABLE JEOH
+ {0xC81C, 0xC81C, prLV}, // Lo HANGUL SYLLABLE JE
+ {0xC81D, 0xC837, prLVT}, // Lo [27] HANGUL SYLLABLE JEG..HANGUL SYLLABLE JEH
+ {0xC838, 0xC838, prLV}, // Lo HANGUL SYLLABLE JYEO
+ {0xC839, 0xC853, prLVT}, // Lo [27] HANGUL SYLLABLE JYEOG..HANGUL SYLLABLE JYEOH
+ {0xC854, 0xC854, prLV}, // Lo HANGUL SYLLABLE JYE
+ {0xC855, 0xC86F, prLVT}, // Lo [27] HANGUL SYLLABLE JYEG..HANGUL SYLLABLE JYEH
+ {0xC870, 0xC870, prLV}, // Lo HANGUL SYLLABLE JO
+ {0xC871, 0xC88B, prLVT}, // Lo [27] HANGUL SYLLABLE JOG..HANGUL SYLLABLE JOH
+ {0xC88C, 0xC88C, prLV}, // Lo HANGUL SYLLABLE JWA
+ {0xC88D, 0xC8A7, prLVT}, // Lo [27] HANGUL SYLLABLE JWAG..HANGUL SYLLABLE JWAH
+ {0xC8A8, 0xC8A8, prLV}, // Lo HANGUL SYLLABLE JWAE
+ {0xC8A9, 0xC8C3, prLVT}, // Lo [27] HANGUL SYLLABLE JWAEG..HANGUL SYLLABLE JWAEH
+ {0xC8C4, 0xC8C4, prLV}, // Lo HANGUL SYLLABLE JOE
+ {0xC8C5, 0xC8DF, prLVT}, // Lo [27] HANGUL SYLLABLE JOEG..HANGUL SYLLABLE JOEH
+ {0xC8E0, 0xC8E0, prLV}, // Lo HANGUL SYLLABLE JYO
+ {0xC8E1, 0xC8FB, prLVT}, // Lo [27] HANGUL SYLLABLE JYOG..HANGUL SYLLABLE JYOH
+ {0xC8FC, 0xC8FC, prLV}, // Lo HANGUL SYLLABLE JU
+ {0xC8FD, 0xC917, prLVT}, // Lo [27] HANGUL SYLLABLE JUG..HANGUL SYLLABLE JUH
+ {0xC918, 0xC918, prLV}, // Lo HANGUL SYLLABLE JWEO
+ {0xC919, 0xC933, prLVT}, // Lo [27] HANGUL SYLLABLE JWEOG..HANGUL SYLLABLE JWEOH
+ {0xC934, 0xC934, prLV}, // Lo HANGUL SYLLABLE JWE
+ {0xC935, 0xC94F, prLVT}, // Lo [27] HANGUL SYLLABLE JWEG..HANGUL SYLLABLE JWEH
+ {0xC950, 0xC950, prLV}, // Lo HANGUL SYLLABLE JWI
+ {0xC951, 0xC96B, prLVT}, // Lo [27] HANGUL SYLLABLE JWIG..HANGUL SYLLABLE JWIH
+ {0xC96C, 0xC96C, prLV}, // Lo HANGUL SYLLABLE JYU
+ {0xC96D, 0xC987, prLVT}, // Lo [27] HANGUL SYLLABLE JYUG..HANGUL SYLLABLE JYUH
+ {0xC988, 0xC988, prLV}, // Lo HANGUL SYLLABLE JEU
+ {0xC989, 0xC9A3, prLVT}, // Lo [27] HANGUL SYLLABLE JEUG..HANGUL SYLLABLE JEUH
+ {0xC9A4, 0xC9A4, prLV}, // Lo HANGUL SYLLABLE JYI
+ {0xC9A5, 0xC9BF, prLVT}, // Lo [27] HANGUL SYLLABLE JYIG..HANGUL SYLLABLE JYIH
+ {0xC9C0, 0xC9C0, prLV}, // Lo HANGUL SYLLABLE JI
+ {0xC9C1, 0xC9DB, prLVT}, // Lo [27] HANGUL SYLLABLE JIG..HANGUL SYLLABLE JIH
+ {0xC9DC, 0xC9DC, prLV}, // Lo HANGUL SYLLABLE JJA
+ {0xC9DD, 0xC9F7, prLVT}, // Lo [27] HANGUL SYLLABLE JJAG..HANGUL SYLLABLE JJAH
+ {0xC9F8, 0xC9F8, prLV}, // Lo HANGUL SYLLABLE JJAE
+ {0xC9F9, 0xCA13, prLVT}, // Lo [27] HANGUL SYLLABLE JJAEG..HANGUL SYLLABLE JJAEH
+ {0xCA14, 0xCA14, prLV}, // Lo HANGUL SYLLABLE JJYA
+ {0xCA15, 0xCA2F, prLVT}, // Lo [27] HANGUL SYLLABLE JJYAG..HANGUL SYLLABLE JJYAH
+ {0xCA30, 0xCA30, prLV}, // Lo HANGUL SYLLABLE JJYAE
+ {0xCA31, 0xCA4B, prLVT}, // Lo [27] HANGUL SYLLABLE JJYAEG..HANGUL SYLLABLE JJYAEH
+ {0xCA4C, 0xCA4C, prLV}, // Lo HANGUL SYLLABLE JJEO
+ {0xCA4D, 0xCA67, prLVT}, // Lo [27] HANGUL SYLLABLE JJEOG..HANGUL SYLLABLE JJEOH
+ {0xCA68, 0xCA68, prLV}, // Lo HANGUL SYLLABLE JJE
+ {0xCA69, 0xCA83, prLVT}, // Lo [27] HANGUL SYLLABLE JJEG..HANGUL SYLLABLE JJEH
+ {0xCA84, 0xCA84, prLV}, // Lo HANGUL SYLLABLE JJYEO
+ {0xCA85, 0xCA9F, prLVT}, // Lo [27] HANGUL SYLLABLE JJYEOG..HANGUL SYLLABLE JJYEOH
+ {0xCAA0, 0xCAA0, prLV}, // Lo HANGUL SYLLABLE JJYE
+ {0xCAA1, 0xCABB, prLVT}, // Lo [27] HANGUL SYLLABLE JJYEG..HANGUL SYLLABLE JJYEH
+ {0xCABC, 0xCABC, prLV}, // Lo HANGUL SYLLABLE JJO
+ {0xCABD, 0xCAD7, prLVT}, // Lo [27] HANGUL SYLLABLE JJOG..HANGUL SYLLABLE JJOH
+ {0xCAD8, 0xCAD8, prLV}, // Lo HANGUL SYLLABLE JJWA
+ {0xCAD9, 0xCAF3, prLVT}, // Lo [27] HANGUL SYLLABLE JJWAG..HANGUL SYLLABLE JJWAH
+ {0xCAF4, 0xCAF4, prLV}, // Lo HANGUL SYLLABLE JJWAE
+ {0xCAF5, 0xCB0F, prLVT}, // Lo [27] HANGUL SYLLABLE JJWAEG..HANGUL SYLLABLE JJWAEH
+ {0xCB10, 0xCB10, prLV}, // Lo HANGUL SYLLABLE JJOE
+ {0xCB11, 0xCB2B, prLVT}, // Lo [27] HANGUL SYLLABLE JJOEG..HANGUL SYLLABLE JJOEH
+ {0xCB2C, 0xCB2C, prLV}, // Lo HANGUL SYLLABLE JJYO
+ {0xCB2D, 0xCB47, prLVT}, // Lo [27] HANGUL SYLLABLE JJYOG..HANGUL SYLLABLE JJYOH
+ {0xCB48, 0xCB48, prLV}, // Lo HANGUL SYLLABLE JJU
+ {0xCB49, 0xCB63, prLVT}, // Lo [27] HANGUL SYLLABLE JJUG..HANGUL SYLLABLE JJUH
+ {0xCB64, 0xCB64, prLV}, // Lo HANGUL SYLLABLE JJWEO
+ {0xCB65, 0xCB7F, prLVT}, // Lo [27] HANGUL SYLLABLE JJWEOG..HANGUL SYLLABLE JJWEOH
+ {0xCB80, 0xCB80, prLV}, // Lo HANGUL SYLLABLE JJWE
+ {0xCB81, 0xCB9B, prLVT}, // Lo [27] HANGUL SYLLABLE JJWEG..HANGUL SYLLABLE JJWEH
+ {0xCB9C, 0xCB9C, prLV}, // Lo HANGUL SYLLABLE JJWI
+ {0xCB9D, 0xCBB7, prLVT}, // Lo [27] HANGUL SYLLABLE JJWIG..HANGUL SYLLABLE JJWIH
+ {0xCBB8, 0xCBB8, prLV}, // Lo HANGUL SYLLABLE JJYU
+ {0xCBB9, 0xCBD3, prLVT}, // Lo [27] HANGUL SYLLABLE JJYUG..HANGUL SYLLABLE JJYUH
+ {0xCBD4, 0xCBD4, prLV}, // Lo HANGUL SYLLABLE JJEU
+ {0xCBD5, 0xCBEF, prLVT}, // Lo [27] HANGUL SYLLABLE JJEUG..HANGUL SYLLABLE JJEUH
+ {0xCBF0, 0xCBF0, prLV}, // Lo HANGUL SYLLABLE JJYI
+ {0xCBF1, 0xCC0B, prLVT}, // Lo [27] HANGUL SYLLABLE JJYIG..HANGUL SYLLABLE JJYIH
+ {0xCC0C, 0xCC0C, prLV}, // Lo HANGUL SYLLABLE JJI
+ {0xCC0D, 0xCC27, prLVT}, // Lo [27] HANGUL SYLLABLE JJIG..HANGUL SYLLABLE JJIH
+ {0xCC28, 0xCC28, prLV}, // Lo HANGUL SYLLABLE CA
+ {0xCC29, 0xCC43, prLVT}, // Lo [27] HANGUL SYLLABLE CAG..HANGUL SYLLABLE CAH
+ {0xCC44, 0xCC44, prLV}, // Lo HANGUL SYLLABLE CAE
+ {0xCC45, 0xCC5F, prLVT}, // Lo [27] HANGUL SYLLABLE CAEG..HANGUL SYLLABLE CAEH
+ {0xCC60, 0xCC60, prLV}, // Lo HANGUL SYLLABLE CYA
+ {0xCC61, 0xCC7B, prLVT}, // Lo [27] HANGUL SYLLABLE CYAG..HANGUL SYLLABLE CYAH
+ {0xCC7C, 0xCC7C, prLV}, // Lo HANGUL SYLLABLE CYAE
+ {0xCC7D, 0xCC97, prLVT}, // Lo [27] HANGUL SYLLABLE CYAEG..HANGUL SYLLABLE CYAEH
+ {0xCC98, 0xCC98, prLV}, // Lo HANGUL SYLLABLE CEO
+ {0xCC99, 0xCCB3, prLVT}, // Lo [27] HANGUL SYLLABLE CEOG..HANGUL SYLLABLE CEOH
+ {0xCCB4, 0xCCB4, prLV}, // Lo HANGUL SYLLABLE CE
+ {0xCCB5, 0xCCCF, prLVT}, // Lo [27] HANGUL SYLLABLE CEG..HANGUL SYLLABLE CEH
+ {0xCCD0, 0xCCD0, prLV}, // Lo HANGUL SYLLABLE CYEO
+ {0xCCD1, 0xCCEB, prLVT}, // Lo [27] HANGUL SYLLABLE CYEOG..HANGUL SYLLABLE CYEOH
+ {0xCCEC, 0xCCEC, prLV}, // Lo HANGUL SYLLABLE CYE
+ {0xCCED, 0xCD07, prLVT}, // Lo [27] HANGUL SYLLABLE CYEG..HANGUL SYLLABLE CYEH
+ {0xCD08, 0xCD08, prLV}, // Lo HANGUL SYLLABLE CO
+ {0xCD09, 0xCD23, prLVT}, // Lo [27] HANGUL SYLLABLE COG..HANGUL SYLLABLE COH
+ {0xCD24, 0xCD24, prLV}, // Lo HANGUL SYLLABLE CWA
+ {0xCD25, 0xCD3F, prLVT}, // Lo [27] HANGUL SYLLABLE CWAG..HANGUL SYLLABLE CWAH
+ {0xCD40, 0xCD40, prLV}, // Lo HANGUL SYLLABLE CWAE
+ {0xCD41, 0xCD5B, prLVT}, // Lo [27] HANGUL SYLLABLE CWAEG..HANGUL SYLLABLE CWAEH
+ {0xCD5C, 0xCD5C, prLV}, // Lo HANGUL SYLLABLE COE
+ {0xCD5D, 0xCD77, prLVT}, // Lo [27] HANGUL SYLLABLE COEG..HANGUL SYLLABLE COEH
+ {0xCD78, 0xCD78, prLV}, // Lo HANGUL SYLLABLE CYO
+ {0xCD79, 0xCD93, prLVT}, // Lo [27] HANGUL SYLLABLE CYOG..HANGUL SYLLABLE CYOH
+ {0xCD94, 0xCD94, prLV}, // Lo HANGUL SYLLABLE CU
+ {0xCD95, 0xCDAF, prLVT}, // Lo [27] HANGUL SYLLABLE CUG..HANGUL SYLLABLE CUH
+ {0xCDB0, 0xCDB0, prLV}, // Lo HANGUL SYLLABLE CWEO
+ {0xCDB1, 0xCDCB, prLVT}, // Lo [27] HANGUL SYLLABLE CWEOG..HANGUL SYLLABLE CWEOH
+ {0xCDCC, 0xCDCC, prLV}, // Lo HANGUL SYLLABLE CWE
+ {0xCDCD, 0xCDE7, prLVT}, // Lo [27] HANGUL SYLLABLE CWEG..HANGUL SYLLABLE CWEH
+ {0xCDE8, 0xCDE8, prLV}, // Lo HANGUL SYLLABLE CWI
+ {0xCDE9, 0xCE03, prLVT}, // Lo [27] HANGUL SYLLABLE CWIG..HANGUL SYLLABLE CWIH
+ {0xCE04, 0xCE04, prLV}, // Lo HANGUL SYLLABLE CYU
+ {0xCE05, 0xCE1F, prLVT}, // Lo [27] HANGUL SYLLABLE CYUG..HANGUL SYLLABLE CYUH
+ {0xCE20, 0xCE20, prLV}, // Lo HANGUL SYLLABLE CEU
+ {0xCE21, 0xCE3B, prLVT}, // Lo [27] HANGUL SYLLABLE CEUG..HANGUL SYLLABLE CEUH
+ {0xCE3C, 0xCE3C, prLV}, // Lo HANGUL SYLLABLE CYI
+ {0xCE3D, 0xCE57, prLVT}, // Lo [27] HANGUL SYLLABLE CYIG..HANGUL SYLLABLE CYIH
+ {0xCE58, 0xCE58, prLV}, // Lo HANGUL SYLLABLE CI
+ {0xCE59, 0xCE73, prLVT}, // Lo [27] HANGUL SYLLABLE CIG..HANGUL SYLLABLE CIH
+ {0xCE74, 0xCE74, prLV}, // Lo HANGUL SYLLABLE KA
+ {0xCE75, 0xCE8F, prLVT}, // Lo [27] HANGUL SYLLABLE KAG..HANGUL SYLLABLE KAH
+ {0xCE90, 0xCE90, prLV}, // Lo HANGUL SYLLABLE KAE
+ {0xCE91, 0xCEAB, prLVT}, // Lo [27] HANGUL SYLLABLE KAEG..HANGUL SYLLABLE KAEH
+ {0xCEAC, 0xCEAC, prLV}, // Lo HANGUL SYLLABLE KYA
+ {0xCEAD, 0xCEC7, prLVT}, // Lo [27] HANGUL SYLLABLE KYAG..HANGUL SYLLABLE KYAH
+ {0xCEC8, 0xCEC8, prLV}, // Lo HANGUL SYLLABLE KYAE
+ {0xCEC9, 0xCEE3, prLVT}, // Lo [27] HANGUL SYLLABLE KYAEG..HANGUL SYLLABLE KYAEH
+ {0xCEE4, 0xCEE4, prLV}, // Lo HANGUL SYLLABLE KEO
+ {0xCEE5, 0xCEFF, prLVT}, // Lo [27] HANGUL SYLLABLE KEOG..HANGUL SYLLABLE KEOH
+ {0xCF00, 0xCF00, prLV}, // Lo HANGUL SYLLABLE KE
+ {0xCF01, 0xCF1B, prLVT}, // Lo [27] HANGUL SYLLABLE KEG..HANGUL SYLLABLE KEH
+ {0xCF1C, 0xCF1C, prLV}, // Lo HANGUL SYLLABLE KYEO
+ {0xCF1D, 0xCF37, prLVT}, // Lo [27] HANGUL SYLLABLE KYEOG..HANGUL SYLLABLE KYEOH
+ {0xCF38, 0xCF38, prLV}, // Lo HANGUL SYLLABLE KYE
+ {0xCF39, 0xCF53, prLVT}, // Lo [27] HANGUL SYLLABLE KYEG..HANGUL SYLLABLE KYEH
+ {0xCF54, 0xCF54, prLV}, // Lo HANGUL SYLLABLE KO
+ {0xCF55, 0xCF6F, prLVT}, // Lo [27] HANGUL SYLLABLE KOG..HANGUL SYLLABLE KOH
+ {0xCF70, 0xCF70, prLV}, // Lo HANGUL SYLLABLE KWA
+ {0xCF71, 0xCF8B, prLVT}, // Lo [27] HANGUL SYLLABLE KWAG..HANGUL SYLLABLE KWAH
+ {0xCF8C, 0xCF8C, prLV}, // Lo HANGUL SYLLABLE KWAE
+ {0xCF8D, 0xCFA7, prLVT}, // Lo [27] HANGUL SYLLABLE KWAEG..HANGUL SYLLABLE KWAEH
+ {0xCFA8, 0xCFA8, prLV}, // Lo HANGUL SYLLABLE KOE
+ {0xCFA9, 0xCFC3, prLVT}, // Lo [27] HANGUL SYLLABLE KOEG..HANGUL SYLLABLE KOEH
+ {0xCFC4, 0xCFC4, prLV}, // Lo HANGUL SYLLABLE KYO
+ {0xCFC5, 0xCFDF, prLVT}, // Lo [27] HANGUL SYLLABLE KYOG..HANGUL SYLLABLE KYOH
+ {0xCFE0, 0xCFE0, prLV}, // Lo HANGUL SYLLABLE KU
+ {0xCFE1, 0xCFFB, prLVT}, // Lo [27] HANGUL SYLLABLE KUG..HANGUL SYLLABLE KUH
+ {0xCFFC, 0xCFFC, prLV}, // Lo HANGUL SYLLABLE KWEO
+ {0xCFFD, 0xD017, prLVT}, // Lo [27] HANGUL SYLLABLE KWEOG..HANGUL SYLLABLE KWEOH
+ {0xD018, 0xD018, prLV}, // Lo HANGUL SYLLABLE KWE
+ {0xD019, 0xD033, prLVT}, // Lo [27] HANGUL SYLLABLE KWEG..HANGUL SYLLABLE KWEH
+ {0xD034, 0xD034, prLV}, // Lo HANGUL SYLLABLE KWI
+ {0xD035, 0xD04F, prLVT}, // Lo [27] HANGUL SYLLABLE KWIG..HANGUL SYLLABLE KWIH
+ {0xD050, 0xD050, prLV}, // Lo HANGUL SYLLABLE KYU
+ {0xD051, 0xD06B, prLVT}, // Lo [27] HANGUL SYLLABLE KYUG..HANGUL SYLLABLE KYUH
+ {0xD06C, 0xD06C, prLV}, // Lo HANGUL SYLLABLE KEU
+ {0xD06D, 0xD087, prLVT}, // Lo [27] HANGUL SYLLABLE KEUG..HANGUL SYLLABLE KEUH
+ {0xD088, 0xD088, prLV}, // Lo HANGUL SYLLABLE KYI
+ {0xD089, 0xD0A3, prLVT}, // Lo [27] HANGUL SYLLABLE KYIG..HANGUL SYLLABLE KYIH
+ {0xD0A4, 0xD0A4, prLV}, // Lo HANGUL SYLLABLE KI
+ {0xD0A5, 0xD0BF, prLVT}, // Lo [27] HANGUL SYLLABLE KIG..HANGUL SYLLABLE KIH
+ {0xD0C0, 0xD0C0, prLV}, // Lo HANGUL SYLLABLE TA
+ {0xD0C1, 0xD0DB, prLVT}, // Lo [27] HANGUL SYLLABLE TAG..HANGUL SYLLABLE TAH
+ {0xD0DC, 0xD0DC, prLV}, // Lo HANGUL SYLLABLE TAE
+ {0xD0DD, 0xD0F7, prLVT}, // Lo [27] HANGUL SYLLABLE TAEG..HANGUL SYLLABLE TAEH
+ {0xD0F8, 0xD0F8, prLV}, // Lo HANGUL SYLLABLE TYA
+ {0xD0F9, 0xD113, prLVT}, // Lo [27] HANGUL SYLLABLE TYAG..HANGUL SYLLABLE TYAH
+ {0xD114, 0xD114, prLV}, // Lo HANGUL SYLLABLE TYAE
+ {0xD115, 0xD12F, prLVT}, // Lo [27] HANGUL SYLLABLE TYAEG..HANGUL SYLLABLE TYAEH
+ {0xD130, 0xD130, prLV}, // Lo HANGUL SYLLABLE TEO
+ {0xD131, 0xD14B, prLVT}, // Lo [27] HANGUL SYLLABLE TEOG..HANGUL SYLLABLE TEOH
+ {0xD14C, 0xD14C, prLV}, // Lo HANGUL SYLLABLE TE
+ {0xD14D, 0xD167, prLVT}, // Lo [27] HANGUL SYLLABLE TEG..HANGUL SYLLABLE TEH
+ {0xD168, 0xD168, prLV}, // Lo HANGUL SYLLABLE TYEO
+ {0xD169, 0xD183, prLVT}, // Lo [27] HANGUL SYLLABLE TYEOG..HANGUL SYLLABLE TYEOH
+ {0xD184, 0xD184, prLV}, // Lo HANGUL SYLLABLE TYE
+ {0xD185, 0xD19F, prLVT}, // Lo [27] HANGUL SYLLABLE TYEG..HANGUL SYLLABLE TYEH
+ {0xD1A0, 0xD1A0, prLV}, // Lo HANGUL SYLLABLE TO
+ {0xD1A1, 0xD1BB, prLVT}, // Lo [27] HANGUL SYLLABLE TOG..HANGUL SYLLABLE TOH
+ {0xD1BC, 0xD1BC, prLV}, // Lo HANGUL SYLLABLE TWA
+ {0xD1BD, 0xD1D7, prLVT}, // Lo [27] HANGUL SYLLABLE TWAG..HANGUL SYLLABLE TWAH
+ {0xD1D8, 0xD1D8, prLV}, // Lo HANGUL SYLLABLE TWAE
+ {0xD1D9, 0xD1F3, prLVT}, // Lo [27] HANGUL SYLLABLE TWAEG..HANGUL SYLLABLE TWAEH
+ {0xD1F4, 0xD1F4, prLV}, // Lo HANGUL SYLLABLE TOE
+ {0xD1F5, 0xD20F, prLVT}, // Lo [27] HANGUL SYLLABLE TOEG..HANGUL SYLLABLE TOEH
+ {0xD210, 0xD210, prLV}, // Lo HANGUL SYLLABLE TYO
+ {0xD211, 0xD22B, prLVT}, // Lo [27] HANGUL SYLLABLE TYOG..HANGUL SYLLABLE TYOH
+ {0xD22C, 0xD22C, prLV}, // Lo HANGUL SYLLABLE TU
+ {0xD22D, 0xD247, prLVT}, // Lo [27] HANGUL SYLLABLE TUG..HANGUL SYLLABLE TUH
+ {0xD248, 0xD248, prLV}, // Lo HANGUL SYLLABLE TWEO
+ {0xD249, 0xD263, prLVT}, // Lo [27] HANGUL SYLLABLE TWEOG..HANGUL SYLLABLE TWEOH
+ {0xD264, 0xD264, prLV}, // Lo HANGUL SYLLABLE TWE
+ {0xD265, 0xD27F, prLVT}, // Lo [27] HANGUL SYLLABLE TWEG..HANGUL SYLLABLE TWEH
+ {0xD280, 0xD280, prLV}, // Lo HANGUL SYLLABLE TWI
+ {0xD281, 0xD29B, prLVT}, // Lo [27] HANGUL SYLLABLE TWIG..HANGUL SYLLABLE TWIH
+ {0xD29C, 0xD29C, prLV}, // Lo HANGUL SYLLABLE TYU
+ {0xD29D, 0xD2B7, prLVT}, // Lo [27] HANGUL SYLLABLE TYUG..HANGUL SYLLABLE TYUH
+ {0xD2B8, 0xD2B8, prLV}, // Lo HANGUL SYLLABLE TEU
+ {0xD2B9, 0xD2D3, prLVT}, // Lo [27] HANGUL SYLLABLE TEUG..HANGUL SYLLABLE TEUH
+ {0xD2D4, 0xD2D4, prLV}, // Lo HANGUL SYLLABLE TYI
+ {0xD2D5, 0xD2EF, prLVT}, // Lo [27] HANGUL SYLLABLE TYIG..HANGUL SYLLABLE TYIH
+ {0xD2F0, 0xD2F0, prLV}, // Lo HANGUL SYLLABLE TI
+ {0xD2F1, 0xD30B, prLVT}, // Lo [27] HANGUL SYLLABLE TIG..HANGUL SYLLABLE TIH
+ {0xD30C, 0xD30C, prLV}, // Lo HANGUL SYLLABLE PA
+ {0xD30D, 0xD327, prLVT}, // Lo [27] HANGUL SYLLABLE PAG..HANGUL SYLLABLE PAH
+ {0xD328, 0xD328, prLV}, // Lo HANGUL SYLLABLE PAE
+ {0xD329, 0xD343, prLVT}, // Lo [27] HANGUL SYLLABLE PAEG..HANGUL SYLLABLE PAEH
+ {0xD344, 0xD344, prLV}, // Lo HANGUL SYLLABLE PYA
+ {0xD345, 0xD35F, prLVT}, // Lo [27] HANGUL SYLLABLE PYAG..HANGUL SYLLABLE PYAH
+ {0xD360, 0xD360, prLV}, // Lo HANGUL SYLLABLE PYAE
+ {0xD361, 0xD37B, prLVT}, // Lo [27] HANGUL SYLLABLE PYAEG..HANGUL SYLLABLE PYAEH
+ {0xD37C, 0xD37C, prLV}, // Lo HANGUL SYLLABLE PEO
+ {0xD37D, 0xD397, prLVT}, // Lo [27] HANGUL SYLLABLE PEOG..HANGUL SYLLABLE PEOH
+ {0xD398, 0xD398, prLV}, // Lo HANGUL SYLLABLE PE
+ {0xD399, 0xD3B3, prLVT}, // Lo [27] HANGUL SYLLABLE PEG..HANGUL SYLLABLE PEH
+ {0xD3B4, 0xD3B4, prLV}, // Lo HANGUL SYLLABLE PYEO
+ {0xD3B5, 0xD3CF, prLVT}, // Lo [27] HANGUL SYLLABLE PYEOG..HANGUL SYLLABLE PYEOH
+ {0xD3D0, 0xD3D0, prLV}, // Lo HANGUL SYLLABLE PYE
+ {0xD3D1, 0xD3EB, prLVT}, // Lo [27] HANGUL SYLLABLE PYEG..HANGUL SYLLABLE PYEH
+ {0xD3EC, 0xD3EC, prLV}, // Lo HANGUL SYLLABLE PO
+ {0xD3ED, 0xD407, prLVT}, // Lo [27] HANGUL SYLLABLE POG..HANGUL SYLLABLE POH
+ {0xD408, 0xD408, prLV}, // Lo HANGUL SYLLABLE PWA
+ {0xD409, 0xD423, prLVT}, // Lo [27] HANGUL SYLLABLE PWAG..HANGUL SYLLABLE PWAH
+ {0xD424, 0xD424, prLV}, // Lo HANGUL SYLLABLE PWAE
+ {0xD425, 0xD43F, prLVT}, // Lo [27] HANGUL SYLLABLE PWAEG..HANGUL SYLLABLE PWAEH
+ {0xD440, 0xD440, prLV}, // Lo HANGUL SYLLABLE POE
+ {0xD441, 0xD45B, prLVT}, // Lo [27] HANGUL SYLLABLE POEG..HANGUL SYLLABLE POEH
+ {0xD45C, 0xD45C, prLV}, // Lo HANGUL SYLLABLE PYO
+ {0xD45D, 0xD477, prLVT}, // Lo [27] HANGUL SYLLABLE PYOG..HANGUL SYLLABLE PYOH
+ {0xD478, 0xD478, prLV}, // Lo HANGUL SYLLABLE PU
+ {0xD479, 0xD493, prLVT}, // Lo [27] HANGUL SYLLABLE PUG..HANGUL SYLLABLE PUH
+ {0xD494, 0xD494, prLV}, // Lo HANGUL SYLLABLE PWEO
+ {0xD495, 0xD4AF, prLVT}, // Lo [27] HANGUL SYLLABLE PWEOG..HANGUL SYLLABLE PWEOH
+ {0xD4B0, 0xD4B0, prLV}, // Lo HANGUL SYLLABLE PWE
+ {0xD4B1, 0xD4CB, prLVT}, // Lo [27] HANGUL SYLLABLE PWEG..HANGUL SYLLABLE PWEH
+ {0xD4CC, 0xD4CC, prLV}, // Lo HANGUL SYLLABLE PWI
+ {0xD4CD, 0xD4E7, prLVT}, // Lo [27] HANGUL SYLLABLE PWIG..HANGUL SYLLABLE PWIH
+ {0xD4E8, 0xD4E8, prLV}, // Lo HANGUL SYLLABLE PYU
+ {0xD4E9, 0xD503, prLVT}, // Lo [27] HANGUL SYLLABLE PYUG..HANGUL SYLLABLE PYUH
+ {0xD504, 0xD504, prLV}, // Lo HANGUL SYLLABLE PEU
+ {0xD505, 0xD51F, prLVT}, // Lo [27] HANGUL SYLLABLE PEUG..HANGUL SYLLABLE PEUH
+ {0xD520, 0xD520, prLV}, // Lo HANGUL SYLLABLE PYI
+ {0xD521, 0xD53B, prLVT}, // Lo [27] HANGUL SYLLABLE PYIG..HANGUL SYLLABLE PYIH
+ {0xD53C, 0xD53C, prLV}, // Lo HANGUL SYLLABLE PI
+ {0xD53D, 0xD557, prLVT}, // Lo [27] HANGUL SYLLABLE PIG..HANGUL SYLLABLE PIH
+ {0xD558, 0xD558, prLV}, // Lo HANGUL SYLLABLE HA
+ {0xD559, 0xD573, prLVT}, // Lo [27] HANGUL SYLLABLE HAG..HANGUL SYLLABLE HAH
+ {0xD574, 0xD574, prLV}, // Lo HANGUL SYLLABLE HAE
+ {0xD575, 0xD58F, prLVT}, // Lo [27] HANGUL SYLLABLE HAEG..HANGUL SYLLABLE HAEH
+ {0xD590, 0xD590, prLV}, // Lo HANGUL SYLLABLE HYA
+ {0xD591, 0xD5AB, prLVT}, // Lo [27] HANGUL SYLLABLE HYAG..HANGUL SYLLABLE HYAH
+ {0xD5AC, 0xD5AC, prLV}, // Lo HANGUL SYLLABLE HYAE
+ {0xD5AD, 0xD5C7, prLVT}, // Lo [27] HANGUL SYLLABLE HYAEG..HANGUL SYLLABLE HYAEH
+ {0xD5C8, 0xD5C8, prLV}, // Lo HANGUL SYLLABLE HEO
+ {0xD5C9, 0xD5E3, prLVT}, // Lo [27] HANGUL SYLLABLE HEOG..HANGUL SYLLABLE HEOH
+ {0xD5E4, 0xD5E4, prLV}, // Lo HANGUL SYLLABLE HE
+ {0xD5E5, 0xD5FF, prLVT}, // Lo [27] HANGUL SYLLABLE HEG..HANGUL SYLLABLE HEH
+ {0xD600, 0xD600, prLV}, // Lo HANGUL SYLLABLE HYEO
+ {0xD601, 0xD61B, prLVT}, // Lo [27] HANGUL SYLLABLE HYEOG..HANGUL SYLLABLE HYEOH
+ {0xD61C, 0xD61C, prLV}, // Lo HANGUL SYLLABLE HYE
+ {0xD61D, 0xD637, prLVT}, // Lo [27] HANGUL SYLLABLE HYEG..HANGUL SYLLABLE HYEH
+ {0xD638, 0xD638, prLV}, // Lo HANGUL SYLLABLE HO
+ {0xD639, 0xD653, prLVT}, // Lo [27] HANGUL SYLLABLE HOG..HANGUL SYLLABLE HOH
+ {0xD654, 0xD654, prLV}, // Lo HANGUL SYLLABLE HWA
+ {0xD655, 0xD66F, prLVT}, // Lo [27] HANGUL SYLLABLE HWAG..HANGUL SYLLABLE HWAH
+ {0xD670, 0xD670, prLV}, // Lo HANGUL SYLLABLE HWAE
+ {0xD671, 0xD68B, prLVT}, // Lo [27] HANGUL SYLLABLE HWAEG..HANGUL SYLLABLE HWAEH
+ {0xD68C, 0xD68C, prLV}, // Lo HANGUL SYLLABLE HOE
+ {0xD68D, 0xD6A7, prLVT}, // Lo [27] HANGUL SYLLABLE HOEG..HANGUL SYLLABLE HOEH
+ {0xD6A8, 0xD6A8, prLV}, // Lo HANGUL SYLLABLE HYO
+ {0xD6A9, 0xD6C3, prLVT}, // Lo [27] HANGUL SYLLABLE HYOG..HANGUL SYLLABLE HYOH
+ {0xD6C4, 0xD6C4, prLV}, // Lo HANGUL SYLLABLE HU
+ {0xD6C5, 0xD6DF, prLVT}, // Lo [27] HANGUL SYLLABLE HUG..HANGUL SYLLABLE HUH
+ {0xD6E0, 0xD6E0, prLV}, // Lo HANGUL SYLLABLE HWEO
+ {0xD6E1, 0xD6FB, prLVT}, // Lo [27] HANGUL SYLLABLE HWEOG..HANGUL SYLLABLE HWEOH
+ {0xD6FC, 0xD6FC, prLV}, // Lo HANGUL SYLLABLE HWE
+ {0xD6FD, 0xD717, prLVT}, // Lo [27] HANGUL SYLLABLE HWEG..HANGUL SYLLABLE HWEH
+ {0xD718, 0xD718, prLV}, // Lo HANGUL SYLLABLE HWI
+ {0xD719, 0xD733, prLVT}, // Lo [27] HANGUL SYLLABLE HWIG..HANGUL SYLLABLE HWIH
+ {0xD734, 0xD734, prLV}, // Lo HANGUL SYLLABLE HYU
+ {0xD735, 0xD74F, prLVT}, // Lo [27] HANGUL SYLLABLE HYUG..HANGUL SYLLABLE HYUH
+ {0xD750, 0xD750, prLV}, // Lo HANGUL SYLLABLE HEU
+ {0xD751, 0xD76B, prLVT}, // Lo [27] HANGUL SYLLABLE HEUG..HANGUL SYLLABLE HEUH
+ {0xD76C, 0xD76C, prLV}, // Lo HANGUL SYLLABLE HYI
+ {0xD76D, 0xD787, prLVT}, // Lo [27] HANGUL SYLLABLE HYIG..HANGUL SYLLABLE HYIH
+ {0xD788, 0xD788, prLV}, // Lo HANGUL SYLLABLE HI
+ {0xD789, 0xD7A3, prLVT}, // Lo [27] HANGUL SYLLABLE HIG..HANGUL SYLLABLE HIH
+ {0xD7B0, 0xD7C6, prV}, // Lo [23] HANGUL JUNGSEONG O-YEO..HANGUL JUNGSEONG ARAEA-E
+ {0xD7CB, 0xD7FB, prT}, // Lo [49] HANGUL JONGSEONG NIEUN-RIEUL..HANGUL JONGSEONG PHIEUPH-THIEUTH
+ {0xFB1E, 0xFB1E, prExtend}, // Mn HEBREW POINT JUDEO-SPANISH VARIKA
+ {0xFE00, 0xFE0F, prExtend}, // Mn [16] VARIATION SELECTOR-1..VARIATION SELECTOR-16
+ {0xFE20, 0xFE2F, prExtend}, // Mn [16] COMBINING LIGATURE LEFT HALF..COMBINING CYRILLIC TITLO RIGHT HALF
+ {0xFEFF, 0xFEFF, prControl}, // Cf ZERO WIDTH NO-BREAK SPACE
+ {0xFF9E, 0xFF9F, prExtend}, // Lm [2] HALFWIDTH KATAKANA VOICED SOUND MARK..HALFWIDTH KATAKANA SEMI-VOICED SOUND MARK
+ {0xFFF0, 0xFFF8, prControl}, // Cn [9] ..
+ {0xFFF9, 0xFFFB, prControl}, // Cf [3] INTERLINEAR ANNOTATION ANCHOR..INTERLINEAR ANNOTATION TERMINATOR
+ {0x101FD, 0x101FD, prExtend}, // Mn PHAISTOS DISC SIGN COMBINING OBLIQUE STROKE
+ {0x102E0, 0x102E0, prExtend}, // Mn COPTIC EPACT THOUSANDS MARK
+ {0x10376, 0x1037A, prExtend}, // Mn [5] COMBINING OLD PERMIC LETTER AN..COMBINING OLD PERMIC LETTER SII
+ {0x10A01, 0x10A03, prExtend}, // Mn [3] KHAROSHTHI VOWEL SIGN I..KHAROSHTHI VOWEL SIGN VOCALIC R
+ {0x10A05, 0x10A06, prExtend}, // Mn [2] KHAROSHTHI VOWEL SIGN E..KHAROSHTHI VOWEL SIGN O
+ {0x10A0C, 0x10A0F, prExtend}, // Mn [4] KHAROSHTHI VOWEL LENGTH MARK..KHAROSHTHI SIGN VISARGA
+ {0x10A38, 0x10A3A, prExtend}, // Mn [3] KHAROSHTHI SIGN BAR ABOVE..KHAROSHTHI SIGN DOT BELOW
+ {0x10A3F, 0x10A3F, prExtend}, // Mn KHAROSHTHI VIRAMA
+ {0x10AE5, 0x10AE6, prExtend}, // Mn [2] MANICHAEAN ABBREVIATION MARK ABOVE..MANICHAEAN ABBREVIATION MARK BELOW
+ {0x10D24, 0x10D27, prExtend}, // Mn [4] HANIFI ROHINGYA SIGN HARBAHAY..HANIFI ROHINGYA SIGN TASSI
+ {0x10F46, 0x10F50, prExtend}, // Mn [11] SOGDIAN COMBINING DOT BELOW..SOGDIAN COMBINING STROKE BELOW
+ {0x11000, 0x11000, prSpacingMark}, // Mc BRAHMI SIGN CANDRABINDU
+ {0x11001, 0x11001, prExtend}, // Mn BRAHMI SIGN ANUSVARA
+ {0x11002, 0x11002, prSpacingMark}, // Mc BRAHMI SIGN VISARGA
+ {0x11038, 0x11046, prExtend}, // Mn [15] BRAHMI VOWEL SIGN AA..BRAHMI VIRAMA
+ {0x1107F, 0x11081, prExtend}, // Mn [3] BRAHMI NUMBER JOINER..KAITHI SIGN ANUSVARA
+ {0x11082, 0x11082, prSpacingMark}, // Mc KAITHI SIGN VISARGA
+ {0x110B0, 0x110B2, prSpacingMark}, // Mc [3] KAITHI VOWEL SIGN AA..KAITHI VOWEL SIGN II
+ {0x110B3, 0x110B6, prExtend}, // Mn [4] KAITHI VOWEL SIGN U..KAITHI VOWEL SIGN AI
+ {0x110B7, 0x110B8, prSpacingMark}, // Mc [2] KAITHI VOWEL SIGN O..KAITHI VOWEL SIGN AU
+ {0x110B9, 0x110BA, prExtend}, // Mn [2] KAITHI SIGN VIRAMA..KAITHI SIGN NUKTA
+ {0x110BD, 0x110BD, prPreprend}, // Cf KAITHI NUMBER SIGN
+ {0x110CD, 0x110CD, prPreprend}, // Cf KAITHI NUMBER SIGN ABOVE
+ {0x11100, 0x11102, prExtend}, // Mn [3] CHAKMA SIGN CANDRABINDU..CHAKMA SIGN VISARGA
+ {0x11127, 0x1112B, prExtend}, // Mn [5] CHAKMA VOWEL SIGN A..CHAKMA VOWEL SIGN UU
+ {0x1112C, 0x1112C, prSpacingMark}, // Mc CHAKMA VOWEL SIGN E
+ {0x1112D, 0x11134, prExtend}, // Mn [8] CHAKMA VOWEL SIGN AI..CHAKMA MAAYYAA
+ {0x11145, 0x11146, prSpacingMark}, // Mc [2] CHAKMA VOWEL SIGN AA..CHAKMA VOWEL SIGN EI
+ {0x11173, 0x11173, prExtend}, // Mn MAHAJANI SIGN NUKTA
+ {0x11180, 0x11181, prExtend}, // Mn [2] SHARADA SIGN CANDRABINDU..SHARADA SIGN ANUSVARA
+ {0x11182, 0x11182, prSpacingMark}, // Mc SHARADA SIGN VISARGA
+ {0x111B3, 0x111B5, prSpacingMark}, // Mc [3] SHARADA VOWEL SIGN AA..SHARADA VOWEL SIGN II
+ {0x111B6, 0x111BE, prExtend}, // Mn [9] SHARADA VOWEL SIGN U..SHARADA VOWEL SIGN O
+ {0x111BF, 0x111C0, prSpacingMark}, // Mc [2] SHARADA VOWEL SIGN AU..SHARADA SIGN VIRAMA
+ {0x111C2, 0x111C3, prPreprend}, // Lo [2] SHARADA SIGN JIHVAMULIYA..SHARADA SIGN UPADHMANIYA
+ {0x111C9, 0x111CC, prExtend}, // Mn [4] SHARADA SANDHI MARK..SHARADA EXTRA SHORT VOWEL MARK
+ {0x1122C, 0x1122E, prSpacingMark}, // Mc [3] KHOJKI VOWEL SIGN AA..KHOJKI VOWEL SIGN II
+ {0x1122F, 0x11231, prExtend}, // Mn [3] KHOJKI VOWEL SIGN U..KHOJKI VOWEL SIGN AI
+ {0x11232, 0x11233, prSpacingMark}, // Mc [2] KHOJKI VOWEL SIGN O..KHOJKI VOWEL SIGN AU
+ {0x11234, 0x11234, prExtend}, // Mn KHOJKI SIGN ANUSVARA
+ {0x11235, 0x11235, prSpacingMark}, // Mc KHOJKI SIGN VIRAMA
+ {0x11236, 0x11237, prExtend}, // Mn [2] KHOJKI SIGN NUKTA..KHOJKI SIGN SHADDA
+ {0x1123E, 0x1123E, prExtend}, // Mn KHOJKI SIGN SUKUN
+ {0x112DF, 0x112DF, prExtend}, // Mn KHUDAWADI SIGN ANUSVARA
+ {0x112E0, 0x112E2, prSpacingMark}, // Mc [3] KHUDAWADI VOWEL SIGN AA..KHUDAWADI VOWEL SIGN II
+ {0x112E3, 0x112EA, prExtend}, // Mn [8] KHUDAWADI VOWEL SIGN U..KHUDAWADI SIGN VIRAMA
+ {0x11300, 0x11301, prExtend}, // Mn [2] GRANTHA SIGN COMBINING ANUSVARA ABOVE..GRANTHA SIGN CANDRABINDU
+ {0x11302, 0x11303, prSpacingMark}, // Mc [2] GRANTHA SIGN ANUSVARA..GRANTHA SIGN VISARGA
+ {0x1133B, 0x1133C, prExtend}, // Mn [2] COMBINING BINDU BELOW..GRANTHA SIGN NUKTA
+ {0x1133E, 0x1133E, prExtend}, // Mc GRANTHA VOWEL SIGN AA
+ {0x1133F, 0x1133F, prSpacingMark}, // Mc GRANTHA VOWEL SIGN I
+ {0x11340, 0x11340, prExtend}, // Mn GRANTHA VOWEL SIGN II
+ {0x11341, 0x11344, prSpacingMark}, // Mc [4] GRANTHA VOWEL SIGN U..GRANTHA VOWEL SIGN VOCALIC RR
+ {0x11347, 0x11348, prSpacingMark}, // Mc [2] GRANTHA VOWEL SIGN EE..GRANTHA VOWEL SIGN AI
+ {0x1134B, 0x1134D, prSpacingMark}, // Mc [3] GRANTHA VOWEL SIGN OO..GRANTHA SIGN VIRAMA
+ {0x11357, 0x11357, prExtend}, // Mc GRANTHA AU LENGTH MARK
+ {0x11362, 0x11363, prSpacingMark}, // Mc [2] GRANTHA VOWEL SIGN VOCALIC L..GRANTHA VOWEL SIGN VOCALIC LL
+ {0x11366, 0x1136C, prExtend}, // Mn [7] COMBINING GRANTHA DIGIT ZERO..COMBINING GRANTHA DIGIT SIX
+ {0x11370, 0x11374, prExtend}, // Mn [5] COMBINING GRANTHA LETTER A..COMBINING GRANTHA LETTER PA
+ {0x11435, 0x11437, prSpacingMark}, // Mc [3] NEWA VOWEL SIGN AA..NEWA VOWEL SIGN II
+ {0x11438, 0x1143F, prExtend}, // Mn [8] NEWA VOWEL SIGN U..NEWA VOWEL SIGN AI
+ {0x11440, 0x11441, prSpacingMark}, // Mc [2] NEWA VOWEL SIGN O..NEWA VOWEL SIGN AU
+ {0x11442, 0x11444, prExtend}, // Mn [3] NEWA SIGN VIRAMA..NEWA SIGN ANUSVARA
+ {0x11445, 0x11445, prSpacingMark}, // Mc NEWA SIGN VISARGA
+ {0x11446, 0x11446, prExtend}, // Mn NEWA SIGN NUKTA
+ {0x1145E, 0x1145E, prExtend}, // Mn NEWA SANDHI MARK
+ {0x114B0, 0x114B0, prExtend}, // Mc TIRHUTA VOWEL SIGN AA
+ {0x114B1, 0x114B2, prSpacingMark}, // Mc [2] TIRHUTA VOWEL SIGN I..TIRHUTA VOWEL SIGN II
+ {0x114B3, 0x114B8, prExtend}, // Mn [6] TIRHUTA VOWEL SIGN U..TIRHUTA VOWEL SIGN VOCALIC LL
+ {0x114B9, 0x114B9, prSpacingMark}, // Mc TIRHUTA VOWEL SIGN E
+ {0x114BA, 0x114BA, prExtend}, // Mn TIRHUTA VOWEL SIGN SHORT E
+ {0x114BB, 0x114BC, prSpacingMark}, // Mc [2] TIRHUTA VOWEL SIGN AI..TIRHUTA VOWEL SIGN O
+ {0x114BD, 0x114BD, prExtend}, // Mc TIRHUTA VOWEL SIGN SHORT O
+ {0x114BE, 0x114BE, prSpacingMark}, // Mc TIRHUTA VOWEL SIGN AU
+ {0x114BF, 0x114C0, prExtend}, // Mn [2] TIRHUTA SIGN CANDRABINDU..TIRHUTA SIGN ANUSVARA
+ {0x114C1, 0x114C1, prSpacingMark}, // Mc TIRHUTA SIGN VISARGA
+ {0x114C2, 0x114C3, prExtend}, // Mn [2] TIRHUTA SIGN VIRAMA..TIRHUTA SIGN NUKTA
+ {0x115AF, 0x115AF, prExtend}, // Mc SIDDHAM VOWEL SIGN AA
+ {0x115B0, 0x115B1, prSpacingMark}, // Mc [2] SIDDHAM VOWEL SIGN I..SIDDHAM VOWEL SIGN II
+ {0x115B2, 0x115B5, prExtend}, // Mn [4] SIDDHAM VOWEL SIGN U..SIDDHAM VOWEL SIGN VOCALIC RR
+ {0x115B8, 0x115BB, prSpacingMark}, // Mc [4] SIDDHAM VOWEL SIGN E..SIDDHAM VOWEL SIGN AU
+ {0x115BC, 0x115BD, prExtend}, // Mn [2] SIDDHAM SIGN CANDRABINDU..SIDDHAM SIGN ANUSVARA
+ {0x115BE, 0x115BE, prSpacingMark}, // Mc SIDDHAM SIGN VISARGA
+ {0x115BF, 0x115C0, prExtend}, // Mn [2] SIDDHAM SIGN VIRAMA..SIDDHAM SIGN NUKTA
+ {0x115DC, 0x115DD, prExtend}, // Mn [2] SIDDHAM VOWEL SIGN ALTERNATE U..SIDDHAM VOWEL SIGN ALTERNATE UU
+ {0x11630, 0x11632, prSpacingMark}, // Mc [3] MODI VOWEL SIGN AA..MODI VOWEL SIGN II
+ {0x11633, 0x1163A, prExtend}, // Mn [8] MODI VOWEL SIGN U..MODI VOWEL SIGN AI
+ {0x1163B, 0x1163C, prSpacingMark}, // Mc [2] MODI VOWEL SIGN O..MODI VOWEL SIGN AU
+ {0x1163D, 0x1163D, prExtend}, // Mn MODI SIGN ANUSVARA
+ {0x1163E, 0x1163E, prSpacingMark}, // Mc MODI SIGN VISARGA
+ {0x1163F, 0x11640, prExtend}, // Mn [2] MODI SIGN VIRAMA..MODI SIGN ARDHACANDRA
+ {0x116AB, 0x116AB, prExtend}, // Mn TAKRI SIGN ANUSVARA
+ {0x116AC, 0x116AC, prSpacingMark}, // Mc TAKRI SIGN VISARGA
+ {0x116AD, 0x116AD, prExtend}, // Mn TAKRI VOWEL SIGN AA
+ {0x116AE, 0x116AF, prSpacingMark}, // Mc [2] TAKRI VOWEL SIGN I..TAKRI VOWEL SIGN II
+ {0x116B0, 0x116B5, prExtend}, // Mn [6] TAKRI VOWEL SIGN U..TAKRI VOWEL SIGN AU
+ {0x116B6, 0x116B6, prSpacingMark}, // Mc TAKRI SIGN VIRAMA
+ {0x116B7, 0x116B7, prExtend}, // Mn TAKRI SIGN NUKTA
+ {0x1171D, 0x1171F, prExtend}, // Mn [3] AHOM CONSONANT SIGN MEDIAL LA..AHOM CONSONANT SIGN MEDIAL LIGATING RA
+ {0x11720, 0x11721, prSpacingMark}, // Mc [2] AHOM VOWEL SIGN A..AHOM VOWEL SIGN AA
+ {0x11722, 0x11725, prExtend}, // Mn [4] AHOM VOWEL SIGN I..AHOM VOWEL SIGN UU
+ {0x11726, 0x11726, prSpacingMark}, // Mc AHOM VOWEL SIGN E
+ {0x11727, 0x1172B, prExtend}, // Mn [5] AHOM VOWEL SIGN AW..AHOM SIGN KILLER
+ {0x1182C, 0x1182E, prSpacingMark}, // Mc [3] DOGRA VOWEL SIGN AA..DOGRA VOWEL SIGN II
+ {0x1182F, 0x11837, prExtend}, // Mn [9] DOGRA VOWEL SIGN U..DOGRA SIGN ANUSVARA
+ {0x11838, 0x11838, prSpacingMark}, // Mc DOGRA SIGN VISARGA
+ {0x11839, 0x1183A, prExtend}, // Mn [2] DOGRA SIGN VIRAMA..DOGRA SIGN NUKTA
+ {0x119D1, 0x119D3, prSpacingMark}, // Mc [3] NANDINAGARI VOWEL SIGN AA..NANDINAGARI VOWEL SIGN II
+ {0x119D4, 0x119D7, prExtend}, // Mn [4] NANDINAGARI VOWEL SIGN U..NANDINAGARI VOWEL SIGN VOCALIC RR
+ {0x119DA, 0x119DB, prExtend}, // Mn [2] NANDINAGARI VOWEL SIGN E..NANDINAGARI VOWEL SIGN AI
+ {0x119DC, 0x119DF, prSpacingMark}, // Mc [4] NANDINAGARI VOWEL SIGN O..NANDINAGARI SIGN VISARGA
+ {0x119E0, 0x119E0, prExtend}, // Mn NANDINAGARI SIGN VIRAMA
+ {0x119E4, 0x119E4, prSpacingMark}, // Mc NANDINAGARI VOWEL SIGN PRISHTHAMATRA E
+ {0x11A01, 0x11A0A, prExtend}, // Mn [10] ZANABAZAR SQUARE VOWEL SIGN I..ZANABAZAR SQUARE VOWEL LENGTH MARK
+ {0x11A33, 0x11A38, prExtend}, // Mn [6] ZANABAZAR SQUARE FINAL CONSONANT MARK..ZANABAZAR SQUARE SIGN ANUSVARA
+ {0x11A39, 0x11A39, prSpacingMark}, // Mc ZANABAZAR SQUARE SIGN VISARGA
+ {0x11A3A, 0x11A3A, prPreprend}, // Lo ZANABAZAR SQUARE CLUSTER-INITIAL LETTER RA
+ {0x11A3B, 0x11A3E, prExtend}, // Mn [4] ZANABAZAR SQUARE CLUSTER-FINAL LETTER YA..ZANABAZAR SQUARE CLUSTER-FINAL LETTER VA
+ {0x11A47, 0x11A47, prExtend}, // Mn ZANABAZAR SQUARE SUBJOINER
+ {0x11A51, 0x11A56, prExtend}, // Mn [6] SOYOMBO VOWEL SIGN I..SOYOMBO VOWEL SIGN OE
+ {0x11A57, 0x11A58, prSpacingMark}, // Mc [2] SOYOMBO VOWEL SIGN AI..SOYOMBO VOWEL SIGN AU
+ {0x11A59, 0x11A5B, prExtend}, // Mn [3] SOYOMBO VOWEL SIGN VOCALIC R..SOYOMBO VOWEL LENGTH MARK
+ {0x11A84, 0x11A89, prPreprend}, // Lo [6] SOYOMBO SIGN JIHVAMULIYA..SOYOMBO CLUSTER-INITIAL LETTER SA
+ {0x11A8A, 0x11A96, prExtend}, // Mn [13] SOYOMBO FINAL CONSONANT SIGN G..SOYOMBO SIGN ANUSVARA
+ {0x11A97, 0x11A97, prSpacingMark}, // Mc SOYOMBO SIGN VISARGA
+ {0x11A98, 0x11A99, prExtend}, // Mn [2] SOYOMBO GEMINATION MARK..SOYOMBO SUBJOINER
+ {0x11C2F, 0x11C2F, prSpacingMark}, // Mc BHAIKSUKI VOWEL SIGN AA
+ {0x11C30, 0x11C36, prExtend}, // Mn [7] BHAIKSUKI VOWEL SIGN I..BHAIKSUKI VOWEL SIGN VOCALIC L
+ {0x11C38, 0x11C3D, prExtend}, // Mn [6] BHAIKSUKI VOWEL SIGN E..BHAIKSUKI SIGN ANUSVARA
+ {0x11C3E, 0x11C3E, prSpacingMark}, // Mc BHAIKSUKI SIGN VISARGA
+ {0x11C3F, 0x11C3F, prExtend}, // Mn BHAIKSUKI SIGN VIRAMA
+ {0x11C92, 0x11CA7, prExtend}, // Mn [22] MARCHEN SUBJOINED LETTER KA..MARCHEN SUBJOINED LETTER ZA
+ {0x11CA9, 0x11CA9, prSpacingMark}, // Mc MARCHEN SUBJOINED LETTER YA
+ {0x11CAA, 0x11CB0, prExtend}, // Mn [7] MARCHEN SUBJOINED LETTER RA..MARCHEN VOWEL SIGN AA
+ {0x11CB1, 0x11CB1, prSpacingMark}, // Mc MARCHEN VOWEL SIGN I
+ {0x11CB2, 0x11CB3, prExtend}, // Mn [2] MARCHEN VOWEL SIGN U..MARCHEN VOWEL SIGN E
+ {0x11CB4, 0x11CB4, prSpacingMark}, // Mc MARCHEN VOWEL SIGN O
+ {0x11CB5, 0x11CB6, prExtend}, // Mn [2] MARCHEN SIGN ANUSVARA..MARCHEN SIGN CANDRABINDU
+ {0x11D31, 0x11D36, prExtend}, // Mn [6] MASARAM GONDI VOWEL SIGN AA..MASARAM GONDI VOWEL SIGN VOCALIC R
+ {0x11D3A, 0x11D3A, prExtend}, // Mn MASARAM GONDI VOWEL SIGN E
+ {0x11D3C, 0x11D3D, prExtend}, // Mn [2] MASARAM GONDI VOWEL SIGN AI..MASARAM GONDI VOWEL SIGN O
+ {0x11D3F, 0x11D45, prExtend}, // Mn [7] MASARAM GONDI VOWEL SIGN AU..MASARAM GONDI VIRAMA
+ {0x11D46, 0x11D46, prPreprend}, // Lo MASARAM GONDI REPHA
+ {0x11D47, 0x11D47, prExtend}, // Mn MASARAM GONDI RA-KARA
+ {0x11D8A, 0x11D8E, prSpacingMark}, // Mc [5] GUNJALA GONDI VOWEL SIGN AA..GUNJALA GONDI VOWEL SIGN UU
+ {0x11D90, 0x11D91, prExtend}, // Mn [2] GUNJALA GONDI VOWEL SIGN EE..GUNJALA GONDI VOWEL SIGN AI
+ {0x11D93, 0x11D94, prSpacingMark}, // Mc [2] GUNJALA GONDI VOWEL SIGN OO..GUNJALA GONDI VOWEL SIGN AU
+ {0x11D95, 0x11D95, prExtend}, // Mn GUNJALA GONDI SIGN ANUSVARA
+ {0x11D96, 0x11D96, prSpacingMark}, // Mc GUNJALA GONDI SIGN VISARGA
+ {0x11D97, 0x11D97, prExtend}, // Mn GUNJALA GONDI VIRAMA
+ {0x11EF3, 0x11EF4, prExtend}, // Mn [2] MAKASAR VOWEL SIGN I..MAKASAR VOWEL SIGN U
+ {0x11EF5, 0x11EF6, prSpacingMark}, // Mc [2] MAKASAR VOWEL SIGN E..MAKASAR VOWEL SIGN O
+ {0x13430, 0x13438, prControl}, // Cf [9] EGYPTIAN HIEROGLYPH VERTICAL JOINER..EGYPTIAN HIEROGLYPH END SEGMENT
+ {0x16AF0, 0x16AF4, prExtend}, // Mn [5] BASSA VAH COMBINING HIGH TONE..BASSA VAH COMBINING HIGH-LOW TONE
+ {0x16B30, 0x16B36, prExtend}, // Mn [7] PAHAWH HMONG MARK CIM TUB..PAHAWH HMONG MARK CIM TAUM
+ {0x16F4F, 0x16F4F, prExtend}, // Mn MIAO SIGN CONSONANT MODIFIER BAR
+ {0x16F51, 0x16F87, prSpacingMark}, // Mc [55] MIAO SIGN ASPIRATION..MIAO VOWEL SIGN UI
+ {0x16F8F, 0x16F92, prExtend}, // Mn [4] MIAO TONE RIGHT..MIAO TONE BELOW
+ {0x1BC9D, 0x1BC9E, prExtend}, // Mn [2] DUPLOYAN THICK LETTER SELECTOR..DUPLOYAN DOUBLE MARK
+ {0x1BCA0, 0x1BCA3, prControl}, // Cf [4] SHORTHAND FORMAT LETTER OVERLAP..SHORTHAND FORMAT UP STEP
+ {0x1D165, 0x1D165, prExtend}, // Mc MUSICAL SYMBOL COMBINING STEM
+ {0x1D166, 0x1D166, prSpacingMark}, // Mc MUSICAL SYMBOL COMBINING SPRECHGESANG STEM
+ {0x1D167, 0x1D169, prExtend}, // Mn [3] MUSICAL SYMBOL COMBINING TREMOLO-1..MUSICAL SYMBOL COMBINING TREMOLO-3
+ {0x1D16D, 0x1D16D, prSpacingMark}, // Mc MUSICAL SYMBOL COMBINING AUGMENTATION DOT
+ {0x1D16E, 0x1D172, prExtend}, // Mc [5] MUSICAL SYMBOL COMBINING FLAG-1..MUSICAL SYMBOL COMBINING FLAG-5
+ {0x1D173, 0x1D17A, prControl}, // Cf [8] MUSICAL SYMBOL BEGIN BEAM..MUSICAL SYMBOL END PHRASE
+ {0x1D17B, 0x1D182, prExtend}, // Mn [8] MUSICAL SYMBOL COMBINING ACCENT..MUSICAL SYMBOL COMBINING LOURE
+ {0x1D185, 0x1D18B, prExtend}, // Mn [7] MUSICAL SYMBOL COMBINING DOIT..MUSICAL SYMBOL COMBINING TRIPLE TONGUE
+ {0x1D1AA, 0x1D1AD, prExtend}, // Mn [4] MUSICAL SYMBOL COMBINING DOWN BOW..MUSICAL SYMBOL COMBINING SNAP PIZZICATO
+ {0x1D242, 0x1D244, prExtend}, // Mn [3] COMBINING GREEK MUSICAL TRISEME..COMBINING GREEK MUSICAL PENTASEME
+ {0x1DA00, 0x1DA36, prExtend}, // Mn [55] SIGNWRITING HEAD RIM..SIGNWRITING AIR SUCKING IN
+ {0x1DA3B, 0x1DA6C, prExtend}, // Mn [50] SIGNWRITING MOUTH CLOSED NEUTRAL..SIGNWRITING EXCITEMENT
+ {0x1DA75, 0x1DA75, prExtend}, // Mn SIGNWRITING UPPER BODY TILTING FROM HIP JOINTS
+ {0x1DA84, 0x1DA84, prExtend}, // Mn SIGNWRITING LOCATION HEAD NECK
+ {0x1DA9B, 0x1DA9F, prExtend}, // Mn [5] SIGNWRITING FILL MODIFIER-2..SIGNWRITING FILL MODIFIER-6
+ {0x1DAA1, 0x1DAAF, prExtend}, // Mn [15] SIGNWRITING ROTATION MODIFIER-2..SIGNWRITING ROTATION MODIFIER-16
+ {0x1E000, 0x1E006, prExtend}, // Mn [7] COMBINING GLAGOLITIC LETTER AZU..COMBINING GLAGOLITIC LETTER ZHIVETE
+ {0x1E008, 0x1E018, prExtend}, // Mn [17] COMBINING GLAGOLITIC LETTER ZEMLJA..COMBINING GLAGOLITIC LETTER HERU
+ {0x1E01B, 0x1E021, prExtend}, // Mn [7] COMBINING GLAGOLITIC LETTER SHTA..COMBINING GLAGOLITIC LETTER YATI
+ {0x1E023, 0x1E024, prExtend}, // Mn [2] COMBINING GLAGOLITIC LETTER YU..COMBINING GLAGOLITIC LETTER SMALL YUS
+ {0x1E026, 0x1E02A, prExtend}, // Mn [5] COMBINING GLAGOLITIC LETTER YO..COMBINING GLAGOLITIC LETTER FITA
+ {0x1E130, 0x1E136, prExtend}, // Mn [7] NYIAKENG PUACHUE HMONG TONE-B..NYIAKENG PUACHUE HMONG TONE-D
+ {0x1E2EC, 0x1E2EF, prExtend}, // Mn [4] WANCHO TONE TUP..WANCHO TONE KOINI
+ {0x1E8D0, 0x1E8D6, prExtend}, // Mn [7] MENDE KIKAKUI COMBINING NUMBER TEENS..MENDE KIKAKUI COMBINING NUMBER MILLIONS
+ {0x1E944, 0x1E94A, prExtend}, // Mn [7] ADLAM ALIF LENGTHENER..ADLAM NUKTA
+ {0x1F000, 0x1F02B, prExtendedPictographic}, // 5.1 [44] (🀀..🀫) MAHJONG TILE EAST WIND..MAHJONG TILE BACK
+ {0x1F02C, 0x1F02F, prExtendedPictographic}, // NA [4] (..) ..
+ {0x1F030, 0x1F093, prExtendedPictographic}, // 5.1[100] (🀰..🂓) DOMINO TILE HORIZONTAL BACK..DOMINO TILE VERTICAL-06-06
+ {0x1F094, 0x1F09F, prExtendedPictographic}, // NA [12] (..) ..
+ {0x1F0A0, 0x1F0AE, prExtendedPictographic}, // 6.0 [15] (🂠..🂮) PLAYING CARD BACK..PLAYING CARD KING OF SPADES
+ {0x1F0AF, 0x1F0B0, prExtendedPictographic}, // NA [2] (..) ..
+ {0x1F0B1, 0x1F0BE, prExtendedPictographic}, // 6.0 [14] (🂱..🂾) PLAYING CARD ACE OF HEARTS..PLAYING CARD KING OF HEARTS
+ {0x1F0BF, 0x1F0BF, prExtendedPictographic}, // 7.0 [1] (🂿) PLAYING CARD RED JOKER
+ {0x1F0C0, 0x1F0C0, prExtendedPictographic}, // NA [1] ()
+ {0x1F0C1, 0x1F0CF, prExtendedPictographic}, // 6.0 [15] (🃁..🃏) PLAYING CARD ACE OF DIAMONDS..joker
+ {0x1F0D0, 0x1F0D0, prExtendedPictographic}, // NA [1] ()
+ {0x1F0D1, 0x1F0DF, prExtendedPictographic}, // 6.0 [15] (🃑..🃟) PLAYING CARD ACE OF CLUBS..PLAYING CARD WHITE JOKER
+ {0x1F0E0, 0x1F0F5, prExtendedPictographic}, // 7.0 [22] (🃠..🃵) PLAYING CARD FOOL..PLAYING CARD TRUMP-21
+ {0x1F0F6, 0x1F0FF, prExtendedPictographic}, // NA [10] (..) ..
+ {0x1F10D, 0x1F10F, prExtendedPictographic}, // NA [3] (🄍..🄏) ..
+ {0x1F12F, 0x1F12F, prExtendedPictographic}, // 11.0 [1] (🄯) COPYLEFT SYMBOL
+ {0x1F16C, 0x1F16C, prExtendedPictographic}, // 12.0 [1] (🅬) RAISED MR SIGN
+ {0x1F16D, 0x1F16F, prExtendedPictographic}, // NA [3] (🅭..🅯) ..
+ {0x1F170, 0x1F171, prExtendedPictographic}, // 6.0 [2] (🅰️..🅱️) A button (blood type)..B button (blood type)
+ {0x1F17E, 0x1F17E, prExtendedPictographic}, // 6.0 [1] (🅾️) O button (blood type)
+ {0x1F17F, 0x1F17F, prExtendedPictographic}, // 5.2 [1] (🅿️) P button
+ {0x1F18E, 0x1F18E, prExtendedPictographic}, // 6.0 [1] (🆎) AB button (blood type)
+ {0x1F191, 0x1F19A, prExtendedPictographic}, // 6.0 [10] (🆑..🆚) CL button..VS button
+ {0x1F1AD, 0x1F1E5, prExtendedPictographic}, // NA [57] (🆭..) ..
+ {0x1F1E6, 0x1F1FF, prRegionalIndicator}, // So [26] REGIONAL INDICATOR SYMBOL LETTER A..REGIONAL INDICATOR SYMBOL LETTER Z
+ {0x1F201, 0x1F202, prExtendedPictographic}, // 6.0 [2] (🈁..🈂️) Japanese “here” button..Japanese “service charge” button
+ {0x1F203, 0x1F20F, prExtendedPictographic}, // NA [13] (..) ..
+ {0x1F21A, 0x1F21A, prExtendedPictographic}, // 5.2 [1] (🈚) Japanese “free of charge” button
+ {0x1F22F, 0x1F22F, prExtendedPictographic}, // 5.2 [1] (🈯) Japanese “reserved” button
+ {0x1F232, 0x1F23A, prExtendedPictographic}, // 6.0 [9] (🈲..🈺) Japanese “prohibited” button..Japanese “open for business” button
+ {0x1F23C, 0x1F23F, prExtendedPictographic}, // NA [4] (..) ..
+ {0x1F249, 0x1F24F, prExtendedPictographic}, // NA [7] (..) ..
+ {0x1F250, 0x1F251, prExtendedPictographic}, // 6.0 [2] (🉐..🉑) Japanese “bargain” button..Japanese “acceptable” button
+ {0x1F252, 0x1F25F, prExtendedPictographic}, // NA [14] (..) ..
+ {0x1F260, 0x1F265, prExtendedPictographic}, // 10.0 [6] (🉠..🉥) ROUNDED SYMBOL FOR FU..ROUNDED SYMBOL FOR CAI
+ {0x1F266, 0x1F2FF, prExtendedPictographic}, // NA[154] (..) ..
+ {0x1F300, 0x1F320, prExtendedPictographic}, // 6.0 [33] (🌀..🌠) cyclone..shooting star
+ {0x1F321, 0x1F32C, prExtendedPictographic}, // 7.0 [12] (🌡️..🌬️) thermometer..wind face
+ {0x1F32D, 0x1F32F, prExtendedPictographic}, // 8.0 [3] (🌭..🌯) hot dog..burrito
+ {0x1F330, 0x1F335, prExtendedPictographic}, // 6.0 [6] (🌰..🌵) chestnut..cactus
+ {0x1F336, 0x1F336, prExtendedPictographic}, // 7.0 [1] (🌶️) hot pepper
+ {0x1F337, 0x1F37C, prExtendedPictographic}, // 6.0 [70] (🌷..🍼) tulip..baby bottle
+ {0x1F37D, 0x1F37D, prExtendedPictographic}, // 7.0 [1] (🍽️) fork and knife with plate
+ {0x1F37E, 0x1F37F, prExtendedPictographic}, // 8.0 [2] (🍾..🍿) bottle with popping cork..popcorn
+ {0x1F380, 0x1F393, prExtendedPictographic}, // 6.0 [20] (🎀..🎓) ribbon..graduation cap
+ {0x1F394, 0x1F39F, prExtendedPictographic}, // 7.0 [12] (🎔..🎟️) HEART WITH TIP ON THE LEFT..admission tickets
+ {0x1F3A0, 0x1F3C4, prExtendedPictographic}, // 6.0 [37] (🎠..🏄) carousel horse..person surfing
+ {0x1F3C5, 0x1F3C5, prExtendedPictographic}, // 7.0 [1] (🏅) sports medal
+ {0x1F3C6, 0x1F3CA, prExtendedPictographic}, // 6.0 [5] (🏆..🏊) trophy..person swimming
+ {0x1F3CB, 0x1F3CE, prExtendedPictographic}, // 7.0 [4] (🏋️..🏎️) person lifting weights..racing car
+ {0x1F3CF, 0x1F3D3, prExtendedPictographic}, // 8.0 [5] (🏏..🏓) cricket game..ping pong
+ {0x1F3D4, 0x1F3DF, prExtendedPictographic}, // 7.0 [12] (🏔️..🏟️) snow-capped mountain..stadium
+ {0x1F3E0, 0x1F3F0, prExtendedPictographic}, // 6.0 [17] (🏠..🏰) house..castle
+ {0x1F3F1, 0x1F3F7, prExtendedPictographic}, // 7.0 [7] (🏱..🏷️) WHITE PENNANT..label
+ {0x1F3F8, 0x1F3FA, prExtendedPictographic}, // 8.0 [3] (🏸..🏺) badminton..amphora
+ {0x1F3FB, 0x1F3FF, prExtend}, // Sk [5] EMOJI MODIFIER FITZPATRICK TYPE-1-2..EMOJI MODIFIER FITZPATRICK TYPE-6
+ {0x1F400, 0x1F43E, prExtendedPictographic}, // 6.0 [63] (🐀..🐾) rat..paw prints
+ {0x1F43F, 0x1F43F, prExtendedPictographic}, // 7.0 [1] (🐿️) chipmunk
+ {0x1F440, 0x1F440, prExtendedPictographic}, // 6.0 [1] (👀) eyes
+ {0x1F441, 0x1F441, prExtendedPictographic}, // 7.0 [1] (👁️) eye
+ {0x1F442, 0x1F4F7, prExtendedPictographic}, // 6.0[182] (👂..📷) ear..camera
+ {0x1F4F8, 0x1F4F8, prExtendedPictographic}, // 7.0 [1] (📸) camera with flash
+ {0x1F4F9, 0x1F4FC, prExtendedPictographic}, // 6.0 [4] (📹..📼) video camera..videocassette
+ {0x1F4FD, 0x1F4FE, prExtendedPictographic}, // 7.0 [2] (📽️..📾) film projector..PORTABLE STEREO
+ {0x1F4FF, 0x1F4FF, prExtendedPictographic}, // 8.0 [1] (📿) prayer beads
+ {0x1F500, 0x1F53D, prExtendedPictographic}, // 6.0 [62] (🔀..🔽) shuffle tracks button..downwards button
+ {0x1F546, 0x1F54A, prExtendedPictographic}, // 7.0 [5] (🕆..🕊️) WHITE LATIN CROSS..dove
+ {0x1F54B, 0x1F54F, prExtendedPictographic}, // 8.0 [5] (🕋..🕏) kaaba..BOWL OF HYGIEIA
+ {0x1F550, 0x1F567, prExtendedPictographic}, // 6.0 [24] (🕐..🕧) one o’clock..twelve-thirty
+ {0x1F568, 0x1F579, prExtendedPictographic}, // 7.0 [18] (🕨..🕹️) RIGHT SPEAKER..joystick
+ {0x1F57A, 0x1F57A, prExtendedPictographic}, // 9.0 [1] (🕺) man dancing
+ {0x1F57B, 0x1F5A3, prExtendedPictographic}, // 7.0 [41] (🕻..🖣) LEFT HAND TELEPHONE RECEIVER..BLACK DOWN POINTING BACKHAND INDEX
+ {0x1F5A4, 0x1F5A4, prExtendedPictographic}, // 9.0 [1] (🖤) black heart
+ {0x1F5A5, 0x1F5FA, prExtendedPictographic}, // 7.0 [86] (🖥️..🗺️) desktop computer..world map
+ {0x1F5FB, 0x1F5FF, prExtendedPictographic}, // 6.0 [5] (🗻..🗿) mount fuji..moai
+ {0x1F600, 0x1F600, prExtendedPictographic}, // 6.1 [1] (😀) grinning face
+ {0x1F601, 0x1F610, prExtendedPictographic}, // 6.0 [16] (😁..😐) beaming face with smiling eyes..neutral face
+ {0x1F611, 0x1F611, prExtendedPictographic}, // 6.1 [1] (😑) expressionless face
+ {0x1F612, 0x1F614, prExtendedPictographic}, // 6.0 [3] (😒..😔) unamused face..pensive face
+ {0x1F615, 0x1F615, prExtendedPictographic}, // 6.1 [1] (😕) confused face
+ {0x1F616, 0x1F616, prExtendedPictographic}, // 6.0 [1] (😖) confounded face
+ {0x1F617, 0x1F617, prExtendedPictographic}, // 6.1 [1] (😗) kissing face
+ {0x1F618, 0x1F618, prExtendedPictographic}, // 6.0 [1] (😘) face blowing a kiss
+ {0x1F619, 0x1F619, prExtendedPictographic}, // 6.1 [1] (😙) kissing face with smiling eyes
+ {0x1F61A, 0x1F61A, prExtendedPictographic}, // 6.0 [1] (😚) kissing face with closed eyes
+ {0x1F61B, 0x1F61B, prExtendedPictographic}, // 6.1 [1] (😛) face with tongue
+ {0x1F61C, 0x1F61E, prExtendedPictographic}, // 6.0 [3] (😜..😞) winking face with tongue..disappointed face
+ {0x1F61F, 0x1F61F, prExtendedPictographic}, // 6.1 [1] (😟) worried face
+ {0x1F620, 0x1F625, prExtendedPictographic}, // 6.0 [6] (😠..😥) angry face..sad but relieved face
+ {0x1F626, 0x1F627, prExtendedPictographic}, // 6.1 [2] (😦..😧) frowning face with open mouth..anguished face
+ {0x1F628, 0x1F62B, prExtendedPictographic}, // 6.0 [4] (😨..😫) fearful face..tired face
+ {0x1F62C, 0x1F62C, prExtendedPictographic}, // 6.1 [1] (😬) grimacing face
+ {0x1F62D, 0x1F62D, prExtendedPictographic}, // 6.0 [1] (😭) loudly crying face
+ {0x1F62E, 0x1F62F, prExtendedPictographic}, // 6.1 [2] (😮..😯) face with open mouth..hushed face
+ {0x1F630, 0x1F633, prExtendedPictographic}, // 6.0 [4] (😰..😳) anxious face with sweat..flushed face
+ {0x1F634, 0x1F634, prExtendedPictographic}, // 6.1 [1] (😴) sleeping face
+ {0x1F635, 0x1F640, prExtendedPictographic}, // 6.0 [12] (😵..🙀) dizzy face..weary cat
+ {0x1F641, 0x1F642, prExtendedPictographic}, // 7.0 [2] (🙁..🙂) slightly frowning face..slightly smiling face
+ {0x1F643, 0x1F644, prExtendedPictographic}, // 8.0 [2] (🙃..🙄) upside-down face..face with rolling eyes
+ {0x1F645, 0x1F64F, prExtendedPictographic}, // 6.0 [11] (🙅..🙏) person gesturing NO..folded hands
+ {0x1F680, 0x1F6C5, prExtendedPictographic}, // 6.0 [70] (🚀..🛅) rocket..left luggage
+ {0x1F6C6, 0x1F6CF, prExtendedPictographic}, // 7.0 [10] (🛆..🛏️) TRIANGLE WITH ROUNDED CORNERS..bed
+ {0x1F6D0, 0x1F6D0, prExtendedPictographic}, // 8.0 [1] (🛐) place of worship
+ {0x1F6D1, 0x1F6D2, prExtendedPictographic}, // 9.0 [2] (🛑..🛒) stop sign..shopping cart
+ {0x1F6D3, 0x1F6D4, prExtendedPictographic}, // 10.0 [2] (🛓..🛔) STUPA..PAGODA
+ {0x1F6D5, 0x1F6D5, prExtendedPictographic}, // 12.0 [1] (🛕) hindu temple
+ {0x1F6D6, 0x1F6DF, prExtendedPictographic}, // NA [10] (🛖..🛟) ..
+ {0x1F6E0, 0x1F6EC, prExtendedPictographic}, // 7.0 [13] (🛠️..🛬) hammer and wrench..airplane arrival
+ {0x1F6ED, 0x1F6EF, prExtendedPictographic}, // NA [3] (..) ..
+ {0x1F6F0, 0x1F6F3, prExtendedPictographic}, // 7.0 [4] (🛰️..🛳️) satellite..passenger ship
+ {0x1F6F4, 0x1F6F6, prExtendedPictographic}, // 9.0 [3] (🛴..🛶) kick scooter..canoe
+ {0x1F6F7, 0x1F6F8, prExtendedPictographic}, // 10.0 [2] (🛷..🛸) sled..flying saucer
+ {0x1F6F9, 0x1F6F9, prExtendedPictographic}, // 11.0 [1] (🛹) skateboard
+ {0x1F6FA, 0x1F6FA, prExtendedPictographic}, // 12.0 [1] (🛺) auto rickshaw
+ {0x1F6FB, 0x1F6FF, prExtendedPictographic}, // NA [5] (🛻..) ..
+ {0x1F774, 0x1F77F, prExtendedPictographic}, // NA [12] (🝴..🝿) ..
+ {0x1F7D5, 0x1F7D8, prExtendedPictographic}, // 11.0 [4] (🟕..🟘) CIRCLED TRIANGLE..NEGATIVE CIRCLED SQUARE
+ {0x1F7D9, 0x1F7DF, prExtendedPictographic}, // NA [7] (🟙..) ..
+ {0x1F7E0, 0x1F7EB, prExtendedPictographic}, // 12.0 [12] (🟠..🟫) orange circle..brown square
+ {0x1F7EC, 0x1F7FF, prExtendedPictographic}, // NA [20] (..) ..
+ {0x1F80C, 0x1F80F, prExtendedPictographic}, // NA [4] (..) ..
+ {0x1F848, 0x1F84F, prExtendedPictographic}, // NA [8] (..) ..
+ {0x1F85A, 0x1F85F, prExtendedPictographic}, // NA [6] (..) ..
+ {0x1F888, 0x1F88F, prExtendedPictographic}, // NA [8] (..) ..
+ {0x1F8AE, 0x1F8FF, prExtendedPictographic}, // NA [82] (..) ..
+ {0x1F90C, 0x1F90C, prExtendedPictographic}, // NA [1] (🤌)
+ {0x1F90D, 0x1F90F, prExtendedPictographic}, // 12.0 [3] (🤍..🤏) white heart..pinching hand
+ {0x1F910, 0x1F918, prExtendedPictographic}, // 8.0 [9] (🤐..🤘) zipper-mouth face..sign of the horns
+ {0x1F919, 0x1F91E, prExtendedPictographic}, // 9.0 [6] (🤙..🤞) call me hand..crossed fingers
+ {0x1F91F, 0x1F91F, prExtendedPictographic}, // 10.0 [1] (🤟) love-you gesture
+ {0x1F920, 0x1F927, prExtendedPictographic}, // 9.0 [8] (🤠..🤧) cowboy hat face..sneezing face
+ {0x1F928, 0x1F92F, prExtendedPictographic}, // 10.0 [8] (🤨..🤯) face with raised eyebrow..exploding head
+ {0x1F930, 0x1F930, prExtendedPictographic}, // 9.0 [1] (🤰) pregnant woman
+ {0x1F931, 0x1F932, prExtendedPictographic}, // 10.0 [2] (🤱..🤲) breast-feeding..palms up together
+ {0x1F933, 0x1F93A, prExtendedPictographic}, // 9.0 [8] (🤳..🤺) selfie..person fencing
+ {0x1F93C, 0x1F93E, prExtendedPictographic}, // 9.0 [3] (🤼..🤾) people wrestling..person playing handball
+ {0x1F93F, 0x1F93F, prExtendedPictographic}, // 12.0 [1] (🤿) diving mask
+ {0x1F940, 0x1F945, prExtendedPictographic}, // 9.0 [6] (🥀..🥅) wilted flower..goal net
+ {0x1F947, 0x1F94B, prExtendedPictographic}, // 9.0 [5] (🥇..🥋) 1st place medal..martial arts uniform
+ {0x1F94C, 0x1F94C, prExtendedPictographic}, // 10.0 [1] (🥌) curling stone
+ {0x1F94D, 0x1F94F, prExtendedPictographic}, // 11.0 [3] (🥍..🥏) lacrosse..flying disc
+ {0x1F950, 0x1F95E, prExtendedPictographic}, // 9.0 [15] (🥐..🥞) croissant..pancakes
+ {0x1F95F, 0x1F96B, prExtendedPictographic}, // 10.0 [13] (🥟..🥫) dumpling..canned food
+ {0x1F96C, 0x1F970, prExtendedPictographic}, // 11.0 [5] (🥬..🥰) leafy green..smiling face with hearts
+ {0x1F971, 0x1F971, prExtendedPictographic}, // 12.0 [1] (🥱) yawning face
+ {0x1F972, 0x1F972, prExtendedPictographic}, // NA [1] (🥲)
+ {0x1F973, 0x1F976, prExtendedPictographic}, // 11.0 [4] (🥳..🥶) partying face..cold face
+ {0x1F977, 0x1F979, prExtendedPictographic}, // NA [3] (🥷..🥹) ..
+ {0x1F97A, 0x1F97A, prExtendedPictographic}, // 11.0 [1] (🥺) pleading face
+ {0x1F97B, 0x1F97B, prExtendedPictographic}, // 12.0 [1] (🥻) sari
+ {0x1F97C, 0x1F97F, prExtendedPictographic}, // 11.0 [4] (🥼..🥿) lab coat..flat shoe
+ {0x1F980, 0x1F984, prExtendedPictographic}, // 8.0 [5] (🦀..🦄) crab..unicorn
+ {0x1F985, 0x1F991, prExtendedPictographic}, // 9.0 [13] (🦅..🦑) eagle..squid
+ {0x1F992, 0x1F997, prExtendedPictographic}, // 10.0 [6] (🦒..🦗) giraffe..cricket
+ {0x1F998, 0x1F9A2, prExtendedPictographic}, // 11.0 [11] (🦘..🦢) kangaroo..swan
+ {0x1F9A3, 0x1F9A4, prExtendedPictographic}, // NA [2] (🦣..🦤) ..
+ {0x1F9A5, 0x1F9AA, prExtendedPictographic}, // 12.0 [6] (🦥..🦪) sloth..oyster
+ {0x1F9AB, 0x1F9AD, prExtendedPictographic}, // NA [3] (🦫..🦭) ..
+ {0x1F9AE, 0x1F9AF, prExtendedPictographic}, // 12.0 [2] (🦮..🦯) guide dog..probing cane
+ {0x1F9B0, 0x1F9B9, prExtendedPictographic}, // 11.0 [10] (🦰..🦹) red hair..supervillain
+ {0x1F9BA, 0x1F9BF, prExtendedPictographic}, // 12.0 [6] (🦺..🦿) safety vest..mechanical leg
+ {0x1F9C0, 0x1F9C0, prExtendedPictographic}, // 8.0 [1] (🧀) cheese wedge
+ {0x1F9C1, 0x1F9C2, prExtendedPictographic}, // 11.0 [2] (🧁..🧂) cupcake..salt
+ {0x1F9C3, 0x1F9CA, prExtendedPictographic}, // 12.0 [8] (🧃..🧊) beverage box..ice cube
+ {0x1F9CB, 0x1F9CC, prExtendedPictographic}, // NA [2] (🧋..🧌) ..
+ {0x1F9CD, 0x1F9CF, prExtendedPictographic}, // 12.0 [3] (🧍..🧏) person standing..deaf person
+ {0x1F9D0, 0x1F9E6, prExtendedPictographic}, // 10.0 [23] (🧐..🧦) face with monocle..socks
+ {0x1F9E7, 0x1F9FF, prExtendedPictographic}, // 11.0 [25] (🧧..🧿) red envelope..nazar amulet
+ {0x1FA00, 0x1FA53, prExtendedPictographic}, // 12.0 [84] (🨀..🩓) NEUTRAL CHESS KING..BLACK CHESS KNIGHT-BISHOP
+ {0x1FA54, 0x1FA5F, prExtendedPictographic}, // NA [12] (..) ..
+ {0x1FA60, 0x1FA6D, prExtendedPictographic}, // 11.0 [14] (🩠..🩭) XIANGQI RED GENERAL..XIANGQI BLACK SOLDIER
+ {0x1FA6E, 0x1FA6F, prExtendedPictographic}, // NA [2] (..) ..
+ {0x1FA70, 0x1FA73, prExtendedPictographic}, // 12.0 [4] (🩰..🩳) ballet shoes..shorts
+ {0x1FA74, 0x1FA77, prExtendedPictographic}, // NA [4] (🩴..🩷) ..
+ {0x1FA78, 0x1FA7A, prExtendedPictographic}, // 12.0 [3] (🩸..🩺) drop of blood..stethoscope
+ {0x1FA7B, 0x1FA7F, prExtendedPictographic}, // NA [5] (🩻..) ..
+ {0x1FA80, 0x1FA82, prExtendedPictographic}, // 12.0 [3] (🪀..🪂) yo-yo..parachute
+ {0x1FA83, 0x1FA8F, prExtendedPictographic}, // NA [13] (🪃..) ..
+ {0x1FA90, 0x1FA95, prExtendedPictographic}, // 12.0 [6] (🪐..🪕) ringed planet..banjo
+ {0x1FA96, 0x1FFFD, prExtendedPictographic}, // NA[1384] (🪖..) ..
+ {0xE0000, 0xE0000, prControl}, // Cn
+ {0xE0001, 0xE0001, prControl}, // Cf LANGUAGE TAG
+ {0xE0002, 0xE001F, prControl}, // Cn [30] ..
+ {0xE0020, 0xE007F, prExtend}, // Cf [96] TAG SPACE..CANCEL TAG
+ {0xE0080, 0xE00FF, prControl}, // Cn [128] ..
+ {0xE0100, 0xE01EF, prExtend}, // Mn [240] VARIATION SELECTOR-17..VARIATION SELECTOR-256
+ {0xE01F0, 0xE0FFF, prControl}, // Cn [3600] ..
+}
+
+// property returns the Unicode property value (see constants above) of the
+// given code point.
+func property(r rune) int {
+ // Run a binary search.
+ from := 0
+ to := len(codePoints)
+ for to > from {
+ middle := (from + to) / 2
+ cpRange := codePoints[middle]
+ if int(r) < cpRange[0] {
+ to = middle
+ continue
+ }
+ if int(r) > cpRange[1] {
+ from = middle + 1
+ continue
+ }
+ return cpRange[2]
+ }
+ return prAny
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 3a896322ec..57d3229e50 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -75,7 +75,7 @@ github.com/containerd/stargz-snapshotter/estargz/errorutil
# github.com/cyphar/filepath-securejoin v0.2.4
## explicit; go 1.13
github.com/cyphar/filepath-securejoin
-# github.com/davecgh/go-spew v1.1.1
+# github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
## explicit
github.com/davecgh/go-spew/spew
# github.com/docker/cli v27.3.1+incompatible
@@ -319,6 +319,10 @@ github.com/inconshreveable/mousetrap
# github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99
## explicit
github.com/jbenet/go-context/io
+# github.com/jedib0t/go-pretty/v6 v6.6.1
+## explicit; go 1.17
+github.com/jedib0t/go-pretty/v6/table
+github.com/jedib0t/go-pretty/v6/text
# github.com/josharian/intern v1.0.0
## explicit; go 1.5
github.com/josharian/intern
@@ -342,6 +346,9 @@ github.com/klauspost/compress/zstd/internal/xxhash
github.com/mailru/easyjson/buffer
github.com/mailru/easyjson/jlexer
github.com/mailru/easyjson/jwriter
+# github.com/mattn/go-runewidth v0.0.15
+## explicit; go 1.9
+github.com/mattn/go-runewidth
# github.com/mitchellh/go-homedir v1.1.0
## explicit
github.com/mitchellh/go-homedir
@@ -405,6 +412,8 @@ github.com/pjbgf/sha1cd/ubc
# github.com/pkg/errors v0.9.1
## explicit
github.com/pkg/errors
+# github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2
+## explicit
# github.com/prometheus/client_golang v1.20.5
## explicit; go 1.20
github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil
@@ -430,6 +439,9 @@ github.com/prometheus/procfs/internal/util
github.com/prometheus/statsd_exporter/pkg/level
github.com/prometheus/statsd_exporter/pkg/mapper
github.com/prometheus/statsd_exporter/pkg/mapper/fsm
+# github.com/rivo/uniseg v0.2.0
+## explicit; go 1.12
+github.com/rivo/uniseg
# github.com/rogpeppe/go-internal v1.12.0
## explicit; go 1.20
# github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3