From 8afa9c25c004c5033d9ecb03f23509e83fda09df Mon Sep 17 00:00:00 2001 From: Matthias Diester Date: Mon, 28 Oct 2024 16:52:09 +0100 Subject: [PATCH] Add file listing for bundle unpack and Git clone Add flag to enable to print a file listing when the unpacking or cloning of a source bundle is complete. Signed-off-by: Matthias Diester --- cmd/bundle/main.go | 11 + cmd/bundle/main_test.go | 16 +- cmd/git/main.go | 13 + cmd/git/main_test.go | 17 + go.mod | 6 +- go.sum | 12 +- pkg/util/filelisting.go | 115 ++ pkg/util/util.go | 6 + .../github.com/jedib0t/go-pretty/v6/LICENSE | 21 + .../jedib0t/go-pretty/v6/table/README.md | 452 +++++ .../jedib0t/go-pretty/v6/table/config.go | 103 + .../jedib0t/go-pretty/v6/table/pager.go | 70 + .../go-pretty/v6/table/pager_options.go | 11 + .../jedib0t/go-pretty/v6/table/render.go | 428 +++++ .../jedib0t/go-pretty/v6/table/render_csv.go | 82 + .../jedib0t/go-pretty/v6/table/render_hint.go | 39 + .../jedib0t/go-pretty/v6/table/render_html.go | 268 +++ .../jedib0t/go-pretty/v6/table/render_init.go | 330 ++++ .../go-pretty/v6/table/render_markdown.go | 109 ++ .../jedib0t/go-pretty/v6/table/render_tsv.go | 73 + .../jedib0t/go-pretty/v6/table/sort.go | 228 +++ .../jedib0t/go-pretty/v6/table/style.go | 918 +++++++++ .../jedib0t/go-pretty/v6/table/table.go | 850 +++++++++ .../jedib0t/go-pretty/v6/table/util.go | 69 + .../jedib0t/go-pretty/v6/table/writer.go | 48 + .../jedib0t/go-pretty/v6/text/README.md | 8 + .../jedib0t/go-pretty/v6/text/align.go | 149 ++ .../jedib0t/go-pretty/v6/text/ansi.go | 57 + .../jedib0t/go-pretty/v6/text/ansi_unix.go | 8 + .../jedib0t/go-pretty/v6/text/ansi_windows.go | 30 + .../jedib0t/go-pretty/v6/text/color.go | 179 ++ .../jedib0t/go-pretty/v6/text/color_html.go | 46 + .../jedib0t/go-pretty/v6/text/cursor.go | 39 + .../jedib0t/go-pretty/v6/text/direction.go | 24 + .../go-pretty/v6/text/escape_seq_parser.go | 201 ++ .../jedib0t/go-pretty/v6/text/filter.go | 12 + .../jedib0t/go-pretty/v6/text/format.go | 100 + .../jedib0t/go-pretty/v6/text/hyperlink.go | 14 + .../jedib0t/go-pretty/v6/text/string.go | 274 +++ .../jedib0t/go-pretty/v6/text/transformer.go | 228 +++ .../jedib0t/go-pretty/v6/text/valign.go | 67 + .../jedib0t/go-pretty/v6/text/wrap.go | 235 +++ vendor/github.com/mattn/go-runewidth/LICENSE | 21 + .../github.com/mattn/go-runewidth/README.md | 27 + .../mattn/go-runewidth/runewidth.go | 358 ++++ .../mattn/go-runewidth/runewidth_appengine.go | 9 + .../mattn/go-runewidth/runewidth_js.go | 9 + .../mattn/go-runewidth/runewidth_posix.go | 81 + .../mattn/go-runewidth/runewidth_table.go | 439 +++++ .../mattn/go-runewidth/runewidth_windows.go | 28 + vendor/github.com/rivo/uniseg/LICENSE.txt | 21 + vendor/github.com/rivo/uniseg/README.md | 62 + vendor/github.com/rivo/uniseg/doc.go | 8 + vendor/github.com/rivo/uniseg/grapheme.go | 268 +++ vendor/github.com/rivo/uniseg/properties.go | 1658 +++++++++++++++++ vendor/modules.txt | 14 +- 56 files changed, 8962 insertions(+), 7 deletions(-) create mode 100644 pkg/util/filelisting.go create mode 100644 pkg/util/util.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/LICENSE create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/README.md create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/config.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/pager.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/pager_options.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/render.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/render_csv.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/render_hint.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/render_html.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/render_init.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/render_markdown.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/render_tsv.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/sort.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/style.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/table.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/util.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/table/writer.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/README.md create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/align.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/ansi.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/ansi_unix.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/ansi_windows.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/color.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/color_html.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/cursor.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/direction.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/escape_seq_parser.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/filter.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/format.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/hyperlink.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/string.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/transformer.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/valign.go create mode 100644 vendor/github.com/jedib0t/go-pretty/v6/text/wrap.go create mode 100644 vendor/github.com/mattn/go-runewidth/LICENSE create mode 100644 vendor/github.com/mattn/go-runewidth/README.md create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_appengine.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_js.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_posix.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_table.go create mode 100644 vendor/github.com/mattn/go-runewidth/runewidth_windows.go create mode 100644 vendor/github.com/rivo/uniseg/LICENSE.txt create mode 100644 vendor/github.com/rivo/uniseg/README.md create mode 100644 vendor/github.com/rivo/uniseg/doc.go create mode 100644 vendor/github.com/rivo/uniseg/grapheme.go create mode 100644 vendor/github.com/rivo/uniseg/properties.go 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 19aa243ac3..5b05028d2e 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 9b17725f72..3ae5f93aee 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: + +Colored Table + +### 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 NameLast NameSalary 
1AryaStark3000 
20JonSnow2000You know nothing, Jon Snow!
300TyrionLannister5000 
  Total10000 
+``` + +### ... 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 NameLast NameSalary 
1AryaStark3000 
20JonSnow2000You know nothing, Jon Snow!
300TyrionLannister5000 
  Total10000 
+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("\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(" \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 786a8e5d26..0e91ed151f 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