mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-10 05:58:59 -06:00
build(deps): bump github.com/olekukonko/tablewriter from 1.1.1 to 1.1.2
Bumps [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) from 1.1.1 to 1.1.2. - [Commits](https://github.com/olekukonko/tablewriter/compare/v1.1.1...v1.1.2) --- updated-dependencies: - dependency-name: github.com/olekukonko/tablewriter dependency-version: 1.1.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com>
This commit is contained in:
committed by
Ralf Haferkamp
parent
e9bd5c4058
commit
063217c3e6
8
go.mod
8
go.mod
@@ -57,7 +57,7 @@ require (
|
||||
github.com/nats-io/nats-server/v2 v2.12.3
|
||||
github.com/nats-io/nats.go v1.47.0
|
||||
github.com/oklog/run v1.2.0
|
||||
github.com/olekukonko/tablewriter v1.1.1
|
||||
github.com/olekukonko/tablewriter v1.1.2
|
||||
github.com/onsi/ginkgo v1.16.5
|
||||
github.com/onsi/ginkgo/v2 v2.27.2
|
||||
github.com/onsi/gomega v1.39.0
|
||||
@@ -165,9 +165,9 @@ require (
|
||||
github.com/ceph/go-ceph v0.37.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.3.1 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.6.0 // indirect
|
||||
github.com/clipperhouse/stringish v0.1.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
@@ -312,7 +312,7 @@ require (
|
||||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 // indirect
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/ll v0.1.2 // indirect
|
||||
github.com/olekukonko/ll v0.1.3 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/opentracing/opentracing-go v1.2.0 // indirect
|
||||
|
||||
16
go.sum
16
go.sum
@@ -223,12 +223,12 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/clipperhouse/displaywidth v0.3.1 h1:k07iN9gD32177o1y4O1jQMzbLdCrsGJh+blirVYybsk=
|
||||
github.com/clipperhouse/displaywidth v0.3.1/go.mod h1:tgLJKKyaDOCadywag3agw4snxS5kYEuYR6Y9+qWDDYM=
|
||||
github.com/clipperhouse/displaywidth v0.6.0 h1:k32vueaksef9WIKCNcoqRNyKbyvkvkysNYnAWz2fN4s=
|
||||
github.com/clipperhouse/displaywidth v0.6.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
|
||||
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
|
||||
github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4=
|
||||
github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudflare/cloudflare-go v0.14.0/go.mod h1:EnwdgGMaFOruiPZRFSgn+TsQ3hQ7C/YWzIGLeu5c304=
|
||||
@@ -940,11 +940,11 @@ github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6 h1:zrbMGy9YXpIeTnGj
|
||||
github.com/olekukonko/cat v0.0.0-20250911104152-50322a0618f6/go.mod h1:rEKTHC9roVVicUIfZK7DYrdIoM0EOr8mK1Hj5s3JjH0=
|
||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.1.2 h1:lkg/k/9mlsy0SxO5aC+WEpbdT5K83ddnNhAepz7TQc0=
|
||||
github.com/olekukonko/ll v0.1.2/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
|
||||
github.com/olekukonko/ll v0.1.3 h1:sV2jrhQGq5B3W0nENUISCR6azIPf7UBUpVq0x/y70Fg=
|
||||
github.com/olekukonko/ll v0.1.3/go.mod h1:b52bVQRRPObe+yyBl0TxNfhesL0nedD4Cht0/zx55Ew=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/olekukonko/tablewriter v1.1.1 h1:b3reP6GCfrHwmKkYwNRFh2rxidGHcT6cgxj/sHiDDx0=
|
||||
github.com/olekukonko/tablewriter v1.1.1/go.mod h1:De/bIcTF+gpBDB3Alv3fEsZA+9unTsSzAg/ZGADCtn4=
|
||||
github.com/olekukonko/tablewriter v1.1.2 h1:L2kI1Y5tZBct/O/TyZK1zIE9GlBj/TVs+AY5tZDCDSc=
|
||||
github.com/olekukonko/tablewriter v1.1.2/go.mod h1:z7SYPugVqGVavWoA2sGsFIoOVNmEHxUAAMrhXONtfkg=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
|
||||
60
vendor/github.com/clipperhouse/displaywidth/CHANGELOG.md
generated
vendored
Normal file
60
vendor/github.com/clipperhouse/displaywidth/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,60 @@
|
||||
# Changelog
|
||||
|
||||
## [0.6.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.5.0...v0.6.0)
|
||||
|
||||
### Added
|
||||
- New `StringGraphemes` and `BytesGraphemes` methods, for iterating over the
|
||||
widths of grapheme clusters.
|
||||
|
||||
### Changed
|
||||
- Added ASCII fast paths
|
||||
|
||||
## [0.5.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.4.1...v0.5.0)
|
||||
|
||||
### Added
|
||||
- Unicode 16 support
|
||||
- Improved emoji presentation handling per Unicode TR51
|
||||
|
||||
### Changed
|
||||
- Corrected VS15 (U+FE0E) handling: now preserves base character width (no-op) per Unicode TR51
|
||||
- Performance optimizations: reduced property lookups
|
||||
|
||||
### Fixed
|
||||
- VS15 variation selector now correctly preserves base character width instead of forcing width 1
|
||||
|
||||
## [0.4.1]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.4.0...v0.4.1)
|
||||
|
||||
### Changed
|
||||
- Updated uax29 dependency
|
||||
- Improved flag handling
|
||||
|
||||
## [0.4.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.3.1...v0.4.0)
|
||||
|
||||
### Added
|
||||
- Support for variation selectors (VS15, VS16) and regional indicator pairs (flags)
|
||||
|
||||
## [0.3.1]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.3.0...v0.3.1)
|
||||
|
||||
### Added
|
||||
- Fuzz testing support
|
||||
|
||||
### Changed
|
||||
- Updated stringish dependency
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.2.0...v0.3.0)
|
||||
|
||||
### Changed
|
||||
- Dropped compatibility with go-runewidth
|
||||
- Trie implementation cleanup
|
||||
110
vendor/github.com/clipperhouse/displaywidth/README.md
generated
vendored
110
vendor/github.com/clipperhouse/displaywidth/README.md
generated
vendored
@@ -5,6 +5,7 @@ A high-performance Go package for measuring the monospace display width of strin
|
||||
[](https://pkg.go.dev/github.com/clipperhouse/displaywidth)
|
||||
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gotest.yml)
|
||||
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gofuzz.yml)
|
||||
|
||||
## Install
|
||||
```bash
|
||||
go get github.com/clipperhouse/displaywidth
|
||||
@@ -32,84 +33,91 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
For most purposes, you should use the `String` or `Bytes` methods.
|
||||
|
||||
|
||||
### Options
|
||||
|
||||
You can specify East Asian Width and Strict Emoji Neutral settings. If
|
||||
unspecified, the default is `EastAsianWidth: false, StrictEmojiNeutral: true`.
|
||||
You can specify East Asian Width settings. When false (default),
|
||||
[East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous)
|
||||
are treated as width 1. When true, East Asian Ambiguous characters are treated
|
||||
as width 2.
|
||||
|
||||
```go
|
||||
options := displaywidth.Options{
|
||||
EastAsianWidth: true,
|
||||
StrictEmojiNeutral: false,
|
||||
myOptions := displaywidth.Options{
|
||||
EastAsianWidth: true,
|
||||
}
|
||||
|
||||
width := options.String("Hello, 世界!")
|
||||
width := myOptions.String("Hello, 世界!")
|
||||
fmt.Println(width)
|
||||
```
|
||||
|
||||
## Details
|
||||
## Technical details
|
||||
|
||||
This package implements the Unicode East Asian Width standard (UAX #11) and is
|
||||
intended to be compatible with `go-runewidth`. It operates on bytes without
|
||||
decoding runes for better performance.
|
||||
This package implements the Unicode East Asian Width standard
|
||||
([UAX #11](https://www.unicode.org/reports/tr11/)), and handles
|
||||
[version selectors](https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)),
|
||||
and [regional indicator pairs](https://en.wikipedia.org/wiki/Regional_indicator_symbol)
|
||||
(flags). We implement [Unicode TR51](https://unicode.org/reports/tr51/).
|
||||
|
||||
`clipperhouse/displaywidth`, `mattn/go-runewidth`, and `rivo/uniseg` will
|
||||
give the same outputs for most real-world text. See extensive details in the
|
||||
[compatibility analysis](comparison/COMPATIBILITY_ANALYSIS.md).
|
||||
|
||||
If you wish to investigate the core logic, see the `lookupProperties` and `width`
|
||||
functions in [width.go](width.go#L135). The essential trie generation logic is in
|
||||
`buildPropertyBitmap` in [unicode.go](internal/gen/unicode.go#L317).
|
||||
|
||||
I (@clipperhouse) am keeping an eye on [emerging standards and test suites](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/).
|
||||
|
||||
## Prior Art
|
||||
|
||||
[mattn/go-runewidth](https://github.com/mattn/go-runewidth)
|
||||
|
||||
[rivo/uniseg](https://github.com/rivo/uniseg)
|
||||
|
||||
[x/text/width](https://pkg.go.dev/golang.org/x/text/width)
|
||||
|
||||
[x/text/internal/triegen](https://pkg.go.dev/golang.org/x/text/internal/triegen)
|
||||
|
||||
## Benchmarks
|
||||
|
||||
Part of my motivation is the insight that we can avoid decoding runes for better performance.
|
||||
|
||||
```bash
|
||||
cd comparison
|
||||
go test -bench=. -benchmem
|
||||
```
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/clipperhouse/displaywidth
|
||||
pkg: github.com/clipperhouse/displaywidth/comparison
|
||||
cpu: Apple M2
|
||||
BenchmarkStringDefault/displaywidth-8 10537 ns/op 160.10 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkStringDefault/go-runewidth-8 14162 ns/op 119.12 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_EAW/displaywidth-8 10776 ns/op 156.55 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_EAW/go-runewidth-8 23987 ns/op 70.33 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_StrictEmoji/displaywidth-8 10892 ns/op 154.88 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_StrictEmoji/go-runewidth-8 14552 ns/op 115.93 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_ASCII/displaywidth-8 1116 ns/op 114.72 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_ASCII/go-runewidth-8 1178 ns/op 108.67 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Unicode/displaywidth-8 896.9 ns/op 148.29 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Unicode/go-runewidth-8 1434 ns/op 92.72 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkStringWidth_Emoji/displaywidth-8 3033 ns/op 238.74 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkStringWidth_Emoji/go-runewidth-8 4841 ns/op 149.56 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Mixed/displaywidth-8 4064 ns/op 124.74 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Mixed/go-runewidth-8 4696 ns/op 107.97 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_ControlChars/displaywidth-8 320.6 ns/op 102.93 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_ControlChars/go-runewidth-8 373.8 ns/op 88.28 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRuneDefault/displaywidth-8 335.5 ns/op 411.35 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRuneDefault/go-runewidth-8 681.2 ns/op 202.58 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRuneWidth_EAW/displaywidth-8 146.7 ns/op 374.80 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRuneWidth_EAW/go-runewidth-8 495.6 ns/op 110.98 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRuneWidth_ASCII/displaywidth-8 63.00 ns/op 460.33 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRuneWidth_ASCII/go-runewidth-8 68.90 ns/op 420.91 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkString_Mixed/clipperhouse/displaywidth-8 10469 ns/op 161.15 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Mixed/mattn/go-runewidth-8 14250 ns/op 118.39 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Mixed/rivo/uniseg-8 19258 ns/op 87.60 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkString_EastAsian/clipperhouse/displaywidth-8 10518 ns/op 160.39 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_EastAsian/mattn/go-runewidth-8 23827 ns/op 70.80 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_EastAsian/rivo/uniseg-8 19537 ns/op 86.35 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkString_ASCII/clipperhouse/displaywidth-8 1027 ns/op 124.61 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_ASCII/mattn/go-runewidth-8 1166 ns/op 109.78 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_ASCII/rivo/uniseg-8 1551 ns/op 82.52 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkString_Emoji/clipperhouse/displaywidth-8 3164 ns/op 228.84 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Emoji/mattn/go-runewidth-8 4728 ns/op 153.13 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Emoji/rivo/uniseg-8 6489 ns/op 111.57 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3429 ns/op 491.96 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_Mixed/mattn/go-runewidth-8 5308 ns/op 317.81 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3419 ns/op 493.49 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_EastAsian/mattn/go-runewidth-8 15321 ns/op 110.11 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_ASCII/clipperhouse/displaywidth-8 254.4 ns/op 503.19 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_ASCII/mattn/go-runewidth-8 264.3 ns/op 484.31 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1374 ns/op 527.02 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_Emoji/mattn/go-runewidth-8 2210 ns/op 327.66 MB/s 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
I use a similar technique in [this grapheme cluster library](https://github.com/clipperhouse/uax29).
|
||||
|
||||
## Compatibility
|
||||
|
||||
`displaywidth` will mostly give the same outputs as `go-runewidth`, but there are some differences:
|
||||
|
||||
- Unicode category Mn (Nonspacing Mark): `displaywidth` will return width 0, `go-runewidth` may return width 1 for some runes.
|
||||
- Unicode category Cf (Format): `displaywidth` will return width 0, `go-runewidth` may return width 1 for some runes.
|
||||
- Unicode category Mc (Spacing Mark): `displaywidth` will return width 1, `go-runewidth` may return width 0 for some runes.
|
||||
- Unicode category Cs (Surrogate): `displaywidth` will return width 0, `go-runewidth` may return width 1 for some runes. Surrogates are not valid UTF-8; some packages may turn them into the replacement character (U+FFFD).
|
||||
- Unicode category Zl (Line separator): `displaywidth` will return width 0, `go-runewidth` may return width 1.
|
||||
- Unicode category Zp (Paragraph separator): `displaywidth` will return width 0, `go-runewidth` may return width 1.
|
||||
- Unicode Noncharacters (U+FFFE and U+FFFF): `displaywidth` will return width 0, `go-runewidth` may return width 1.
|
||||
|
||||
See `TestCompatibility` for more details.
|
||||
|
||||
72
vendor/github.com/clipperhouse/displaywidth/graphemes.go
generated
vendored
Normal file
72
vendor/github.com/clipperhouse/displaywidth/graphemes.go
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
package displaywidth
|
||||
|
||||
import (
|
||||
"github.com/clipperhouse/stringish"
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
)
|
||||
|
||||
// Graphemes is an iterator over grapheme clusters.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
type Graphemes[T stringish.Interface] struct {
|
||||
iter graphemes.Iterator[T]
|
||||
options Options
|
||||
}
|
||||
|
||||
// Next advances the iterator to the next grapheme cluster.
|
||||
func (g *Graphemes[T]) Next() bool {
|
||||
return g.iter.Next()
|
||||
}
|
||||
|
||||
// Value returns the current grapheme cluster.
|
||||
func (g *Graphemes[T]) Value() T {
|
||||
return g.iter.Value()
|
||||
}
|
||||
|
||||
// Width returns the display width of the current grapheme cluster.
|
||||
func (g *Graphemes[T]) Width() int {
|
||||
return graphemeWidth(g.Value(), g.options)
|
||||
}
|
||||
|
||||
// StringGraphemes returns an iterator over grapheme clusters for the given
|
||||
// string.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func StringGraphemes(s string) Graphemes[string] {
|
||||
return DefaultOptions.StringGraphemes(s)
|
||||
}
|
||||
|
||||
// StringGraphemes returns an iterator over grapheme clusters for the given
|
||||
// string, with the given options.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func (options Options) StringGraphemes(s string) Graphemes[string] {
|
||||
return Graphemes[string]{
|
||||
iter: graphemes.FromString(s),
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
// BytesGraphemes returns an iterator over grapheme clusters for the given
|
||||
// []byte.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func BytesGraphemes(s []byte) Graphemes[[]byte] {
|
||||
return DefaultOptions.BytesGraphemes(s)
|
||||
}
|
||||
|
||||
// BytesGraphemes returns an iterator over grapheme clusters for the given
|
||||
// []byte, with the given options.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func (options Options) BytesGraphemes(s []byte) Graphemes[[]byte] {
|
||||
return Graphemes[[]byte]{
|
||||
iter: graphemes.FromBytes(s),
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
91
vendor/github.com/clipperhouse/displaywidth/tables.go
generated
vendored
Normal file
91
vendor/github.com/clipperhouse/displaywidth/tables.go
generated
vendored
Normal file
@@ -0,0 +1,91 @@
|
||||
package displaywidth
|
||||
|
||||
// propertyWidths is a jump table of sorts, instead of a switch
|
||||
var propertyWidths = [5]int{
|
||||
_Default: 1,
|
||||
_Zero_Width: 0,
|
||||
_East_Asian_Wide: 2,
|
||||
_East_Asian_Ambiguous: 1,
|
||||
_Emoji: 2,
|
||||
}
|
||||
|
||||
// asciiWidths is a lookup table for single-byte character widths. Printable
|
||||
// ASCII characters have width 1, control characters have width 0.
|
||||
//
|
||||
// It is intended for valid single-byte UTF-8, which means <128.
|
||||
//
|
||||
// If you look up an index >= 128, that is either:
|
||||
// - invalid UTF-8, or
|
||||
// - a multi-byte UTF-8 sequence, in which case you should be operating on
|
||||
// the grapheme cluster, and not using this table
|
||||
//
|
||||
// We will return a default value of 1 in those cases, so as not to panic.
|
||||
var asciiWidths = [256]int8{
|
||||
// Control characters (0x00-0x1F): width 0
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
// Printable ASCII (0x20-0x7E): width 1
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
// DEL (0x7F): width 0
|
||||
0,
|
||||
// >= 128
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
||||
}
|
||||
|
||||
// asciiProperties is a lookup table for single-byte character properties.
|
||||
// It is intended for valid single-byte UTF-8, which means <128.
|
||||
//
|
||||
// If you look up an index >= 128, that is either:
|
||||
// - invalid UTF-8, or
|
||||
// - a multi-byte UTF-8 sequence, in which case you should be operating on
|
||||
// the grapheme cluster, and not using this table
|
||||
//
|
||||
// We will return a default value of _Default in those cases, so as not to
|
||||
// panic.
|
||||
var asciiProperties = [256]property{
|
||||
// Control characters (0x00-0x1F): _Zero_Width
|
||||
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
|
||||
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
|
||||
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
|
||||
_Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width, _Zero_Width,
|
||||
// Printable ASCII (0x20-0x7E): _Default
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
// DEL (0x7F): _Zero_Width
|
||||
_Zero_Width,
|
||||
// >= 128
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
_Default, _Default, _Default, _Default, _Default, _Default, _Default, _Default,
|
||||
}
|
||||
2727
vendor/github.com/clipperhouse/displaywidth/trie.go
generated
vendored
2727
vendor/github.com/clipperhouse/displaywidth/trie.go
generated
vendored
File diff suppressed because it is too large
Load Diff
280
vendor/github.com/clipperhouse/displaywidth/width.go
generated
vendored
280
vendor/github.com/clipperhouse/displaywidth/width.go
generated
vendored
@@ -7,153 +7,205 @@ import (
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
)
|
||||
|
||||
// String calculates the display width of a string
|
||||
// using the [DefaultOptions]
|
||||
// Options allows you to specify the treatment of ambiguous East Asian
|
||||
// characters. When EastAsianWidth is false (default), ambiguous East Asian
|
||||
// characters are treated as width 1. When EastAsianWidth is true, ambiguous
|
||||
// East Asian characters are treated as width 2.
|
||||
type Options struct {
|
||||
EastAsianWidth bool
|
||||
}
|
||||
|
||||
// DefaultOptions is the default options for the display width
|
||||
// calculation, which is EastAsianWidth: false.
|
||||
var DefaultOptions = Options{EastAsianWidth: false}
|
||||
|
||||
// String calculates the display width of a string,
|
||||
// by iterating over grapheme clusters in the string
|
||||
// and summing their widths.
|
||||
func String(s string) int {
|
||||
return DefaultOptions.String(s)
|
||||
}
|
||||
|
||||
// Bytes calculates the display width of a []byte
|
||||
// using the [DefaultOptions]
|
||||
// String calculates the display width of a string, for the given options, by
|
||||
// iterating over grapheme clusters in the string and summing their widths.
|
||||
func (options Options) String(s string) int {
|
||||
// Optimization: no need to parse grapheme
|
||||
switch len(s) {
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return int(asciiWidths[s[0]])
|
||||
}
|
||||
|
||||
width := 0
|
||||
g := graphemes.FromString(s)
|
||||
for g.Next() {
|
||||
width += graphemeWidth(g.Value(), options)
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
// Bytes calculates the display width of a []byte,
|
||||
// by iterating over grapheme clusters in the byte slice
|
||||
// and summing their widths.
|
||||
func Bytes(s []byte) int {
|
||||
return DefaultOptions.Bytes(s)
|
||||
}
|
||||
|
||||
// Bytes calculates the display width of a []byte, for the given options, by
|
||||
// iterating over grapheme clusters in the slice and summing their widths.
|
||||
func (options Options) Bytes(s []byte) int {
|
||||
// Optimization: no need to parse grapheme
|
||||
switch len(s) {
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return int(asciiWidths[s[0]])
|
||||
}
|
||||
|
||||
width := 0
|
||||
g := graphemes.FromBytes(s)
|
||||
for g.Next() {
|
||||
width += graphemeWidth(g.Value(), options)
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
// Rune calculates the display width of a rune. You
|
||||
// should almost certainly use [String] or [Bytes] for
|
||||
// most purposes.
|
||||
//
|
||||
// The smallest unit of display width is a grapheme
|
||||
// cluster, not a rune. Iterating over runes to measure
|
||||
// width is incorrect in many cases.
|
||||
func Rune(r rune) int {
|
||||
return DefaultOptions.Rune(r)
|
||||
}
|
||||
|
||||
type Options struct {
|
||||
EastAsianWidth bool
|
||||
StrictEmojiNeutral bool
|
||||
}
|
||||
|
||||
var DefaultOptions = Options{
|
||||
EastAsianWidth: false,
|
||||
StrictEmojiNeutral: true,
|
||||
}
|
||||
|
||||
// String calculates the display width of a string
|
||||
// for the given options
|
||||
func (options Options) String(s string) int {
|
||||
if len(s) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
total := 0
|
||||
g := graphemes.FromString(s)
|
||||
for g.Next() {
|
||||
// The first character in the grapheme cluster determines the width;
|
||||
// modifiers and joiners do not contribute to the width.
|
||||
props, _ := lookupProperties(g.Value())
|
||||
total += props.width(options)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// BytesOptions calculates the display width of a []byte
|
||||
// for the given options
|
||||
func (options Options) Bytes(s []byte) int {
|
||||
if len(s) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
total := 0
|
||||
g := graphemes.FromBytes(s)
|
||||
for g.Next() {
|
||||
// The first character in the grapheme cluster determines the width;
|
||||
// modifiers and joiners do not contribute to the width.
|
||||
props, _ := lookupProperties(g.Value())
|
||||
total += props.width(options)
|
||||
}
|
||||
return total
|
||||
}
|
||||
|
||||
// Rune calculates the display width of a rune, for the given options.
|
||||
//
|
||||
// You should almost certainly use [String] or [Bytes] for most purposes.
|
||||
//
|
||||
// The smallest unit of display width is a grapheme cluster, not a rune.
|
||||
// Iterating over runes to measure width is incorrect in many cases.
|
||||
func (options Options) Rune(r rune) int {
|
||||
// Fast path for ASCII
|
||||
if r < utf8.RuneSelf {
|
||||
if isASCIIControl(byte(r)) {
|
||||
// Control (0x00-0x1F) and DEL (0x7F)
|
||||
return 0
|
||||
}
|
||||
// ASCII printable (0x20-0x7E)
|
||||
return 1
|
||||
return int(asciiWidths[byte(r)])
|
||||
}
|
||||
|
||||
// Surrogates (U+D800-U+DFFF) are invalid UTF-8 and have zero width
|
||||
// Other packages might turn them into the replacement character (U+FFFD)
|
||||
// in which case, we won't see it.
|
||||
// Surrogates (U+D800-U+DFFF) are invalid UTF-8.
|
||||
if r >= 0xD800 && r <= 0xDFFF {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Stack-allocated to avoid heap allocation
|
||||
var buf [4]byte // UTF-8 is at most 4 bytes
|
||||
var buf [4]byte
|
||||
n := utf8.EncodeRune(buf[:], r)
|
||||
// Skip the grapheme iterator and directly lookup properties
|
||||
props, _ := lookupProperties(buf[:n])
|
||||
return props.width(options)
|
||||
|
||||
// Skip the grapheme iterator
|
||||
return lookupProperties(buf[:n]).width(options)
|
||||
}
|
||||
|
||||
func isASCIIControl(b byte) bool {
|
||||
return b < 0x20 || b == 0x7F
|
||||
}
|
||||
|
||||
const defaultWidth = 1
|
||||
|
||||
// is returns true if the property flag is set
|
||||
func (p property) is(flag property) bool {
|
||||
return p&flag != 0
|
||||
}
|
||||
|
||||
// lookupProperties returns the properties for the first character in a string
|
||||
func lookupProperties[T stringish.Interface](s T) (property, int) {
|
||||
if len(s) == 0 {
|
||||
return 0, 0
|
||||
// graphemeWidth returns the display width of a grapheme cluster.
|
||||
// The passed string must be a single grapheme cluster.
|
||||
func graphemeWidth[T stringish.Interface](s T, options Options) int {
|
||||
// Optimization: no need to look up properties
|
||||
switch len(s) {
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return int(asciiWidths[s[0]])
|
||||
}
|
||||
|
||||
// Fast path for ASCII characters (single byte)
|
||||
b := s[0]
|
||||
if b < utf8.RuneSelf { // Single-byte ASCII
|
||||
if isASCIIControl(b) {
|
||||
// Control characters (0x00-0x1F) and DEL (0x7F) - width 0
|
||||
return _ZeroWidth, 1
|
||||
return lookupProperties(s).width(options)
|
||||
}
|
||||
|
||||
// isRIPrefix checks if the slice matches the Regional Indicator prefix
|
||||
// (F0 9F 87). It assumes len(s) >= 3.
|
||||
func isRIPrefix[T stringish.Interface](s T) bool {
|
||||
return s[0] == 0xF0 && s[1] == 0x9F && s[2] == 0x87
|
||||
}
|
||||
|
||||
// isVS16 checks if the slice matches VS16 (U+FE0F) UTF-8 encoding
|
||||
// (EF B8 8F). It assumes len(s) >= 3.
|
||||
func isVS16[T stringish.Interface](s T) bool {
|
||||
return s[0] == 0xEF && s[1] == 0xB8 && s[2] == 0x8F
|
||||
}
|
||||
|
||||
// lookupProperties returns the properties for a grapheme.
|
||||
// The passed string must be at least one byte long.
|
||||
//
|
||||
// Callers must handle zero and single-byte strings upstream, both as an
|
||||
// optimization, and to reduce the scope of this function.
|
||||
func lookupProperties[T stringish.Interface](s T) property {
|
||||
l := len(s)
|
||||
|
||||
if s[0] < utf8.RuneSelf {
|
||||
// Check for variation selector after ASCII (e.g., keycap sequences like 1️⃣)
|
||||
if l >= 4 {
|
||||
// Subslice may help eliminate bounds checks
|
||||
vs := s[1:4]
|
||||
if isVS16(vs) {
|
||||
// VS16 requests emoji presentation (width 2)
|
||||
return _Emoji
|
||||
}
|
||||
// VS15 (0x8E) requests text presentation but does not affect width,
|
||||
// in my reading of Unicode TR51. Falls through to _Default.
|
||||
}
|
||||
// ASCII printable characters (0x20-0x7E) - width 1
|
||||
// Return 0 properties, width calculation will default to 1
|
||||
return 0, 1
|
||||
return asciiProperties[s[0]]
|
||||
}
|
||||
|
||||
// Use the generated trie for lookup
|
||||
props, size := lookup(s)
|
||||
return property(props), size
|
||||
// Regional indicator pair (flag)
|
||||
if l >= 8 {
|
||||
// Subslice may help eliminate bounds checks
|
||||
ri := s[:8]
|
||||
// First rune
|
||||
if isRIPrefix(ri[0:3]) {
|
||||
b3 := ri[3]
|
||||
if b3 >= 0xA6 && b3 <= 0xBF {
|
||||
// Second rune
|
||||
if isRIPrefix(ri[4:7]) {
|
||||
b7 := ri[7]
|
||||
if b7 >= 0xA6 && b7 <= 0xBF {
|
||||
return _Emoji
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p, sz := lookup(s)
|
||||
|
||||
// Variation Selectors
|
||||
if sz > 0 && l >= sz+3 {
|
||||
// Subslice may help eliminate bounds checks
|
||||
vs := s[sz : sz+3]
|
||||
if isVS16(vs) {
|
||||
// VS16 requests emoji presentation (width 2)
|
||||
return _Emoji
|
||||
}
|
||||
// VS15 (0x8E) requests text presentation but does not affect width,
|
||||
// in my reading of Unicode TR51. Falls through to return the base
|
||||
// character's property.
|
||||
}
|
||||
|
||||
return property(p)
|
||||
}
|
||||
|
||||
// width determines the display width of a character based on its properties
|
||||
const _Default property = 0
|
||||
const boundsCheck = property(len(propertyWidths) - 1)
|
||||
|
||||
// width determines the display width of a character based on its properties,
|
||||
// and configuration options
|
||||
func (p property) width(options Options) int {
|
||||
if p == 0 {
|
||||
// Character not in trie, use default behavior
|
||||
return defaultWidth
|
||||
}
|
||||
|
||||
if p.is(_ZeroWidth) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if options.EastAsianWidth {
|
||||
if p.is(_East_Asian_Ambiguous) {
|
||||
return 2
|
||||
}
|
||||
if p.is(_East_Asian_Ambiguous|_Emoji) && !options.StrictEmojiNeutral {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
if p.is(_East_Asian_Full_Wide) {
|
||||
if options.EastAsianWidth && p == _East_Asian_Ambiguous {
|
||||
return 2
|
||||
}
|
||||
|
||||
// Default width for all other characters
|
||||
return defaultWidth
|
||||
// Bounds check may help the compiler eliminate its bounds check,
|
||||
// and safety of course.
|
||||
if p > boundsCheck {
|
||||
return 1 // default width
|
||||
}
|
||||
|
||||
return propertyWidths[p]
|
||||
}
|
||||
|
||||
24
vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md
generated
vendored
24
vendor/github.com/clipperhouse/uax29/v2/graphemes/README.md
generated
vendored
@@ -1,5 +1,9 @@
|
||||
An implementation of grapheme cluster boundaries from [Unicode text segmentation](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (UAX 29), for Unicode version 15.0.0.
|
||||
|
||||
[](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes)
|
||||

|
||||

|
||||
|
||||
## Quick start
|
||||
|
||||
```
|
||||
@@ -18,15 +22,14 @@ for tokens.Next() { // Next() returns true until end of data
|
||||
}
|
||||
```
|
||||
|
||||
[](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes)
|
||||
|
||||
_A grapheme is a “single visible character”, which might be a simple as a single letter, or a complex emoji that consists of several Unicode code points._
|
||||
|
||||
## Conformance
|
||||
|
||||
We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Tests29). Status:
|
||||
We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Tests29).
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## APIs
|
||||
|
||||
@@ -71,9 +74,18 @@ for tokens.Next() { // Next() returns true until end of data
|
||||
}
|
||||
```
|
||||
|
||||
### Performance
|
||||
### Benchmarks
|
||||
|
||||
On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second. You should see ~constant memory, and no allocations.
|
||||
On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second, and no allocations.
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/clipperhouse/uax29/graphemes/comparative
|
||||
cpu: Apple M2
|
||||
BenchmarkGraphemes/clipperhouse/uax29-8 173805 ns/op 201.16 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkGraphemes/rivo/uniseg-8 2045128 ns/op 17.10 MB/s 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
### Invalid inputs
|
||||
|
||||
|
||||
7
vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go
generated
vendored
7
vendor/github.com/clipperhouse/uax29/v2/graphemes/iterator.go
generated
vendored
@@ -1,8 +1,11 @@
|
||||
package graphemes
|
||||
|
||||
import "github.com/clipperhouse/uax29/v2/internal/iterators"
|
||||
import (
|
||||
"github.com/clipperhouse/stringish"
|
||||
"github.com/clipperhouse/uax29/v2/internal/iterators"
|
||||
)
|
||||
|
||||
type Iterator[T iterators.Stringish] struct {
|
||||
type Iterator[T stringish.Interface] struct {
|
||||
*iterators.Iterator[T]
|
||||
}
|
||||
|
||||
|
||||
4
vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go
generated
vendored
4
vendor/github.com/clipperhouse/uax29/v2/graphemes/splitfunc.go
generated
vendored
@@ -3,7 +3,7 @@ package graphemes
|
||||
import (
|
||||
"bufio"
|
||||
|
||||
"github.com/clipperhouse/uax29/v2/internal/iterators"
|
||||
"github.com/clipperhouse/stringish"
|
||||
)
|
||||
|
||||
// is determines if lookup intersects propert(ies)
|
||||
@@ -18,7 +18,7 @@ const _Ignore = _Extend
|
||||
// See https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries.
|
||||
var SplitFunc bufio.SplitFunc = splitFunc[[]byte]
|
||||
|
||||
func splitFunc[T iterators.Stringish](data T, atEOF bool) (advance int, token T, err error) {
|
||||
func splitFunc[T stringish.Interface](data T, atEOF bool) (advance int, token T, err error) {
|
||||
var empty T
|
||||
if len(data) == 0 {
|
||||
return 0, empty, nil
|
||||
|
||||
6
vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go
generated
vendored
6
vendor/github.com/clipperhouse/uax29/v2/graphemes/trie.go
generated
vendored
@@ -1,10 +1,10 @@
|
||||
package graphemes
|
||||
|
||||
import "github.com/clipperhouse/stringish"
|
||||
|
||||
// generated by github.com/clipperhouse/uax29/v2
|
||||
// from https://www.unicode.org/Public/15.0.0/ucd/auxiliary/GraphemeBreakProperty.txt
|
||||
|
||||
import "github.com/clipperhouse/uax29/v2/internal/iterators"
|
||||
|
||||
type property uint16
|
||||
|
||||
const (
|
||||
@@ -27,7 +27,7 @@ const (
|
||||
// lookup returns the trie value for the first UTF-8 encoding in s and
|
||||
// the width in bytes of this encoding. The size will be 0 if s does not
|
||||
// hold enough bytes to complete the encoding. len(s) must be greater than 0.
|
||||
func lookup[T iterators.Stringish](s T) (v property, sz int) {
|
||||
func lookup[T stringish.Interface](s T) (v property, sz int) {
|
||||
c0 := s[0]
|
||||
switch {
|
||||
case c0 < 0x80: // is ASCII
|
||||
|
||||
27
vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go
generated
vendored
27
vendor/github.com/clipperhouse/uax29/v2/internal/iterators/iterator.go
generated
vendored
@@ -1,14 +1,12 @@
|
||||
package iterators
|
||||
|
||||
type Stringish interface {
|
||||
[]byte | string
|
||||
}
|
||||
import "github.com/clipperhouse/stringish"
|
||||
|
||||
type SplitFunc[T Stringish] func(T, bool) (int, T, error)
|
||||
type SplitFunc[T stringish.Interface] func(T, bool) (int, T, error)
|
||||
|
||||
// Iterator is a generic iterator for words that are either []byte or string.
|
||||
// Iterate while Next() is true, and access the word via Value().
|
||||
type Iterator[T Stringish] struct {
|
||||
type Iterator[T stringish.Interface] struct {
|
||||
split SplitFunc[T]
|
||||
data T
|
||||
start int
|
||||
@@ -16,7 +14,7 @@ type Iterator[T Stringish] struct {
|
||||
}
|
||||
|
||||
// New creates a new Iterator for the given data and SplitFunc.
|
||||
func New[T Stringish](split SplitFunc[T], data T) *Iterator[T] {
|
||||
func New[T stringish.Interface](split SplitFunc[T], data T) *Iterator[T] {
|
||||
return &Iterator[T]{
|
||||
split: split,
|
||||
data: data,
|
||||
@@ -83,3 +81,20 @@ func (iter *Iterator[T]) Reset() {
|
||||
iter.start = 0
|
||||
iter.pos = 0
|
||||
}
|
||||
|
||||
func (iter *Iterator[T]) First() T {
|
||||
if len(iter.data) == 0 {
|
||||
return iter.data
|
||||
}
|
||||
advance, _, err := iter.split(iter.data, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if advance <= 0 {
|
||||
panic("SplitFunc returned a zero or negative advance")
|
||||
}
|
||||
if advance > len(iter.data) {
|
||||
panic("SplitFunc advanced beyond the end of the data")
|
||||
}
|
||||
return iter.data[:advance]
|
||||
}
|
||||
|
||||
7
vendor/github.com/olekukonko/ll/global.go
generated
vendored
7
vendor/github.com/olekukonko/ll/global.go
generated
vendored
@@ -657,6 +657,13 @@ func Mark(names ...string) {
|
||||
// It is similar to Dbg but formats the output as JSON for better readability. It is thread-safe and respects
|
||||
// the logger’s configuration (e.g., enabled, level, suspend, handler, middleware).
|
||||
func Output(values ...interface{}) {
|
||||
defaultLogger.output(2, values...)
|
||||
|
||||
}
|
||||
|
||||
// Inspect logs one or more values in a **developer-friendly, deeply introspective format** at Info level.
|
||||
// It includes the caller file and line number, and reveals **all fields** — including:
|
||||
func Inspect(values ...interface{}) {
|
||||
o := NewInspector(defaultLogger)
|
||||
o.Log(2, values...)
|
||||
}
|
||||
|
||||
2
vendor/github.com/olekukonko/ll/inspector.go
generated
vendored
2
vendor/github.com/olekukonko/ll/inspector.go
generated
vendored
@@ -79,7 +79,7 @@ func (o *Inspector) Log(skip int, values ...interface{}) {
|
||||
}
|
||||
|
||||
// Construct log message with file, line, and JSON data
|
||||
msg := fmt.Sprintf("[%s:%d] DUMP: %s", shortFile, line, string(jsonData))
|
||||
msg := fmt.Sprintf("[%s:%d] INSPECT: %s", shortFile, line, string(jsonData))
|
||||
o.logger.log(lx.LevelInfo, lx.ClassText, msg, nil, false)
|
||||
}
|
||||
}
|
||||
|
||||
61
vendor/github.com/olekukonko/ll/ll.go
generated
vendored
61
vendor/github.com/olekukonko/ll/ll.go
generated
vendored
@@ -350,17 +350,58 @@ func (l *Logger) Dump(values ...interface{}) {
|
||||
}
|
||||
}
|
||||
|
||||
// Output logs data in a human-readable JSON format at Info level, including caller file and line information.
|
||||
// It is similar to Dbg but formats the output as JSON for better readability. It is thread-safe and respects
|
||||
// the logger's configuration (e.g., enabled, level, suspend, handler, middleware).
|
||||
// Example:
|
||||
//
|
||||
// logger := New("app").Enable()
|
||||
// x := map[string]int{"key": 42}
|
||||
// logger.Output(x) // Output: [app] INFO: [file.go:123] JSON: {"key": 42}
|
||||
//
|
||||
// Logger method to provide access to Output functionality
|
||||
// Output logs each value as pretty-printed JSON for REST debugging.
|
||||
// Each value is logged on its own line with [file:line] and a blank line after the header.
|
||||
// Ideal for inspecting outgoing/incoming REST payloads.
|
||||
func (l *Logger) Output(values ...interface{}) {
|
||||
l.output(2, values...)
|
||||
}
|
||||
|
||||
func (l *Logger) output(skip int, values ...interface{}) {
|
||||
if !l.shouldLog(lx.LevelInfo) {
|
||||
return
|
||||
}
|
||||
|
||||
_, file, line, ok := runtime.Caller(skip)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
shortFile := file
|
||||
if idx := strings.LastIndex(file, "/"); idx >= 0 {
|
||||
shortFile = file[idx+1:]
|
||||
}
|
||||
|
||||
header := fmt.Sprintf("[%s:%d] JSON:\n", shortFile, line)
|
||||
|
||||
for _, v := range values {
|
||||
// Always pretty-print with indent
|
||||
b, err := json.MarshalIndent(v, " ", " ")
|
||||
if err != nil {
|
||||
b, _ = json.MarshalIndent(map[string]any{
|
||||
"value": fmt.Sprintf("%+v", v),
|
||||
"error": err.Error(),
|
||||
}, " ", " ")
|
||||
}
|
||||
l.log(lx.LevelInfo, lx.ClassText, header+string(b), nil, false)
|
||||
}
|
||||
}
|
||||
|
||||
// Inspect logs one or more values in a **developer-friendly, deeply introspective format** at Info level.
|
||||
// It includes the caller file and line number, and reveals **all fields** — including:
|
||||
//
|
||||
// - Private (unexported) fields → prefixed with `(field)`
|
||||
// - Embedded structs (inlined)
|
||||
// - Pointers and nil values → shown as `*(field)` or `nil`
|
||||
// - Full struct nesting and type information
|
||||
//
|
||||
// This method uses `NewInspector` under the hood, which performs **full reflection-based traversal**.
|
||||
// It is **not** meant for production logging or REST APIs — use `Output` for that.
|
||||
//
|
||||
// Ideal for:
|
||||
// - Debugging complex internal state
|
||||
// - Inspecting structs with private fields
|
||||
// - Understanding struct embedding and pointer behavior
|
||||
func (l *Logger) Inspect(values ...interface{}) {
|
||||
o := NewInspector(l)
|
||||
o.Log(2, values...)
|
||||
}
|
||||
|
||||
6
vendor/github.com/olekukonko/tablewriter/README.md
generated
vendored
6
vendor/github.com/olekukonko/tablewriter/README.md
generated
vendored
@@ -28,7 +28,7 @@ go get github.com/olekukonko/tablewriter@v0.0.5
|
||||
#### Latest Version
|
||||
The latest stable version
|
||||
```bash
|
||||
go get github.com/olekukonko/tablewriter@v1.1.1
|
||||
go get github.com/olekukonko/tablewriter@v1.1.2
|
||||
```
|
||||
|
||||
**Warning:** Version `v1.0.0` contains missing functionality and should not be used.
|
||||
@@ -62,7 +62,7 @@ func main() {
|
||||
data := [][]string{
|
||||
{"Package", "Version", "Status"},
|
||||
{"tablewriter", "v0.0.5", "legacy"},
|
||||
{"tablewriter", "v1.1.1", "latest"},
|
||||
{"tablewriter", "v1.1.2", "latest"},
|
||||
}
|
||||
|
||||
table := tablewriter.NewWriter(os.Stdout)
|
||||
@@ -77,7 +77,7 @@ func main() {
|
||||
│ PACKAGE │ VERSION │ STATUS │
|
||||
├─────────────┼─────────┼────────┤
|
||||
│ tablewriter │ v0.0.5 │ legacy │
|
||||
│ tablewriter │ v1.1.1 │ latest │
|
||||
│ tablewriter │ v1.1.2 │ latest │
|
||||
└─────────────┴─────────┴────────┘
|
||||
```
|
||||
|
||||
|
||||
15
vendor/github.com/olekukonko/tablewriter/deprecated.go
generated
vendored
15
vendor/github.com/olekukonko/tablewriter/deprecated.go
generated
vendored
@@ -1,6 +1,8 @@
|
||||
package tablewriter
|
||||
|
||||
import (
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/olekukonko/tablewriter/pkg/twwidth"
|
||||
"github.com/olekukonko/tablewriter/tw"
|
||||
)
|
||||
|
||||
@@ -218,3 +220,16 @@ func WithTableMax(width int) Option {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Deprecated: use WithEastAsian instead.
|
||||
// WithCondition provides a way to set a custom global runewidth.Condition
|
||||
// that will be used for all subsequent display width calculations by the twwidth (twdw) package.
|
||||
//
|
||||
// The runewidth.Condition object allows for more fine-grained control over how rune widths
|
||||
// are determined, beyond just toggling EastAsianWidth. This could include settings for
|
||||
// ambiguous width characters or other future properties of runewidth.Condition.
|
||||
func WithCondition(cond *runewidth.Condition) Option {
|
||||
return func(target *Table) {
|
||||
twwidth.SetCondition(cond)
|
||||
}
|
||||
}
|
||||
|
||||
67
vendor/github.com/olekukonko/tablewriter/option.go
generated
vendored
67
vendor/github.com/olekukonko/tablewriter/option.go
generated
vendored
@@ -3,8 +3,8 @@ package tablewriter
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/olekukonko/ll"
|
||||
"github.com/olekukonko/tablewriter/pkg/twcache"
|
||||
"github.com/olekukonko/tablewriter/pkg/twwidth"
|
||||
"github.com/olekukonko/tablewriter/tw"
|
||||
)
|
||||
@@ -471,22 +471,48 @@ func WithStreaming(c tw.StreamConfig) Option {
|
||||
func WithStringer(stringer interface{}) Option {
|
||||
return func(t *Table) {
|
||||
t.stringer = stringer
|
||||
t.stringerCacheMu.Lock()
|
||||
t.stringerCache = make(map[reflect.Type]reflect.Value)
|
||||
t.stringerCacheMu.Unlock()
|
||||
t.stringerCache = twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity)
|
||||
if t.logger != nil {
|
||||
t.logger.Debug("Stringer updated, cache cleared")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithStringerCache enables caching for the stringer function.
|
||||
// Logs the change if debugging is enabled.
|
||||
// WithStringerCache enables the default LRU caching for the stringer function.
|
||||
// It initializes the cache with a default capacity if one does not already exist.
|
||||
func WithStringerCache() Option {
|
||||
return func(t *Table) {
|
||||
t.stringerCacheEnabled = true
|
||||
// Initialize default cache if strictly necessary (nil),
|
||||
// or if you want to ensure the default implementation is used.
|
||||
if t.stringerCache == nil {
|
||||
// NewLRU returns (Instance, error). We ignore the error here assuming capacity > 0.
|
||||
cache := twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity)
|
||||
t.stringerCache = cache
|
||||
}
|
||||
|
||||
if t.logger != nil {
|
||||
t.logger.Debug("Option: WithStringerCache enabled")
|
||||
t.logger.Debug("Option: WithStringerCache enabled (Default LRU)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// WithStringerCacheCustom enables caching for the stringer function using a specific implementation.
|
||||
// Passing nil disables caching entirely.
|
||||
func WithStringerCacheCustom(cache twcache.Cache[reflect.Type, reflect.Value]) Option {
|
||||
return func(t *Table) {
|
||||
if cache == nil {
|
||||
t.stringerCache = nil
|
||||
if t.logger != nil {
|
||||
t.logger.Debug("Option: WithStringerCacheCustom called with nil (Caching Disabled)")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Set the custom cache and enable the flag
|
||||
t.stringerCache = cache
|
||||
|
||||
if t.logger != nil {
|
||||
t.logger.Debug("Option: WithStringerCacheCustom enabled")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -629,27 +655,20 @@ func WithRendition(rendition tw.Rendition) Option {
|
||||
}
|
||||
|
||||
// WithEastAsian configures the global East Asian width calculation setting.
|
||||
// - enable=true: Enables East Asian width calculations. CJK and ambiguous characters
|
||||
// - state=tw.On: Enables East Asian width calculations. CJK and ambiguous characters
|
||||
// are typically measured as double width.
|
||||
// - enable=false: Disables East Asian width calculations. Characters are generally
|
||||
// - state=tw.Off: Disables East Asian width calculations. Characters are generally
|
||||
// measured as single width, subject to Unicode standards.
|
||||
//
|
||||
// This setting affects all subsequent display width calculations using the twdw package.
|
||||
func WithEastAsian(enable bool) Option {
|
||||
func WithEastAsian(state tw.State) Option {
|
||||
return func(target *Table) {
|
||||
twwidth.SetEastAsian(enable)
|
||||
}
|
||||
}
|
||||
|
||||
// WithCondition provides a way to set a custom global runewidth.Condition
|
||||
// that will be used for all subsequent display width calculations by the twwidth (twdw) package.
|
||||
//
|
||||
// The runewidth.Condition object allows for more fine-grained control over how rune widths
|
||||
// are determined, beyond just toggling EastAsianWidth. This could include settings for
|
||||
// ambiguous width characters or other future properties of runewidth.Condition.
|
||||
func WithCondition(cond *runewidth.Condition) Option {
|
||||
return func(target *Table) {
|
||||
twwidth.SetCondition(cond)
|
||||
if state.Enabled() {
|
||||
twwidth.SetEastAsian(true)
|
||||
}
|
||||
if state.Disabled() {
|
||||
twwidth.SetEastAsian(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
12
vendor/github.com/olekukonko/tablewriter/pkg/twcache/cache.go
generated
vendored
Normal file
12
vendor/github.com/olekukonko/tablewriter/pkg/twcache/cache.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
package twcache
|
||||
|
||||
// Cache defines a generic interface for a key-value storage with type constraints on keys and values.
|
||||
// The keys must be of a type that supports comparison.
|
||||
// Add inserts a new key-value pair, potentially evicting an item if necessary.
|
||||
// Get retrieves a value associated with the given key, returning a boolean to indicate if the key was found.
|
||||
// Purge clears all items from the cache.
|
||||
type Cache[K comparable, V any] interface {
|
||||
Add(key K, value V) (evicted bool)
|
||||
Get(key K) (value V, ok bool)
|
||||
Purge()
|
||||
}
|
||||
289
vendor/github.com/olekukonko/tablewriter/pkg/twcache/lru.go
generated
vendored
Normal file
289
vendor/github.com/olekukonko/tablewriter/pkg/twcache/lru.go
generated
vendored
Normal file
@@ -0,0 +1,289 @@
|
||||
package twcache
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// EvictCallback is a function called when an entry is evicted.
|
||||
// This includes evictions during Purge or Resize operations.
|
||||
type EvictCallback[K comparable, V any] func(key K, value V)
|
||||
|
||||
// LRU is a thread-safe, generic LRU cache with a fixed size.
|
||||
// It has zero dependencies, high performance, and full features.
|
||||
type LRU[K comparable, V any] struct {
|
||||
size int
|
||||
items map[K]*entry[K, V]
|
||||
head *entry[K, V] // Most Recently Used
|
||||
tail *entry[K, V] // Least Recently Used
|
||||
onEvict EvictCallback[K, V]
|
||||
|
||||
mu sync.Mutex
|
||||
hits atomic.Int64
|
||||
misses atomic.Int64
|
||||
}
|
||||
|
||||
// entry represents a single item in the LRU linked list.
|
||||
// It holds the key, value, and pointers to prev/next entries.
|
||||
type entry[K comparable, V any] struct {
|
||||
key K
|
||||
value V
|
||||
prev *entry[K, V]
|
||||
next *entry[K, V]
|
||||
}
|
||||
|
||||
// NewLRU creates a new LRU cache with the given size.
|
||||
// Returns nil if size <= 0, acting as a disabled cache.
|
||||
// Caps size at 100,000 for reasonableness.
|
||||
func NewLRU[K comparable, V any](size int) *LRU[K, V] {
|
||||
return NewLRUEvict[K, V](size, nil)
|
||||
}
|
||||
|
||||
// NewLRUEvict creates a new LRU cache with an eviction callback.
|
||||
// The callback is optional and called on evictions.
|
||||
// Returns nil if size <= 0.
|
||||
func NewLRUEvict[K comparable, V any](size int, onEvict EvictCallback[K, V]) *LRU[K, V] {
|
||||
if size <= 0 {
|
||||
return nil // nil = disabled cache (fast path in hot code)
|
||||
}
|
||||
if size > 100_000 {
|
||||
size = 100_000 // reasonable upper bound
|
||||
}
|
||||
return &LRU[K, V]{
|
||||
size: size,
|
||||
items: make(map[K]*entry[K, V], size),
|
||||
onEvict: onEvict,
|
||||
}
|
||||
}
|
||||
|
||||
// GetOrCompute retrieves a value or computes it if missing.
|
||||
// Ensures no double computation under concurrency.
|
||||
// Ideal for expensive computations like twwidth.
|
||||
func (c *LRU[K, V]) GetOrCompute(key K, compute func() V) V {
|
||||
if c == nil || c.size <= 0 {
|
||||
return compute()
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
if e, ok := c.items[key]; ok {
|
||||
c.moveToFront(e)
|
||||
c.hits.Add(1)
|
||||
c.mu.Unlock()
|
||||
return e.value
|
||||
}
|
||||
|
||||
c.misses.Add(1)
|
||||
value := compute() // expensive work only on real miss
|
||||
|
||||
// Double-check: someone might have added it while computing
|
||||
if e, ok := c.items[key]; ok {
|
||||
e.value = value
|
||||
c.moveToFront(e)
|
||||
c.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// Evict if needed
|
||||
if len(c.items) >= c.size {
|
||||
c.removeOldest()
|
||||
}
|
||||
|
||||
e := &entry[K, V]{key: key, value: value}
|
||||
c.addToFront(e)
|
||||
c.items[key] = e
|
||||
c.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// Get retrieves a value by key if it exists.
|
||||
// Returns the value and true if found, else zero and false.
|
||||
// Updates the entry to most recently used.
|
||||
func (c *LRU[K, V]) Get(key K) (V, bool) {
|
||||
if c == nil || c.size <= 0 {
|
||||
var zero V
|
||||
return zero, false
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
e, ok := c.items[key]
|
||||
if !ok {
|
||||
c.misses.Add(1)
|
||||
var zero V
|
||||
return zero, false
|
||||
}
|
||||
c.hits.Add(1)
|
||||
c.moveToFront(e)
|
||||
return e.value, true
|
||||
}
|
||||
|
||||
// Add inserts or updates a key-value pair.
|
||||
// Evicts the oldest if cache is full.
|
||||
// Returns true if an eviction occurred.
|
||||
func (c *LRU[K, V]) Add(key K, value V) (evicted bool) {
|
||||
if c == nil || c.size <= 0 {
|
||||
return false
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if e, ok := c.items[key]; ok {
|
||||
e.value = value
|
||||
c.moveToFront(e)
|
||||
return false
|
||||
}
|
||||
|
||||
if len(c.items) >= c.size {
|
||||
c.removeOldest()
|
||||
evicted = true
|
||||
}
|
||||
|
||||
e := &entry[K, V]{key: key, value: value}
|
||||
c.addToFront(e)
|
||||
c.items[key] = e
|
||||
return evicted
|
||||
}
|
||||
|
||||
// Remove deletes a key from the cache.
|
||||
// Returns true if the key was found and removed.
|
||||
func (c *LRU[K, V]) Remove(key K) bool {
|
||||
if c == nil || c.size <= 0 {
|
||||
return false
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
e, ok := c.items[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
c.removeNode(e)
|
||||
delete(c.items, key)
|
||||
return true
|
||||
}
|
||||
|
||||
// Purge clears all entries from the cache.
|
||||
// Calls onEvict for each entry if set.
|
||||
// Resets hit/miss counters.
|
||||
func (c *LRU[K, V]) Purge() {
|
||||
if c == nil || c.size <= 0 {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
if c.onEvict != nil {
|
||||
for key, e := range c.items {
|
||||
c.onEvict(key, e.value)
|
||||
}
|
||||
}
|
||||
c.items = make(map[K]*entry[K, V], c.size)
|
||||
c.head = nil
|
||||
c.tail = nil
|
||||
c.hits.Store(0)
|
||||
c.misses.Store(0)
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
// Len returns the current number of items in the cache.
|
||||
func (c *LRU[K, V]) Len() int {
|
||||
if c == nil || c.size <= 0 {
|
||||
return 0
|
||||
}
|
||||
c.mu.Lock()
|
||||
n := len(c.items)
|
||||
c.mu.Unlock()
|
||||
return n
|
||||
}
|
||||
|
||||
// Cap returns the maximum capacity of the cache.
|
||||
func (c *LRU[K, V]) Cap() int {
|
||||
if c == nil {
|
||||
return 0
|
||||
}
|
||||
return c.size
|
||||
}
|
||||
|
||||
// HitRate returns the cache hit ratio (0.0 to 1.0).
|
||||
// Based on hits / (hits + misses).
|
||||
func (c *LRU[K, V]) HitRate() float64 {
|
||||
h := c.hits.Load()
|
||||
m := c.misses.Load()
|
||||
total := h + m
|
||||
if total == 0 {
|
||||
return 0.0
|
||||
}
|
||||
return float64(h) / float64(total)
|
||||
}
|
||||
|
||||
// RemoveOldest removes and returns the least recently used item.
|
||||
// Returns key, value, and true if an item was removed.
|
||||
// Calls onEvict if set.
|
||||
func (c *LRU[K, V]) RemoveOldest() (key K, value V, ok bool) {
|
||||
if c == nil || c.size <= 0 {
|
||||
return
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
if c.tail == nil {
|
||||
return
|
||||
}
|
||||
|
||||
key = c.tail.key
|
||||
value = c.tail.value
|
||||
|
||||
c.removeOldest()
|
||||
return key, value, true
|
||||
}
|
||||
|
||||
// moveToFront moves an entry to the front (MRU position).
|
||||
func (c *LRU[K, V]) moveToFront(e *entry[K, V]) {
|
||||
if c.head == e {
|
||||
return
|
||||
}
|
||||
c.removeNode(e)
|
||||
c.addToFront(e)
|
||||
}
|
||||
|
||||
// addToFront adds an entry to the front of the list.
|
||||
func (c *LRU[K, V]) addToFront(e *entry[K, V]) {
|
||||
e.prev = nil
|
||||
e.next = c.head
|
||||
if c.head != nil {
|
||||
c.head.prev = e
|
||||
}
|
||||
c.head = e
|
||||
if c.tail == nil {
|
||||
c.tail = e
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// removeNode removes an entry from the linked list.
|
||||
func (c *LRU[K, V]) removeNode(e *entry[K, V]) {
|
||||
if e.prev != nil {
|
||||
e.prev.next = e.next
|
||||
} else {
|
||||
c.head = e.next
|
||||
}
|
||||
if e.next != nil {
|
||||
e.next.prev = e.prev
|
||||
} else {
|
||||
c.tail = e.prev
|
||||
}
|
||||
e.prev = nil
|
||||
e.next = nil
|
||||
}
|
||||
|
||||
// removeOldest removes the tail entry (LRU).
|
||||
// Calls onEvict if set and deletes from map.
|
||||
func (c *LRU[K, V]) removeOldest() {
|
||||
if c.tail == nil {
|
||||
return
|
||||
}
|
||||
e := c.tail
|
||||
if c.onEvict != nil {
|
||||
c.onEvict(e.key, e.value)
|
||||
}
|
||||
c.removeNode(e)
|
||||
delete(c.items, e.key)
|
||||
}
|
||||
250
vendor/github.com/olekukonko/tablewriter/pkg/twwidth/width.go
generated
vendored
250
vendor/github.com/olekukonko/tablewriter/pkg/twwidth/width.go
generated
vendored
@@ -8,42 +8,53 @@ import (
|
||||
|
||||
"github.com/clipperhouse/displaywidth"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/olekukonko/tablewriter/pkg/twcache"
|
||||
)
|
||||
|
||||
// globalOptions holds the global displaywidth configuration, including East Asian width settings.
|
||||
var globalOptions displaywidth.Options
|
||||
const (
|
||||
cacheCapacity = 8192
|
||||
|
||||
// mu protects access to condition and widthCache for thread safety.
|
||||
cachePrefix = "0:"
|
||||
cacheEastAsianPrefix = "1:"
|
||||
)
|
||||
|
||||
// Options allows for configuring width calculation on a per-call basis.
|
||||
type Options struct {
|
||||
EastAsianWidth bool
|
||||
}
|
||||
|
||||
// globalOptions holds the global displaywidth configuration, including East Asian width settings.
|
||||
var globalOptions Options
|
||||
|
||||
// mu protects access to globalOptions for thread safety.
|
||||
var mu sync.Mutex
|
||||
|
||||
// widthCache stores memoized results of Width calculations to improve performance.
|
||||
var widthCache *twcache.LRU[string, int]
|
||||
|
||||
// ansi is a compiled regular expression for stripping ANSI escape codes from strings.
|
||||
var ansi = Filter()
|
||||
|
||||
func init() {
|
||||
globalOptions = newOptions()
|
||||
widthCache = make(map[cacheKey]int)
|
||||
}
|
||||
|
||||
func newOptions() displaywidth.Options {
|
||||
// go-runewidth has default logic based on env variables and locale,
|
||||
// we want to keep that compatibility
|
||||
// Initialize global options by detecting from the environment,
|
||||
// which is the one key feature we get from go-runewidth.
|
||||
cond := runewidth.NewCondition()
|
||||
options := displaywidth.Options{
|
||||
EastAsianWidth: cond.EastAsianWidth,
|
||||
StrictEmojiNeutral: cond.StrictEmojiNeutral,
|
||||
globalOptions = Options{
|
||||
EastAsianWidth: cond.EastAsianWidth,
|
||||
}
|
||||
return options
|
||||
widthCache = twcache.NewLRU[string, int](cacheCapacity)
|
||||
}
|
||||
|
||||
// cacheKey is used as a key for memoizing string width results in widthCache.
|
||||
type cacheKey struct {
|
||||
str string // Input string
|
||||
eastAsianWidth bool // East Asian width setting
|
||||
// makeCacheKey generates a string key for the LRU cache from the input string
|
||||
// and the current East Asian width setting.
|
||||
// Prefix "0:" for false, "1:" for true.
|
||||
func makeCacheKey(str string, eastAsianWidth bool) string {
|
||||
if eastAsianWidth {
|
||||
return cacheEastAsianPrefix + str
|
||||
}
|
||||
return cachePrefix + str
|
||||
}
|
||||
|
||||
// widthCache stores memoized results of Width calculations to improve performance.
|
||||
var widthCache map[cacheKey]int
|
||||
|
||||
// Filter compiles and returns a regular expression for matching ANSI escape sequences,
|
||||
// including CSI (Control Sequence Introducer) and OSC (Operating System Command) sequences.
|
||||
// The returned regex can be used to strip ANSI codes from strings.
|
||||
@@ -62,20 +73,25 @@ func Filter() *regexp.Regexp {
|
||||
return regexp.MustCompile("(" + regCSI + "|" + regOSC + ")")
|
||||
}
|
||||
|
||||
// SetEastAsian enables or disables East Asian width handling for width calculations.
|
||||
// When the setting changes, the width cache is cleared to ensure accuracy.
|
||||
// SetOptions sets the global options for width calculation.
|
||||
// This function is thread-safe.
|
||||
func SetOptions(opts Options) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if globalOptions.EastAsianWidth != opts.EastAsianWidth {
|
||||
globalOptions = opts
|
||||
widthCache.Purge()
|
||||
}
|
||||
}
|
||||
|
||||
// SetEastAsian enables or disables East Asian width handling globally.
|
||||
// This function is thread-safe.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// twdw.SetEastAsian(true) // Enable East Asian width handling
|
||||
func SetEastAsian(enable bool) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if globalOptions.EastAsianWidth != enable {
|
||||
globalOptions.EastAsianWidth = enable
|
||||
widthCache = make(map[cacheKey]int) // Clear cache on setting change
|
||||
}
|
||||
SetOptions(Options{EastAsianWidth: enable})
|
||||
}
|
||||
|
||||
// IsEastAsian returns the current East Asian width setting.
|
||||
@@ -92,85 +108,67 @@ func IsEastAsian() bool {
|
||||
return globalOptions.EastAsianWidth
|
||||
}
|
||||
|
||||
// SetCondition updates the global runewidth.Condition used for width calculations.
|
||||
// This method is kept for backward compatibility. The condition is converted to
|
||||
// displaywidth.Options internally for better performance.
|
||||
// Deprecated: use SetOptions with the new twwidth.Options struct instead.
|
||||
// This function is kept for backward compatibility.
|
||||
func SetCondition(cond *runewidth.Condition) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
widthCache = make(map[cacheKey]int) // Clear cache on setting change
|
||||
globalOptions = conditionToOptions(cond)
|
||||
}
|
||||
|
||||
// Convert runewidth.Condition to displaywidth.Options
|
||||
func conditionToOptions(cond *runewidth.Condition) displaywidth.Options {
|
||||
return displaywidth.Options{
|
||||
EastAsianWidth: cond.EastAsianWidth,
|
||||
StrictEmojiNeutral: cond.StrictEmojiNeutral,
|
||||
newEastAsianWidth := cond.EastAsianWidth
|
||||
if globalOptions.EastAsianWidth != newEastAsianWidth {
|
||||
globalOptions.EastAsianWidth = newEastAsianWidth
|
||||
widthCache.Purge()
|
||||
}
|
||||
}
|
||||
|
||||
// Width calculates the visual width of a string, excluding ANSI escape sequences,
|
||||
// using the go-runewidth package for accurate Unicode handling. It accounts for the
|
||||
// current East Asian width setting and caches results for performance.
|
||||
// Width calculates the visual width of a string using the global cache for performance.
|
||||
// It excludes ANSI escape sequences and accounts for the global East Asian width setting.
|
||||
// This function is thread-safe.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// width := twdw.Width("Hello\x1b[31mWorld") // Returns 10
|
||||
func Width(str string) int {
|
||||
mu.Lock()
|
||||
key := cacheKey{str: str, eastAsianWidth: globalOptions.EastAsianWidth}
|
||||
if w, found := widthCache[key]; found {
|
||||
mu.Unlock()
|
||||
currentEA := IsEastAsian()
|
||||
key := makeCacheKey(str, currentEA)
|
||||
|
||||
if w, found := widthCache.Get(key); found {
|
||||
return w
|
||||
}
|
||||
mu.Unlock()
|
||||
|
||||
options := newOptions()
|
||||
options.EastAsianWidth = key.eastAsianWidth
|
||||
|
||||
opts := displaywidth.Options{EastAsianWidth: currentEA}
|
||||
stripped := ansi.ReplaceAllLiteralString(str, "")
|
||||
calculatedWidth := options.String(stripped)
|
||||
|
||||
mu.Lock()
|
||||
widthCache[key] = calculatedWidth
|
||||
mu.Unlock()
|
||||
calculatedWidth := opts.String(stripped)
|
||||
|
||||
widthCache.Add(key, calculatedWidth)
|
||||
return calculatedWidth
|
||||
}
|
||||
|
||||
// WidthNoCache calculates the visual width of a string without using or
|
||||
// updating the global cache. It uses the current global East Asian width setting.
|
||||
// This function is intended for internal use (e.g., benchmarking) and is thread-safe.
|
||||
// WidthWithOptions calculates the visual width of a string with specific options,
|
||||
// bypassing the global settings and cache. This is useful for one-shot calculations
|
||||
// where global state is not desired.
|
||||
func WidthWithOptions(str string, opts Options) int {
|
||||
dwOpts := displaywidth.Options{EastAsianWidth: opts.EastAsianWidth}
|
||||
stripped := ansi.ReplaceAllLiteralString(str, "")
|
||||
return dwOpts.String(stripped)
|
||||
}
|
||||
|
||||
// WidthNoCache calculates the visual width of a string without using the global cache.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// width := twdw.WidthNoCache("Hello\x1b[31mWorld") // Returns 10
|
||||
func WidthNoCache(str string) int {
|
||||
mu.Lock()
|
||||
currentEA := globalOptions.EastAsianWidth
|
||||
mu.Unlock()
|
||||
|
||||
options := newOptions()
|
||||
options.EastAsianWidth = currentEA
|
||||
|
||||
stripped := ansi.ReplaceAllLiteralString(str, "")
|
||||
return options.String(stripped)
|
||||
// This function's behavior is equivalent to a one-shot calculation
|
||||
// using the current global options. The WidthWithOptions function
|
||||
// does not interact with the cache, thus fulfilling the requirement.
|
||||
return WidthWithOptions(str, Options{EastAsianWidth: IsEastAsian()})
|
||||
}
|
||||
|
||||
// Display calculates the visual width of a string, excluding ANSI escape sequences,
|
||||
// using the provided runewidth condition. Unlike Width, it does not use caching
|
||||
// and is intended for cases where a specific condition is required.
|
||||
// This function is thread-safe with respect to the provided condition.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// cond := runewidth.NewCondition()
|
||||
// width := twdw.Display(cond, "Hello\x1b[31mWorld") // Returns 10
|
||||
// Deprecated: use WidthWithOptions with the new twwidth.Options struct instead.
|
||||
// This function is kept for backward compatibility.
|
||||
func Display(cond *runewidth.Condition, str string) int {
|
||||
options := conditionToOptions(cond)
|
||||
return options.String(ansi.ReplaceAllLiteralString(str, ""))
|
||||
opts := Options{EastAsianWidth: cond.EastAsianWidth}
|
||||
return WidthWithOptions(str, opts)
|
||||
}
|
||||
|
||||
// Truncate shortens a string to fit within a specified visual width, optionally
|
||||
@@ -217,31 +215,34 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
|
||||
// Case 3: String fits completely or fits with suffix.
|
||||
// Here, maxWidth is the total budget for the line.
|
||||
if sDisplayWidth <= maxWidth {
|
||||
// If the string contains ANSI, we must ensure it ends with a reset
|
||||
// to prevent bleeding, even if we don't truncate.
|
||||
safeS := s
|
||||
if strings.Contains(s, "\x1b") && !strings.HasSuffix(s, "\x1b[0m") {
|
||||
safeS += "\x1b[0m"
|
||||
}
|
||||
|
||||
if len(suffixStr) == 0 { // No suffix.
|
||||
return s
|
||||
return safeS
|
||||
}
|
||||
// Suffix is provided. Check if s + suffix fits.
|
||||
if sDisplayWidth+suffixDisplayWidth <= maxWidth {
|
||||
return s + suffixStr
|
||||
return safeS + suffixStr
|
||||
}
|
||||
// s fits, but s + suffix is too long. Return s.
|
||||
return s
|
||||
// s fits, but s + suffix is too long. Return s (with reset if needed).
|
||||
return safeS
|
||||
}
|
||||
|
||||
// Case 4: String needs truncation (sDisplayWidth > maxWidth).
|
||||
// maxWidth is the total budget for the final string (content + suffix).
|
||||
|
||||
// Capture the global EastAsianWidth setting once for consistent use
|
||||
mu.Lock()
|
||||
currentGlobalEastAsianWidth := globalOptions.EastAsianWidth
|
||||
mu.Unlock()
|
||||
currentGlobalEastAsianWidth := IsEastAsian()
|
||||
|
||||
// Special case for EastAsian true: if only suffix fits, return suffix.
|
||||
// This was derived from previous test behavior.
|
||||
if len(suffixStr) > 0 && currentGlobalEastAsianWidth {
|
||||
provisionalContentWidth := maxWidth - suffixDisplayWidth
|
||||
if provisionalContentWidth == 0 { // Exactly enough space for suffix only
|
||||
return suffixStr // <<<< MODIFIED: No ANSI reset here
|
||||
return suffixStr
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,7 +264,6 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
|
||||
}
|
||||
return "" // Cannot fit anything.
|
||||
}
|
||||
// If targetContentForIteration is 0, loop won't run, result will be empty string, then suffix is added.
|
||||
|
||||
var contentBuf bytes.Buffer
|
||||
var currentContentDisplayWidth int
|
||||
@@ -271,8 +271,7 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
|
||||
inAnsiSequence := false
|
||||
ansiWrittenToContent := false
|
||||
|
||||
options := newOptions()
|
||||
options.EastAsianWidth = currentGlobalEastAsianWidth
|
||||
dwOpts := displaywidth.Options{EastAsianWidth: currentGlobalEastAsianWidth}
|
||||
|
||||
for _, r := range s {
|
||||
if r == '\x1b' {
|
||||
@@ -306,7 +305,7 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
|
||||
ansiSeqBuf.Reset()
|
||||
}
|
||||
} else { // Normal character
|
||||
runeDisplayWidth := options.Rune(r)
|
||||
runeDisplayWidth := dwOpts.Rune(r)
|
||||
if targetContentForIteration == 0 { // No budget for content at all
|
||||
break
|
||||
}
|
||||
@@ -320,32 +319,51 @@ func Truncate(s string, maxWidth int, suffix ...string) string {
|
||||
|
||||
result := contentBuf.String()
|
||||
|
||||
// Suffix is added if:
|
||||
// 1. A suffix string is provided.
|
||||
// 2. Truncation actually happened (sDisplayWidth > maxWidth originally)
|
||||
// OR if the content part is empty but a suffix is meant to be shown
|
||||
// (e.g. targetContentForIteration was 0).
|
||||
if len(suffixStr) > 0 {
|
||||
// Add suffix if we are in the truncation path (sDisplayWidth > maxWidth)
|
||||
// OR if targetContentForIteration was 0 (meaning only suffix should be shown)
|
||||
// but we must ensure we don't exceed original maxWidth.
|
||||
// The logic above for targetContentForIteration already ensures space.
|
||||
|
||||
needsReset := false
|
||||
// Condition for reset: if styling was active in 's' and might affect suffix
|
||||
if (ansiWrittenToContent || (inAnsiSequence && strings.Contains(s, "\x1b["))) && (currentContentDisplayWidth > 0 || ansiWrittenToContent) {
|
||||
if !strings.HasSuffix(result, "\x1b[0m") {
|
||||
needsReset = true
|
||||
}
|
||||
} else if currentContentDisplayWidth > 0 && strings.Contains(result, "\x1b[") && !strings.HasSuffix(result, "\x1b[0m") && strings.Contains(s, "\x1b[") {
|
||||
// If result has content and ANSI, and original had ANSI, and result not already reset
|
||||
// Determine if we need to append a reset sequence to prevent color bleeding.
|
||||
// This is needed if we wrote any ANSI codes or if the input had active codes
|
||||
// that we might have cut off or left open.
|
||||
needsReset := false
|
||||
if (ansiWrittenToContent || (inAnsiSequence && strings.Contains(s, "\x1b["))) && (currentContentDisplayWidth > 0 || ansiWrittenToContent) {
|
||||
if !strings.HasSuffix(result, "\x1b[0m") {
|
||||
needsReset = true
|
||||
}
|
||||
} else if currentContentDisplayWidth > 0 && strings.Contains(result, "\x1b[") && !strings.HasSuffix(result, "\x1b[0m") && strings.Contains(s, "\x1b[") {
|
||||
needsReset = true
|
||||
}
|
||||
|
||||
if needsReset {
|
||||
result += "\x1b[0m"
|
||||
}
|
||||
if needsReset {
|
||||
result += "\x1b[0m"
|
||||
}
|
||||
|
||||
// Suffix is added if provided.
|
||||
if len(suffixStr) > 0 {
|
||||
result += suffixStr
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// SetCacheCapacity changes the cache size dynamically
|
||||
// If capacity <= 0, disables caching entirely
|
||||
func SetCacheCapacity(capacity int) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if capacity <= 0 {
|
||||
widthCache = nil // nil = fully disabled
|
||||
return
|
||||
}
|
||||
|
||||
newCache := twcache.NewLRU[string, int](capacity)
|
||||
widthCache = newCache
|
||||
}
|
||||
|
||||
// GetCacheStats returns current cache statistics
|
||||
func GetCacheStats() (size, capacity int, hitRate float64) {
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
if widthCache == nil {
|
||||
return 0, 0, 0
|
||||
}
|
||||
return widthCache.Len(), widthCache.Cap(), widthCache.HitRate()
|
||||
}
|
||||
|
||||
18
vendor/github.com/olekukonko/tablewriter/tablewriter.go
generated
vendored
18
vendor/github.com/olekukonko/tablewriter/tablewriter.go
generated
vendored
@@ -8,11 +8,11 @@ import (
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/olekukonko/errors"
|
||||
"github.com/olekukonko/ll"
|
||||
"github.com/olekukonko/ll/lh"
|
||||
"github.com/olekukonko/tablewriter/pkg/twcache"
|
||||
"github.com/olekukonko/tablewriter/pkg/twwarp"
|
||||
"github.com/olekukonko/tablewriter/pkg/twwidth"
|
||||
"github.com/olekukonko/tablewriter/renderer"
|
||||
@@ -52,9 +52,7 @@ type Table struct {
|
||||
streamRowCounter int // Counter for rows rendered in streaming mode (0-indexed logical rows)
|
||||
|
||||
// cache
|
||||
stringerCache map[reflect.Type]reflect.Value // Cache for stringer reflection
|
||||
stringerCacheMu sync.RWMutex // Mutex for thread-safe cache access
|
||||
stringerCacheEnabled bool // Flag to enable/disable caching
|
||||
stringerCache twcache.Cache[reflect.Type, reflect.Value] // Cache for stringer reflection
|
||||
|
||||
batchRenderNumCols int
|
||||
isBatchRenderNumColsSet bool
|
||||
@@ -126,8 +124,7 @@ func NewTable(w io.Writer, opts ...Option) *Table {
|
||||
streamRowCounter: 0,
|
||||
|
||||
// Cache
|
||||
stringerCache: make(map[reflect.Type]reflect.Value),
|
||||
stringerCacheEnabled: false, // Disabled by default
|
||||
stringerCache: twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity),
|
||||
}
|
||||
|
||||
// set Options
|
||||
@@ -483,10 +480,11 @@ func (t *Table) Reset() {
|
||||
t.streamRowCounter = 0
|
||||
|
||||
// The stringer and its cache are part of the table's configuration,
|
||||
if t.stringerCacheEnabled {
|
||||
t.stringerCacheMu.Lock()
|
||||
t.stringerCache = make(map[reflect.Type]reflect.Value)
|
||||
t.stringerCacheMu.Unlock()
|
||||
if t.stringerCache == nil {
|
||||
t.stringerCache = twcache.NewLRU[reflect.Type, reflect.Value](tw.DefaultCacheStringCapacity)
|
||||
t.logger.Debug("Reset(): Stringer cache reset to default capacity.")
|
||||
} else {
|
||||
t.stringerCache.Purge()
|
||||
t.logger.Debug("Reset(): Stringer cache cleared.")
|
||||
}
|
||||
|
||||
|
||||
2
vendor/github.com/olekukonko/tablewriter/tw/tw.go
generated
vendored
2
vendor/github.com/olekukonko/tablewriter/tw/tw.go
generated
vendored
@@ -8,6 +8,8 @@ const (
|
||||
Success = 1 // Operation succeeded
|
||||
|
||||
MinimumColumnWidth = 8
|
||||
|
||||
DefaultCacheStringCapacity = 10 * 1024 // 10 KB
|
||||
)
|
||||
|
||||
const (
|
||||
|
||||
29
vendor/github.com/olekukonko/tablewriter/zoo.go
generated
vendored
29
vendor/github.com/olekukonko/tablewriter/zoo.go
generated
vendored
@@ -1064,17 +1064,13 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
|
||||
t.logger.Debugf("convertToString attempt %v using %v", input, t.stringer)
|
||||
|
||||
inputType := reflect.TypeOf(input)
|
||||
stringerFuncVal := reflect.ValueOf(t.stringer)
|
||||
stringerFuncType := stringerFuncVal.Type()
|
||||
|
||||
// Cache lookup (simplified, actual cache logic can be more complex)
|
||||
if t.stringerCacheEnabled {
|
||||
t.stringerCacheMu.RLock()
|
||||
cachedFunc, ok := t.stringerCache[inputType]
|
||||
t.stringerCacheMu.RUnlock()
|
||||
if ok {
|
||||
// Add proper type checking for cachedFunc against input here if necessary
|
||||
// Cache lookup using twcache.LRU
|
||||
// This assumes t.stringerCache is *twcache.LRU[reflect.Type, reflect.Value]
|
||||
if t.stringerCache != nil {
|
||||
if cachedFunc, ok := t.stringerCache.Get(inputType); ok {
|
||||
t.logger.Debugf("convertToStringer: Cache hit for type %v", inputType)
|
||||
// We can proceed to call it immediately because it's already been validated/cached
|
||||
results := cachedFunc.Call([]reflect.Value{reflect.ValueOf(input)})
|
||||
if len(results) == 1 && results[0].Type() == reflect.TypeOf([]string{}) {
|
||||
return results[0].Interface().([]string), nil
|
||||
@@ -1082,6 +1078,9 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
stringerFuncVal := reflect.ValueOf(t.stringer)
|
||||
stringerFuncType := stringerFuncVal.Type()
|
||||
|
||||
// Robust type checking for the stringer function
|
||||
validSignature := stringerFuncVal.Kind() == reflect.Func &&
|
||||
stringerFuncType.NumIn() == 1 &&
|
||||
@@ -1105,10 +1104,6 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
|
||||
}
|
||||
} else if paramType.Kind() == reflect.Interface || (paramType.Kind() == reflect.Ptr && paramType.Elem().Kind() != reflect.Interface) {
|
||||
// If input is nil, it can be assigned if stringer expects an interface or a pointer type
|
||||
// (but not a pointer to an interface, which is rare for stringers).
|
||||
// A nil value for a concrete type parameter would cause a panic on Call.
|
||||
// So, if paramType is not an interface/pointer, and input is nil, it's an issue.
|
||||
// This needs careful handling. For now, assume assignable if interface/pointer.
|
||||
assignable = true
|
||||
}
|
||||
|
||||
@@ -1120,7 +1115,6 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
|
||||
if input == nil {
|
||||
// If input is nil, we must pass a zero value of the stringer's parameter type
|
||||
// if that type is a pointer or interface.
|
||||
// Passing reflect.ValueOf(nil) directly will cause issues if paramType is concrete.
|
||||
callArgs = []reflect.Value{reflect.Zero(paramType)}
|
||||
} else {
|
||||
callArgs = []reflect.Value{reflect.ValueOf(input)}
|
||||
@@ -1128,10 +1122,9 @@ func (t *Table) convertToStringer(input interface{}) ([]string, error) {
|
||||
|
||||
resultValues := stringerFuncVal.Call(callArgs)
|
||||
|
||||
if t.stringerCacheEnabled && inputType != nil { // Only cache if inputType is valid
|
||||
t.stringerCacheMu.Lock()
|
||||
t.stringerCache[inputType] = stringerFuncVal
|
||||
t.stringerCacheMu.Unlock()
|
||||
// Add to cache if enabled (not nil) and input type is valid
|
||||
if t.stringerCache != nil && inputType != nil {
|
||||
t.stringerCache.Add(inputType, stringerFuncVal)
|
||||
}
|
||||
|
||||
return resultValues[0].Interface().([]string), nil
|
||||
|
||||
9
vendor/modules.txt
vendored
9
vendor/modules.txt
vendored
@@ -255,13 +255,13 @@ github.com/cespare/xxhash/v2
|
||||
# github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73
|
||||
## explicit
|
||||
github.com/cevaris/ordered_map
|
||||
# github.com/clipperhouse/displaywidth v0.3.1
|
||||
# github.com/clipperhouse/displaywidth v0.6.0
|
||||
## explicit; go 1.18
|
||||
github.com/clipperhouse/displaywidth
|
||||
# github.com/clipperhouse/stringish v0.1.1
|
||||
## explicit; go 1.18
|
||||
github.com/clipperhouse/stringish
|
||||
# github.com/clipperhouse/uax29/v2 v2.2.0
|
||||
# github.com/clipperhouse/uax29/v2 v2.3.0
|
||||
## explicit; go 1.18
|
||||
github.com/clipperhouse/uax29/v2/graphemes
|
||||
github.com/clipperhouse/uax29/v2/internal/iterators
|
||||
@@ -1197,14 +1197,15 @@ github.com/olekukonko/cat
|
||||
# github.com/olekukonko/errors v1.1.0
|
||||
## explicit; go 1.21
|
||||
github.com/olekukonko/errors
|
||||
# github.com/olekukonko/ll v0.1.2
|
||||
# github.com/olekukonko/ll v0.1.3
|
||||
## explicit; go 1.21
|
||||
github.com/olekukonko/ll
|
||||
github.com/olekukonko/ll/lh
|
||||
github.com/olekukonko/ll/lx
|
||||
# github.com/olekukonko/tablewriter v1.1.1
|
||||
# github.com/olekukonko/tablewriter v1.1.2
|
||||
## explicit; go 1.21
|
||||
github.com/olekukonko/tablewriter
|
||||
github.com/olekukonko/tablewriter/pkg/twcache
|
||||
github.com/olekukonko/tablewriter/pkg/twwarp
|
||||
github.com/olekukonko/tablewriter/pkg/twwidth
|
||||
github.com/olekukonko/tablewriter/renderer
|
||||
|
||||
Reference in New Issue
Block a user