first commit
This commit is contained in:
commit
dc00175f01
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
report.xml
|
||||
coverage.out
|
||||
*.xlsx
|
81
Earthfile
Normal file
81
Earthfile
Normal file
@ -0,0 +1,81 @@
|
||||
VERSION 0.7
|
||||
ARG --global GO_VERSION=1.20.2
|
||||
ARG --global GOLANGCILINT_VERSION=1.51.2
|
||||
ARG --global GORELEASER_VERSION=1.16.1
|
||||
ARG --global GOTESTSUM_VERSION=1.9.0
|
||||
FROM golang:$GO_VERSION-alpine3.16
|
||||
WORKDIR /amseltools
|
||||
|
||||
SAVE_CODE:
|
||||
COMMAND
|
||||
SAVE ARTIFACT go.mod AS LOCAL go.mod
|
||||
SAVE ARTIFACT go.sum AS LOCAL go.sum
|
||||
SAVE ARTIFACT *.go AS LOCAL .
|
||||
|
||||
ci:
|
||||
BUILD +lint
|
||||
BUILD +test
|
||||
# BUILD +release-dev
|
||||
|
||||
code:
|
||||
COPY go.mod go.sum ./
|
||||
COPY *.go ./
|
||||
COPY --dir vendor ./
|
||||
COPY --dir internal ./
|
||||
SAVE ARTIFACT . code
|
||||
|
||||
install-deps:
|
||||
FROM +base
|
||||
RUN set -ex \
|
||||
&& apk add --no-cache \
|
||||
build-base \
|
||||
coreutils \
|
||||
git
|
||||
RUN go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@v$GOLANGCILINT_VERSION
|
||||
RUN go install -v github.com/goreleaser/goreleaser@v$GORELEASER_VERSION
|
||||
RUN go install -v gotest.tools/gotestsum@v$GOTESTSUM_VERSION
|
||||
RUN go install -v golang.org/x/tools/cmd/stringer@latest
|
||||
SAVE ARTIFACT /go/bin gobin
|
||||
SAVE ARTIFACT /lib lib
|
||||
SAVE ARTIFACT /usr usr
|
||||
SAVE IMAGE --push git.wobcom.de:5050/smartmetering/mbuslight:cache-deps
|
||||
|
||||
deps:
|
||||
COPY +install-deps/gobin /go/bin
|
||||
COPY +install-deps/lib /lib
|
||||
COPY +install-deps/usr /usr
|
||||
|
||||
generate:
|
||||
FROM +deps
|
||||
COPY +code/code .
|
||||
RUN go generate ./...
|
||||
DO +SAVE_CODE
|
||||
|
||||
test:
|
||||
FROM +deps
|
||||
COPY +code/code ./
|
||||
RUN gotestsum \
|
||||
--junitfile report.xml \
|
||||
--format standard-verbose \
|
||||
-- \
|
||||
-race \
|
||||
-cover -coverprofile=coverage.out \
|
||||
-failfast \
|
||||
./... \
|
||||
-timeout=120m
|
||||
SAVE ARTIFACT report.xml AS LOCAL report.xml
|
||||
SAVE ARTIFACT coverage.out AS LOCAL coverage.out
|
||||
|
||||
lint:
|
||||
FROM +deps
|
||||
COPY +code/code ./
|
||||
RUN golangci-lint run
|
||||
|
||||
tidy:
|
||||
RUN apk add --no-cache git
|
||||
COPY +code/code ./
|
||||
RUN go mod tidy
|
||||
RUN go mod vendor
|
||||
SAVE ARTIFACT go.mod AS LOCAL go.mod
|
||||
SAVE ARTIFACT go.sum AS LOCAL go.sum
|
||||
SAVE ARTIFACT vendor AS LOCAL vendor
|
33
go.mod
Normal file
33
go.mod
Normal file
@ -0,0 +1,33 @@
|
||||
module go.xsfx.dev/amseltools
|
||||
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/charmbracelet/log v0.2.1
|
||||
github.com/matryer/is v1.4.1
|
||||
github.com/spf13/cobra v1.6.1
|
||||
github.com/xuri/excelize/v2 v2.7.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.7.1 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.18 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.1 // indirect
|
||||
github.com/richardlehane/mscfb v1.0.4 // indirect
|
||||
github.com/richardlehane/msoleps v1.0.3 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 // indirect
|
||||
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 // indirect
|
||||
golang.org/x/crypto v0.5.0 // indirect
|
||||
golang.org/x/net v0.5.0 // indirect
|
||||
golang.org/x/sys v0.6.0 // indirect
|
||||
golang.org/x/text v0.6.0 // indirect
|
||||
)
|
94
go.sum
Normal file
94
go.sum
Normal file
@ -0,0 +1,94 @@
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/charmbracelet/lipgloss v0.7.1 h1:17WMwi7N1b1rVWOjMT+rCh7sQkvDU75B2hbZpc5Kc1E=
|
||||
github.com/charmbracelet/lipgloss v0.7.1/go.mod h1:yG0k3giv8Qj8edTCbbg6AlQ5e8KNWpFujkNawKNhE2c=
|
||||
github.com/charmbracelet/log v0.2.1 h1:1z7jpkk4yKyjwlmKmKMM5qnEDSpV32E7XtWhuv0mTZE=
|
||||
github.com/charmbracelet/log v0.2.1/go.mod h1:GwFfjewhcVDWLrpAbY5A0Hin9YOlEn40eWT4PNaxFT4=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
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/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
|
||||
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/matryer/is v1.4.1 h1:55ehd8zaGABKLXQUe2awZ99BD/PTc2ls+KV/dXphgEQ=
|
||||
github.com/matryer/is v1.4.1/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98=
|
||||
github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
|
||||
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
|
||||
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
|
||||
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/richardlehane/mscfb v1.0.4 h1:WULscsljNPConisD5hR0+OyZjwK46Pfyr6mPu5ZawpM=
|
||||
github.com/richardlehane/mscfb v1.0.4/go.mod h1:YzVpcZg9czvAuhk9T+a3avCpcFPMUWm7gK3DypaEsUk=
|
||||
github.com/richardlehane/msoleps v1.0.1/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/richardlehane/msoleps v1.0.3 h1:aznSZzrwYRl3rLKRT3gUk9am7T/mLNSnJINvN0AQoVM=
|
||||
github.com/richardlehane/msoleps v1.0.3/go.mod h1:BWev5JBpU9Ko2WAgmZEuiz4/u3ZYTKbjLycmwiWUfWg=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=
|
||||
github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470 h1:6932x8ltq1w4utjmfMPVj09jdMlkY0aiA6+Skbtl3/c=
|
||||
github.com/xuri/efp v0.0.0-20220603152613-6918739fd470/go.mod h1:ybY/Jr0T0GTCnYjKqmdwxyxn2BQf2RcQIIvex5QldPI=
|
||||
github.com/xuri/excelize/v2 v2.7.0 h1:Hri/czwyRCW6f6zrCDWXcXKshlq4xAZNpNOpdfnFhEw=
|
||||
github.com/xuri/excelize/v2 v2.7.0/go.mod h1:ebKlRoS+rGyLMyUx3ErBECXs/HNYqyj+PbkkKRK5vSI=
|
||||
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22 h1:OAmKAfT06//esDdpi/DZ8Qsdt4+M5+ltca05dA5bG2M=
|
||||
github.com/xuri/nfp v0.0.0-20220409054826-5e722a1d9e22/go.mod h1:WwHg+CVyzlv/TX9xqBFXEZAuxOPxn2k1GNHwG41IIUQ=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69 h1:Lj6HJGCSn5AjxRAH2+r35Mir4icalbqku+CLUtjnvXY=
|
||||
golang.org/x/image v0.0.0-20220902085622-e7cb96979f69/go.mod h1:doUCurBvlfPMKfmIpRIywoHmhN3VyhnoFDbvIEWF4hY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.5.0 h1:GyT4nK/YDHSqa1c4753ouYCDajOYKTja9Xb/OHtgvSw=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
97
internal/dirtoexcel/dirtoexcel.go
Normal file
97
internal/dirtoexcel/dirtoexcel.go
Normal file
@ -0,0 +1,97 @@
|
||||
package dirtoexcel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/xuri/excelize/v2"
|
||||
)
|
||||
|
||||
const re = `^(?P<company>[a-zA-Z0-9-]+)(?:_[a-zA-Z]+)?_(?P<value>\d+\.\d{1,2})`
|
||||
|
||||
func init() {
|
||||
log.SetDefault(
|
||||
log.NewWithOptions(os.Stderr, log.Options{ReportTimestamp: true, TimeFormat: time.Kitchen}),
|
||||
)
|
||||
}
|
||||
|
||||
func Run(cmd *cobra.Command, args []string) {
|
||||
outfile, err := cmd.Flags().GetString("out")
|
||||
if err != nil {
|
||||
log.Fatal("failed to get outfile string", "error", err)
|
||||
}
|
||||
|
||||
if outfile == "" {
|
||||
outfile = fmt.Sprintf("%d.xlsx", time.Now().Unix())
|
||||
}
|
||||
|
||||
if err := Create(outfile, args[0]); err != nil {
|
||||
log.Fatal("failed to create excel", "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Create(out, dir string) error {
|
||||
log := log.With("dir", dir, "out", out)
|
||||
|
||||
f := excelize.NewFile()
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
log.Error("failed to close excel file")
|
||||
}
|
||||
}()
|
||||
|
||||
files, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read dir: %w", err)
|
||||
}
|
||||
|
||||
r, err := regexp.Compile(re)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to compile regex: %w", err)
|
||||
}
|
||||
|
||||
for i, j := range files {
|
||||
if j.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
log.Info("found", "file", j.Name())
|
||||
|
||||
res := r.FindAllStringSubmatch(j.Name(), -1)
|
||||
if res == nil {
|
||||
return fmt.Errorf("nothing found")
|
||||
}
|
||||
|
||||
if len(res) != 1 || len(res[0]) != 3 {
|
||||
return fmt.Errorf("strange numbers of matches")
|
||||
}
|
||||
|
||||
if err := f.SetCellStr("Sheet1", fmt.Sprintf("A%d", i+1), res[0][1]); err != nil {
|
||||
return fmt.Errorf("failed to set cell: %w", err)
|
||||
}
|
||||
|
||||
value, err := strconv.ParseFloat(res[0][2], 32)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse value: %w", err)
|
||||
}
|
||||
|
||||
if err := f.SetCellValue("Sheet1", fmt.Sprintf("B%d", i+1), math.Round(value*100)/100); err != nil {
|
||||
return fmt.Errorf("failed to set cell: %w", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if err := f.SaveAs(out); err != nil {
|
||||
return fmt.Errorf("failed to save excel: %w", err)
|
||||
}
|
||||
|
||||
log.Info("created excel")
|
||||
|
||||
return nil
|
||||
}
|
84
internal/dirtoexcel/dirtoexcel_test.go
Normal file
84
internal/dirtoexcel/dirtoexcel_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
package dirtoexcel_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/matryer/is"
|
||||
"github.com/xuri/excelize/v2"
|
||||
"go.xsfx.dev/amseltools/internal/dirtoexcel"
|
||||
)
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
type want struct {
|
||||
Company string
|
||||
Value string
|
||||
}
|
||||
|
||||
tables := []struct {
|
||||
name string
|
||||
files []string
|
||||
want []want
|
||||
}{
|
||||
{
|
||||
"00",
|
||||
[]string{
|
||||
"amazon_foo_12.34_bar.jpg",
|
||||
"bma-zon_12.34_bar.jpg",
|
||||
},
|
||||
[]want{
|
||||
{
|
||||
"amazon",
|
||||
"12.34",
|
||||
},
|
||||
{
|
||||
"bma-zon",
|
||||
"12.34",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
is := is.New(t)
|
||||
|
||||
for _, tt := range tables {
|
||||
tt := tt
|
||||
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
dir := t.TempDir()
|
||||
for _, i := range tt.files {
|
||||
_, err := os.Create(path.Join(dir, i))
|
||||
is.NoErr(err)
|
||||
}
|
||||
|
||||
outfile := path.Join(dir, "foo.xlsx")
|
||||
|
||||
// Create the excel.
|
||||
err := dirtoexcel.Create(outfile, dir)
|
||||
is.NoErr(err)
|
||||
|
||||
// Read the excel.
|
||||
f, err := excelize.OpenFile(outfile)
|
||||
is.NoErr(err)
|
||||
|
||||
defer func() {
|
||||
err := f.Close()
|
||||
is.NoErr(err)
|
||||
}()
|
||||
|
||||
for i := range tt.files {
|
||||
company, err := f.GetCellValue("sheet1", fmt.Sprintf("A%d", i+1))
|
||||
is.NoErr(err)
|
||||
|
||||
is.Equal(tt.want[i].Company, company)
|
||||
|
||||
value, err := f.GetCellValue("sheet1", fmt.Sprintf("B%d", i+1))
|
||||
is.NoErr(err)
|
||||
|
||||
is.Equal(tt.want[i].Value, value)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
29
main.go
Normal file
29
main.go
Normal file
@ -0,0 +1,29 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"go.xsfx.dev/amseltools/internal/dirtoexcel"
|
||||
)
|
||||
|
||||
var rootCmd = &cobra.Command{}
|
||||
|
||||
var dirToExcelCmd = &cobra.Command{
|
||||
Use: "dir-to-excel [directory]",
|
||||
Run: dirtoexcel.Run,
|
||||
Args: cobra.ExactArgs(1),
|
||||
}
|
||||
|
||||
func main() {
|
||||
rootCmd.AddCommand(dirToExcelCmd)
|
||||
|
||||
// Flags.
|
||||
dirToExcelCmd.Flags().StringP("out", "o", "", "output file")
|
||||
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
21
vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE
generated
vendored
Normal file
21
vendor/github.com/aymanbagabas/go-osc52/v2/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Ayman Bagabas
|
||||
|
||||
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.
|
83
vendor/github.com/aymanbagabas/go-osc52/v2/README.md
generated
vendored
Normal file
83
vendor/github.com/aymanbagabas/go-osc52/v2/README.md
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
|
||||
# go-osc52
|
||||
|
||||
<p>
|
||||
<a href="https://github.com/aymanbagabas/go-osc52/releases"><img src="https://img.shields.io/github/release/aymanbagabas/go-osc52.svg" alt="Latest Release"></a>
|
||||
<a href="https://pkg.go.dev/github.com/aymanbagabas/go-osc52/v2?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
|
||||
</p>
|
||||
|
||||
A Go library to work with the [ANSI OSC52](https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands) terminal sequence.
|
||||
|
||||
## Usage
|
||||
|
||||
You can use this small library to construct an ANSI OSC52 sequence suitable for
|
||||
your terminal.
|
||||
|
||||
|
||||
### Example
|
||||
|
||||
```go
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
|
||||
"github.com/aymanbagabas/go-osc52/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := "Hello World!"
|
||||
|
||||
// Copy `s` to system clipboard
|
||||
osc52.New(s).WriteTo(os.Stderr)
|
||||
|
||||
// Copy `s` to primary clipboard (X11)
|
||||
osc52.New(s).Primary().WriteTo(os.Stderr)
|
||||
|
||||
// Query the clipboard
|
||||
osc52.Query().WriteTo(os.Stderr)
|
||||
|
||||
// Clear system clipboard
|
||||
osc52.Clear().WriteTo(os.Stderr)
|
||||
|
||||
// Use the fmt.Stringer interface to copy `s` to system clipboard
|
||||
fmt.Fprint(os.Stderr, osc52.New(s))
|
||||
|
||||
// Or to primary clipboard
|
||||
fmt.Fprint(os.Stderr, osc52.New(s).Primary())
|
||||
}
|
||||
```
|
||||
|
||||
## SSH Example
|
||||
|
||||
You can use this over SSH using [gliderlabs/ssh](https://github.com/gliderlabs/ssh) for instance:
|
||||
|
||||
```go
|
||||
var sshSession ssh.Session
|
||||
seq := osc52.New("Hello awesome!")
|
||||
// Check if term is screen or tmux
|
||||
pty, _, _ := s.Pty()
|
||||
if pty.Term == "screen" {
|
||||
seq = seq.Screen()
|
||||
} else if isTmux {
|
||||
seq = seq.Tmux()
|
||||
}
|
||||
seq.WriteTo(sshSession.Stderr())
|
||||
```
|
||||
|
||||
## Tmux
|
||||
|
||||
Make sure you have `set-clipboard on` in your config, otherwise, tmux won't
|
||||
allow your application to access the clipboard [^1].
|
||||
|
||||
Using the tmux option, `osc52.TmuxMode` or `osc52.New(...).Tmux()`, wraps the
|
||||
OSC52 sequence in a special tmux DCS sequence and pass it to the outer
|
||||
terminal. This requires `allow-passthrough on` in your config.
|
||||
`allow-passthrough` is no longer enabled by default
|
||||
[since tmux 3.3a](https://github.com/tmux/tmux/issues/3218#issuecomment-1153089282) [^2].
|
||||
|
||||
[^1]: See [tmux clipboard](https://github.com/tmux/tmux/wiki/Clipboard)
|
||||
[^2]: [What is allow-passthrough](https://github.com/tmux/tmux/wiki/FAQ#what-is-the-passthrough-escape-sequence-and-how-do-i-use-it)
|
||||
|
||||
## Credits
|
||||
|
||||
* [vim-oscyank](https://github.com/ojroques/vim-oscyank) this is heavily inspired by vim-oscyank.
|
305
vendor/github.com/aymanbagabas/go-osc52/v2/osc52.go
generated
vendored
Normal file
305
vendor/github.com/aymanbagabas/go-osc52/v2/osc52.go
generated
vendored
Normal file
@ -0,0 +1,305 @@
|
||||
// OSC52 is a terminal escape sequence that allows copying text to the clipboard.
|
||||
//
|
||||
// The sequence consists of the following:
|
||||
//
|
||||
// OSC 52 ; Pc ; Pd BEL
|
||||
//
|
||||
// Pc is the clipboard choice:
|
||||
//
|
||||
// c: clipboard
|
||||
// p: primary
|
||||
// q: secondary (not supported)
|
||||
// s: select (not supported)
|
||||
// 0-7: cut-buffers (not supported)
|
||||
//
|
||||
// Pd is the data to copy to the clipboard. This string should be encoded in
|
||||
// base64 (RFC-4648).
|
||||
//
|
||||
// If Pd is "?", the terminal replies to the host with the current contents of
|
||||
// the clipboard.
|
||||
//
|
||||
// If Pd is neither a base64 string nor "?", the terminal clears the clipboard.
|
||||
//
|
||||
// See https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Operating-System-Commands
|
||||
// where Ps = 52 => Manipulate Selection Data.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// // copy "hello world" to the system clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.New("hello world"))
|
||||
//
|
||||
// // copy "hello world" to the primary Clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.New("hello world").Primary())
|
||||
//
|
||||
// // limit the size of the string to copy 10 bytes
|
||||
// fmt.Fprint(os.Stderr, osc52.New("0123456789").Limit(10))
|
||||
//
|
||||
// // escape the OSC52 sequence for screen using DCS sequences
|
||||
// fmt.Fprint(os.Stderr, osc52.New("hello world").Screen())
|
||||
//
|
||||
// // escape the OSC52 sequence for Tmux
|
||||
// fmt.Fprint(os.Stderr, osc52.New("hello world").Tmux())
|
||||
//
|
||||
// // query the system Clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.Query())
|
||||
//
|
||||
// // query the primary clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.Query().Primary())
|
||||
//
|
||||
// // clear the system Clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.Clear())
|
||||
//
|
||||
// // clear the primary Clipboard
|
||||
// fmt.Fprint(os.Stderr, osc52.Clear().Primary())
|
||||
package osc52
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Clipboard is the clipboard buffer to use.
|
||||
type Clipboard rune
|
||||
|
||||
const (
|
||||
// SystemClipboard is the system clipboard buffer.
|
||||
SystemClipboard Clipboard = 'c'
|
||||
// PrimaryClipboard is the primary clipboard buffer (X11).
|
||||
PrimaryClipboard = 'p'
|
||||
)
|
||||
|
||||
// Mode is the mode to use for the OSC52 sequence.
|
||||
type Mode uint
|
||||
|
||||
const (
|
||||
// DefaultMode is the default OSC52 sequence mode.
|
||||
DefaultMode Mode = iota
|
||||
// ScreenMode escapes the OSC52 sequence for screen using DCS sequences.
|
||||
ScreenMode
|
||||
// TmuxMode escapes the OSC52 sequence for tmux. Not needed if tmux
|
||||
// clipboard is set to `set-clipboard on`
|
||||
TmuxMode
|
||||
)
|
||||
|
||||
// Operation is the OSC52 operation.
|
||||
type Operation uint
|
||||
|
||||
const (
|
||||
// SetOperation is the copy operation.
|
||||
SetOperation Operation = iota
|
||||
// QueryOperation is the query operation.
|
||||
QueryOperation
|
||||
// ClearOperation is the clear operation.
|
||||
ClearOperation
|
||||
)
|
||||
|
||||
// Sequence is the OSC52 sequence.
|
||||
type Sequence struct {
|
||||
str string
|
||||
limit int
|
||||
op Operation
|
||||
mode Mode
|
||||
clipboard Clipboard
|
||||
}
|
||||
|
||||
var _ fmt.Stringer = Sequence{}
|
||||
|
||||
var _ io.WriterTo = Sequence{}
|
||||
|
||||
// String returns the OSC52 sequence.
|
||||
func (s Sequence) String() string {
|
||||
var seq strings.Builder
|
||||
// mode escape sequences start
|
||||
seq.WriteString(s.seqStart())
|
||||
// actual OSC52 sequence start
|
||||
seq.WriteString(fmt.Sprintf("\x1b]52;%c;", s.clipboard))
|
||||
switch s.op {
|
||||
case SetOperation:
|
||||
str := s.str
|
||||
if s.limit > 0 && len(str) > s.limit {
|
||||
return ""
|
||||
}
|
||||
b64 := base64.StdEncoding.EncodeToString([]byte(str))
|
||||
switch s.mode {
|
||||
case ScreenMode:
|
||||
// Screen doesn't support OSC52 but will pass the contents of a DCS
|
||||
// sequence to the outer terminal unchanged.
|
||||
//
|
||||
// Here, we split the encoded string into 76 bytes chunks and then
|
||||
// join the chunks with <end-dsc><start-dsc> sequences. Finally,
|
||||
// wrap the whole thing in
|
||||
// <start-dsc><start-osc52><joined-chunks><end-osc52><end-dsc>.
|
||||
// s := strings.SplitN(b64, "", 76)
|
||||
s := make([]string, 0, len(b64)/76+1)
|
||||
for i := 0; i < len(b64); i += 76 {
|
||||
end := i + 76
|
||||
if end > len(b64) {
|
||||
end = len(b64)
|
||||
}
|
||||
s = append(s, b64[i:end])
|
||||
}
|
||||
seq.WriteString(strings.Join(s, "\x1b\\\x1bP"))
|
||||
default:
|
||||
seq.WriteString(b64)
|
||||
}
|
||||
case QueryOperation:
|
||||
// OSC52 queries the clipboard using "?"
|
||||
seq.WriteString("?")
|
||||
case ClearOperation:
|
||||
// OSC52 clears the clipboard if the data is neither a base64 string nor "?"
|
||||
// we're using "!" as a default
|
||||
seq.WriteString("!")
|
||||
}
|
||||
// actual OSC52 sequence end
|
||||
seq.WriteString("\x07")
|
||||
// mode escape end
|
||||
seq.WriteString(s.seqEnd())
|
||||
return seq.String()
|
||||
}
|
||||
|
||||
// WriteTo writes the OSC52 sequence to the writer.
|
||||
func (s Sequence) WriteTo(out io.Writer) (int64, error) {
|
||||
n, err := out.Write([]byte(s.String()))
|
||||
return int64(n), err
|
||||
}
|
||||
|
||||
// Mode sets the mode for the OSC52 sequence.
|
||||
func (s Sequence) Mode(m Mode) Sequence {
|
||||
s.mode = m
|
||||
return s
|
||||
}
|
||||
|
||||
// Tmux sets the mode to TmuxMode.
|
||||
// Used to escape the OSC52 sequence for `tmux`.
|
||||
//
|
||||
// Note: this is not needed if tmux clipboard is set to `set-clipboard on`. If
|
||||
// TmuxMode is used, tmux must have `allow-passthrough on` set.
|
||||
//
|
||||
// This is a syntactic sugar for s.Mode(TmuxMode).
|
||||
func (s Sequence) Tmux() Sequence {
|
||||
return s.Mode(TmuxMode)
|
||||
}
|
||||
|
||||
// Screen sets the mode to ScreenMode.
|
||||
// Used to escape the OSC52 sequence for `screen`.
|
||||
//
|
||||
// This is a syntactic sugar for s.Mode(ScreenMode).
|
||||
func (s Sequence) Screen() Sequence {
|
||||
return s.Mode(ScreenMode)
|
||||
}
|
||||
|
||||
// Clipboard sets the clipboard buffer for the OSC52 sequence.
|
||||
func (s Sequence) Clipboard(c Clipboard) Sequence {
|
||||
s.clipboard = c
|
||||
return s
|
||||
}
|
||||
|
||||
// Primary sets the clipboard buffer to PrimaryClipboard.
|
||||
// This is the X11 primary clipboard.
|
||||
//
|
||||
// This is a syntactic sugar for s.Clipboard(PrimaryClipboard).
|
||||
func (s Sequence) Primary() Sequence {
|
||||
return s.Clipboard(PrimaryClipboard)
|
||||
}
|
||||
|
||||
// Limit sets the limit for the OSC52 sequence.
|
||||
// The default limit is 0 (no limit).
|
||||
//
|
||||
// Strings longer than the limit get ignored. Settting the limit to 0 or a
|
||||
// negative value disables the limit. Each terminal defines its own escapse
|
||||
// sequence limit.
|
||||
func (s Sequence) Limit(l int) Sequence {
|
||||
if l < 0 {
|
||||
s.limit = 0
|
||||
} else {
|
||||
s.limit = l
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Operation sets the operation for the OSC52 sequence.
|
||||
// The default operation is SetOperation.
|
||||
func (s Sequence) Operation(o Operation) Sequence {
|
||||
s.op = o
|
||||
return s
|
||||
}
|
||||
|
||||
// Clear sets the operation to ClearOperation.
|
||||
// This clears the clipboard.
|
||||
//
|
||||
// This is a syntactic sugar for s.Operation(ClearOperation).
|
||||
func (s Sequence) Clear() Sequence {
|
||||
return s.Operation(ClearOperation)
|
||||
}
|
||||
|
||||
// Query sets the operation to QueryOperation.
|
||||
// This queries the clipboard contents.
|
||||
//
|
||||
// This is a syntactic sugar for s.Operation(QueryOperation).
|
||||
func (s Sequence) Query() Sequence {
|
||||
return s.Operation(QueryOperation)
|
||||
}
|
||||
|
||||
// SetString sets the string for the OSC52 sequence. Strings are joined with a
|
||||
// space character.
|
||||
func (s Sequence) SetString(strs ...string) Sequence {
|
||||
s.str = strings.Join(strs, " ")
|
||||
return s
|
||||
}
|
||||
|
||||
// New creates a new OSC52 sequence with the given string(s). Strings are
|
||||
// joined with a space character.
|
||||
func New(strs ...string) Sequence {
|
||||
s := Sequence{
|
||||
str: strings.Join(strs, " "),
|
||||
limit: 0,
|
||||
mode: DefaultMode,
|
||||
clipboard: SystemClipboard,
|
||||
op: SetOperation,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Query creates a new OSC52 sequence with the QueryOperation.
|
||||
// This returns a new OSC52 sequence to query the clipboard contents.
|
||||
//
|
||||
// This is a syntactic sugar for New().Query().
|
||||
func Query() Sequence {
|
||||
return New().Query()
|
||||
}
|
||||
|
||||
// Clear creates a new OSC52 sequence with the ClearOperation.
|
||||
// This returns a new OSC52 sequence to clear the clipboard.
|
||||
//
|
||||
// This is a syntactic sugar for New().Clear().
|
||||
func Clear() Sequence {
|
||||
return New().Clear()
|
||||
}
|
||||
|
||||
func (s Sequence) seqStart() string {
|
||||
switch s.mode {
|
||||
case TmuxMode:
|
||||
// Write the start of a tmux escape sequence.
|
||||
return "\x1bPtmux;\x1b"
|
||||
case ScreenMode:
|
||||
// Write the start of a DCS sequence.
|
||||
return "\x1bP"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
func (s Sequence) seqEnd() string {
|
||||
switch s.mode {
|
||||
case TmuxMode:
|
||||
// Terminate the tmux escape sequence.
|
||||
return "\x1b\\"
|
||||
case ScreenMode:
|
||||
// Write the end of a DCS sequence.
|
||||
return "\x1b\x5c"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
1
vendor/github.com/charmbracelet/lipgloss/.gitignore
generated
vendored
Normal file
1
vendor/github.com/charmbracelet/lipgloss/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
ssh_example_ed25519*
|
47
vendor/github.com/charmbracelet/lipgloss/.golangci-soft.yml
generated
vendored
Normal file
47
vendor/github.com/charmbracelet/lipgloss/.golangci-soft.yml
generated
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
run:
|
||||
tests: false
|
||||
|
||||
issues:
|
||||
include:
|
||||
- EXC0001
|
||||
- EXC0005
|
||||
- EXC0011
|
||||
- EXC0012
|
||||
- EXC0013
|
||||
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
linters:
|
||||
enable:
|
||||
# - dupl
|
||||
- exhaustive
|
||||
# - exhaustivestruct
|
||||
- goconst
|
||||
- godot
|
||||
- godox
|
||||
- gomnd
|
||||
- gomoddirectives
|
||||
- goprintffuncname
|
||||
- ifshort
|
||||
# - lll
|
||||
- misspell
|
||||
- nakedret
|
||||
- nestif
|
||||
- noctx
|
||||
- nolintlint
|
||||
- prealloc
|
||||
- wrapcheck
|
||||
|
||||
# disable default linters, they are already enabled in .golangci.yml
|
||||
disable:
|
||||
- deadcode
|
||||
- errcheck
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- structcheck
|
||||
- typecheck
|
||||
- unused
|
||||
- varcheck
|
29
vendor/github.com/charmbracelet/lipgloss/.golangci.yml
generated
vendored
Normal file
29
vendor/github.com/charmbracelet/lipgloss/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,29 @@
|
||||
run:
|
||||
tests: false
|
||||
|
||||
issues:
|
||||
include:
|
||||
- EXC0001
|
||||
- EXC0005
|
||||
- EXC0011
|
||||
- EXC0012
|
||||
- EXC0013
|
||||
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- bodyclose
|
||||
- exportloopref
|
||||
- goimports
|
||||
- gosec
|
||||
- nilerr
|
||||
- predeclared
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- tparallel
|
||||
- unconvert
|
||||
- unparam
|
||||
- whitespace
|
21
vendor/github.com/charmbracelet/lipgloss/LICENSE
generated
vendored
Normal file
21
vendor/github.com/charmbracelet/lipgloss/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021 Charmbracelet, Inc
|
||||
|
||||
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.
|
458
vendor/github.com/charmbracelet/lipgloss/README.md
generated
vendored
Normal file
458
vendor/github.com/charmbracelet/lipgloss/README.md
generated
vendored
Normal file
@ -0,0 +1,458 @@
|
||||
Lip Gloss
|
||||
=========
|
||||
|
||||
<p>
|
||||
<img src="https://stuff.charm.sh/lipgloss/lipgloss-header-github.png" width="340" alt="Lip Gloss Title Treatment"><br>
|
||||
<a href="https://github.com/charmbracelet/lipgloss/releases"><img src="https://img.shields.io/github/release/charmbracelet/lipgloss.svg" alt="Latest Release"></a>
|
||||
<a href="https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="GoDoc"></a>
|
||||
<a href="https://github.com/charmbracelet/lipgloss/actions"><img src="https://github.com/charmbracelet/lipgloss/workflows/build/badge.svg" alt="Build Status"></a>
|
||||
</p>
|
||||
|
||||
Style definitions for nice terminal layouts. Built with TUIs in mind.
|
||||
|
||||

|
||||
|
||||
Lip Gloss takes an expressive, declarative approach to terminal rendering.
|
||||
Users familiar with CSS will feel at home with Lip Gloss.
|
||||
|
||||
```go
|
||||
|
||||
import "github.com/charmbracelet/lipgloss"
|
||||
|
||||
var style = lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Foreground(lipgloss.Color("#FAFAFA")).
|
||||
Background(lipgloss.Color("#7D56F4")).
|
||||
PaddingTop(2).
|
||||
PaddingLeft(4).
|
||||
Width(22)
|
||||
|
||||
fmt.Println(style.Render("Hello, kitty"))
|
||||
```
|
||||
|
||||
## Colors
|
||||
|
||||
Lip Gloss supports the following color profiles:
|
||||
|
||||
### ANSI 16 colors (4-bit)
|
||||
|
||||
```go
|
||||
lipgloss.Color("5") // magenta
|
||||
lipgloss.Color("9") // red
|
||||
lipgloss.Color("12") // light blue
|
||||
```
|
||||
|
||||
### ANSI 256 Colors (8-bit)
|
||||
|
||||
```go
|
||||
lipgloss.Color("86") // aqua
|
||||
lipgloss.Color("201") // hot pink
|
||||
lipgloss.Color("202") // orange
|
||||
```
|
||||
|
||||
### True Color (16,777,216 colors; 24-bit)
|
||||
|
||||
```go
|
||||
lipgloss.Color("#0000FF") // good ol' 100% blue
|
||||
lipgloss.Color("#04B575") // a green
|
||||
lipgloss.Color("#3C3C3C") // a dark gray
|
||||
```
|
||||
|
||||
...as well as a 1-bit ASCII profile, which is black and white only.
|
||||
|
||||
The terminal's color profile will be automatically detected, and colors outside
|
||||
the gamut of the current palette will be automatically coerced to their closest
|
||||
available value.
|
||||
|
||||
|
||||
### Adaptive Colors
|
||||
|
||||
You can also specify color options for light and dark backgrounds:
|
||||
|
||||
```go
|
||||
lipgloss.AdaptiveColor{Light: "236", Dark: "248"}
|
||||
```
|
||||
|
||||
The terminal's background color will automatically be detected and the
|
||||
appropriate color will be chosen at runtime.
|
||||
|
||||
### Complete Colors
|
||||
|
||||
CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
|
||||
profiles.
|
||||
|
||||
```go
|
||||
lipgloss.CompleteColor{True: "#0000FF", ANSI256: "86", ANSI: "5"}
|
||||
```
|
||||
|
||||
Automatic color degradation will not be performed in this case and it will be
|
||||
based on the color specified.
|
||||
|
||||
### Complete Adaptive Colors
|
||||
|
||||
You can use CompleteColor with AdaptiveColor to specify the exact values for
|
||||
light and dark backgrounds without automatic color degradation.
|
||||
|
||||
```go
|
||||
lipgloss.CompleteAdaptiveColor{
|
||||
Light: CompleteColor{TrueColor: "#d7ffae", ANSI256: "193", ANSI: "11"},
|
||||
Dark: CompleteColor{TrueColor: "#d75fee", ANSI256: "163", ANSI: "5"},
|
||||
}
|
||||
```
|
||||
|
||||
## Inline Formatting
|
||||
|
||||
Lip Gloss supports the usual ANSI text formatting options:
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().
|
||||
Bold(true).
|
||||
Italic(true).
|
||||
Faint(true).
|
||||
Blink(true).
|
||||
Strikethrough(true).
|
||||
Underline(true).
|
||||
Reverse(true)
|
||||
```
|
||||
|
||||
|
||||
## Block-Level Formatting
|
||||
|
||||
Lip Gloss also supports rules for block-level formatting:
|
||||
|
||||
```go
|
||||
// Padding
|
||||
var style = lipgloss.NewStyle().
|
||||
PaddingTop(2).
|
||||
PaddingRight(4).
|
||||
PaddingBottom(2).
|
||||
PaddingLeft(4)
|
||||
|
||||
// Margins
|
||||
var style = lipgloss.NewStyle().
|
||||
MarginTop(2).
|
||||
MarginRight(4).
|
||||
MarginBottom(2).
|
||||
MarginLeft(4)
|
||||
```
|
||||
|
||||
There is also shorthand syntax for margins and padding, which follows the same
|
||||
format as CSS:
|
||||
|
||||
```go
|
||||
// 2 cells on all sides
|
||||
lipgloss.NewStyle().Padding(2)
|
||||
|
||||
// 2 cells on the top and bottom, 4 cells on the left and right
|
||||
lipgloss.NewStyle().Margin(2, 4)
|
||||
|
||||
// 1 cell on the top, 4 cells on the sides, 2 cells on the bottom
|
||||
lipgloss.NewStyle().Padding(1, 4, 2)
|
||||
|
||||
// Clockwise, starting from the top: 2 cells on the top, 4 on the right, 3 on
|
||||
// the bottom, and 1 on the left
|
||||
lipgloss.NewStyle().Margin(2, 4, 3, 1)
|
||||
```
|
||||
|
||||
|
||||
## Aligning Text
|
||||
|
||||
You can align paragraphs of text to the left, right, or center.
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().
|
||||
Width(24).
|
||||
Align(lipgloss.Left). // align it left
|
||||
Align(lipgloss.Right). // no wait, align it right
|
||||
Align(lipgloss.Center) // just kidding, align it in the center
|
||||
```
|
||||
|
||||
|
||||
## Width and Height
|
||||
|
||||
Setting a minimum width and height is simple and straightforward.
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().
|
||||
SetString("What’s for lunch?").
|
||||
Width(24).
|
||||
Height(32).
|
||||
Foreground(lipgloss.Color("63"))
|
||||
```
|
||||
|
||||
|
||||
## Borders
|
||||
|
||||
Adding borders is easy:
|
||||
|
||||
```go
|
||||
// Add a purple, rectangular border
|
||||
var style = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.NormalBorder()).
|
||||
BorderForeground(lipgloss.Color("63"))
|
||||
|
||||
// Set a rounded, yellow-on-purple border to the top and left
|
||||
var anotherStyle = lipgloss.NewStyle().
|
||||
BorderStyle(lipgloss.RoundedBorder()).
|
||||
BorderForeground(lipgloss.Color("228")).
|
||||
BorderBackground(lipgloss.Color("63")).
|
||||
BorderTop(true).
|
||||
BorderLeft(true)
|
||||
|
||||
// Make your own border
|
||||
var myCuteBorder = lipgloss.Border{
|
||||
Top: "._.:*:",
|
||||
Bottom: "._.:*:",
|
||||
Left: "|*",
|
||||
Right: "|*",
|
||||
TopLeft: "*",
|
||||
TopRight: "*",
|
||||
BottomLeft: "*",
|
||||
BottomRight: "*",
|
||||
}
|
||||
```
|
||||
|
||||
There are also shorthand functions for defining borders, which follow a similar
|
||||
pattern to the margin and padding shorthand functions.
|
||||
|
||||
```go
|
||||
// Add a thick border to the top and bottom
|
||||
lipgloss.NewStyle().
|
||||
Border(lipgloss.ThickBorder(), true, false)
|
||||
|
||||
// Add a thick border to the right and bottom sides. Rules are set clockwise
|
||||
// from top.
|
||||
lipgloss.NewStyle().
|
||||
Border(lipgloss.DoubleBorder(), true, false, false, true)
|
||||
```
|
||||
|
||||
For more on borders see [the docs][docs].
|
||||
|
||||
|
||||
## Copying Styles
|
||||
|
||||
Just use `Copy()`:
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().Foreground(lipgloss.Color("219"))
|
||||
|
||||
var wildStyle = style.Copy().Blink(true)
|
||||
```
|
||||
|
||||
`Copy()` performs a copy on the underlying data structure ensuring that you get
|
||||
a true, dereferenced copy of a style. Without copying, it's possible to mutate
|
||||
styles.
|
||||
|
||||
|
||||
## Inheritance
|
||||
|
||||
Styles can inherit rules from other styles. When inheriting, only unset rules
|
||||
on the receiver are inherited.
|
||||
|
||||
```go
|
||||
var styleA = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("229")).
|
||||
Background(lipgloss.Color("63"))
|
||||
|
||||
// Only the background color will be inherited here, because the foreground
|
||||
// color will have been already set:
|
||||
var styleB = lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("201")).
|
||||
Inherit(styleA)
|
||||
```
|
||||
|
||||
|
||||
## Unsetting Rules
|
||||
|
||||
All rules can be unset:
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().
|
||||
Bold(true). // make it bold
|
||||
UnsetBold(). // jk don't make it bold
|
||||
Background(lipgloss.Color("227")). // yellow background
|
||||
UnsetBackground() // never mind
|
||||
```
|
||||
|
||||
When a rule is unset, it won't be inherited or copied.
|
||||
|
||||
|
||||
## Enforcing Rules
|
||||
|
||||
Sometimes, such as when developing a component, you want to make sure style
|
||||
definitions respect their intended purpose in the UI. This is where `Inline`
|
||||
and `MaxWidth`, and `MaxHeight` come in:
|
||||
|
||||
```go
|
||||
// Force rendering onto a single line, ignoring margins, padding, and borders.
|
||||
someStyle.Inline(true).Render("yadda yadda")
|
||||
|
||||
// Also limit rendering to five cells
|
||||
someStyle.Inline(true).MaxWidth(5).Render("yadda yadda")
|
||||
|
||||
// Limit rendering to a 5x5 cell block
|
||||
someStyle.MaxWidth(5).MaxHeight(5).Render("yadda yadda")
|
||||
```
|
||||
|
||||
## Rendering
|
||||
|
||||
Generally, you just call the `Render(string...)` method on a `lipgloss.Style`:
|
||||
|
||||
```go
|
||||
style := lipgloss.NewStyle().Bold(true).SetString("Hello,")
|
||||
fmt.Println(style.Render("kitty.")) // Hello, kitty.
|
||||
fmt.Println(style.Render("puppy.")) // Hello, puppy.
|
||||
```
|
||||
|
||||
But you could also use the Stringer interface:
|
||||
|
||||
```go
|
||||
var style = lipgloss.NewStyle().SetString("你好,猫咪。").Bold(true)
|
||||
fmt.Println(style) // 你好,猫咪。
|
||||
```
|
||||
|
||||
### Custom Renderers
|
||||
|
||||
Custom renderers allow you to render to a specific outputs. This is
|
||||
particularly important when you want to render to different outputs and
|
||||
correctly detect the color profile and dark background status for each, such as
|
||||
in a server-client situation.
|
||||
|
||||
```go
|
||||
func myLittleHandler(sess ssh.Session) {
|
||||
// Create a renderer for the client.
|
||||
renderer := lipgloss.NewRenderer(sess)
|
||||
|
||||
// Create a new style on the renderer.
|
||||
style := renderer.NewStyle().Background(lipgloss.AdaptiveColor{Light: "63", Dark: "228"})
|
||||
|
||||
// Render. The color profile and dark background state will be correctly detected.
|
||||
io.WriteString(sess, style.Render("Heyyyyyyy"))
|
||||
}
|
||||
```
|
||||
|
||||
For an example on using a custom renderer over SSH with [Wish][wish] see the
|
||||
[SSH example][ssh-example].
|
||||
|
||||
## Utilities
|
||||
|
||||
In addition to pure styling, Lip Gloss also ships with some utilities to help
|
||||
assemble your layouts.
|
||||
|
||||
|
||||
### Joining Paragraphs
|
||||
|
||||
Horizontally and vertically joining paragraphs is a cinch.
|
||||
|
||||
```go
|
||||
// Horizontally join three paragraphs along their bottom edges
|
||||
lipgloss.JoinHorizontal(lipgloss.Bottom, paragraphA, paragraphB, paragraphC)
|
||||
|
||||
// Vertically join two paragraphs along their center axes
|
||||
lipgloss.JoinVertical(lipgloss.Center, paragraphA, paragraphB)
|
||||
|
||||
// Horizontally join three paragraphs, with the shorter ones aligning 20%
|
||||
// from the top of the tallest
|
||||
lipgloss.JoinHorizontal(0.2, paragraphA, paragraphB, paragraphC)
|
||||
```
|
||||
|
||||
|
||||
### Measuring Width and Height
|
||||
|
||||
Sometimes you’ll want to know the width and height of text blocks when building
|
||||
your layouts.
|
||||
|
||||
```go
|
||||
// Render a block of text.
|
||||
var style = lipgloss.NewStyle().
|
||||
Width(40).
|
||||
Padding(2)
|
||||
var block string = style.Render(someLongString)
|
||||
|
||||
// Get the actual, physical dimensions of the text block.
|
||||
width := lipgloss.Width(block)
|
||||
height := lipgloss.Height(block)
|
||||
|
||||
// Here's a shorthand function.
|
||||
w, h := lipgloss.Size(block)
|
||||
```
|
||||
|
||||
|
||||
### Placing Text in Whitespace
|
||||
|
||||
Sometimes you’ll simply want to place a block of text in whitespace.
|
||||
|
||||
```go
|
||||
// Center a paragraph horizontally in a space 80 cells wide. The height of
|
||||
// the block returned will be as tall as the input paragraph.
|
||||
block := lipgloss.PlaceHorizontal(80, lipgloss.Center, fancyStyledParagraph)
|
||||
|
||||
// Place a paragraph at the bottom of a space 30 cells tall. The width of
|
||||
// the text block returned will be as wide as the input paragraph.
|
||||
block := lipgloss.PlaceVertical(30, lipgloss.Bottom, fancyStyledParagraph)
|
||||
|
||||
// Place a paragraph in the bottom right corner of a 30x80 cell space.
|
||||
block := lipgloss.Place(30, 80, lipgloss.Right, lipgloss.Bottom, fancyStyledParagraph)
|
||||
```
|
||||
|
||||
You can also style the whitespace. For details, see [the docs][docs].
|
||||
|
||||
|
||||
***
|
||||
|
||||
|
||||
## What about [Bubble Tea][tea]?
|
||||
|
||||
Lip Gloss doesn’t replace Bubble Tea. Rather, it is an excellent Bubble Tea
|
||||
companion. It was designed to make assembling terminal user interface views as
|
||||
simple and fun as possible so that you can focus on building your application
|
||||
instead of concerning yourself with low-level layout details.
|
||||
|
||||
In simple terms, you can use Lip Gloss to help build your Bubble Tea views.
|
||||
|
||||
[tea]: https://github.com/charmbracelet/tea
|
||||
|
||||
|
||||
## Under the Hood
|
||||
|
||||
Lip Gloss is built on the excellent [Termenv][termenv] and [Reflow][reflow]
|
||||
libraries which deal with color and ANSI-aware text operations, respectively.
|
||||
For many use cases Termenv and Reflow will be sufficient for your needs.
|
||||
|
||||
[termenv]: https://github.com/muesli/termenv
|
||||
[reflow]: https://github.com/muesli/reflow
|
||||
|
||||
|
||||
## Rendering Markdown
|
||||
|
||||
For a more document-centric rendering solution with support for things like
|
||||
lists, tables, and syntax-highlighted code have a look at [Glamour][glamour],
|
||||
the stylesheet-based Markdown renderer.
|
||||
|
||||
[glamour]: https://github.com/charmbracelet/glamour
|
||||
|
||||
|
||||
## Feedback
|
||||
|
||||
We’d love to hear your thoughts on this project. Feel free to drop us a note!
|
||||
|
||||
* [Twitter](https://twitter.com/charmcli)
|
||||
* [The Fediverse](https://mastodon.social/@charmcli)
|
||||
* [Discord](https://charm.sh/chat)
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/charmbracelet/lipgloss/raw/master/LICENSE)
|
||||
|
||||
***
|
||||
|
||||
Part of [Charm](https://charm.sh).
|
||||
|
||||
<a href="https://charm.sh/"><img alt="The Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>
|
||||
|
||||
Charm热爱开源 • Charm loves open source
|
||||
|
||||
|
||||
[docs]: https://pkg.go.dev/github.com/charmbracelet/lipgloss?tab=doc
|
||||
[wish]: https://github.com/charmbracelet/wish
|
||||
[ssh-example]: examples/ssh
|
82
vendor/github.com/charmbracelet/lipgloss/align.go
generated
vendored
Normal file
82
vendor/github.com/charmbracelet/lipgloss/align.go
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/muesli/reflow/ansi"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// Perform text alignment. If the string is multi-lined, we also make all lines
|
||||
// the same width by padding them with spaces. If a termenv style is passed,
|
||||
// use that to style the spaces added.
|
||||
func alignTextHorizontal(str string, pos Position, width int, style *termenv.Style) string {
|
||||
lines, widestLine := getLines(str)
|
||||
var b strings.Builder
|
||||
|
||||
for i, l := range lines {
|
||||
lineWidth := ansi.PrintableRuneWidth(l)
|
||||
|
||||
shortAmount := widestLine - lineWidth // difference from the widest line
|
||||
shortAmount += max(0, width-(shortAmount+lineWidth)) // difference from the total width, if set
|
||||
|
||||
if shortAmount > 0 {
|
||||
switch pos {
|
||||
case Right:
|
||||
s := strings.Repeat(" ", shortAmount)
|
||||
if style != nil {
|
||||
s = style.Styled(s)
|
||||
}
|
||||
l = s + l
|
||||
case Center:
|
||||
left := shortAmount / 2
|
||||
right := left + shortAmount%2 // note that we put the remainder on the right
|
||||
|
||||
leftSpaces := strings.Repeat(" ", left)
|
||||
rightSpaces := strings.Repeat(" ", right)
|
||||
|
||||
if style != nil {
|
||||
leftSpaces = style.Styled(leftSpaces)
|
||||
rightSpaces = style.Styled(rightSpaces)
|
||||
}
|
||||
l = leftSpaces + l + rightSpaces
|
||||
default: // Left
|
||||
s := strings.Repeat(" ", shortAmount)
|
||||
if style != nil {
|
||||
s = style.Styled(s)
|
||||
}
|
||||
l += s
|
||||
}
|
||||
}
|
||||
|
||||
b.WriteString(l)
|
||||
if i < len(lines)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func alignTextVertical(str string, pos Position, height int, _ *termenv.Style) string {
|
||||
strHeight := strings.Count(str, "\n") + 1
|
||||
if height < strHeight {
|
||||
return str
|
||||
}
|
||||
|
||||
switch pos {
|
||||
case Top:
|
||||
return str + strings.Repeat("\n", height-strHeight)
|
||||
case Center:
|
||||
var topPadding, bottomPadding = (height - strHeight) / 2, (height - strHeight) / 2
|
||||
if strHeight+topPadding+bottomPadding > height {
|
||||
topPadding--
|
||||
} else if strHeight+topPadding+bottomPadding < height {
|
||||
bottomPadding++
|
||||
}
|
||||
return strings.Repeat("\n", topPadding) + str + strings.Repeat("\n", bottomPadding)
|
||||
case Bottom:
|
||||
return strings.Repeat("\n", height-strHeight) + str
|
||||
}
|
||||
return str
|
||||
}
|
7
vendor/github.com/charmbracelet/lipgloss/ansi_unix.go
generated
vendored
Normal file
7
vendor/github.com/charmbracelet/lipgloss/ansi_unix.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package lipgloss
|
||||
|
||||
// enableLegacyWindowsANSI is only needed on Windows.
|
||||
func enableLegacyWindowsANSI() {}
|
22
vendor/github.com/charmbracelet/lipgloss/ansi_windows.go
generated
vendored
Normal file
22
vendor/github.com/charmbracelet/lipgloss/ansi_windows.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
var enableANSI sync.Once
|
||||
|
||||
// enableANSIColors enables support for ANSI color sequences in the Windows
|
||||
// default console (cmd.exe and the PowerShell application). Note that this
|
||||
// only works with Windows 10. Also note that Windows Terminal supports colors
|
||||
// by default.
|
||||
func enableLegacyWindowsANSI() {
|
||||
enableANSI.Do(func() {
|
||||
_, _ = termenv.EnableWindowsANSIConsole()
|
||||
})
|
||||
}
|
412
vendor/github.com/charmbracelet/lipgloss/borders.go
generated
vendored
Normal file
412
vendor/github.com/charmbracelet/lipgloss/borders.go
generated
vendored
Normal file
@ -0,0 +1,412 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/muesli/reflow/ansi"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// Border contains a series of values which comprise the various parts of a
|
||||
// border.
|
||||
type Border struct {
|
||||
Top string
|
||||
Bottom string
|
||||
Left string
|
||||
Right string
|
||||
TopLeft string
|
||||
TopRight string
|
||||
BottomRight string
|
||||
BottomLeft string
|
||||
}
|
||||
|
||||
// GetTopSize returns the width of the top border. If borders contain runes of
|
||||
// varying widths, the widest rune is returned. If no border exists on the top
|
||||
// edge, 0 is returned.
|
||||
func (b Border) GetTopSize() int {
|
||||
return getBorderEdgeWidth(b.TopLeft, b.Top, b.TopRight)
|
||||
}
|
||||
|
||||
// GetRightSize returns the width of the right border. If borders contain
|
||||
// runes of varying widths, the widest rune is returned. If no border exists on
|
||||
// the right edge, 0 is returned.
|
||||
func (b Border) GetRightSize() int {
|
||||
return getBorderEdgeWidth(b.TopRight, b.Top, b.BottomRight)
|
||||
}
|
||||
|
||||
// GetBottomSize returns the width of the bottom border. If borders contain
|
||||
// runes of varying widths, the widest rune is returned. If no border exists on
|
||||
// the bottom edge, 0 is returned.
|
||||
func (b Border) GetBottomSize() int {
|
||||
return getBorderEdgeWidth(b.BottomLeft, b.Bottom, b.BottomRight)
|
||||
}
|
||||
|
||||
// GetLeftSize returns the width of the left border. If borders contain runes
|
||||
// of varying widths, the widest rune is returned. If no border exists on the
|
||||
// left edge, 0 is returned.
|
||||
func (b Border) GetLeftSize() int {
|
||||
return getBorderEdgeWidth(b.TopLeft, b.Left, b.TopRight)
|
||||
}
|
||||
|
||||
func getBorderEdgeWidth(borderParts ...string) (maxWidth int) {
|
||||
for _, piece := range borderParts {
|
||||
w := maxRuneWidth(piece)
|
||||
if w > maxWidth {
|
||||
maxWidth = w
|
||||
}
|
||||
}
|
||||
return maxWidth
|
||||
}
|
||||
|
||||
var (
|
||||
noBorder = Border{}
|
||||
|
||||
normalBorder = Border{
|
||||
Top: "─",
|
||||
Bottom: "─",
|
||||
Left: "│",
|
||||
Right: "│",
|
||||
TopLeft: "┌",
|
||||
TopRight: "┐",
|
||||
BottomLeft: "└",
|
||||
BottomRight: "┘",
|
||||
}
|
||||
|
||||
roundedBorder = Border{
|
||||
Top: "─",
|
||||
Bottom: "─",
|
||||
Left: "│",
|
||||
Right: "│",
|
||||
TopLeft: "╭",
|
||||
TopRight: "╮",
|
||||
BottomLeft: "╰",
|
||||
BottomRight: "╯",
|
||||
}
|
||||
|
||||
blockBorder = Border{
|
||||
Top: "█",
|
||||
Bottom: "█",
|
||||
Left: "█",
|
||||
Right: "█",
|
||||
TopLeft: "█",
|
||||
TopRight: "█",
|
||||
BottomLeft: "█",
|
||||
BottomRight: "█",
|
||||
}
|
||||
|
||||
outerHalfBlockBorder = Border{
|
||||
Top: "▀",
|
||||
Bottom: "▄",
|
||||
Left: "▌",
|
||||
Right: "▐",
|
||||
TopLeft: "▛",
|
||||
TopRight: "▜",
|
||||
BottomLeft: "▙",
|
||||
BottomRight: "▟",
|
||||
}
|
||||
|
||||
innerHalfBlockBorder = Border{
|
||||
Top: "▄",
|
||||
Bottom: "▀",
|
||||
Left: "▐",
|
||||
Right: "▌",
|
||||
TopLeft: "▗",
|
||||
TopRight: "▖",
|
||||
BottomLeft: "▝",
|
||||
BottomRight: "▘",
|
||||
}
|
||||
|
||||
thickBorder = Border{
|
||||
Top: "━",
|
||||
Bottom: "━",
|
||||
Left: "┃",
|
||||
Right: "┃",
|
||||
TopLeft: "┏",
|
||||
TopRight: "┓",
|
||||
BottomLeft: "┗",
|
||||
BottomRight: "┛",
|
||||
}
|
||||
|
||||
doubleBorder = Border{
|
||||
Top: "═",
|
||||
Bottom: "═",
|
||||
Left: "║",
|
||||
Right: "║",
|
||||
TopLeft: "╔",
|
||||
TopRight: "╗",
|
||||
BottomLeft: "╚",
|
||||
BottomRight: "╝",
|
||||
}
|
||||
|
||||
hiddenBorder = Border{
|
||||
Top: " ",
|
||||
Bottom: " ",
|
||||
Left: " ",
|
||||
Right: " ",
|
||||
TopLeft: " ",
|
||||
TopRight: " ",
|
||||
BottomLeft: " ",
|
||||
BottomRight: " ",
|
||||
}
|
||||
)
|
||||
|
||||
// NormalBorder returns a standard-type border with a normal weight and 90
|
||||
// degree corners.
|
||||
func NormalBorder() Border {
|
||||
return normalBorder
|
||||
}
|
||||
|
||||
// RoundedBorder returns a border with rounded corners.
|
||||
func RoundedBorder() Border {
|
||||
return roundedBorder
|
||||
}
|
||||
|
||||
// BlockBorder returns a border that takes the whole block.
|
||||
func BlockBorder() Border {
|
||||
return blockBorder
|
||||
}
|
||||
|
||||
// OuterHalfBlockBorder returns a half-block border that sits outside the frame.
|
||||
func OuterHalfBlockBorder() Border {
|
||||
return outerHalfBlockBorder
|
||||
}
|
||||
|
||||
// InnerHalfBlockBorder returns a half-block border that sits inside the frame.
|
||||
func InnerHalfBlockBorder() Border {
|
||||
return innerHalfBlockBorder
|
||||
}
|
||||
|
||||
// ThickBorder returns a border that's thicker than the one returned by
|
||||
// NormalBorder.
|
||||
func ThickBorder() Border {
|
||||
return thickBorder
|
||||
}
|
||||
|
||||
// DoubleBorder returns a border comprised of two thin strokes.
|
||||
func DoubleBorder() Border {
|
||||
return doubleBorder
|
||||
}
|
||||
|
||||
// HiddenBorder returns a border that renders as a series of single-cell
|
||||
// spaces. It's useful for cases when you want to remove a standard border but
|
||||
// maintain layout positioning. This said, you can still apply a background
|
||||
// color to a hidden border.
|
||||
func HiddenBorder() Border {
|
||||
return hiddenBorder
|
||||
}
|
||||
|
||||
func (s Style) applyBorder(str string) string {
|
||||
var (
|
||||
topSet = s.isSet(borderTopKey)
|
||||
rightSet = s.isSet(borderRightKey)
|
||||
bottomSet = s.isSet(borderBottomKey)
|
||||
leftSet = s.isSet(borderLeftKey)
|
||||
|
||||
border = s.getBorderStyle()
|
||||
hasTop = s.getAsBool(borderTopKey, false)
|
||||
hasRight = s.getAsBool(borderRightKey, false)
|
||||
hasBottom = s.getAsBool(borderBottomKey, false)
|
||||
hasLeft = s.getAsBool(borderLeftKey, false)
|
||||
|
||||
topFG = s.getAsColor(borderTopForegroundKey)
|
||||
rightFG = s.getAsColor(borderRightForegroundKey)
|
||||
bottomFG = s.getAsColor(borderBottomForegroundKey)
|
||||
leftFG = s.getAsColor(borderLeftForegroundKey)
|
||||
|
||||
topBG = s.getAsColor(borderTopBackgroundKey)
|
||||
rightBG = s.getAsColor(borderRightBackgroundKey)
|
||||
bottomBG = s.getAsColor(borderBottomBackgroundKey)
|
||||
leftBG = s.getAsColor(borderLeftBackgroundKey)
|
||||
)
|
||||
|
||||
// If a border is set and no sides have been specifically turned on or off
|
||||
// render borders on all sides.
|
||||
if border != noBorder && !(topSet || rightSet || bottomSet || leftSet) {
|
||||
hasTop = true
|
||||
hasRight = true
|
||||
hasBottom = true
|
||||
hasLeft = true
|
||||
}
|
||||
|
||||
// If no border is set or all borders are been disabled, abort.
|
||||
if border == noBorder || (!hasTop && !hasRight && !hasBottom && !hasLeft) {
|
||||
return str
|
||||
}
|
||||
|
||||
lines, width := getLines(str)
|
||||
|
||||
if hasLeft {
|
||||
if border.Left == "" {
|
||||
border.Left = " "
|
||||
}
|
||||
width += maxRuneWidth(border.Left)
|
||||
}
|
||||
|
||||
if hasRight && border.Right == "" {
|
||||
border.Right = " "
|
||||
}
|
||||
|
||||
// If corners should be rendered but are set with the empty string, fill them
|
||||
// with a single space.
|
||||
if hasTop && hasLeft && border.TopLeft == "" {
|
||||
border.TopLeft = " "
|
||||
}
|
||||
if hasTop && hasRight && border.TopRight == "" {
|
||||
border.TopRight = " "
|
||||
}
|
||||
if hasBottom && hasLeft && border.BottomLeft == "" {
|
||||
border.BottomLeft = " "
|
||||
}
|
||||
if hasBottom && hasRight && border.BottomRight == "" {
|
||||
border.BottomRight = " "
|
||||
}
|
||||
|
||||
// Figure out which corners we should actually be using based on which
|
||||
// sides are set to show.
|
||||
if hasTop {
|
||||
switch {
|
||||
case !hasLeft && !hasRight:
|
||||
border.TopLeft = ""
|
||||
border.TopRight = ""
|
||||
case !hasLeft:
|
||||
border.TopLeft = ""
|
||||
case !hasRight:
|
||||
border.TopRight = ""
|
||||
}
|
||||
}
|
||||
if hasBottom {
|
||||
switch {
|
||||
case !hasLeft && !hasRight:
|
||||
border.BottomLeft = ""
|
||||
border.BottomRight = ""
|
||||
case !hasLeft:
|
||||
border.BottomLeft = ""
|
||||
case !hasRight:
|
||||
border.BottomRight = ""
|
||||
}
|
||||
}
|
||||
|
||||
// For now, limit corners to one rune.
|
||||
border.TopLeft = getFirstRuneAsString(border.TopLeft)
|
||||
border.TopRight = getFirstRuneAsString(border.TopRight)
|
||||
border.BottomRight = getFirstRuneAsString(border.BottomRight)
|
||||
border.BottomLeft = getFirstRuneAsString(border.BottomLeft)
|
||||
|
||||
var out strings.Builder
|
||||
|
||||
// Render top
|
||||
if hasTop {
|
||||
top := renderHorizontalEdge(border.TopLeft, border.Top, border.TopRight, width)
|
||||
top = s.styleBorder(top, topFG, topBG)
|
||||
out.WriteString(top)
|
||||
out.WriteRune('\n')
|
||||
}
|
||||
|
||||
leftRunes := []rune(border.Left)
|
||||
leftIndex := 0
|
||||
|
||||
rightRunes := []rune(border.Right)
|
||||
rightIndex := 0
|
||||
|
||||
// Render sides
|
||||
for i, l := range lines {
|
||||
if hasLeft {
|
||||
r := string(leftRunes[leftIndex])
|
||||
leftIndex++
|
||||
if leftIndex >= len(leftRunes) {
|
||||
leftIndex = 0
|
||||
}
|
||||
out.WriteString(s.styleBorder(r, leftFG, leftBG))
|
||||
}
|
||||
out.WriteString(l)
|
||||
if hasRight {
|
||||
r := string(rightRunes[rightIndex])
|
||||
rightIndex++
|
||||
if rightIndex >= len(rightRunes) {
|
||||
rightIndex = 0
|
||||
}
|
||||
out.WriteString(s.styleBorder(r, rightFG, rightBG))
|
||||
}
|
||||
if i < len(lines)-1 {
|
||||
out.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
// Render bottom
|
||||
if hasBottom {
|
||||
bottom := renderHorizontalEdge(border.BottomLeft, border.Bottom, border.BottomRight, width)
|
||||
bottom = s.styleBorder(bottom, bottomFG, bottomBG)
|
||||
out.WriteRune('\n')
|
||||
out.WriteString(bottom)
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// Render the horizontal (top or bottom) portion of a border.
|
||||
func renderHorizontalEdge(left, middle, right string, width int) string {
|
||||
if width < 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
if middle == "" {
|
||||
middle = " "
|
||||
}
|
||||
|
||||
leftWidth := ansi.PrintableRuneWidth(left)
|
||||
rightWidth := ansi.PrintableRuneWidth(right)
|
||||
|
||||
runes := []rune(middle)
|
||||
j := 0
|
||||
|
||||
out := strings.Builder{}
|
||||
out.WriteString(left)
|
||||
for i := leftWidth + rightWidth; i < width+rightWidth; {
|
||||
out.WriteRune(runes[j])
|
||||
j++
|
||||
if j >= len(runes) {
|
||||
j = 0
|
||||
}
|
||||
i += ansi.PrintableRuneWidth(string(runes[j]))
|
||||
}
|
||||
out.WriteString(right)
|
||||
|
||||
return out.String()
|
||||
}
|
||||
|
||||
// Apply foreground and background styling to a border.
|
||||
func (s Style) styleBorder(border string, fg, bg TerminalColor) string {
|
||||
if fg == noColor && bg == noColor {
|
||||
return border
|
||||
}
|
||||
|
||||
var style = termenv.Style{}
|
||||
|
||||
if fg != noColor {
|
||||
style = style.Foreground(fg.color(s.r))
|
||||
}
|
||||
if bg != noColor {
|
||||
style = style.Background(bg.color(s.r))
|
||||
}
|
||||
|
||||
return style.Styled(border)
|
||||
}
|
||||
|
||||
func maxRuneWidth(str string) (width int) {
|
||||
for _, r := range str {
|
||||
w := runewidth.RuneWidth(r)
|
||||
if w > width {
|
||||
width = w
|
||||
}
|
||||
}
|
||||
return width
|
||||
}
|
||||
|
||||
func getFirstRuneAsString(str string) string {
|
||||
if str == "" {
|
||||
return str
|
||||
}
|
||||
r := []rune(str)
|
||||
return string(r[0])
|
||||
}
|
172
vendor/github.com/charmbracelet/lipgloss/color.go
generated
vendored
Normal file
172
vendor/github.com/charmbracelet/lipgloss/color.go
generated
vendored
Normal file
@ -0,0 +1,172 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// TerminalColor is a color intended to be rendered in the terminal.
|
||||
type TerminalColor interface {
|
||||
color(*Renderer) termenv.Color
|
||||
RGBA() (r, g, b, a uint32)
|
||||
}
|
||||
|
||||
var noColor = NoColor{}
|
||||
|
||||
// NoColor is used to specify the absence of color styling. When this is active
|
||||
// foreground colors will be rendered with the terminal's default text color,
|
||||
// and background colors will not be drawn at all.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// var style = someStyle.Copy().Background(lipgloss.NoColor{})
|
||||
type NoColor struct{}
|
||||
|
||||
func (NoColor) color(*Renderer) termenv.Color {
|
||||
return termenv.NoColor{}
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. Because we have to return
|
||||
// something, despite this color being the absence of color, we're returning
|
||||
// black with 100% opacity.
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
//
|
||||
// Deprecated.
|
||||
func (n NoColor) RGBA() (r, g, b, a uint32) {
|
||||
return 0x0, 0x0, 0x0, 0xFFFF
|
||||
}
|
||||
|
||||
// Color specifies a color by hex or ANSI value. For example:
|
||||
//
|
||||
// ansiColor := lipgloss.Color("21")
|
||||
// hexColor := lipgloss.Color("#0000ff")
|
||||
type Color string
|
||||
|
||||
func (c Color) color(r *Renderer) termenv.Color {
|
||||
return r.ColorProfile().Color(string(c))
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
||||
// interface. Note that on error we return black with 100% opacity, or:
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
//
|
||||
// Deprecated.
|
||||
func (c Color) RGBA() (r, g, b, a uint32) {
|
||||
return termenv.ConvertToRGB(c.color(renderer)).RGBA()
|
||||
}
|
||||
|
||||
// ANSIColor is a color specified by an ANSI color value. It's merely syntactic
|
||||
// sugar for the more general Color function. Invalid colors will render as
|
||||
// black.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// // These two statements are equivalent.
|
||||
// colorA := lipgloss.ANSIColor(21)
|
||||
// colorB := lipgloss.Color("21")
|
||||
type ANSIColor uint
|
||||
|
||||
func (ac ANSIColor) color(r *Renderer) termenv.Color {
|
||||
return Color(strconv.FormatUint(uint64(ac), 10)).color(r)
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
||||
// interface. Note that on error we return black with 100% opacity, or:
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
//
|
||||
// Deprecated.
|
||||
func (ac ANSIColor) RGBA() (r, g, b, a uint32) {
|
||||
cf := Color(strconv.FormatUint(uint64(ac), 10))
|
||||
return cf.RGBA()
|
||||
}
|
||||
|
||||
// AdaptiveColor provides color options for light and dark backgrounds. The
|
||||
// appropriate color will be returned at runtime based on the darkness of the
|
||||
// terminal background color.
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// color := lipgloss.AdaptiveColor{Light: "#0000ff", Dark: "#000099"}
|
||||
type AdaptiveColor struct {
|
||||
Light string
|
||||
Dark string
|
||||
}
|
||||
|
||||
func (ac AdaptiveColor) color(r *Renderer) termenv.Color {
|
||||
if r.HasDarkBackground() {
|
||||
return Color(ac.Dark).color(r)
|
||||
}
|
||||
return Color(ac.Light).color(r)
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
||||
// interface. Note that on error we return black with 100% opacity, or:
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
//
|
||||
// Deprecated.
|
||||
func (ac AdaptiveColor) RGBA() (r, g, b, a uint32) {
|
||||
return termenv.ConvertToRGB(ac.color(renderer)).RGBA()
|
||||
}
|
||||
|
||||
// CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
|
||||
// profiles. Automatic color degradation will not be performed.
|
||||
type CompleteColor struct {
|
||||
TrueColor string
|
||||
ANSI256 string
|
||||
ANSI string
|
||||
}
|
||||
|
||||
func (c CompleteColor) color(r *Renderer) termenv.Color {
|
||||
p := r.ColorProfile()
|
||||
switch p {
|
||||
case termenv.TrueColor:
|
||||
return p.Color(c.TrueColor)
|
||||
case termenv.ANSI256:
|
||||
return p.Color(c.ANSI256)
|
||||
case termenv.ANSI:
|
||||
return p.Color(c.ANSI)
|
||||
default:
|
||||
return termenv.NoColor{}
|
||||
}
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
||||
// interface. Note that on error we return black with 100% opacity, or:
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
// CompleteAdaptiveColor specifies exact values for truecolor, ANSI256, and ANSI color
|
||||
//
|
||||
// Deprecated.
|
||||
func (c CompleteColor) RGBA() (r, g, b, a uint32) {
|
||||
return termenv.ConvertToRGB(c.color(renderer)).RGBA()
|
||||
}
|
||||
|
||||
// CompleteColor specifies exact values for truecolor, ANSI256, and ANSI color
|
||||
// profiles, with separate options for light and dark backgrounds. Automatic
|
||||
// color degradation will not be performed.
|
||||
type CompleteAdaptiveColor struct {
|
||||
Light CompleteColor
|
||||
Dark CompleteColor
|
||||
}
|
||||
|
||||
func (cac CompleteAdaptiveColor) color(r *Renderer) termenv.Color {
|
||||
if r.HasDarkBackground() {
|
||||
return cac.Dark.color(r)
|
||||
}
|
||||
return cac.Light.color(r)
|
||||
}
|
||||
|
||||
// RGBA returns the RGBA value of this color. This satisfies the Go Color
|
||||
// interface. Note that on error we return black with 100% opacity, or:
|
||||
//
|
||||
// Red: 0x0, Green: 0x0, Blue: 0x0, Alpha: 0xFFFF.
|
||||
//
|
||||
// Deprecated.
|
||||
func (cac CompleteAdaptiveColor) RGBA() (r, g, b, a uint32) {
|
||||
return termenv.ConvertToRGB(cac.color(renderer)).RGBA()
|
||||
}
|
481
vendor/github.com/charmbracelet/lipgloss/get.go
generated
vendored
Normal file
481
vendor/github.com/charmbracelet/lipgloss/get.go
generated
vendored
Normal file
@ -0,0 +1,481 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/muesli/reflow/ansi"
|
||||
)
|
||||
|
||||
// GetBold returns the style's bold value. If no value is set false is returned.
|
||||
func (s Style) GetBold() bool {
|
||||
return s.getAsBool(boldKey, false)
|
||||
}
|
||||
|
||||
// GetItalic returns the style's italic value. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetItalic() bool {
|
||||
return s.getAsBool(italicKey, false)
|
||||
}
|
||||
|
||||
// GetUnderline returns the style's underline value. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetUnderline() bool {
|
||||
return s.getAsBool(underlineKey, false)
|
||||
}
|
||||
|
||||
// GetStrikethrough returns the style's strikethrough value. If no value is set false
|
||||
// is returned.
|
||||
func (s Style) GetStrikethrough() bool {
|
||||
return s.getAsBool(strikethroughKey, false)
|
||||
}
|
||||
|
||||
// GetReverse returns the style's reverse value. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetReverse() bool {
|
||||
return s.getAsBool(reverseKey, false)
|
||||
}
|
||||
|
||||
// GetBlink returns the style's blink value. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetBlink() bool {
|
||||
return s.getAsBool(blinkKey, false)
|
||||
}
|
||||
|
||||
// GetFaint returns the style's faint value. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetFaint() bool {
|
||||
return s.getAsBool(faintKey, false)
|
||||
}
|
||||
|
||||
// GetForeground returns the style's foreground color. If no value is set
|
||||
// NoColor{} is returned.
|
||||
func (s Style) GetForeground() TerminalColor {
|
||||
return s.getAsColor(foregroundKey)
|
||||
}
|
||||
|
||||
// GetBackground returns the style's back color. If no value is set
|
||||
// NoColor{} is returned.
|
||||
func (s Style) GetBackground() TerminalColor {
|
||||
return s.getAsColor(backgroundKey)
|
||||
}
|
||||
|
||||
// GetWidth returns the style's width setting. If no width is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetWidth() int {
|
||||
return s.getAsInt(widthKey)
|
||||
}
|
||||
|
||||
// GetHeight returns the style's height setting. If no height is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetHeight() int {
|
||||
return s.getAsInt(heightKey)
|
||||
}
|
||||
|
||||
// GetAlign returns the style's implicit horizontal alignment setting.
|
||||
// If no alignment is set Position.Left is returned.
|
||||
func (s Style) GetAlign() Position {
|
||||
v := s.getAsPosition(alignHorizontalKey)
|
||||
if v == Position(0) {
|
||||
return Left
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GetAlignHorizontal returns the style's implicit horizontal alignment setting.
|
||||
// If no alignment is set Position.Left is returned.
|
||||
func (s Style) GetAlignHorizontal() Position {
|
||||
v := s.getAsPosition(alignHorizontalKey)
|
||||
if v == Position(0) {
|
||||
return Left
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GetAlignVertical returns the style's implicit vertical alignment setting.
|
||||
// If no alignment is set Position.Top is returned.
|
||||
func (s Style) GetAlignVertical() Position {
|
||||
v := s.getAsPosition(alignVerticalKey)
|
||||
if v == Position(0) {
|
||||
return Top
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// GetPadding returns the style's top, right, bottom, and left padding values,
|
||||
// in that order. 0 is returned for unset values.
|
||||
func (s Style) GetPadding() (top, right, bottom, left int) {
|
||||
return s.getAsInt(paddingTopKey),
|
||||
s.getAsInt(paddingRightKey),
|
||||
s.getAsInt(paddingBottomKey),
|
||||
s.getAsInt(paddingLeftKey)
|
||||
}
|
||||
|
||||
// GetPaddingTop returns the style's top padding. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetPaddingTop() int {
|
||||
return s.getAsInt(paddingTopKey)
|
||||
}
|
||||
|
||||
// GetPaddingRight returns the style's right padding. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetPaddingRight() int {
|
||||
return s.getAsInt(paddingRightKey)
|
||||
}
|
||||
|
||||
// GetPaddingBottom returns the style's bottom padding. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetPaddingBottom() int {
|
||||
return s.getAsInt(paddingBottomKey)
|
||||
}
|
||||
|
||||
// GetPaddingLeft returns the style's left padding. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetPaddingLeft() int {
|
||||
return s.getAsInt(paddingLeftKey)
|
||||
}
|
||||
|
||||
// GetHorizontalPadding returns the style's left and right padding. Unset
|
||||
// values are measured as 0.
|
||||
func (s Style) GetHorizontalPadding() int {
|
||||
return s.getAsInt(paddingLeftKey) + s.getAsInt(paddingRightKey)
|
||||
}
|
||||
|
||||
// GetVerticalPadding returns the style's top and bottom padding. Unset values
|
||||
// are measured as 0.
|
||||
func (s Style) GetVerticalPadding() int {
|
||||
return s.getAsInt(paddingTopKey) + s.getAsInt(paddingBottomKey)
|
||||
}
|
||||
|
||||
// GetColorWhitespace returns the style's whitespace coloring setting. If no
|
||||
// value is set false is returned.
|
||||
func (s Style) GetColorWhitespace() bool {
|
||||
return s.getAsBool(colorWhitespaceKey, false)
|
||||
}
|
||||
|
||||
// GetMargin returns the style's top, right, bottom, and left margins, in that
|
||||
// order. 0 is returned for unset values.
|
||||
func (s Style) GetMargin() (top, right, bottom, left int) {
|
||||
return s.getAsInt(marginTopKey),
|
||||
s.getAsInt(marginRightKey),
|
||||
s.getAsInt(marginBottomKey),
|
||||
s.getAsInt(marginLeftKey)
|
||||
}
|
||||
|
||||
// GetMarginTop returns the style's top margin. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMarginTop() int {
|
||||
return s.getAsInt(marginTopKey)
|
||||
}
|
||||
|
||||
// GetMarginRight returns the style's right margin. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMarginRight() int {
|
||||
return s.getAsInt(marginRightKey)
|
||||
}
|
||||
|
||||
// GetMarginBottom returns the style's bottom margin. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMarginBottom() int {
|
||||
return s.getAsInt(marginBottomKey)
|
||||
}
|
||||
|
||||
// GetMarginLeft returns the style's left margin. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMarginLeft() int {
|
||||
return s.getAsInt(marginLeftKey)
|
||||
}
|
||||
|
||||
// GetHorizontalMargins returns the style's left and right margins. Unset
|
||||
// values are measured as 0.
|
||||
func (s Style) GetHorizontalMargins() int {
|
||||
return s.getAsInt(marginLeftKey) + s.getAsInt(marginRightKey)
|
||||
}
|
||||
|
||||
// GetVerticalMargins returns the style's top and bottom padding. Unset values
|
||||
// are measured as 0.
|
||||
func (s Style) GetVerticalMargins() int {
|
||||
return s.getAsInt(marginTopKey) + s.getAsInt(marginBottomKey)
|
||||
}
|
||||
|
||||
// GetBorder returns the style's border style (type Border) and value for the
|
||||
// top, right, bottom, and left in that order. If no value is set for the
|
||||
// border style, Border{} is returned. For all other unset values false is
|
||||
// returned.
|
||||
func (s Style) GetBorder() (b Border, top, right, bottom, left bool) {
|
||||
return s.getBorderStyle(),
|
||||
s.getAsBool(borderTopKey, false),
|
||||
s.getAsBool(borderRightKey, false),
|
||||
s.getAsBool(borderBottomKey, false),
|
||||
s.getAsBool(borderLeftKey, false)
|
||||
}
|
||||
|
||||
// GetBorderStyle returns the style's border style (type Border). If no value
|
||||
// is set Border{} is returned.
|
||||
func (s Style) GetBorderStyle() Border {
|
||||
return s.getBorderStyle()
|
||||
}
|
||||
|
||||
// GetBorderTop returns the style's top border setting. If no value is set
|
||||
// false is returned.
|
||||
func (s Style) GetBorderTop() bool {
|
||||
return s.getAsBool(borderTopKey, false)
|
||||
}
|
||||
|
||||
// GetBorderRight returns the style's right border setting. If no value is set
|
||||
// false is returned.
|
||||
func (s Style) GetBorderRight() bool {
|
||||
return s.getAsBool(borderRightKey, false)
|
||||
}
|
||||
|
||||
// GetBorderBottom returns the style's bottom border setting. If no value is
|
||||
// set false is returned.
|
||||
func (s Style) GetBorderBottom() bool {
|
||||
return s.getAsBool(borderBottomKey, false)
|
||||
}
|
||||
|
||||
// GetBorderLeft returns the style's left border setting. If no value is
|
||||
// set false is returned.
|
||||
func (s Style) GetBorderLeft() bool {
|
||||
return s.getAsBool(borderLeftKey, false)
|
||||
}
|
||||
|
||||
// GetBorderTopForeground returns the style's border top foreground color. If
|
||||
// no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderTopForeground() TerminalColor {
|
||||
return s.getAsColor(borderTopForegroundKey)
|
||||
}
|
||||
|
||||
// GetBorderRightForeground returns the style's border right foreground color.
|
||||
// If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderRightForeground() TerminalColor {
|
||||
return s.getAsColor(borderRightForegroundKey)
|
||||
}
|
||||
|
||||
// GetBorderBottomForeground returns the style's border bottom foreground
|
||||
// color. If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderBottomForeground() TerminalColor {
|
||||
return s.getAsColor(borderBottomForegroundKey)
|
||||
}
|
||||
|
||||
// GetBorderLeftForeground returns the style's border bottom foreground
|
||||
// color. If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderLeftForeground() TerminalColor {
|
||||
return s.getAsColor(borderLeftForegroundKey)
|
||||
}
|
||||
|
||||
// GetBorderTopBackground returns the style's border top background color. If
|
||||
// no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderTopBackground() TerminalColor {
|
||||
return s.getAsColor(borderTopBackgroundKey)
|
||||
}
|
||||
|
||||
// GetBorderRightBackground returns the style's border right background color.
|
||||
// If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderRightBackground() TerminalColor {
|
||||
return s.getAsColor(borderRightBackgroundKey)
|
||||
}
|
||||
|
||||
// GetBorderBottomBackground returns the style's border bottom background
|
||||
// color. If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderBottomBackground() TerminalColor {
|
||||
return s.getAsColor(borderBottomBackgroundKey)
|
||||
}
|
||||
|
||||
// GetBorderLeftBackground returns the style's border bottom background
|
||||
// color. If no value is set NoColor{} is returned.
|
||||
func (s Style) GetBorderLeftBackground() TerminalColor {
|
||||
return s.getAsColor(borderLeftBackgroundKey)
|
||||
}
|
||||
|
||||
// GetBorderTopWidth returns the width of the top border. If borders contain
|
||||
// runes of varying widths, the widest rune is returned. If no border exists on
|
||||
// the top edge, 0 is returned.
|
||||
//
|
||||
// Deprecated: This function simply calls Style.GetBorderTopSize.
|
||||
func (s Style) GetBorderTopWidth() int {
|
||||
return s.GetBorderTopSize()
|
||||
}
|
||||
|
||||
// GetBorderTopSize returns the width of the top border. If borders contain
|
||||
// runes of varying widths, the widest rune is returned. If no border exists on
|
||||
// the top edge, 0 is returned.
|
||||
func (s Style) GetBorderTopSize() int {
|
||||
if !s.getAsBool(borderTopKey, false) {
|
||||
return 0
|
||||
}
|
||||
return s.getBorderStyle().GetTopSize()
|
||||
}
|
||||
|
||||
// GetBorderLeftSize returns the width of the left border. If borders contain
|
||||
// runes of varying widths, the widest rune is returned. If no border exists on
|
||||
// the left edge, 0 is returned.
|
||||
func (s Style) GetBorderLeftSize() int {
|
||||
if !s.getAsBool(borderLeftKey, false) {
|
||||
return 0
|
||||
}
|
||||
return s.getBorderStyle().GetLeftSize()
|
||||
}
|
||||
|
||||
// GetBorderBottomSize returns the width of the bottom border. If borders
|
||||
// contain runes of varying widths, the widest rune is returned. If no border
|
||||
// exists on the left edge, 0 is returned.
|
||||
func (s Style) GetBorderBottomSize() int {
|
||||
if !s.getAsBool(borderBottomKey, false) {
|
||||
return 0
|
||||
}
|
||||
return s.getBorderStyle().GetBottomSize()
|
||||
}
|
||||
|
||||
// GetBorderRightSize returns the width of the right border. If borders
|
||||
// contain runes of varying widths, the widest rune is returned. If no border
|
||||
// exists on the right edge, 0 is returned.
|
||||
func (s Style) GetBorderRightSize() int {
|
||||
if !s.getAsBool(borderRightKey, false) {
|
||||
return 0
|
||||
}
|
||||
return s.getBorderStyle().GetBottomSize()
|
||||
}
|
||||
|
||||
// GetHorizontalBorderSize returns the width of the horizontal borders. If
|
||||
// borders contain runes of varying widths, the widest rune is returned. If no
|
||||
// border exists on the horizontal edges, 0 is returned.
|
||||
func (s Style) GetHorizontalBorderSize() int {
|
||||
b := s.getBorderStyle()
|
||||
return b.GetLeftSize() + b.GetRightSize()
|
||||
}
|
||||
|
||||
// GetVerticalBorderSize returns the width of the horizontal borders. If
|
||||
// borders contain runes of varying widths, the widest rune is returned. If no
|
||||
// border exists on the horizontal edges, 0 is returned.
|
||||
func (s Style) GetVerticalBorderSize() int {
|
||||
b := s.getBorderStyle()
|
||||
return b.GetTopSize() + b.GetBottomSize()
|
||||
}
|
||||
|
||||
// GetInline returns the style's inline setting. If no value is set false is
|
||||
// returned.
|
||||
func (s Style) GetInline() bool {
|
||||
return s.getAsBool(inlineKey, false)
|
||||
}
|
||||
|
||||
// GetMaxWidth returns the style's max width setting. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMaxWidth() int {
|
||||
return s.getAsInt(maxWidthKey)
|
||||
}
|
||||
|
||||
// GetMaxHeight returns the style's max width setting. If no value is set 0 is
|
||||
// returned.
|
||||
func (s Style) GetMaxHeight() int {
|
||||
return s.getAsInt(maxHeightKey)
|
||||
}
|
||||
|
||||
// GetUnderlineSpaces returns whether or not the style is set to underline
|
||||
// spaces. If not value is set false is returned.
|
||||
func (s Style) GetUnderlineSpaces() bool {
|
||||
return s.getAsBool(underlineSpacesKey, false)
|
||||
}
|
||||
|
||||
// GetStrikethroughSpaces returns whether or not the style is set to underline
|
||||
// spaces. If not value is set false is returned.
|
||||
func (s Style) GetStrikethroughSpaces() bool {
|
||||
return s.getAsBool(strikethroughSpacesKey, false)
|
||||
}
|
||||
|
||||
// GetHorizontalFrameSize returns the sum of the style's horizontal margins, padding
|
||||
// and border widths.
|
||||
//
|
||||
// Provisional: this method may be renamed.
|
||||
func (s Style) GetHorizontalFrameSize() int {
|
||||
return s.GetHorizontalMargins() + s.GetHorizontalPadding() + s.GetHorizontalBorderSize()
|
||||
}
|
||||
|
||||
// GetVerticalFrameSize returns the sum of the style's horizontal margins, padding
|
||||
// and border widths.
|
||||
//
|
||||
// Provisional: this method may be renamed.
|
||||
func (s Style) GetVerticalFrameSize() int {
|
||||
return s.GetVerticalMargins() + s.GetVerticalPadding() + s.GetVerticalBorderSize()
|
||||
}
|
||||
|
||||
// GetFrameSize returns the sum of the margins, padding and border width for
|
||||
// both the horizontal and vertical margins.
|
||||
func (s Style) GetFrameSize() (x, y int) {
|
||||
return s.GetHorizontalFrameSize(), s.GetVerticalFrameSize()
|
||||
}
|
||||
|
||||
// Returns whether or not the given property is set.
|
||||
func (s Style) isSet(k propKey) bool {
|
||||
_, exists := s.rules[k]
|
||||
return exists
|
||||
}
|
||||
|
||||
func (s Style) getAsBool(k propKey, defaultVal bool) bool {
|
||||
v, ok := s.rules[k]
|
||||
if !ok {
|
||||
return defaultVal
|
||||
}
|
||||
if b, ok := v.(bool); ok {
|
||||
return b
|
||||
}
|
||||
return defaultVal
|
||||
}
|
||||
|
||||
func (s Style) getAsColor(k propKey) TerminalColor {
|
||||
v, ok := s.rules[k]
|
||||
if !ok {
|
||||
return noColor
|
||||
}
|
||||
if c, ok := v.(TerminalColor); ok {
|
||||
return c
|
||||
}
|
||||
return noColor
|
||||
}
|
||||
|
||||
func (s Style) getAsInt(k propKey) int {
|
||||
v, ok := s.rules[k]
|
||||
if !ok {
|
||||
return 0
|
||||
}
|
||||
if i, ok := v.(int); ok {
|
||||
return i
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (s Style) getAsPosition(k propKey) Position {
|
||||
v, ok := s.rules[k]
|
||||
if !ok {
|
||||
return Position(0)
|
||||
}
|
||||
if p, ok := v.(Position); ok {
|
||||
return p
|
||||
}
|
||||
return Position(0)
|
||||
}
|
||||
|
||||
func (s Style) getBorderStyle() Border {
|
||||
v, ok := s.rules[borderStyleKey]
|
||||
if !ok {
|
||||
return noBorder
|
||||
}
|
||||
if b, ok := v.(Border); ok {
|
||||
return b
|
||||
}
|
||||
return noBorder
|
||||
}
|
||||
|
||||
// Split a string into lines, additionally returning the size of the widest
|
||||
// line.
|
||||
func getLines(s string) (lines []string, widest int) {
|
||||
lines = strings.Split(s, "\n")
|
||||
|
||||
for _, l := range lines {
|
||||
w := ansi.PrintableRuneWidth(l)
|
||||
if widest < w {
|
||||
widest = w
|
||||
}
|
||||
}
|
||||
|
||||
return lines, widest
|
||||
}
|
175
vendor/github.com/charmbracelet/lipgloss/join.go
generated
vendored
Normal file
175
vendor/github.com/charmbracelet/lipgloss/join.go
generated
vendored
Normal file
@ -0,0 +1,175 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/muesli/reflow/ansi"
|
||||
)
|
||||
|
||||
// JoinHorizontal is a utility function for horizontally joining two
|
||||
// potentially multi-lined strings along a vertical axis. The first argument is
|
||||
// the position, with 0 being all the way at the top and 1 being all the way
|
||||
// at the bottom.
|
||||
//
|
||||
// If you just want to align to the left, right or center you may as well just
|
||||
// use the helper constants Top, Center, and Bottom.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// blockB := "...\n...\n..."
|
||||
// blockA := "...\n...\n...\n...\n..."
|
||||
//
|
||||
// // Join 20% from the top
|
||||
// str := lipgloss.JoinHorizontal(0.2, blockA, blockB)
|
||||
//
|
||||
// // Join on the top edge
|
||||
// str := lipgloss.JoinHorizontal(lipgloss.Top, blockA, blockB)
|
||||
func JoinHorizontal(pos Position, strs ...string) string {
|
||||
if len(strs) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(strs) == 1 {
|
||||
return strs[0]
|
||||
}
|
||||
|
||||
var (
|
||||
// Groups of strings broken into multiple lines
|
||||
blocks = make([][]string, len(strs))
|
||||
|
||||
// Max line widths for the above text blocks
|
||||
maxWidths = make([]int, len(strs))
|
||||
|
||||
// Height of the tallest block
|
||||
maxHeight int
|
||||
)
|
||||
|
||||
// Break text blocks into lines and get max widths for each text block
|
||||
for i, str := range strs {
|
||||
blocks[i], maxWidths[i] = getLines(str)
|
||||
if len(blocks[i]) > maxHeight {
|
||||
maxHeight = len(blocks[i])
|
||||
}
|
||||
}
|
||||
|
||||
// Add extra lines to make each side the same height
|
||||
for i := range blocks {
|
||||
if len(blocks[i]) >= maxHeight {
|
||||
continue
|
||||
}
|
||||
|
||||
extraLines := make([]string, maxHeight-len(blocks[i]))
|
||||
|
||||
switch pos {
|
||||
case Top:
|
||||
blocks[i] = append(blocks[i], extraLines...)
|
||||
|
||||
case Bottom:
|
||||
blocks[i] = append(extraLines, blocks[i]...)
|
||||
|
||||
default: // Somewhere in the middle
|
||||
n := len(extraLines)
|
||||
split := int(math.Round(float64(n) * pos.value()))
|
||||
top := n - split
|
||||
bottom := n - top
|
||||
|
||||
blocks[i] = append(extraLines[top:], blocks[i]...)
|
||||
blocks[i] = append(blocks[i], extraLines[bottom:]...)
|
||||
}
|
||||
}
|
||||
|
||||
// Merge lines
|
||||
var b strings.Builder
|
||||
for i := range blocks[0] { // remember, all blocks have the same number of members now
|
||||
for j, block := range blocks {
|
||||
b.WriteString(block[i])
|
||||
|
||||
// Also make lines the same length
|
||||
b.WriteString(strings.Repeat(" ", maxWidths[j]-ansi.PrintableRuneWidth(block[i])))
|
||||
}
|
||||
if i < len(blocks[0])-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// JoinVertical is a utility function for vertically joining two potentially
|
||||
// multi-lined strings along a horizontal axis. The first argument is the
|
||||
// position, with 0 being all the way to the left and 1 being all the way to
|
||||
// the right.
|
||||
//
|
||||
// If you just want to align to the left, right or center you may as well just
|
||||
// use the helper constants Left, Center, and Right.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// blockB := "...\n...\n..."
|
||||
// blockA := "...\n...\n...\n...\n..."
|
||||
//
|
||||
// // Join 20% from the top
|
||||
// str := lipgloss.JoinVertical(0.2, blockA, blockB)
|
||||
//
|
||||
// // Join on the right edge
|
||||
// str := lipgloss.JoinVertical(lipgloss.Right, blockA, blockB)
|
||||
func JoinVertical(pos Position, strs ...string) string {
|
||||
if len(strs) == 0 {
|
||||
return ""
|
||||
}
|
||||
if len(strs) == 1 {
|
||||
return strs[0]
|
||||
}
|
||||
|
||||
var (
|
||||
blocks = make([][]string, len(strs))
|
||||
maxWidth int
|
||||
)
|
||||
|
||||
for i := range strs {
|
||||
var w int
|
||||
blocks[i], w = getLines(strs[i])
|
||||
if w > maxWidth {
|
||||
maxWidth = w
|
||||
}
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
for i, block := range blocks {
|
||||
for j, line := range block {
|
||||
w := maxWidth - ansi.PrintableRuneWidth(line)
|
||||
|
||||
switch pos {
|
||||
case Left:
|
||||
b.WriteString(line)
|
||||
b.WriteString(strings.Repeat(" ", w))
|
||||
|
||||
case Right:
|
||||
b.WriteString(strings.Repeat(" ", w))
|
||||
b.WriteString(line)
|
||||
|
||||
default: // Somewhere in the middle
|
||||
if w < 1 {
|
||||
b.WriteString(line)
|
||||
break
|
||||
}
|
||||
|
||||
split := int(math.Round(float64(w) * pos.value()))
|
||||
right := w - split
|
||||
left := w - right
|
||||
|
||||
b.WriteString(strings.Repeat(" ", left))
|
||||
b.WriteString(line)
|
||||
b.WriteString(strings.Repeat(" ", right))
|
||||
}
|
||||
|
||||
// Write a newline as long as we're not on the last line of the
|
||||
// last block.
|
||||
if !(i == len(blocks)-1 && j == len(block)-1) {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
154
vendor/github.com/charmbracelet/lipgloss/position.go
generated
vendored
Normal file
154
vendor/github.com/charmbracelet/lipgloss/position.go
generated
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"math"
|
||||
"strings"
|
||||
|
||||
"github.com/muesli/reflow/ansi"
|
||||
)
|
||||
|
||||
// Position represents a position along a horizontal or vertical axis. It's in
|
||||
// situations where an axis is involved, like alignment, joining, placement and
|
||||
// so on.
|
||||
//
|
||||
// A value of 0 represents the start (the left or top) and 1 represents the end
|
||||
// (the right or bottom). 0.5 represents the center.
|
||||
//
|
||||
// There are constants Top, Bottom, Center, Left and Right in this package that
|
||||
// can be used to aid readability.
|
||||
type Position float64
|
||||
|
||||
func (p Position) value() float64 {
|
||||
return math.Min(1, math.Max(0, float64(p)))
|
||||
}
|
||||
|
||||
// Position aliases.
|
||||
const (
|
||||
Top Position = 0.0
|
||||
Bottom Position = 1.0
|
||||
Center Position = 0.5
|
||||
Left Position = 0.0
|
||||
Right Position = 1.0
|
||||
)
|
||||
|
||||
// Place places a string or text block vertically in an unstyled box of a given
|
||||
// width or height.
|
||||
func Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string {
|
||||
return renderer.Place(width, height, hPos, vPos, str, opts...)
|
||||
}
|
||||
|
||||
// Place places a string or text block vertically in an unstyled box of a given
|
||||
// width or height.
|
||||
func (r *Renderer) Place(width, height int, hPos, vPos Position, str string, opts ...WhitespaceOption) string {
|
||||
return r.PlaceVertical(height, vPos, r.PlaceHorizontal(width, hPos, str, opts...), opts...)
|
||||
}
|
||||
|
||||
// PlaceHorizontal places a string or text block horizontally in an unstyled
|
||||
// block of a given width. If the given width is shorter than the max width of
|
||||
// the string (measured by its longest line) this will be a noop.
|
||||
func PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
|
||||
return renderer.PlaceHorizontal(width, pos, str, opts...)
|
||||
}
|
||||
|
||||
// PlaceHorizontal places a string or text block horizontally in an unstyled
|
||||
// block of a given width. If the given width is shorter than the max width of
|
||||
// the string (measured by it's longest line) this will be a noöp.
|
||||
func (r *Renderer) PlaceHorizontal(width int, pos Position, str string, opts ...WhitespaceOption) string {
|
||||
lines, contentWidth := getLines(str)
|
||||
gap := width - contentWidth
|
||||
|
||||
if gap <= 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
ws := newWhitespace(r, opts...)
|
||||
|
||||
var b strings.Builder
|
||||
for i, l := range lines {
|
||||
// Is this line shorter than the longest line?
|
||||
short := max(0, contentWidth-ansi.PrintableRuneWidth(l))
|
||||
|
||||
switch pos {
|
||||
case Left:
|
||||
b.WriteString(l)
|
||||
b.WriteString(ws.render(gap + short))
|
||||
|
||||
case Right:
|
||||
b.WriteString(ws.render(gap + short))
|
||||
b.WriteString(l)
|
||||
|
||||
default: // somewhere in the middle
|
||||
totalGap := gap + short
|
||||
|
||||
split := int(math.Round(float64(totalGap) * pos.value()))
|
||||
left := totalGap - split
|
||||
right := totalGap - left
|
||||
|
||||
b.WriteString(ws.render(left))
|
||||
b.WriteString(l)
|
||||
b.WriteString(ws.render(right))
|
||||
}
|
||||
|
||||
if i < len(lines)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// PlaceVertical places a string or text block vertically in an unstyled block
|
||||
// of a given height. If the given height is shorter than the height of the
|
||||
// string (measured by its newlines) then this will be a noop.
|
||||
func PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
|
||||
return renderer.PlaceVertical(height, pos, str, opts...)
|
||||
}
|
||||
|
||||
// PlaceVertical places a string or text block vertically in an unstyled block
|
||||
// of a given height. If the given height is shorter than the height of the
|
||||
// string (measured by it's newlines) then this will be a noöp.
|
||||
func (r *Renderer) PlaceVertical(height int, pos Position, str string, opts ...WhitespaceOption) string {
|
||||
contentHeight := strings.Count(str, "\n") + 1
|
||||
gap := height - contentHeight
|
||||
|
||||
if gap <= 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
ws := newWhitespace(r, opts...)
|
||||
|
||||
_, width := getLines(str)
|
||||
emptyLine := ws.render(width)
|
||||
b := strings.Builder{}
|
||||
|
||||
switch pos {
|
||||
case Top:
|
||||
b.WriteString(str)
|
||||
b.WriteRune('\n')
|
||||
for i := 0; i < gap; i++ {
|
||||
b.WriteString(emptyLine)
|
||||
if i < gap-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
case Bottom:
|
||||
b.WriteString(strings.Repeat(emptyLine+"\n", gap))
|
||||
b.WriteString(str)
|
||||
|
||||
default: // Somewhere in the middle
|
||||
split := int(math.Round(float64(gap) * pos.value()))
|
||||
top := gap - split
|
||||
bottom := gap - top
|
||||
|
||||
b.WriteString(strings.Repeat(emptyLine+"\n", top))
|
||||
b.WriteString(str)
|
||||
|
||||
for i := 0; i < bottom; i++ {
|
||||
b.WriteRune('\n')
|
||||
b.WriteString(emptyLine)
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
143
vendor/github.com/charmbracelet/lipgloss/renderer.go
generated
vendored
Normal file
143
vendor/github.com/charmbracelet/lipgloss/renderer.go
generated
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// We're manually creating the struct here to avoid initializing the output and
|
||||
// query the terminal multiple times.
|
||||
var renderer = &Renderer{
|
||||
output: termenv.DefaultOutput(),
|
||||
}
|
||||
|
||||
// Renderer is a lipgloss terminal renderer.
|
||||
type Renderer struct {
|
||||
output *termenv.Output
|
||||
hasDarkBackground *bool
|
||||
}
|
||||
|
||||
// RendererOption is a function that can be used to configure a [Renderer].
|
||||
type RendererOption func(r *Renderer)
|
||||
|
||||
// DefaultRenderer returns the default renderer.
|
||||
func DefaultRenderer() *Renderer {
|
||||
return renderer
|
||||
}
|
||||
|
||||
// SetDefaultRenderer sets the default global renderer.
|
||||
func SetDefaultRenderer(r *Renderer) {
|
||||
renderer = r
|
||||
}
|
||||
|
||||
// NewRenderer creates a new Renderer.
|
||||
//
|
||||
// w will be used to determine the terminal's color capabilities.
|
||||
func NewRenderer(w io.Writer, opts ...termenv.OutputOption) *Renderer {
|
||||
r := &Renderer{
|
||||
output: termenv.NewOutput(w, opts...),
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Output returns the termenv output.
|
||||
func (r *Renderer) Output() *termenv.Output {
|
||||
return r.output
|
||||
}
|
||||
|
||||
// SetOutput sets the termenv output.
|
||||
func (r *Renderer) SetOutput(o *termenv.Output) {
|
||||
r.output = o
|
||||
}
|
||||
|
||||
// ColorProfile returns the detected termenv color profile.
|
||||
func (r *Renderer) ColorProfile() termenv.Profile {
|
||||
return r.output.Profile
|
||||
}
|
||||
|
||||
// ColorProfile returns the detected termenv color profile.
|
||||
func ColorProfile() termenv.Profile {
|
||||
return renderer.ColorProfile()
|
||||
}
|
||||
|
||||
// SetColorProfile sets the color profile on the renderer. This function exists
|
||||
// mostly for testing purposes so that you can assure you're testing against
|
||||
// a specific profile.
|
||||
//
|
||||
// Outside of testing you likely won't want to use this function as the color
|
||||
// profile will detect and cache the terminal's color capabilities and choose
|
||||
// the best available profile.
|
||||
//
|
||||
// Available color profiles are:
|
||||
//
|
||||
// termenv.Ascii // no color, 1-bit
|
||||
// termenv.ANSI //16 colors, 4-bit
|
||||
// termenv.ANSI256 // 256 colors, 8-bit
|
||||
// termenv.TrueColor // 16,777,216 colors, 24-bit
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func (r *Renderer) SetColorProfile(p termenv.Profile) {
|
||||
r.output.Profile = p
|
||||
}
|
||||
|
||||
// SetColorProfile sets the color profile on the default renderer. This
|
||||
// function exists mostly for testing purposes so that you can assure you're
|
||||
// testing against a specific profile.
|
||||
//
|
||||
// Outside of testing you likely won't want to use this function as the color
|
||||
// profile will detect and cache the terminal's color capabilities and choose
|
||||
// the best available profile.
|
||||
//
|
||||
// Available color profiles are:
|
||||
//
|
||||
// termenv.Ascii // no color, 1-bit
|
||||
// termenv.ANSI //16 colors, 4-bit
|
||||
// termenv.ANSI256 // 256 colors, 8-bit
|
||||
// termenv.TrueColor // 16,777,216 colors, 24-bit
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func SetColorProfile(p termenv.Profile) {
|
||||
renderer.SetColorProfile(p)
|
||||
}
|
||||
|
||||
// HasDarkBackground returns whether or not the terminal has a dark background.
|
||||
func HasDarkBackground() bool {
|
||||
return renderer.HasDarkBackground()
|
||||
}
|
||||
|
||||
// HasDarkBackground returns whether or not the renderer will render to a dark
|
||||
// background. A dark background can either be auto-detected, or set explicitly
|
||||
// on the renderer.
|
||||
func (r *Renderer) HasDarkBackground() bool {
|
||||
if r.hasDarkBackground != nil {
|
||||
return *r.hasDarkBackground
|
||||
}
|
||||
return r.output.HasDarkBackground()
|
||||
}
|
||||
|
||||
// SetHasDarkBackground sets the background color detection value for the
|
||||
// default renderer. This function exists mostly for testing purposes so that
|
||||
// you can assure you're testing against a specific background color setting.
|
||||
//
|
||||
// Outside of testing you likely won't want to use this function as the
|
||||
// backgrounds value will be automatically detected and cached against the
|
||||
// terminal's current background color setting.
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func SetHasDarkBackground(b bool) {
|
||||
renderer.SetHasDarkBackground(b)
|
||||
}
|
||||
|
||||
// SetHasDarkBackground sets the background color detection value on the
|
||||
// renderer. This function exists mostly for testing purposes so that you can
|
||||
// assure you're testing against a specific background color setting.
|
||||
//
|
||||
// Outside of testing you likely won't want to use this function as the
|
||||
// backgrounds value will be automatically detected and cached against the
|
||||
// terminal's current background color setting.
|
||||
//
|
||||
// This function is thread-safe.
|
||||
func (r *Renderer) SetHasDarkBackground(b bool) {
|
||||
r.hasDarkBackground = &b
|
||||
}
|
43
vendor/github.com/charmbracelet/lipgloss/runes.go
generated
vendored
Normal file
43
vendor/github.com/charmbracelet/lipgloss/runes.go
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
// StyleRunes apply a given style to runes at the given indices in the string.
|
||||
// Note that you must provide styling options for both matched and unmatched
|
||||
// runes. Indices out of bounds will be ignored.
|
||||
func StyleRunes(str string, indices []int, matched, unmatched Style) string {
|
||||
// Convert slice of indices to a map for easier lookups
|
||||
m := make(map[int]struct{})
|
||||
for _, i := range indices {
|
||||
m[i] = struct{}{}
|
||||
}
|
||||
|
||||
var (
|
||||
out strings.Builder
|
||||
group strings.Builder
|
||||
style Style
|
||||
runes = []rune(str)
|
||||
)
|
||||
|
||||
for i, r := range runes {
|
||||
group.WriteRune(r)
|
||||
|
||||
_, matches := m[i]
|
||||
_, nextMatches := m[i+1]
|
||||
|
||||
if matches != nextMatches || i == len(runes)-1 {
|
||||
// Flush
|
||||
if matches {
|
||||
style = matched
|
||||
} else {
|
||||
style = unmatched
|
||||
}
|
||||
out.WriteString(style.Render(group.String()))
|
||||
group.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
634
vendor/github.com/charmbracelet/lipgloss/set.go
generated
vendored
Normal file
634
vendor/github.com/charmbracelet/lipgloss/set.go
generated
vendored
Normal file
@ -0,0 +1,634 @@
|
||||
package lipgloss
|
||||
|
||||
// This could (should) probably just be moved into NewStyle(). We've broken it
|
||||
// out, so we can call it in a lazy way.
|
||||
func (s *Style) init() {
|
||||
if s.rules == nil {
|
||||
s.rules = make(rules)
|
||||
}
|
||||
}
|
||||
|
||||
// Set a value on the underlying rules map.
|
||||
func (s *Style) set(key propKey, value interface{}) {
|
||||
s.init()
|
||||
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
// We don't allow negative integers on any of our values, so just keep
|
||||
// them at zero or above. We could use uints instead, but the
|
||||
// conversions are a little tedious, so we're sticking with ints for
|
||||
// sake of usability.
|
||||
s.rules[key] = max(0, v)
|
||||
default:
|
||||
s.rules[key] = v
|
||||
}
|
||||
}
|
||||
|
||||
// Bold sets a bold formatting rule.
|
||||
func (s Style) Bold(v bool) Style {
|
||||
s.set(boldKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Italic sets an italic formatting rule. In some terminal emulators this will
|
||||
// render with "reverse" coloring if not italic font variant is available.
|
||||
func (s Style) Italic(v bool) Style {
|
||||
s.set(italicKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Underline sets an underline rule. By default, underlines will not be drawn on
|
||||
// whitespace like margins and padding. To change this behavior set
|
||||
// UnderlineSpaces.
|
||||
func (s Style) Underline(v bool) Style {
|
||||
s.set(underlineKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Strikethrough sets a strikethrough rule. By default, strikes will not be
|
||||
// drawn on whitespace like margins and padding. To change this behavior set
|
||||
// StrikethroughSpaces.
|
||||
func (s Style) Strikethrough(v bool) Style {
|
||||
s.set(strikethroughKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Reverse sets a rule for inverting foreground and background colors.
|
||||
func (s Style) Reverse(v bool) Style {
|
||||
s.set(reverseKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Blink sets a rule for blinking foreground text.
|
||||
func (s Style) Blink(v bool) Style {
|
||||
s.set(blinkKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Faint sets a rule for rendering the foreground color in a dimmer shade.
|
||||
func (s Style) Faint(v bool) Style {
|
||||
s.set(faintKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Foreground sets a foreground color.
|
||||
//
|
||||
// // Sets the foreground to blue
|
||||
// s := lipgloss.NewStyle().Foreground(lipgloss.Color("#0000ff"))
|
||||
//
|
||||
// // Removes the foreground color
|
||||
// s.Foreground(lipgloss.NoColor)
|
||||
func (s Style) Foreground(c TerminalColor) Style {
|
||||
s.set(foregroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// Background sets a background color.
|
||||
func (s Style) Background(c TerminalColor) Style {
|
||||
s.set(backgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// Width sets the width of the block before applying margins. The width, if
|
||||
// set, also determines where text will wrap.
|
||||
func (s Style) Width(i int) Style {
|
||||
s.set(widthKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// Height sets the height of the block before applying margins. If the height of
|
||||
// the text block is less than this value after applying padding (or not), the
|
||||
// block will be set to this height.
|
||||
func (s Style) Height(i int) Style {
|
||||
s.set(heightKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// Align is a shorthand method for setting horizontal and vertical alignment.
|
||||
//
|
||||
// With one argument, the position value is applied to the horizontal alignment.
|
||||
//
|
||||
// With two arguments, the value is applied to the vertical and horizontal
|
||||
// alignments, in that order.
|
||||
func (s Style) Align(p ...Position) Style {
|
||||
if len(p) > 0 {
|
||||
s.set(alignHorizontalKey, p[0])
|
||||
}
|
||||
if len(p) > 1 {
|
||||
s.set(alignVerticalKey, p[1])
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// AlignHorizontal sets a horizontal text alignment rule.
|
||||
func (s Style) AlignHorizontal(p Position) Style {
|
||||
s.set(alignHorizontalKey, p)
|
||||
return s
|
||||
}
|
||||
|
||||
// AlignVertical sets a text alignment rule.
|
||||
func (s Style) AlignVertical(p Position) Style {
|
||||
s.set(alignVerticalKey, p)
|
||||
return s
|
||||
}
|
||||
|
||||
// Padding is a shorthand method for setting padding on all sides at once.
|
||||
//
|
||||
// With one argument, the value is applied to all sides.
|
||||
//
|
||||
// With two arguments, the value is applied to the vertical and horizontal
|
||||
// sides, in that order.
|
||||
//
|
||||
// With three arguments, the value is applied to the top side, the horizontal
|
||||
// sides, and the bottom side, in that order.
|
||||
//
|
||||
// With four arguments, the value is applied clockwise starting from the top
|
||||
// side, followed by the right side, then the bottom, and finally the left.
|
||||
//
|
||||
// With more than four arguments no padding will be added.
|
||||
func (s Style) Padding(i ...int) Style {
|
||||
top, right, bottom, left, ok := whichSidesInt(i...)
|
||||
if !ok {
|
||||
return s
|
||||
}
|
||||
|
||||
s.set(paddingTopKey, top)
|
||||
s.set(paddingRightKey, right)
|
||||
s.set(paddingBottomKey, bottom)
|
||||
s.set(paddingLeftKey, left)
|
||||
return s
|
||||
}
|
||||
|
||||
// PaddingLeft adds padding on the left.
|
||||
func (s Style) PaddingLeft(i int) Style {
|
||||
s.set(paddingLeftKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// PaddingRight adds padding on the right.
|
||||
func (s Style) PaddingRight(i int) Style {
|
||||
s.set(paddingRightKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// PaddingTop adds padding to the top of the block.
|
||||
func (s Style) PaddingTop(i int) Style {
|
||||
s.set(paddingTopKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// PaddingBottom adds padding to the bottom of the block.
|
||||
func (s Style) PaddingBottom(i int) Style {
|
||||
s.set(paddingBottomKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// ColorWhitespace determines whether or not the background color should be
|
||||
// applied to the padding. This is true by default as it's more than likely the
|
||||
// desired and expected behavior, but it can be disabled for certain graphic
|
||||
// effects.
|
||||
func (s Style) ColorWhitespace(v bool) Style {
|
||||
s.set(colorWhitespaceKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Margin is a shorthand method for setting margins on all sides at once.
|
||||
//
|
||||
// With one argument, the value is applied to all sides.
|
||||
//
|
||||
// With two arguments, the value is applied to the vertical and horizontal
|
||||
// sides, in that order.
|
||||
//
|
||||
// With three arguments, the value is applied to the top side, the horizontal
|
||||
// sides, and the bottom side, in that order.
|
||||
//
|
||||
// With four arguments, the value is applied clockwise starting from the top
|
||||
// side, followed by the right side, then the bottom, and finally the left.
|
||||
//
|
||||
// With more than four arguments no margin will be added.
|
||||
func (s Style) Margin(i ...int) Style {
|
||||
top, right, bottom, left, ok := whichSidesInt(i...)
|
||||
if !ok {
|
||||
return s
|
||||
}
|
||||
|
||||
s.set(marginTopKey, top)
|
||||
s.set(marginRightKey, right)
|
||||
s.set(marginBottomKey, bottom)
|
||||
s.set(marginLeftKey, left)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarginLeft sets the value of the left margin.
|
||||
func (s Style) MarginLeft(i int) Style {
|
||||
s.set(marginLeftKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarginRight sets the value of the right margin.
|
||||
func (s Style) MarginRight(i int) Style {
|
||||
s.set(marginRightKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarginTop sets the value of the top margin.
|
||||
func (s Style) MarginTop(i int) Style {
|
||||
s.set(marginTopKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarginBottom sets the value of the bottom margin.
|
||||
func (s Style) MarginBottom(i int) Style {
|
||||
s.set(marginBottomKey, i)
|
||||
return s
|
||||
}
|
||||
|
||||
// MarginBackground sets the background color of the margin. Note that this is
|
||||
// also set when inheriting from a style with a background color. In that case
|
||||
// the background color on that style will set the margin color on this style.
|
||||
func (s Style) MarginBackground(c TerminalColor) Style {
|
||||
s.set(marginBackgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// Border is shorthand for setting the border style and which sides should
|
||||
// have a border at once. The variadic argument sides works as follows:
|
||||
//
|
||||
// With one value, the value is applied to all sides.
|
||||
//
|
||||
// With two values, the values are applied to the vertical and horizontal
|
||||
// sides, in that order.
|
||||
//
|
||||
// With three values, the values are applied to the top side, the horizontal
|
||||
// sides, and the bottom side, in that order.
|
||||
//
|
||||
// With four values, the values are applied clockwise starting from the top
|
||||
// side, followed by the right side, then the bottom, and finally the left.
|
||||
//
|
||||
// With more than four arguments the border will be applied to all sides.
|
||||
//
|
||||
// Examples:
|
||||
//
|
||||
// // Applies borders to the top and bottom only
|
||||
// lipgloss.NewStyle().Border(lipgloss.NormalBorder(), true, false)
|
||||
//
|
||||
// // Applies rounded borders to the right and bottom only
|
||||
// lipgloss.NewStyle().Border(lipgloss.RoundedBorder(), false, true, true, false)
|
||||
func (s Style) Border(b Border, sides ...bool) Style {
|
||||
s.set(borderStyleKey, b)
|
||||
|
||||
top, right, bottom, left, ok := whichSidesBool(sides...)
|
||||
if !ok {
|
||||
top = true
|
||||
right = true
|
||||
bottom = true
|
||||
left = true
|
||||
}
|
||||
|
||||
s.set(borderTopKey, top)
|
||||
s.set(borderRightKey, right)
|
||||
s.set(borderBottomKey, bottom)
|
||||
s.set(borderLeftKey, left)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderStyle defines the Border on a style. A Border contains a series of
|
||||
// definitions for the sides and corners of a border.
|
||||
//
|
||||
// Note that if border visibility has not been set for any sides when setting
|
||||
// the border style, the border will be enabled for all sides during rendering.
|
||||
//
|
||||
// You can define border characters as you'd like, though several default
|
||||
// styles are included: NormalBorder(), RoundedBorder(), BlockBorder(),
|
||||
// OuterHalfBlockBorder(), InnerHalfBlockBorder(), ThickBorder(),
|
||||
// and DoubleBorder().
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// lipgloss.NewStyle().BorderStyle(lipgloss.ThickBorder())
|
||||
func (s Style) BorderStyle(b Border) Style {
|
||||
s.set(borderStyleKey, b)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderTop determines whether or not to draw a top border.
|
||||
func (s Style) BorderTop(v bool) Style {
|
||||
s.set(borderTopKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderRight determines whether or not to draw a right border.
|
||||
func (s Style) BorderRight(v bool) Style {
|
||||
s.set(borderRightKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderBottom determines whether or not to draw a bottom border.
|
||||
func (s Style) BorderBottom(v bool) Style {
|
||||
s.set(borderBottomKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderLeft determines whether or not to draw a left border.
|
||||
func (s Style) BorderLeft(v bool) Style {
|
||||
s.set(borderLeftKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderForeground is a shorthand function for setting all of the
|
||||
// foreground colors of the borders at once. The arguments work as follows:
|
||||
//
|
||||
// With one argument, the argument is applied to all sides.
|
||||
//
|
||||
// With two arguments, the arguments are applied to the vertical and horizontal
|
||||
// sides, in that order.
|
||||
//
|
||||
// With three arguments, the arguments are applied to the top side, the
|
||||
// horizontal sides, and the bottom side, in that order.
|
||||
//
|
||||
// With four arguments, the arguments are applied clockwise starting from the
|
||||
// top side, followed by the right side, then the bottom, and finally the left.
|
||||
//
|
||||
// With more than four arguments nothing will be set.
|
||||
func (s Style) BorderForeground(c ...TerminalColor) Style {
|
||||
if len(c) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
top, right, bottom, left, ok := whichSidesColor(c...)
|
||||
if !ok {
|
||||
return s
|
||||
}
|
||||
|
||||
s.set(borderTopForegroundKey, top)
|
||||
s.set(borderRightForegroundKey, right)
|
||||
s.set(borderBottomForegroundKey, bottom)
|
||||
s.set(borderLeftForegroundKey, left)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderTopForeground set the foreground color for the top of the border.
|
||||
func (s Style) BorderTopForeground(c TerminalColor) Style {
|
||||
s.set(borderTopForegroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderRightForeground sets the foreground color for the right side of the
|
||||
// border.
|
||||
func (s Style) BorderRightForeground(c TerminalColor) Style {
|
||||
s.set(borderRightForegroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderBottomForeground sets the foreground color for the bottom of the
|
||||
// border.
|
||||
func (s Style) BorderBottomForeground(c TerminalColor) Style {
|
||||
s.set(borderBottomForegroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderLeftForeground sets the foreground color for the left side of the
|
||||
// border.
|
||||
func (s Style) BorderLeftForeground(c TerminalColor) Style {
|
||||
s.set(borderLeftForegroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderBackground is a shorthand function for setting all of the
|
||||
// background colors of the borders at once. The arguments work as follows:
|
||||
//
|
||||
// With one argument, the argument is applied to all sides.
|
||||
//
|
||||
// With two arguments, the arguments are applied to the vertical and horizontal
|
||||
// sides, in that order.
|
||||
//
|
||||
// With three arguments, the arguments are applied to the top side, the
|
||||
// horizontal sides, and the bottom side, in that order.
|
||||
//
|
||||
// With four arguments, the arguments are applied clockwise starting from the
|
||||
// top side, followed by the right side, then the bottom, and finally the left.
|
||||
//
|
||||
// With more than four arguments nothing will be set.
|
||||
func (s Style) BorderBackground(c ...TerminalColor) Style {
|
||||
if len(c) == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
top, right, bottom, left, ok := whichSidesColor(c...)
|
||||
if !ok {
|
||||
return s
|
||||
}
|
||||
|
||||
s.set(borderTopBackgroundKey, top)
|
||||
s.set(borderRightBackgroundKey, right)
|
||||
s.set(borderBottomBackgroundKey, bottom)
|
||||
s.set(borderLeftBackgroundKey, left)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderTopBackground sets the background color of the top of the border.
|
||||
func (s Style) BorderTopBackground(c TerminalColor) Style {
|
||||
s.set(borderTopBackgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderRightBackground sets the background color of right side the border.
|
||||
func (s Style) BorderRightBackground(c TerminalColor) Style {
|
||||
s.set(borderRightBackgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderBottomBackground sets the background color of the bottom of the
|
||||
// border.
|
||||
func (s Style) BorderBottomBackground(c TerminalColor) Style {
|
||||
s.set(borderBottomBackgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// BorderLeftBackground set the background color of the left side of the
|
||||
// border.
|
||||
func (s Style) BorderLeftBackground(c TerminalColor) Style {
|
||||
s.set(borderLeftBackgroundKey, c)
|
||||
return s
|
||||
}
|
||||
|
||||
// Inline makes rendering output one line and disables the rendering of
|
||||
// margins, padding and borders. This is useful when you need a style to apply
|
||||
// only to font rendering and don't want it to change any physical dimensions.
|
||||
// It works well with Style.MaxWidth.
|
||||
//
|
||||
// Because this in intended to be used at the time of render, this method will
|
||||
// not mutate the style and instead return a copy.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var userInput string = "..."
|
||||
// var userStyle = text.Style{ /* ... */ }
|
||||
// fmt.Println(userStyle.Inline(true).Render(userInput))
|
||||
func (s Style) Inline(v bool) Style {
|
||||
o := s.Copy()
|
||||
o.set(inlineKey, v)
|
||||
return o
|
||||
}
|
||||
|
||||
// MaxWidth applies a max width to a given style. This is useful in enforcing
|
||||
// a certain width at render time, particularly with arbitrary strings and
|
||||
// styles.
|
||||
//
|
||||
// Because this in intended to be used at the time of render, this method will
|
||||
// not mutate the style and instead return a copy.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// var userInput string = "..."
|
||||
// var userStyle = text.Style{ /* ... */ }
|
||||
// fmt.Println(userStyle.MaxWidth(16).Render(userInput))
|
||||
func (s Style) MaxWidth(n int) Style {
|
||||
o := s.Copy()
|
||||
o.set(maxWidthKey, n)
|
||||
return o
|
||||
}
|
||||
|
||||
// MaxHeight applies a max height to a given style. This is useful in enforcing
|
||||
// a certain height at render time, particularly with arbitrary strings and
|
||||
// styles.
|
||||
//
|
||||
// Because this in intended to be used at the time of render, this method will
|
||||
// not mutate the style and instead return a copy.
|
||||
func (s Style) MaxHeight(n int) Style {
|
||||
o := s.Copy()
|
||||
o.set(maxHeightKey, n)
|
||||
return o
|
||||
}
|
||||
|
||||
// UnderlineSpaces determines whether to underline spaces between words. By
|
||||
// default, this is true. Spaces can also be underlined without underlining the
|
||||
// text itself.
|
||||
func (s Style) UnderlineSpaces(v bool) Style {
|
||||
s.set(underlineSpacesKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// StrikethroughSpaces determines whether to apply strikethroughs to spaces
|
||||
// between words. By default, this is true. Spaces can also be struck without
|
||||
// underlining the text itself.
|
||||
func (s Style) StrikethroughSpaces(v bool) Style {
|
||||
s.set(strikethroughSpacesKey, v)
|
||||
return s
|
||||
}
|
||||
|
||||
// Renderer sets the renderer for the style. This is useful for changing the
|
||||
// renderer for a style that is being used in a different context.
|
||||
func (s Style) Renderer(r *Renderer) Style {
|
||||
s.r = r
|
||||
return s
|
||||
}
|
||||
|
||||
// whichSidesInt is a helper method for setting values on sides of a block based
|
||||
// on the number of arguments. It follows the CSS shorthand rules for blocks
|
||||
// like margin, padding. and borders. Here are how the rules work:
|
||||
//
|
||||
// 0 args: do nothing
|
||||
// 1 arg: all sides
|
||||
// 2 args: top -> bottom
|
||||
// 3 args: top -> horizontal -> bottom
|
||||
// 4 args: top -> right -> bottom -> left
|
||||
// 5+ args: do nothing.
|
||||
func whichSidesInt(i ...int) (top, right, bottom, left int, ok bool) {
|
||||
switch len(i) {
|
||||
case 1:
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[0]
|
||||
right = i[0]
|
||||
ok = true
|
||||
case 2:
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
ok = true
|
||||
case 3:
|
||||
top = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
ok = true
|
||||
case 4:
|
||||
top = i[0]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
left = i[3]
|
||||
ok = true
|
||||
}
|
||||
return top, right, bottom, left, ok
|
||||
}
|
||||
|
||||
// whichSidesBool is like whichSidesInt, except it operates on a series of
|
||||
// boolean values. See the comment on whichSidesInt for details on how this
|
||||
// works.
|
||||
func whichSidesBool(i ...bool) (top, right, bottom, left bool, ok bool) {
|
||||
switch len(i) {
|
||||
case 1:
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[0]
|
||||
right = i[0]
|
||||
ok = true
|
||||
case 2:
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
ok = true
|
||||
case 3:
|
||||
top = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
ok = true
|
||||
case 4:
|
||||
top = i[0]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
left = i[3]
|
||||
ok = true
|
||||
}
|
||||
return top, right, bottom, left, ok
|
||||
}
|
||||
|
||||
// whichSidesColor is like whichSides, except it operates on a series of
|
||||
// boolean values. See the comment on whichSidesInt for details on how this
|
||||
// works.
|
||||
func whichSidesColor(i ...TerminalColor) (top, right, bottom, left TerminalColor, ok bool) {
|
||||
switch len(i) {
|
||||
case 1:
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[0]
|
||||
right = i[0]
|
||||
ok = true
|
||||
case 2:
|
||||
top = i[0]
|
||||
bottom = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
ok = true
|
||||
case 3:
|
||||
top = i[0]
|
||||
left = i[1]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
ok = true
|
||||
case 4:
|
||||
top = i[0]
|
||||
right = i[1]
|
||||
bottom = i[2]
|
||||
left = i[3]
|
||||
ok = true
|
||||
}
|
||||
return top, right, bottom, left, ok
|
||||
}
|
41
vendor/github.com/charmbracelet/lipgloss/size.go
generated
vendored
Normal file
41
vendor/github.com/charmbracelet/lipgloss/size.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/muesli/reflow/ansi"
|
||||
)
|
||||
|
||||
// Width returns the cell width of characters in the string. ANSI sequences are
|
||||
// ignored and characters wider than one cell (such as Chinese characters and
|
||||
// emojis) are appropriately measured.
|
||||
//
|
||||
// You should use this instead of len(string) len([]rune(string) as neither
|
||||
// will give you accurate results.
|
||||
func Width(str string) (width int) {
|
||||
for _, l := range strings.Split(str, "\n") {
|
||||
w := ansi.PrintableRuneWidth(l)
|
||||
if w > width {
|
||||
width = w
|
||||
}
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
|
||||
// Height returns height of a string in cells. This is done simply by
|
||||
// counting \n characters. If your strings use \r\n for newlines you should
|
||||
// convert them to \n first, or simply write a separate function for measuring
|
||||
// height.
|
||||
func Height(str string) int {
|
||||
return strings.Count(str, "\n") + 1
|
||||
}
|
||||
|
||||
// Size returns the width and height of the string in cells. ANSI sequences are
|
||||
// ignored and characters wider than one cell (such as Chinese characters and
|
||||
// emojis) are appropriately measured.
|
||||
func Size(str string) (width, height int) {
|
||||
width = Width(str)
|
||||
height = Height(str)
|
||||
return width, height
|
||||
}
|
497
vendor/github.com/charmbracelet/lipgloss/style.go
generated
vendored
Normal file
497
vendor/github.com/charmbracelet/lipgloss/style.go
generated
vendored
Normal file
@ -0,0 +1,497 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
|
||||
"github.com/muesli/reflow/truncate"
|
||||
"github.com/muesli/reflow/wordwrap"
|
||||
"github.com/muesli/reflow/wrap"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// Property for a key.
|
||||
type propKey int
|
||||
|
||||
// Available properties.
|
||||
const (
|
||||
boldKey propKey = iota
|
||||
italicKey
|
||||
underlineKey
|
||||
strikethroughKey
|
||||
reverseKey
|
||||
blinkKey
|
||||
faintKey
|
||||
foregroundKey
|
||||
backgroundKey
|
||||
widthKey
|
||||
heightKey
|
||||
alignHorizontalKey
|
||||
alignVerticalKey
|
||||
|
||||
// Padding.
|
||||
paddingTopKey
|
||||
paddingRightKey
|
||||
paddingBottomKey
|
||||
paddingLeftKey
|
||||
|
||||
colorWhitespaceKey
|
||||
|
||||
// Margins.
|
||||
marginTopKey
|
||||
marginRightKey
|
||||
marginBottomKey
|
||||
marginLeftKey
|
||||
marginBackgroundKey
|
||||
|
||||
// Border runes.
|
||||
borderStyleKey
|
||||
|
||||
// Border edges.
|
||||
borderTopKey
|
||||
borderRightKey
|
||||
borderBottomKey
|
||||
borderLeftKey
|
||||
|
||||
// Border foreground colors.
|
||||
borderTopForegroundKey
|
||||
borderRightForegroundKey
|
||||
borderBottomForegroundKey
|
||||
borderLeftForegroundKey
|
||||
|
||||
// Border background colors.
|
||||
borderTopBackgroundKey
|
||||
borderRightBackgroundKey
|
||||
borderBottomBackgroundKey
|
||||
borderLeftBackgroundKey
|
||||
|
||||
inlineKey
|
||||
maxWidthKey
|
||||
maxHeightKey
|
||||
underlineSpacesKey
|
||||
strikethroughSpacesKey
|
||||
)
|
||||
|
||||
// A set of properties.
|
||||
type rules map[propKey]interface{}
|
||||
|
||||
// NewStyle returns a new, empty Style. While it's syntactic sugar for the
|
||||
// Style{} primitive, it's recommended to use this function for creating styles
|
||||
// in case the underlying implementation changes. It takes an optional string
|
||||
// value to be set as the underlying string value for this style.
|
||||
func NewStyle() Style {
|
||||
return renderer.NewStyle()
|
||||
}
|
||||
|
||||
// NewStyle returns a new, empty Style. While it's syntactic sugar for the
|
||||
// Style{} primitive, it's recommended to use this function for creating styles
|
||||
// in case the underlying implementation changes. It takes an optional string
|
||||
// value to be set as the underlying string value for this style.
|
||||
func (r *Renderer) NewStyle() Style {
|
||||
s := Style{r: r}
|
||||
return s
|
||||
}
|
||||
|
||||
// Style contains a set of rules that comprise a style as a whole.
|
||||
type Style struct {
|
||||
r *Renderer
|
||||
rules map[propKey]interface{}
|
||||
value string
|
||||
}
|
||||
|
||||
// joinString joins a list of strings into a single string separated with a
|
||||
// space.
|
||||
func joinString(strs ...string) string {
|
||||
return strings.Join(strs, " ")
|
||||
}
|
||||
|
||||
// SetString sets the underlying string value for this style. To render once
|
||||
// the underlying string is set, use the Style.String. This method is
|
||||
// a convenience for cases when having a stringer implementation is handy, such
|
||||
// as when using fmt.Sprintf. You can also simply define a style and render out
|
||||
// strings directly with Style.Render.
|
||||
func (s Style) SetString(strs ...string) Style {
|
||||
s.value = joinString(strs...)
|
||||
return s
|
||||
}
|
||||
|
||||
// Value returns the raw, unformatted, underlying string value for this style.
|
||||
func (s Style) Value() string {
|
||||
return s.value
|
||||
}
|
||||
|
||||
// String implements stringer for a Style, returning the rendered result based
|
||||
// on the rules in this style. An underlying string value must be set with
|
||||
// Style.SetString prior to using this method.
|
||||
func (s Style) String() string {
|
||||
return s.Render()
|
||||
}
|
||||
|
||||
// Copy returns a copy of this style, including any underlying string values.
|
||||
func (s Style) Copy() Style {
|
||||
o := NewStyle()
|
||||
o.init()
|
||||
for k, v := range s.rules {
|
||||
o.rules[k] = v
|
||||
}
|
||||
o.r = s.r
|
||||
o.value = s.value
|
||||
return o
|
||||
}
|
||||
|
||||
// Inherit overlays the style in the argument onto this style by copying each explicitly
|
||||
// set value from the argument style onto this style if it is not already explicitly set.
|
||||
// Existing set values are kept intact and not overwritten.
|
||||
//
|
||||
// Margins, padding, and underlying string values are not inherited.
|
||||
func (s Style) Inherit(i Style) Style {
|
||||
s.init()
|
||||
|
||||
for k, v := range i.rules {
|
||||
switch k {
|
||||
case marginTopKey, marginRightKey, marginBottomKey, marginLeftKey:
|
||||
// Margins are not inherited
|
||||
continue
|
||||
case paddingTopKey, paddingRightKey, paddingBottomKey, paddingLeftKey:
|
||||
// Padding is not inherited
|
||||
continue
|
||||
case backgroundKey:
|
||||
// The margins also inherit the background color
|
||||
if !s.isSet(marginBackgroundKey) && !i.isSet(marginBackgroundKey) {
|
||||
s.rules[marginBackgroundKey] = v
|
||||
}
|
||||
}
|
||||
|
||||
if _, exists := s.rules[k]; exists {
|
||||
continue
|
||||
}
|
||||
s.rules[k] = v
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// Render applies the defined style formatting to a given string.
|
||||
func (s Style) Render(strs ...string) string {
|
||||
if s.r == nil {
|
||||
s.r = renderer
|
||||
}
|
||||
if s.value != "" {
|
||||
strs = append([]string{s.value}, strs...)
|
||||
}
|
||||
|
||||
var (
|
||||
str = joinString(strs...)
|
||||
|
||||
te = s.r.ColorProfile().String()
|
||||
teSpace = s.r.ColorProfile().String()
|
||||
teWhitespace = s.r.ColorProfile().String()
|
||||
|
||||
bold = s.getAsBool(boldKey, false)
|
||||
italic = s.getAsBool(italicKey, false)
|
||||
underline = s.getAsBool(underlineKey, false)
|
||||
strikethrough = s.getAsBool(strikethroughKey, false)
|
||||
reverse = s.getAsBool(reverseKey, false)
|
||||
blink = s.getAsBool(blinkKey, false)
|
||||
faint = s.getAsBool(faintKey, false)
|
||||
|
||||
fg = s.getAsColor(foregroundKey)
|
||||
bg = s.getAsColor(backgroundKey)
|
||||
|
||||
width = s.getAsInt(widthKey)
|
||||
height = s.getAsInt(heightKey)
|
||||
horizontalAlign = s.getAsPosition(alignHorizontalKey)
|
||||
verticalAlign = s.getAsPosition(alignVerticalKey)
|
||||
|
||||
topPadding = s.getAsInt(paddingTopKey)
|
||||
rightPadding = s.getAsInt(paddingRightKey)
|
||||
bottomPadding = s.getAsInt(paddingBottomKey)
|
||||
leftPadding = s.getAsInt(paddingLeftKey)
|
||||
|
||||
colorWhitespace = s.getAsBool(colorWhitespaceKey, true)
|
||||
inline = s.getAsBool(inlineKey, false)
|
||||
maxWidth = s.getAsInt(maxWidthKey)
|
||||
maxHeight = s.getAsInt(maxHeightKey)
|
||||
|
||||
underlineSpaces = underline && s.getAsBool(underlineSpacesKey, true)
|
||||
strikethroughSpaces = strikethrough && s.getAsBool(strikethroughSpacesKey, true)
|
||||
|
||||
// Do we need to style whitespace (padding and space outside
|
||||
// paragraphs) separately?
|
||||
styleWhitespace = reverse
|
||||
|
||||
// Do we need to style spaces separately?
|
||||
useSpaceStyler = underlineSpaces || strikethroughSpaces
|
||||
)
|
||||
|
||||
if len(s.rules) == 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
// Enable support for ANSI on the legacy Windows cmd.exe console. This is a
|
||||
// no-op on non-Windows systems and on Windows runs only once.
|
||||
enableLegacyWindowsANSI()
|
||||
|
||||
if bold {
|
||||
te = te.Bold()
|
||||
}
|
||||
if italic {
|
||||
te = te.Italic()
|
||||
}
|
||||
if underline {
|
||||
te = te.Underline()
|
||||
}
|
||||
if reverse {
|
||||
if reverse {
|
||||
teWhitespace = teWhitespace.Reverse()
|
||||
}
|
||||
te = te.Reverse()
|
||||
}
|
||||
if blink {
|
||||
te = te.Blink()
|
||||
}
|
||||
if faint {
|
||||
te = te.Faint()
|
||||
}
|
||||
|
||||
if fg != noColor {
|
||||
te = te.Foreground(fg.color(s.r))
|
||||
if styleWhitespace {
|
||||
teWhitespace = teWhitespace.Foreground(fg.color(s.r))
|
||||
}
|
||||
if useSpaceStyler {
|
||||
teSpace = teSpace.Foreground(fg.color(s.r))
|
||||
}
|
||||
}
|
||||
|
||||
if bg != noColor {
|
||||
te = te.Background(bg.color(s.r))
|
||||
if colorWhitespace {
|
||||
teWhitespace = teWhitespace.Background(bg.color(s.r))
|
||||
}
|
||||
if useSpaceStyler {
|
||||
teSpace = teSpace.Background(bg.color(s.r))
|
||||
}
|
||||
}
|
||||
|
||||
if underline {
|
||||
te = te.Underline()
|
||||
}
|
||||
if strikethrough {
|
||||
te = te.CrossOut()
|
||||
}
|
||||
|
||||
if underlineSpaces {
|
||||
teSpace = teSpace.Underline()
|
||||
}
|
||||
if strikethroughSpaces {
|
||||
teSpace = teSpace.CrossOut()
|
||||
}
|
||||
|
||||
// Strip newlines in single line mode
|
||||
if inline {
|
||||
str = strings.ReplaceAll(str, "\n", "")
|
||||
}
|
||||
|
||||
// Word wrap
|
||||
if !inline && width > 0 {
|
||||
wrapAt := width - leftPadding - rightPadding
|
||||
str = wordwrap.String(str, wrapAt)
|
||||
str = wrap.String(str, wrapAt) // force-wrap long strings
|
||||
}
|
||||
|
||||
// Render core text
|
||||
{
|
||||
var b strings.Builder
|
||||
|
||||
l := strings.Split(str, "\n")
|
||||
for i := range l {
|
||||
if useSpaceStyler {
|
||||
// Look for spaces and apply a different styler
|
||||
for _, r := range l[i] {
|
||||
if unicode.IsSpace(r) {
|
||||
b.WriteString(teSpace.Styled(string(r)))
|
||||
continue
|
||||
}
|
||||
b.WriteString(te.Styled(string(r)))
|
||||
}
|
||||
} else {
|
||||
b.WriteString(te.Styled(l[i]))
|
||||
}
|
||||
if i != len(l)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
str = b.String()
|
||||
}
|
||||
|
||||
// Padding
|
||||
if !inline {
|
||||
if leftPadding > 0 {
|
||||
var st *termenv.Style
|
||||
if colorWhitespace || styleWhitespace {
|
||||
st = &teWhitespace
|
||||
}
|
||||
str = padLeft(str, leftPadding, st)
|
||||
}
|
||||
|
||||
if rightPadding > 0 {
|
||||
var st *termenv.Style
|
||||
if colorWhitespace || styleWhitespace {
|
||||
st = &teWhitespace
|
||||
}
|
||||
str = padRight(str, rightPadding, st)
|
||||
}
|
||||
|
||||
if topPadding > 0 {
|
||||
str = strings.Repeat("\n", topPadding) + str
|
||||
}
|
||||
|
||||
if bottomPadding > 0 {
|
||||
str += strings.Repeat("\n", bottomPadding)
|
||||
}
|
||||
}
|
||||
|
||||
// Height
|
||||
if height > 0 {
|
||||
str = alignTextVertical(str, verticalAlign, height, nil)
|
||||
}
|
||||
|
||||
// Set alignment. This will also pad short lines with spaces so that all
|
||||
// lines are the same length, so we run it under a few different conditions
|
||||
// beyond alignment.
|
||||
{
|
||||
numLines := strings.Count(str, "\n")
|
||||
|
||||
if !(numLines == 0 && width == 0) {
|
||||
var st *termenv.Style
|
||||
if colorWhitespace || styleWhitespace {
|
||||
st = &teWhitespace
|
||||
}
|
||||
str = alignTextHorizontal(str, horizontalAlign, width, st)
|
||||
}
|
||||
}
|
||||
|
||||
if !inline {
|
||||
str = s.applyBorder(str)
|
||||
str = s.applyMargins(str, inline)
|
||||
}
|
||||
|
||||
// Truncate according to MaxWidth
|
||||
if maxWidth > 0 {
|
||||
lines := strings.Split(str, "\n")
|
||||
|
||||
for i := range lines {
|
||||
lines[i] = truncate.String(lines[i], uint(maxWidth))
|
||||
}
|
||||
|
||||
str = strings.Join(lines, "\n")
|
||||
}
|
||||
|
||||
// Truncate according to MaxHeight
|
||||
if maxHeight > 0 {
|
||||
lines := strings.Split(str, "\n")
|
||||
str = strings.Join(lines[:min(maxHeight, len(lines))], "\n")
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
func (s Style) applyMargins(str string, inline bool) string {
|
||||
var (
|
||||
topMargin = s.getAsInt(marginTopKey)
|
||||
rightMargin = s.getAsInt(marginRightKey)
|
||||
bottomMargin = s.getAsInt(marginBottomKey)
|
||||
leftMargin = s.getAsInt(marginLeftKey)
|
||||
|
||||
styler termenv.Style
|
||||
)
|
||||
|
||||
bgc := s.getAsColor(marginBackgroundKey)
|
||||
if bgc != noColor {
|
||||
styler = styler.Background(bgc.color(s.r))
|
||||
}
|
||||
|
||||
// Add left and right margin
|
||||
str = padLeft(str, leftMargin, &styler)
|
||||
str = padRight(str, rightMargin, &styler)
|
||||
|
||||
// Top/bottom margin
|
||||
if !inline {
|
||||
_, width := getLines(str)
|
||||
spaces := strings.Repeat(" ", width)
|
||||
|
||||
if topMargin > 0 {
|
||||
str = styler.Styled(strings.Repeat(spaces+"\n", topMargin)) + str
|
||||
}
|
||||
if bottomMargin > 0 {
|
||||
str += styler.Styled(strings.Repeat("\n"+spaces, bottomMargin))
|
||||
}
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
// Apply left padding.
|
||||
func padLeft(str string, n int, style *termenv.Style) string {
|
||||
if n == 0 {
|
||||
return str
|
||||
}
|
||||
|
||||
sp := strings.Repeat(" ", n)
|
||||
if style != nil {
|
||||
sp = style.Styled(sp)
|
||||
}
|
||||
|
||||
b := strings.Builder{}
|
||||
l := strings.Split(str, "\n")
|
||||
|
||||
for i := range l {
|
||||
b.WriteString(sp)
|
||||
b.WriteString(l[i])
|
||||
if i != len(l)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// Apply right padding.
|
||||
func padRight(str string, n int, style *termenv.Style) string {
|
||||
if n == 0 || str == "" {
|
||||
return str
|
||||
}
|
||||
|
||||
sp := strings.Repeat(" ", n)
|
||||
if style != nil {
|
||||
sp = style.Styled(sp)
|
||||
}
|
||||
|
||||
b := strings.Builder{}
|
||||
l := strings.Split(str, "\n")
|
||||
|
||||
for i := range l {
|
||||
b.WriteString(l[i])
|
||||
b.WriteString(sp)
|
||||
if i != len(l)-1 {
|
||||
b.WriteRune('\n')
|
||||
}
|
||||
}
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func max(a, b int) int {
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func min(a, b int) int {
|
||||
if a < b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
306
vendor/github.com/charmbracelet/lipgloss/unset.go
generated
vendored
Normal file
306
vendor/github.com/charmbracelet/lipgloss/unset.go
generated
vendored
Normal file
@ -0,0 +1,306 @@
|
||||
package lipgloss
|
||||
|
||||
// UnsetBold removes the bold style rule, if set.
|
||||
func (s Style) UnsetBold() Style {
|
||||
delete(s.rules, boldKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetItalic removes the italic style rule, if set.
|
||||
func (s Style) UnsetItalic() Style {
|
||||
delete(s.rules, italicKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetUnderline removes the underline style rule, if set.
|
||||
func (s Style) UnsetUnderline() Style {
|
||||
delete(s.rules, underlineKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetStrikethrough removes the strikethrough style rule, if set.
|
||||
func (s Style) UnsetStrikethrough() Style {
|
||||
delete(s.rules, strikethroughKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetReverse removes the reverse style rule, if set.
|
||||
func (s Style) UnsetReverse() Style {
|
||||
delete(s.rules, reverseKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBlink removes the bold style rule, if set.
|
||||
func (s Style) UnsetBlink() Style {
|
||||
delete(s.rules, blinkKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetFaint removes the faint style rule, if set.
|
||||
func (s Style) UnsetFaint() Style {
|
||||
delete(s.rules, faintKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetForeground removes the foreground style rule, if set.
|
||||
func (s Style) UnsetForeground() Style {
|
||||
delete(s.rules, foregroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBackground removes the background style rule, if set.
|
||||
func (s Style) UnsetBackground() Style {
|
||||
delete(s.rules, backgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetWidth removes the width style rule, if set.
|
||||
func (s Style) UnsetWidth() Style {
|
||||
delete(s.rules, widthKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetHeight removes the height style rule, if set.
|
||||
func (s Style) UnsetHeight() Style {
|
||||
delete(s.rules, heightKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetAlign removes the horizontal and vertical text alignment style rule, if set.
|
||||
func (s Style) UnsetAlign() Style {
|
||||
delete(s.rules, alignHorizontalKey)
|
||||
delete(s.rules, alignVerticalKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetAlignHorizontal removes the horizontal text alignment style rule, if set.
|
||||
func (s Style) UnsetAlignHorizontal() Style {
|
||||
delete(s.rules, alignHorizontalKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetAlignVertical removes the vertical text alignment style rule, if set.
|
||||
func (s Style) UnsetAlignVertical() Style {
|
||||
delete(s.rules, alignVerticalKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetPadding removes all padding style rules.
|
||||
func (s Style) UnsetPadding() Style {
|
||||
delete(s.rules, paddingLeftKey)
|
||||
delete(s.rules, paddingRightKey)
|
||||
delete(s.rules, paddingTopKey)
|
||||
delete(s.rules, paddingBottomKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetPaddingLeft removes the left padding style rule, if set.
|
||||
func (s Style) UnsetPaddingLeft() Style {
|
||||
delete(s.rules, paddingLeftKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetPaddingRight removes the right padding style rule, if set.
|
||||
func (s Style) UnsetPaddingRight() Style {
|
||||
delete(s.rules, paddingRightKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetPaddingTop removes the top padding style rule, if set.
|
||||
func (s Style) UnsetPaddingTop() Style {
|
||||
delete(s.rules, paddingTopKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetPaddingBottom removes the bottom style rule, if set.
|
||||
func (s Style) UnsetPaddingBottom() Style {
|
||||
delete(s.rules, paddingBottomKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetColorWhitespace removes the rule for coloring padding, if set.
|
||||
func (s Style) UnsetColorWhitespace() Style {
|
||||
delete(s.rules, colorWhitespaceKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMargins removes all margin style rules.
|
||||
func (s Style) UnsetMargins() Style {
|
||||
delete(s.rules, marginLeftKey)
|
||||
delete(s.rules, marginRightKey)
|
||||
delete(s.rules, marginTopKey)
|
||||
delete(s.rules, marginBottomKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMarginLeft removes the left margin style rule, if set.
|
||||
func (s Style) UnsetMarginLeft() Style {
|
||||
delete(s.rules, marginLeftKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMarginRight removes the right margin style rule, if set.
|
||||
func (s Style) UnsetMarginRight() Style {
|
||||
delete(s.rules, marginRightKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMarginTop removes the top margin style rule, if set.
|
||||
func (s Style) UnsetMarginTop() Style {
|
||||
delete(s.rules, marginTopKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMarginBottom removes the bottom margin style rule, if set.
|
||||
func (s Style) UnsetMarginBottom() Style {
|
||||
delete(s.rules, marginBottomKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMarginBackground removes the margin's background color. Note that the
|
||||
// margin's background color can be set from the background color of another
|
||||
// style during inheritance.
|
||||
func (s Style) UnsetMarginBackground() Style {
|
||||
delete(s.rules, marginBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderStyle removes the border style rule, if set.
|
||||
func (s Style) UnsetBorderStyle() Style {
|
||||
delete(s.rules, borderStyleKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderTop removes the border top style rule, if set.
|
||||
func (s Style) UnsetBorderTop() Style {
|
||||
delete(s.rules, borderTopKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderRight removes the border right style rule, if set.
|
||||
func (s Style) UnsetBorderRight() Style {
|
||||
delete(s.rules, borderRightKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderBottom removes the border bottom style rule, if set.
|
||||
func (s Style) UnsetBorderBottom() Style {
|
||||
delete(s.rules, borderBottomKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderLeft removes the border left style rule, if set.
|
||||
func (s Style) UnsetBorderLeft() Style {
|
||||
delete(s.rules, borderLeftKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderForeground removes all border foreground color styles, if set.
|
||||
func (s Style) UnsetBorderForeground() Style {
|
||||
delete(s.rules, borderTopForegroundKey)
|
||||
delete(s.rules, borderRightForegroundKey)
|
||||
delete(s.rules, borderBottomForegroundKey)
|
||||
delete(s.rules, borderLeftForegroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderTopForeground removes the top border foreground color rule,
|
||||
// if set.
|
||||
func (s Style) UnsetBorderTopForeground() Style {
|
||||
delete(s.rules, borderTopForegroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderRightForeground removes the right border foreground color rule,
|
||||
// if set.
|
||||
func (s Style) UnsetBorderRightForeground() Style {
|
||||
delete(s.rules, borderRightForegroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderBottomForeground removes the bottom border foreground color
|
||||
// rule, if set.
|
||||
func (s Style) UnsetBorderBottomForeground() Style {
|
||||
delete(s.rules, borderBottomForegroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderLeftForeground removes the left border foreground color rule,
|
||||
// if set.
|
||||
func (s Style) UnsetBorderLeftForeground() Style {
|
||||
delete(s.rules, borderLeftForegroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderBackground removes all border background color styles, if
|
||||
// set.
|
||||
func (s Style) UnsetBorderBackground() Style {
|
||||
delete(s.rules, borderTopBackgroundKey)
|
||||
delete(s.rules, borderRightBackgroundKey)
|
||||
delete(s.rules, borderBottomBackgroundKey)
|
||||
delete(s.rules, borderLeftBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderTopBackgroundColor removes the top border background color rule,
|
||||
// if set.
|
||||
func (s Style) UnsetBorderTopBackgroundColor() Style {
|
||||
delete(s.rules, borderTopBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderRightBackground removes the right border background color
|
||||
// rule, if set.
|
||||
func (s Style) UnsetBorderRightBackground() Style {
|
||||
delete(s.rules, borderRightBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderBottomBackground removes the bottom border background color
|
||||
// rule, if set.
|
||||
func (s Style) UnsetBorderBottomBackground() Style {
|
||||
delete(s.rules, borderBottomBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetBorderLeftBackground removes the left border color rule, if set.
|
||||
func (s Style) UnsetBorderLeftBackground() Style {
|
||||
delete(s.rules, borderLeftBackgroundKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetInline removes the inline style rule, if set.
|
||||
func (s Style) UnsetInline() Style {
|
||||
delete(s.rules, inlineKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMaxWidth removes the max width style rule, if set.
|
||||
func (s Style) UnsetMaxWidth() Style {
|
||||
delete(s.rules, maxWidthKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetMaxHeight removes the max height style rule, if set.
|
||||
func (s Style) UnsetMaxHeight() Style {
|
||||
delete(s.rules, maxHeightKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetUnderlineSpaces removes the value set by UnderlineSpaces.
|
||||
func (s Style) UnsetUnderlineSpaces() Style {
|
||||
delete(s.rules, underlineSpacesKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetStrikethroughSpaces removes the value set by StrikethroughSpaces.
|
||||
func (s Style) UnsetStrikethroughSpaces() Style {
|
||||
delete(s.rules, strikethroughSpacesKey)
|
||||
return s
|
||||
}
|
||||
|
||||
// UnsetString sets the underlying string value to the empty string.
|
||||
func (s Style) UnsetString() Style {
|
||||
s.value = ""
|
||||
return s
|
||||
}
|
83
vendor/github.com/charmbracelet/lipgloss/whitespace.go
generated
vendored
Normal file
83
vendor/github.com/charmbracelet/lipgloss/whitespace.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
package lipgloss
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/muesli/reflow/ansi"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
// whitespace is a whitespace renderer.
|
||||
type whitespace struct {
|
||||
re *Renderer
|
||||
style termenv.Style
|
||||
chars string
|
||||
}
|
||||
|
||||
// newWhitespace creates a new whitespace renderer. The order of the options
|
||||
// matters, it you'r using WithWhitespaceRenderer, make sure it comes first as
|
||||
// other options might depend on it.
|
||||
func newWhitespace(r *Renderer, opts ...WhitespaceOption) *whitespace {
|
||||
w := &whitespace{
|
||||
re: r,
|
||||
style: r.ColorProfile().String(),
|
||||
}
|
||||
for _, opt := range opts {
|
||||
opt(w)
|
||||
}
|
||||
return w
|
||||
}
|
||||
|
||||
// Render whitespaces.
|
||||
func (w whitespace) render(width int) string {
|
||||
if w.chars == "" {
|
||||
w.chars = " "
|
||||
}
|
||||
|
||||
r := []rune(w.chars)
|
||||
j := 0
|
||||
b := strings.Builder{}
|
||||
|
||||
// Cycle through runes and print them into the whitespace.
|
||||
for i := 0; i < width; {
|
||||
b.WriteRune(r[j])
|
||||
j++
|
||||
if j >= len(r) {
|
||||
j = 0
|
||||
}
|
||||
i += ansi.PrintableRuneWidth(string(r[j]))
|
||||
}
|
||||
|
||||
// Fill any extra gaps white spaces. This might be necessary if any runes
|
||||
// are more than one cell wide, which could leave a one-rune gap.
|
||||
short := width - ansi.PrintableRuneWidth(b.String())
|
||||
if short > 0 {
|
||||
b.WriteString(strings.Repeat(" ", short))
|
||||
}
|
||||
|
||||
return w.style.Styled(b.String())
|
||||
}
|
||||
|
||||
// WhitespaceOption sets a styling rule for rendering whitespace.
|
||||
type WhitespaceOption func(*whitespace)
|
||||
|
||||
// WithWhitespaceForeground sets the color of the characters in the whitespace.
|
||||
func WithWhitespaceForeground(c TerminalColor) WhitespaceOption {
|
||||
return func(w *whitespace) {
|
||||
w.style = w.style.Foreground(c.color(w.re))
|
||||
}
|
||||
}
|
||||
|
||||
// WithWhitespaceBackground sets the background color of the whitespace.
|
||||
func WithWhitespaceBackground(c TerminalColor) WhitespaceOption {
|
||||
return func(w *whitespace) {
|
||||
w.style = w.style.Background(c.color(w.re))
|
||||
}
|
||||
}
|
||||
|
||||
// WithWhitespaceChars sets the characters to be rendered in the whitespace.
|
||||
func WithWhitespaceChars(s string) WhitespaceOption {
|
||||
return func(w *whitespace) {
|
||||
w.chars = s
|
||||
}
|
||||
}
|
13
vendor/github.com/charmbracelet/log/.gitignore
generated
vendored
Normal file
13
vendor/github.com/charmbracelet/log/.gitignore
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
*.txt
|
||||
*.gif
|
||||
|
||||
examples/batch2/batch2
|
||||
examples/chocolate-chips/chocolate-chips
|
||||
examples/cookie/cookie
|
||||
examples/error/error
|
||||
examples/format/format
|
||||
examples/log/log
|
||||
examples/new/new
|
||||
examples/options/options
|
||||
examples/oven/oven
|
||||
examples/temperature/temperature
|
34
vendor/github.com/charmbracelet/log/.golangci.yml
generated
vendored
Normal file
34
vendor/github.com/charmbracelet/log/.golangci.yml
generated
vendored
Normal file
@ -0,0 +1,34 @@
|
||||
run:
|
||||
tests: false
|
||||
|
||||
issues:
|
||||
include:
|
||||
- EXC0001
|
||||
- EXC0005
|
||||
- EXC0011
|
||||
- EXC0012
|
||||
- EXC0013
|
||||
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- bodyclose
|
||||
- dupl
|
||||
- exportloopref
|
||||
- goconst
|
||||
- godot
|
||||
- godox
|
||||
- goimports
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
- ifshort
|
||||
- misspell
|
||||
- prealloc
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sqlclosecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- whitespace
|
3
vendor/github.com/charmbracelet/log/.goreleaser.yml
generated
vendored
Normal file
3
vendor/github.com/charmbracelet/log/.goreleaser.yml
generated
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
includes:
|
||||
- from_url:
|
||||
url: charmbracelet/meta/main/goreleaser-lib.yaml
|
21
vendor/github.com/charmbracelet/log/LICENSE
generated
vendored
Normal file
21
vendor/github.com/charmbracelet/log/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2022 Charmbracelet, Inc
|
||||
|
||||
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.
|
342
vendor/github.com/charmbracelet/log/README.md
generated
vendored
Normal file
342
vendor/github.com/charmbracelet/log/README.md
generated
vendored
Normal file
@ -0,0 +1,342 @@
|
||||
# Log
|
||||
|
||||
<p>
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://user-images.githubusercontent.com/25087/219742757-c8afe0d9-608a-4845-a555-ef59c0af9ebc.png" width="359">
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://user-images.githubusercontent.com/25087/219743408-3d7bef51-1409-40c0-8159-acc6e52f078e.png" width="359">
|
||||
<img src="https://user-images.githubusercontent.com/25087/219742757-c8afe0d9-608a-4845-a555-ef59c0af9ebc.png" width="359" />
|
||||
</picture>
|
||||
<br>
|
||||
<a href="https://github.com/charmbracelet/log/releases"><img src="https://img.shields.io/github/release/charmbracelet/log.svg" alt="Latest Release"></a>
|
||||
<a href="https://pkg.go.dev/github.com/charmbracelet/log?tab=doc"><img src="https://godoc.org/github.com/golang/gddo?status.svg" alt="Go Docs"></a>
|
||||
<a href="https://github.com/charmbracelet/log/actions"><img src="https://github.com/charmbracelet/log/workflows/build/badge.svg" alt="Build Status"></a>
|
||||
<a href="https://codecov.io/gh/charmbracelet/log"><img alt="Codecov branch" src="https://img.shields.io/codecov/c/github/charmbracelet/log/main.svg"></a>
|
||||
<a href="https://goreportcard.com/report/github.com/charmbracelet/log"><img alt="Go Report Card" src="https://goreportcard.com/badge/github.com/charmbracelet/log"></a>
|
||||
</p>
|
||||
|
||||
A minimal and colorful Go logging library. 🪵
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" srcset="https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif">
|
||||
<source media="(prefers-color-scheme: light)" srcset="https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" srcset="https://vhs.charm.sh/vhs-2NvOYS29AauVRgRRPmquXx.gif"> -->
|
||||
<img src="https://vhs.charm.sh/vhs-1wBImk2iSIuiiD7Ib9rufi.gif" alt="Made with VHS" />
|
||||
</picture>
|
||||
|
||||
It provides a leveled structured human readable logger with a small API. Unlike
|
||||
[standard `log`][stdlog], the Charm logger provides customizable colorful human
|
||||
readable logging with batteries included.
|
||||
|
||||
- Uses [Lip Gloss][lipgloss] to style and colorize the output.
|
||||
- Colorful, human readable format.
|
||||
- Ability to customize the time stamp format.
|
||||
- Skips caller frames and marks function as helpers.
|
||||
- Leveled logging.
|
||||
- Text, JSON, and Logfmt formatters.
|
||||
- Store and retrieve logger in and from context.
|
||||
- Standard log adapter.
|
||||
|
||||
## Usage
|
||||
|
||||
Use `go get` to download the dependency.
|
||||
|
||||
```bash
|
||||
go get github.com/charmbracelet/log@latest
|
||||
```
|
||||
|
||||
Then, `import` it in your Go files:
|
||||
|
||||
```go
|
||||
import "github.com/charmbracelet/log"
|
||||
```
|
||||
|
||||
The Charm logger comes with a global package-wise logger with timestamps turned
|
||||
on, and the logging level set to `info`.
|
||||
|
||||
```go
|
||||
log.Debug("Cookie 🍪") // won't print anything
|
||||
log.Info("Hello World!")
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="400" srcset="https://vhs.charm.sh/vhs-cKiS8OuRrF1VVVpscM9e3.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-cKiS8OuRrF1VVVpscM9e3.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-4AeLaEuO3tDbECR1qe9Jvp.gif"> -->
|
||||
<img width="400" src="https://vhs.charm.sh/vhs-4AeLaEuO3tDbECR1qe9Jvp.gif" alt="Made with VHS" />
|
||||
</picture>
|
||||
|
||||
All logging levels accept optional key/value pairs to be printed along with a
|
||||
message.
|
||||
|
||||
```go
|
||||
err := fmt.Errorf("too much sugar")
|
||||
log.Error("failed to bake cookies", "err", err)
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="600" srcset="https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif" >
|
||||
<source media="(prefers-color-scheme: light)" width="600" srcset="https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif" >
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="600" srcset="https://vhs.charm.sh/vhs-7rk8wALXRDoFw8SLFwn9rW.gif"> -->
|
||||
<img width="600" src="https://vhs.charm.sh/vhs-65KIpGw4FTESK0IzkDB9VQ.gif" alt="Made with VHS">
|
||||
</picture>
|
||||
|
||||
You can use `log.Print()` to print messages without a level prefix.
|
||||
|
||||
```go
|
||||
log.Print("Baking 101")
|
||||
// 2023/01/04 10:04:06 Baking 101
|
||||
```
|
||||
|
||||
### New loggers
|
||||
|
||||
Use `New()` to create new loggers.
|
||||
|
||||
```go
|
||||
logger := log.New(os.Stderr)
|
||||
if butter {
|
||||
logger.Warn("chewy!", "butter", true)
|
||||
}
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="300" srcset="https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="300" srcset="https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="300" srcset="https://vhs.charm.sh/vhs-1nrhNSuFnQkxWD4RoMlE4O.gif"> -->
|
||||
<img width="300" src="https://vhs.charm.sh/vhs-3QQdzOW4Zc0bN2tOhAest9.gif">
|
||||
</picture>
|
||||
|
||||
### Levels
|
||||
|
||||
Log offers multiple levels to filter your logs on. Available levels are:
|
||||
|
||||
```go
|
||||
log.DebugLevel
|
||||
log.InfoLevel
|
||||
log.WarnLevel
|
||||
log.ErrorLevel
|
||||
log.FatalLevel
|
||||
```
|
||||
|
||||
Use `log.SetLevel()` to set the log level. You can also create a new logger with
|
||||
a specific log level using `log.Options{Level: }`.
|
||||
|
||||
Use the corresponding function to log a message:
|
||||
|
||||
```go
|
||||
err := errors.New("Baking error 101")
|
||||
log.Debug(err)
|
||||
log.Info(err)
|
||||
log.Warn(err)
|
||||
log.Error(err)
|
||||
log.Fatal(err) // this calls os.Exit(1)
|
||||
log.Print(err) // prints regardless of log level
|
||||
```
|
||||
|
||||
Or use the formatter variant:
|
||||
|
||||
```go
|
||||
format := "%s %d"
|
||||
log.Debugf(format, "chocolate", 10)
|
||||
log.Warnf(format, "adding more", 5)
|
||||
log.Errorf(format, "increasing temp", 420)
|
||||
log.Fatalf(format, "too hot!", 500) // this calls os.Exit(1)
|
||||
log.Printf(format, "baking cookies") // prints regardless of log level
|
||||
|
||||
// Use these in conjunction with `With(...)` to add more context
|
||||
log.With("err", err).Errorf("unable to start %s", "oven")
|
||||
```
|
||||
|
||||
### Structured
|
||||
|
||||
All the functions above take a message and key-value pairs of anything. The
|
||||
message can also be of type any.
|
||||
|
||||
```go
|
||||
ingredients := []string{"flour", "butter", "sugar", "chocolate"}
|
||||
log.Debug("Available ingredients", "ingredients", ingredients)
|
||||
// DEBUG Available ingredients ingredients="[flour butter sugar chocolate]"
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
You can customize the logger with options. Use `log.NewWithOptions()` and
|
||||
`log.Options{}` to customize your new logger.
|
||||
|
||||
```go
|
||||
logger := log.NewWithOptions(os.Stderr, log.Options{
|
||||
ReportCaller: true,
|
||||
ReportTimestamp: true,
|
||||
TimeFormat: time.Kitchen,
|
||||
Prefix: "Baking 🍪 "
|
||||
})
|
||||
logger.Info("Starting oven!", "degree", 375)
|
||||
time.Sleep(10 * time.Minute)
|
||||
logger.Info("Finished baking")
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-2X8Esd8ZsHo4DVPVgR36yx.gif"> -->
|
||||
<img width="700" src="https://vhs.charm.sh/vhs-6oSCJcQ5EmFKKELcskJhLo.gif">
|
||||
</picture>
|
||||
|
||||
You can also use logger setters to customize the logger.
|
||||
|
||||
```go
|
||||
logger := log.New(os.Stderr)
|
||||
logger.SetReportTimestamp(false)
|
||||
logger.SetReportCaller(false)
|
||||
logger.SetLevel(log.DebugLevel)
|
||||
```
|
||||
|
||||
Use `log.SetFormatter()` or `log.Options{Formatter: }` to change the output
|
||||
format. Available options are:
|
||||
|
||||
- `log.TextFormatter` (_default_)
|
||||
- `log.JSONFormatter`
|
||||
- `log.LogfmtFormatter`
|
||||
|
||||
> **Note** styling only affects the `TextFormatter`. Styling is disabled if the
|
||||
> output is not a TTY.
|
||||
|
||||
For a list of available options, refer to [options.go](./options.go).
|
||||
|
||||
### Styles
|
||||
|
||||
You can customize the logger styles using [Lipgloss][lipgloss]. The styles are
|
||||
defined at a global level in [styles.go](./styles.go).
|
||||
|
||||
```go
|
||||
// Override the default error level style.
|
||||
log.ErrorLevelStyle = lipgloss.NewStyle().
|
||||
SetString("ERROR!!").
|
||||
Padding(0, 1, 0, 1).
|
||||
Background(lipgloss.AdaptiveColor{
|
||||
Light: "203",
|
||||
Dark: "204",
|
||||
}).
|
||||
Foreground(lipgloss.Color("0"))
|
||||
// Add a custom style for key `err`
|
||||
log.KeyStyles["err"] = lipgloss.NewStyle().Foreground(lipgloss.Color("204"))
|
||||
log.ValueStyles["err"] = lipgloss.NewStyle().Bold(true)
|
||||
log.Error("Whoops!", "err", "kitchen on fire")
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="400" srcset="https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="400" srcset="https://vhs.charm.sh/vhs-4f6qLnIfudMMLDD9sxXUrv.gif"> -->
|
||||
<img width="400" src="https://vhs.charm.sh/vhs-4LXsGvzyH4RdjJaTF4a9MG.gif">
|
||||
</picture>
|
||||
|
||||
### Sub-logger
|
||||
|
||||
Create sub-loggers with their specific fields.
|
||||
|
||||
```go
|
||||
batch2 := logger.With("batch", 2, "chocolateChips", true)
|
||||
batch2.Debug("Preparing batch 2...")
|
||||
batch2.Debug("Adding chocolate chips")
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif">
|
||||
<img width="700" src="https://vhs.charm.sh/vhs-1JgP5ZRL0oXVspeg50CczR.gif">
|
||||
</picture>
|
||||
|
||||
### Format Messages
|
||||
|
||||
You can use `fmt.Sprintf()` to format messages.
|
||||
|
||||
```go
|
||||
for item := 1; i <= 100; i++ {
|
||||
log.Info(fmt.Sprintf("Baking %d/100...", item))
|
||||
}
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="500" srcset="https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="500" srcset="https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="500" srcset="https://vhs.charm.sh/vhs-4RHXd4JSucomcPqJGZTpKh.gif"> -->
|
||||
<img width="500" src="https://vhs.charm.sh/vhs-4nX5I7qHT09aJ2gU7OaGV5.gif">
|
||||
</picture>
|
||||
|
||||
Or arguments:
|
||||
|
||||
```go
|
||||
for temp := 375; temp <= 400; temp++ {
|
||||
log.Info("Increasing temperature", "degree", fmt.Sprintf("%d°F", temp))
|
||||
}
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-4AvAnoA2S53QTOteX8krp4.gif"> -->
|
||||
<img width="700" src="https://vhs.charm.sh/vhs-79YvXcDOsqgHte3bv42UTr.gif">
|
||||
</picture>
|
||||
|
||||
### Helper Functions
|
||||
|
||||
Skip caller frames in helper functions. Similar to what you can do with
|
||||
`testing.TB().Helper()`.
|
||||
|
||||
```go
|
||||
function startOven(degree int) {
|
||||
log.Helper()
|
||||
log.Info("Starting oven", "degree", degree)
|
||||
}
|
||||
|
||||
log.SetReportCaller(true)
|
||||
startOven(400) // INFO <cookies/oven.go:123> Starting oven degree=400
|
||||
```
|
||||
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: dark)" width="700" srcset="https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif">
|
||||
<source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif">
|
||||
<!-- <source media="(prefers-color-scheme: light)" width="700" srcset="https://vhs.charm.sh/vhs-6DPg0bVL4K4TkfoHkAn2ap.gif"> -->
|
||||
<img width="700" src="https://vhs.charm.sh/vhs-6CeQGIV8Ovgr8GD0N6NgTq.gif">
|
||||
</picture>
|
||||
|
||||
This will use the _caller_ function (`startOven`) line number instead of the
|
||||
logging function (`log.Info`) to report the source location.
|
||||
|
||||
### Standard Log Adapter
|
||||
|
||||
Some Go libraries, especially the ones in the standard library, will only accept
|
||||
the [standard logger][stdlog] interface. For instance, the HTTP Server from
|
||||
`net/http` will only take a `*log.Logger` for its `ErrorLog` field.
|
||||
|
||||
For this, you can use the standard log adapter, which simply wraps the logger in
|
||||
a `*log.Logger` interface.
|
||||
|
||||
```go
|
||||
logger := log.NewWithOptions(os.Stderr, log.Options{Prefix: "http"})
|
||||
stdlog := logger.StandardLog(log.StandardLogOptions{
|
||||
ForceLevel: log.ErrorLevel,
|
||||
})
|
||||
s := &http.Server{
|
||||
Addr: ":8080",
|
||||
Handler: handler,
|
||||
ErrorLog: stdlog,
|
||||
}
|
||||
stdlog.Printf("Failed to make bake request, %s", fmt.Errorf("temperature is too low"))
|
||||
// ERROR http: Failed to make bake request, temperature is too low
|
||||
```
|
||||
|
||||
[lipgloss]: https://github.com/charmbracelet/lipgloss
|
||||
[stdlog]: https://pkg.go.dev/log
|
||||
|
||||
## License
|
||||
|
||||
[MIT](https://github.com/charmbracelet/log/raw/master/LICENSE)
|
||||
|
||||
---
|
||||
|
||||
Part of [Charm](https://charm.sh).
|
||||
|
||||
<a href="https://charm.sh/"><img alt="the Charm logo" src="https://stuff.charm.sh/charm-badge.jpg" width="400"></a>
|
||||
|
||||
Charm热爱开源 • Charm loves open source • نحنُ نحب المصادر المفتوحة
|
22
vendor/github.com/charmbracelet/log/context.go
generated
vendored
Normal file
22
vendor/github.com/charmbracelet/log/context.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
package log
|
||||
|
||||
import "context"
|
||||
|
||||
// WithContext wraps the given logger in context.
|
||||
func WithContext(ctx context.Context, logger *Logger) context.Context {
|
||||
return context.WithValue(ctx, loggerContextKey, logger)
|
||||
}
|
||||
|
||||
// FromContext returns the logger from the given context.
|
||||
// This will return the default package logger if no logger
|
||||
// found in context.
|
||||
func FromContext(ctx context.Context) *Logger {
|
||||
if logger, ok := ctx.Value(loggerContextKey).(*Logger); ok {
|
||||
return logger
|
||||
}
|
||||
return defaultLogger
|
||||
}
|
||||
|
||||
type contextKey struct{}
|
||||
|
||||
var loggerContextKey = contextKey{}
|
27
vendor/github.com/charmbracelet/log/formatter.go
generated
vendored
Normal file
27
vendor/github.com/charmbracelet/log/formatter.go
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package log
|
||||
|
||||
// Formatter is a formatter for log messages.
|
||||
type Formatter uint8
|
||||
|
||||
const (
|
||||
// TextFormatter is a formatter that formats log messages as text. Suitable for
|
||||
// console output and log files.
|
||||
TextFormatter Formatter = iota
|
||||
// JSONFormatter is a formatter that formats log messages as JSON.
|
||||
JSONFormatter
|
||||
// LogfmtFormatter is a formatter that formats log messages as logfmt.
|
||||
LogfmtFormatter
|
||||
)
|
||||
|
||||
var (
|
||||
// TimestampKey is the key for the timestamp.
|
||||
TimestampKey = "ts"
|
||||
// MessageKey is the key for the message.
|
||||
MessageKey = "msg"
|
||||
// LevelKey is the key for the level.
|
||||
LevelKey = "lvl"
|
||||
// CallerKey is the key for the caller.
|
||||
CallerKey = "caller"
|
||||
// PrefixKey is the key for the prefix.
|
||||
PrefixKey = "prefix"
|
||||
)
|
61
vendor/github.com/charmbracelet/log/json.go
generated
vendored
Normal file
61
vendor/github.com/charmbracelet/log/json.go
generated
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func (l *Logger) jsonFormatter(keyvals ...interface{}) {
|
||||
m := make(map[string]interface{}, len(keyvals)/2)
|
||||
for i := 0; i < len(keyvals); i += 2 {
|
||||
switch keyvals[i] {
|
||||
case TimestampKey:
|
||||
if t, ok := keyvals[i+1].(time.Time); ok {
|
||||
m[TimestampKey] = t.Format(l.timeFormat)
|
||||
}
|
||||
case LevelKey:
|
||||
if level, ok := keyvals[i+1].(Level); ok {
|
||||
m[LevelKey] = level.String()
|
||||
}
|
||||
case CallerKey:
|
||||
if caller, ok := keyvals[i+1].(string); ok {
|
||||
m[CallerKey] = caller
|
||||
}
|
||||
case PrefixKey:
|
||||
if prefix, ok := keyvals[i+1].(string); ok {
|
||||
m[PrefixKey] = prefix
|
||||
}
|
||||
case MessageKey:
|
||||
if msg := keyvals[i+1]; msg != nil {
|
||||
m[MessageKey] = fmt.Sprint(msg)
|
||||
}
|
||||
default:
|
||||
var (
|
||||
key string
|
||||
val interface{}
|
||||
)
|
||||
switch k := keyvals[i].(type) {
|
||||
case fmt.Stringer:
|
||||
key = k.String()
|
||||
case error:
|
||||
key = k.Error()
|
||||
default:
|
||||
key = fmt.Sprint(k)
|
||||
}
|
||||
switch v := keyvals[i+1].(type) {
|
||||
case error:
|
||||
val = v.Error()
|
||||
case fmt.Stringer:
|
||||
val = v.String()
|
||||
default:
|
||||
val = v
|
||||
}
|
||||
m[key] = val
|
||||
}
|
||||
}
|
||||
|
||||
e := json.NewEncoder(&l.b)
|
||||
e.SetEscapeHTML(false)
|
||||
_ = e.Encode(m)
|
||||
}
|
57
vendor/github.com/charmbracelet/log/level.go
generated
vendored
Normal file
57
vendor/github.com/charmbracelet/log/level.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package log
|
||||
|
||||
import "strings"
|
||||
|
||||
// Level is a logging level.
|
||||
type Level int32
|
||||
|
||||
const (
|
||||
// DebugLevel is the debug level.
|
||||
DebugLevel Level = iota - 1
|
||||
// InfoLevel is the info level.
|
||||
InfoLevel
|
||||
// WarnLevel is the warn level.
|
||||
WarnLevel
|
||||
// ErrorLevel is the error level.
|
||||
ErrorLevel
|
||||
// FatalLevel is the fatal level.
|
||||
FatalLevel
|
||||
// noLevel is used with log.Print.
|
||||
noLevel
|
||||
)
|
||||
|
||||
// String returns the string representation of the level.
|
||||
func (l Level) String() string {
|
||||
switch l {
|
||||
case DebugLevel:
|
||||
return "debug"
|
||||
case InfoLevel:
|
||||
return "info"
|
||||
case WarnLevel:
|
||||
return "warn"
|
||||
case ErrorLevel:
|
||||
return "error"
|
||||
case FatalLevel:
|
||||
return "fatal"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// ParseLevel converts level in string to Level type. Default level is InfoLevel.
|
||||
func ParseLevel(level string) Level {
|
||||
switch strings.ToLower(level) {
|
||||
case DebugLevel.String():
|
||||
return DebugLevel
|
||||
case InfoLevel.String():
|
||||
return InfoLevel
|
||||
case WarnLevel.String():
|
||||
return WarnLevel
|
||||
case ErrorLevel.String():
|
||||
return ErrorLevel
|
||||
case FatalLevel.String():
|
||||
return FatalLevel
|
||||
default:
|
||||
return InfoLevel
|
||||
}
|
||||
}
|
32
vendor/github.com/charmbracelet/log/logfmt.go
generated
vendored
Normal file
32
vendor/github.com/charmbracelet/log/logfmt.go
generated
vendored
Normal file
@ -0,0 +1,32 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/go-logfmt/logfmt"
|
||||
)
|
||||
|
||||
func (l *Logger) logfmtFormatter(keyvals ...interface{}) {
|
||||
e := logfmt.NewEncoder(&l.b)
|
||||
|
||||
for i := 0; i < len(keyvals); i += 2 {
|
||||
switch keyvals[i] {
|
||||
case TimestampKey:
|
||||
if t, ok := keyvals[i+1].(time.Time); ok {
|
||||
keyvals[i+1] = t.Format(l.timeFormat)
|
||||
}
|
||||
default:
|
||||
if key := fmt.Sprint(keyvals[i]); key != "" {
|
||||
keyvals[i] = key
|
||||
}
|
||||
}
|
||||
err := e.EncodeKeyval(keyvals[i], keyvals[i+1])
|
||||
if err != nil && errors.Is(err, logfmt.ErrUnsupportedValueType) {
|
||||
// If the value is not supported by logfmt, we try to convert it to a string.
|
||||
_ = e.EncodeKeyval(keyvals[i], fmt.Sprintf("%+v", keyvals[i+1]))
|
||||
}
|
||||
}
|
||||
_ = e.EndRecord()
|
||||
}
|
356
vendor/github.com/charmbracelet/log/logger.go
generated
vendored
Normal file
356
vendor/github.com/charmbracelet/log/logger.go
generated
vendored
Normal file
@ -0,0 +1,356 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/muesli/termenv"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrMissingValue is returned when a key is missing a value.
|
||||
ErrMissingValue = fmt.Errorf("missing value")
|
||||
)
|
||||
|
||||
// LoggerOption is an option for a logger.
|
||||
type LoggerOption = func(*Logger)
|
||||
|
||||
// Logger is a Logger that implements Logger.
|
||||
type Logger struct {
|
||||
w io.Writer
|
||||
b bytes.Buffer
|
||||
mu *sync.RWMutex
|
||||
re *lipgloss.Renderer
|
||||
|
||||
isDiscard uint32
|
||||
|
||||
level int32
|
||||
prefix string
|
||||
timeFunc TimeFunction
|
||||
timeFormat string
|
||||
callerOffset int
|
||||
callerFormatter CallerFormatter
|
||||
formatter Formatter
|
||||
|
||||
reportCaller bool
|
||||
reportTimestamp bool
|
||||
|
||||
fields []interface{}
|
||||
|
||||
helpers *sync.Map
|
||||
}
|
||||
|
||||
func (l *Logger) log(level Level, msg interface{}, keyvals ...interface{}) {
|
||||
if atomic.LoadUint32(&l.isDiscard) != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
// check if the level is allowed
|
||||
if atomic.LoadInt32(&l.level) > int32(level) {
|
||||
return
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
defer l.b.Reset()
|
||||
|
||||
var kvs []interface{}
|
||||
if l.reportTimestamp {
|
||||
kvs = append(kvs, TimestampKey, l.timeFunc())
|
||||
}
|
||||
|
||||
if level != noLevel {
|
||||
kvs = append(kvs, LevelKey, level)
|
||||
}
|
||||
|
||||
if l.reportCaller {
|
||||
// Call stack is log.Error -> log.log (2)
|
||||
file, line, fn := l.fillLoc(l.callerOffset + 2)
|
||||
caller := l.callerFormatter(file, line, fn)
|
||||
kvs = append(kvs, CallerKey, caller)
|
||||
}
|
||||
|
||||
if l.prefix != "" {
|
||||
kvs = append(kvs, PrefixKey, l.prefix+":")
|
||||
}
|
||||
|
||||
if msg != nil {
|
||||
m := fmt.Sprint(msg)
|
||||
kvs = append(kvs, MessageKey, m)
|
||||
}
|
||||
|
||||
// append logger fields
|
||||
kvs = append(kvs, l.fields...)
|
||||
if len(l.fields)%2 != 0 {
|
||||
kvs = append(kvs, ErrMissingValue)
|
||||
}
|
||||
// append the rest
|
||||
kvs = append(kvs, keyvals...)
|
||||
if len(keyvals)%2 != 0 {
|
||||
kvs = append(kvs, ErrMissingValue)
|
||||
}
|
||||
|
||||
switch l.formatter {
|
||||
case LogfmtFormatter:
|
||||
l.logfmtFormatter(kvs...)
|
||||
case JSONFormatter:
|
||||
l.jsonFormatter(kvs...)
|
||||
default:
|
||||
l.textFormatter(kvs...)
|
||||
}
|
||||
|
||||
_, _ = l.w.Write(l.b.Bytes())
|
||||
}
|
||||
|
||||
// Helper marks the calling function as a helper
|
||||
// and skips it for source location information.
|
||||
// It's the equivalent of testing.TB.Helper().
|
||||
func (l *Logger) Helper() {
|
||||
l.helper(1)
|
||||
}
|
||||
|
||||
func (l *Logger) helper(skip int) {
|
||||
_, _, fn := location(skip + 1)
|
||||
l.helpers.LoadOrStore(fn, struct{}{})
|
||||
}
|
||||
|
||||
func (l *Logger) fillLoc(skip int) (file string, line int, fn string) {
|
||||
// Copied from testing.T
|
||||
const maxStackLen = 50
|
||||
var pc [maxStackLen]uintptr
|
||||
|
||||
// Skip two extra frames to account for this function
|
||||
// and runtime.Callers itself.
|
||||
n := runtime.Callers(skip+2, pc[:])
|
||||
frames := runtime.CallersFrames(pc[:n])
|
||||
for {
|
||||
frame, more := frames.Next()
|
||||
_, helper := l.helpers.Load(frame.Function)
|
||||
if !helper || !more {
|
||||
// Found a frame that wasn't a helper function.
|
||||
// Or we ran out of frames to check.
|
||||
return frame.File, frame.Line, frame.Function
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func location(skip int) (file string, line int, fn string) {
|
||||
pc, file, line, _ := runtime.Caller(skip + 1)
|
||||
f := runtime.FuncForPC(pc)
|
||||
return file, line, f.Name()
|
||||
}
|
||||
|
||||
// Cleanup a path by returning the last n segments of the path only.
|
||||
func trimCallerPath(path string, n int) string {
|
||||
// lovely borrowed from zap
|
||||
// nb. To make sure we trim the path correctly on Windows too, we
|
||||
// counter-intuitively need to use '/' and *not* os.PathSeparator here,
|
||||
// because the path given originates from Go stdlib, specifically
|
||||
// runtime.Caller() which (as of Mar/17) returns forward slashes even on
|
||||
// Windows.
|
||||
//
|
||||
// See https://github.com/golang/go/issues/3335
|
||||
// and https://github.com/golang/go/issues/18151
|
||||
//
|
||||
// for discussion on the issue on Go side.
|
||||
|
||||
// Return the full path if n is 0.
|
||||
if n <= 0 {
|
||||
return path
|
||||
}
|
||||
|
||||
// Find the last separator.
|
||||
idx := strings.LastIndexByte(path, '/')
|
||||
if idx == -1 {
|
||||
return path
|
||||
}
|
||||
|
||||
for i := 0; i < n-1; i++ {
|
||||
// Find the penultimate separator.
|
||||
idx = strings.LastIndexByte(path[:idx], '/')
|
||||
if idx == -1 {
|
||||
return path
|
||||
}
|
||||
}
|
||||
|
||||
return path[idx+1:]
|
||||
}
|
||||
|
||||
// SetReportTimestamp sets whether the timestamp should be reported.
|
||||
func (l *Logger) SetReportTimestamp(report bool) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.reportTimestamp = report
|
||||
}
|
||||
|
||||
// SetReportCaller sets whether the caller location should be reported.
|
||||
func (l *Logger) SetReportCaller(report bool) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.reportCaller = report
|
||||
}
|
||||
|
||||
// GetLevel returns the current level.
|
||||
func (l *Logger) GetLevel() Level {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
return Level(l.level)
|
||||
}
|
||||
|
||||
// SetLevel sets the current level.
|
||||
func (l *Logger) SetLevel(level Level) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
atomic.StoreInt32(&l.level, int32(level))
|
||||
}
|
||||
|
||||
// GetPrefix returns the current prefix.
|
||||
func (l *Logger) GetPrefix() string {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
return l.prefix
|
||||
}
|
||||
|
||||
// SetPrefix sets the current prefix.
|
||||
func (l *Logger) SetPrefix(prefix string) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.prefix = prefix
|
||||
}
|
||||
|
||||
// SetTimeFormat sets the time format.
|
||||
func (l *Logger) SetTimeFormat(format string) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.timeFormat = format
|
||||
}
|
||||
|
||||
// SetTimeFunction sets the time function.
|
||||
func (l *Logger) SetTimeFunction(f TimeFunction) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.timeFunc = f
|
||||
}
|
||||
|
||||
// SetOutput sets the output destination.
|
||||
func (l *Logger) SetOutput(w io.Writer) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if w == nil {
|
||||
w = os.Stderr
|
||||
}
|
||||
l.w = w
|
||||
var isDiscard uint32
|
||||
if w == ioutil.Discard {
|
||||
isDiscard = 1
|
||||
}
|
||||
atomic.StoreUint32(&l.isDiscard, isDiscard)
|
||||
// Reuse cached renderers
|
||||
if v, ok := registry.Load(w); ok {
|
||||
l.re = v.(*lipgloss.Renderer)
|
||||
} else {
|
||||
l.re = lipgloss.NewRenderer(w, termenv.WithColorCache(true))
|
||||
registry.Store(w, l.re)
|
||||
}
|
||||
}
|
||||
|
||||
// SetFormatter sets the formatter.
|
||||
func (l *Logger) SetFormatter(f Formatter) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.formatter = f
|
||||
}
|
||||
|
||||
// SetCallerFormatter sets the caller formatter.
|
||||
func (l *Logger) SetCallerFormatter(f CallerFormatter) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
l.callerFormatter = f
|
||||
}
|
||||
|
||||
// With returns a new logger with the given keyvals added.
|
||||
func (l *Logger) With(keyvals ...interface{}) *Logger {
|
||||
sl := *l
|
||||
sl.b = bytes.Buffer{}
|
||||
sl.mu = &sync.RWMutex{}
|
||||
sl.helpers = &sync.Map{}
|
||||
sl.fields = append(l.fields, keyvals...)
|
||||
return &sl
|
||||
}
|
||||
|
||||
// WithPrefix returns a new logger with the given prefix.
|
||||
func (l *Logger) WithPrefix(prefix string) *Logger {
|
||||
sl := l.With()
|
||||
sl.SetPrefix(prefix)
|
||||
return sl
|
||||
}
|
||||
|
||||
// Debug prints a debug message.
|
||||
func (l *Logger) Debug(msg interface{}, keyvals ...interface{}) {
|
||||
l.log(DebugLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Info prints an info message.
|
||||
func (l *Logger) Info(msg interface{}, keyvals ...interface{}) {
|
||||
l.log(InfoLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Warn prints a warning message.
|
||||
func (l *Logger) Warn(msg interface{}, keyvals ...interface{}) {
|
||||
l.log(WarnLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Error prints an error message.
|
||||
func (l *Logger) Error(msg interface{}, keyvals ...interface{}) {
|
||||
l.log(ErrorLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Fatal prints a fatal message and exits.
|
||||
func (l *Logger) Fatal(msg interface{}, keyvals ...interface{}) {
|
||||
l.log(FatalLevel, msg, keyvals...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Print prints a message with no level.
|
||||
func (l *Logger) Print(msg interface{}, keyvals ...interface{}) {
|
||||
l.log(noLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Debugf prints a debug message with formatting.
|
||||
func (l *Logger) Debugf(format string, args ...interface{}) {
|
||||
l.log(DebugLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Infof prints an info message with formatting.
|
||||
func (l *Logger) Infof(format string, args ...interface{}) {
|
||||
l.log(InfoLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Warnf prints a warning message with formatting.
|
||||
func (l *Logger) Warnf(format string, args ...interface{}) {
|
||||
l.log(WarnLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Errorf prints an error message with formatting.
|
||||
func (l *Logger) Errorf(format string, args ...interface{}) {
|
||||
l.log(ErrorLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Fatalf prints a fatal message with formatting and exits.
|
||||
func (l *Logger) Fatalf(format string, args ...interface{}) {
|
||||
l.log(FatalLevel, fmt.Sprintf(format, args...))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Printf prints a message with no level and formatting.
|
||||
func (l *Logger) Printf(format string, args ...interface{}) {
|
||||
l.log(noLevel, fmt.Sprintf(format, args...))
|
||||
}
|
59
vendor/github.com/charmbracelet/log/options.go
generated
vendored
Normal file
59
vendor/github.com/charmbracelet/log/options.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// DefaultTimeFormat is the default time format.
|
||||
const DefaultTimeFormat = "2006/01/02 15:04:05"
|
||||
|
||||
// TimeFunction is a function that returns a time.Time.
|
||||
type TimeFunction = func() time.Time
|
||||
|
||||
// NowUTC is a convenient function that returns the
|
||||
// current time in UTC timezone.
|
||||
//
|
||||
// This is to be used as a time function.
|
||||
// For example:
|
||||
//
|
||||
// log.SetTimeFunction(log.NowUTC)
|
||||
func NowUTC() time.Time {
|
||||
return time.Now().UTC()
|
||||
}
|
||||
|
||||
// CallerFormatter is the caller formatter.
|
||||
type CallerFormatter func(string, int, string) string
|
||||
|
||||
// ShortCallerFormatter is a caller formatter that returns the last 2 levels of the path
|
||||
// and line number.
|
||||
func ShortCallerFormatter(file string, line int, funcName string) string {
|
||||
return fmt.Sprintf("%s:%d", trimCallerPath(file, 2), line)
|
||||
}
|
||||
|
||||
// LongCallerFormatter is a caller formatter that returns the full path and line number.
|
||||
func LongCallerFormatter(file string, line int, funcName string) string {
|
||||
return fmt.Sprintf("%s:%d", file, line)
|
||||
}
|
||||
|
||||
// Options is the options for the logger.
|
||||
type Options struct {
|
||||
// TimeFunction is the time function for the logger. The default is time.Now.
|
||||
TimeFunction TimeFunction
|
||||
// TimeFormat is the time format for the logger. The default is "2006/01/02 15:04:05".
|
||||
TimeFormat string
|
||||
// Level is the level for the logger. The default is InfoLevel.
|
||||
Level Level
|
||||
// Prefix is the prefix for the logger. The default is no prefix.
|
||||
Prefix string
|
||||
// ReportTimestamp is whether the logger should report the timestamp. The default is false.
|
||||
ReportTimestamp bool
|
||||
// ReportCaller is whether the logger should report the caller location. The default is false.
|
||||
ReportCaller bool
|
||||
// CallerFormatter is the caller format for the logger. The default is CallerShort.
|
||||
CallerFormatter CallerFormatter
|
||||
// Fields is the fields for the logger. The default is no fields.
|
||||
Fields []interface{}
|
||||
// Formatter is the formatter for the logger. The default is TextFormatter.
|
||||
Formatter Formatter
|
||||
}
|
209
vendor/github.com/charmbracelet/log/pkg.go
generated
vendored
Normal file
209
vendor/github.com/charmbracelet/log/pkg.go
generated
vendored
Normal file
@ -0,0 +1,209 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
// registry is a map of all registered lipgloss renderers.
|
||||
registry = sync.Map{}
|
||||
|
||||
// defaultLogger is the default global logger instance.
|
||||
defaultLogger = NewWithOptions(os.Stderr, Options{ReportTimestamp: true})
|
||||
)
|
||||
|
||||
// Default returns the default logger. The default logger comes with timestamp enabled.
|
||||
func Default() *Logger {
|
||||
return defaultLogger
|
||||
}
|
||||
|
||||
// SetDefault sets the default global logger.
|
||||
func SetDefault(logger *Logger) {
|
||||
defaultLogger = logger
|
||||
}
|
||||
|
||||
// New returns a new logger with the default options.
|
||||
func New(w io.Writer) *Logger {
|
||||
return NewWithOptions(w, Options{})
|
||||
}
|
||||
|
||||
// NewWithOptions returns a new logger using the provided options.
|
||||
func NewWithOptions(w io.Writer, o Options) *Logger {
|
||||
l := &Logger{
|
||||
b: bytes.Buffer{},
|
||||
mu: &sync.RWMutex{},
|
||||
helpers: &sync.Map{},
|
||||
level: int32(o.Level),
|
||||
reportTimestamp: o.ReportTimestamp,
|
||||
reportCaller: o.ReportCaller,
|
||||
prefix: o.Prefix,
|
||||
timeFunc: o.TimeFunction,
|
||||
timeFormat: o.TimeFormat,
|
||||
formatter: o.Formatter,
|
||||
fields: o.Fields,
|
||||
callerFormatter: o.CallerFormatter,
|
||||
}
|
||||
|
||||
l.SetOutput(w)
|
||||
l.SetLevel(Level(l.level))
|
||||
|
||||
if l.callerFormatter == nil {
|
||||
l.callerFormatter = ShortCallerFormatter
|
||||
}
|
||||
|
||||
if l.timeFunc == nil {
|
||||
l.timeFunc = time.Now
|
||||
}
|
||||
|
||||
if l.timeFormat == "" {
|
||||
l.timeFormat = DefaultTimeFormat
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
// SetReportTimestamp sets whether to report timestamp for the default logger.
|
||||
func SetReportTimestamp(report bool) {
|
||||
defaultLogger.SetReportTimestamp(report)
|
||||
}
|
||||
|
||||
// SetReportCaller sets whether to report caller location for the default logger.
|
||||
func SetReportCaller(report bool) {
|
||||
defaultLogger.SetReportCaller(report)
|
||||
}
|
||||
|
||||
// SetLevel sets the level for the default logger.
|
||||
func SetLevel(level Level) {
|
||||
defaultLogger.SetLevel(level)
|
||||
}
|
||||
|
||||
// GetLevel returns the level for the default logger.
|
||||
func GetLevel() Level {
|
||||
return defaultLogger.GetLevel()
|
||||
}
|
||||
|
||||
// SetTimeFormat sets the time format for the default logger.
|
||||
func SetTimeFormat(format string) {
|
||||
defaultLogger.SetTimeFormat(format)
|
||||
}
|
||||
|
||||
// SetTimeFunction sets the time function for the default logger.
|
||||
func SetTimeFunction(f TimeFunction) {
|
||||
defaultLogger.SetTimeFunction(f)
|
||||
}
|
||||
|
||||
// SetOutput sets the output for the default logger.
|
||||
func SetOutput(w io.Writer) {
|
||||
defaultLogger.SetOutput(w)
|
||||
}
|
||||
|
||||
// SetFormatter sets the formatter for the default logger.
|
||||
func SetFormatter(f Formatter) {
|
||||
defaultLogger.SetFormatter(f)
|
||||
}
|
||||
|
||||
// SetCallerFormatter sets the caller formatter for the default logger.
|
||||
func SetCallerFormatter(f CallerFormatter) {
|
||||
defaultLogger.SetCallerFormatter(f)
|
||||
}
|
||||
|
||||
// SetPrefix sets the prefix for the default logger.
|
||||
func SetPrefix(prefix string) {
|
||||
defaultLogger.SetPrefix(prefix)
|
||||
}
|
||||
|
||||
// GetPrefix returns the prefix for the default logger.
|
||||
func GetPrefix() string {
|
||||
return defaultLogger.GetPrefix()
|
||||
}
|
||||
|
||||
// With returns a new logger with the given keyvals.
|
||||
func With(keyvals ...interface{}) *Logger {
|
||||
return defaultLogger.With(keyvals...)
|
||||
}
|
||||
|
||||
// WithPrefix returns a new logger with the given prefix.
|
||||
func WithPrefix(prefix string) *Logger {
|
||||
return defaultLogger.WithPrefix(prefix)
|
||||
}
|
||||
|
||||
// Helper marks the calling function as a helper
|
||||
// and skips it for source location information.
|
||||
// It's the equivalent of testing.TB.Helper().
|
||||
func Helper() {
|
||||
// skip this function frame
|
||||
defaultLogger.helper(1)
|
||||
}
|
||||
|
||||
// Debug logs a debug message.
|
||||
func Debug(msg interface{}, keyvals ...interface{}) {
|
||||
defaultLogger.log(DebugLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Info logs an info message.
|
||||
func Info(msg interface{}, keyvals ...interface{}) {
|
||||
defaultLogger.log(InfoLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Warn logs a warning message.
|
||||
func Warn(msg interface{}, keyvals ...interface{}) {
|
||||
defaultLogger.log(WarnLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Error logs an error message.
|
||||
func Error(msg interface{}, keyvals ...interface{}) {
|
||||
defaultLogger.log(ErrorLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Fatal logs a fatal message and exit.
|
||||
func Fatal(msg interface{}, keyvals ...interface{}) {
|
||||
defaultLogger.log(FatalLevel, msg, keyvals...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Print logs a message with no level.
|
||||
func Print(msg interface{}, keyvals ...interface{}) {
|
||||
defaultLogger.log(noLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Debugf logs a debug message with formatting.
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
defaultLogger.log(DebugLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Infof logs an info message with formatting.
|
||||
func Infof(format string, args ...interface{}) {
|
||||
defaultLogger.log(InfoLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Warnf logs a warning message with formatting.
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
defaultLogger.log(WarnLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Errorf logs an error message with formatting.
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
defaultLogger.log(ErrorLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Fatalf logs a fatal message with formatting and exit.
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
defaultLogger.log(FatalLevel, fmt.Sprintf(format, args...))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Printf logs a message with formatting and no level.
|
||||
func Printf(format string, args ...interface{}) {
|
||||
defaultLogger.log(noLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// StandardLog returns a standard logger from the default logger.
|
||||
func StandardLog(opts ...StandardLogOptions) *log.Logger {
|
||||
return defaultLogger.StandardLog(opts...)
|
||||
}
|
70
vendor/github.com/charmbracelet/log/stdlog.go
generated
vendored
Normal file
70
vendor/github.com/charmbracelet/log/stdlog.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"log"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type stdLogWriter struct {
|
||||
l *Logger
|
||||
opt *StandardLogOptions
|
||||
}
|
||||
|
||||
func (l *stdLogWriter) Write(p []byte) (n int, err error) {
|
||||
str := strings.TrimSuffix(string(p), "\n")
|
||||
|
||||
if l.opt != nil {
|
||||
switch l.opt.ForceLevel {
|
||||
case DebugLevel:
|
||||
l.l.Debug(str)
|
||||
case InfoLevel:
|
||||
l.l.Info(str)
|
||||
case WarnLevel:
|
||||
l.l.Warn(str)
|
||||
case ErrorLevel:
|
||||
l.l.Error(str)
|
||||
}
|
||||
} else {
|
||||
switch {
|
||||
case strings.HasPrefix(str, "DEBUG"):
|
||||
l.l.Debug(strings.TrimSpace(str[5:]))
|
||||
case strings.HasPrefix(str, "INFO"):
|
||||
l.l.Info(strings.TrimSpace(str[4:]))
|
||||
case strings.HasPrefix(str, "WARN"):
|
||||
l.l.Warn(strings.TrimSpace(str[4:]))
|
||||
case strings.HasPrefix(str, "ERROR"):
|
||||
l.l.Error(strings.TrimSpace(str[5:]))
|
||||
case strings.HasPrefix(str, "ERR"):
|
||||
l.l.Error(strings.TrimSpace(str[3:]))
|
||||
default:
|
||||
l.l.Info(str)
|
||||
}
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// StandardLogOptions can be used to configure the standard log adapter.
|
||||
type StandardLogOptions struct {
|
||||
ForceLevel Level
|
||||
}
|
||||
|
||||
// StandardLog returns a standard logger from Logger. The returned logger
|
||||
// can infer log levels from message prefix. Expected prefixes are DEBUG, INFO,
|
||||
// WARN, ERROR, and ERR.
|
||||
func (l *Logger) StandardLog(opts ...StandardLogOptions) *log.Logger {
|
||||
nl := *l
|
||||
nl.mu = &sync.RWMutex{}
|
||||
nl.helpers = &sync.Map{}
|
||||
// The caller stack is
|
||||
// log.Printf() -> l.Output() -> l.out.Write(stdLogger.Write)
|
||||
nl.callerOffset += 3
|
||||
sl := &stdLogWriter{
|
||||
l: &nl,
|
||||
}
|
||||
if len(opts) > 0 {
|
||||
sl.opt = &opts[0]
|
||||
}
|
||||
return log.New(sl, "", 0)
|
||||
}
|
104
vendor/github.com/charmbracelet/log/styles.go
generated
vendored
Normal file
104
vendor/github.com/charmbracelet/log/styles.go
generated
vendored
Normal file
@ -0,0 +1,104 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
var (
|
||||
// TimestampStyle is the style for timestamps.
|
||||
TimestampStyle = lipgloss.NewStyle()
|
||||
|
||||
// CallerStyle is the style for caller.
|
||||
CallerStyle = lipgloss.NewStyle().Faint(true)
|
||||
|
||||
// PrefixStyle is the style for prefix.
|
||||
PrefixStyle = lipgloss.NewStyle().Bold(true).Faint(true)
|
||||
|
||||
// MessageStyle is the style for messages.
|
||||
MessageStyle = lipgloss.NewStyle()
|
||||
|
||||
// KeyStyle is the style for keys.
|
||||
KeyStyle = lipgloss.NewStyle().Faint(true)
|
||||
|
||||
// ValueStyle is the style for values.
|
||||
ValueStyle = lipgloss.NewStyle()
|
||||
|
||||
// SeparatorStyle is the style for separators.
|
||||
SeparatorStyle = lipgloss.NewStyle().Faint(true)
|
||||
|
||||
// DebugLevel is the style for debug level.
|
||||
DebugLevelStyle = lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(DebugLevel.String())).
|
||||
Bold(true).
|
||||
MaxWidth(4).
|
||||
Foreground(lipgloss.AdaptiveColor{
|
||||
Light: "63",
|
||||
Dark: "63",
|
||||
})
|
||||
|
||||
// InfoLevel is the style for info level.
|
||||
InfoLevelStyle = lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(InfoLevel.String())).
|
||||
Bold(true).
|
||||
MaxWidth(4).
|
||||
Foreground(lipgloss.AdaptiveColor{
|
||||
Light: "39",
|
||||
Dark: "86",
|
||||
})
|
||||
|
||||
// WarnLevel is the style for warn level.
|
||||
WarnLevelStyle = lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(WarnLevel.String())).
|
||||
Bold(true).
|
||||
MaxWidth(4).
|
||||
Foreground(lipgloss.AdaptiveColor{
|
||||
Light: "208",
|
||||
Dark: "192",
|
||||
})
|
||||
|
||||
// ErrorLevel is the style for error level.
|
||||
ErrorLevelStyle = lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(ErrorLevel.String())).
|
||||
Bold(true).
|
||||
MaxWidth(4).
|
||||
Foreground(lipgloss.AdaptiveColor{
|
||||
Light: "203",
|
||||
Dark: "204",
|
||||
})
|
||||
|
||||
// FatalLevel is the style for error level.
|
||||
FatalLevelStyle = lipgloss.NewStyle().
|
||||
SetString(strings.ToUpper(FatalLevel.String())).
|
||||
Bold(true).
|
||||
MaxWidth(4).
|
||||
Foreground(lipgloss.AdaptiveColor{
|
||||
Light: "133",
|
||||
Dark: "134",
|
||||
})
|
||||
|
||||
// KeyStyles overrides styles for specific keys.
|
||||
KeyStyles = map[string]lipgloss.Style{}
|
||||
|
||||
// ValueStyles overrides value styles for specific keys.
|
||||
ValueStyles = map[string]lipgloss.Style{}
|
||||
)
|
||||
|
||||
// levelStyle is a helper function to get the style for a level.
|
||||
func levelStyle(level Level) lipgloss.Style {
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
return DebugLevelStyle
|
||||
case InfoLevel:
|
||||
return InfoLevelStyle
|
||||
case WarnLevel:
|
||||
return WarnLevelStyle
|
||||
case ErrorLevel:
|
||||
return ErrorLevelStyle
|
||||
case FatalLevel:
|
||||
return FatalLevelStyle
|
||||
default:
|
||||
return lipgloss.NewStyle()
|
||||
}
|
||||
}
|
240
vendor/github.com/charmbracelet/log/text.go
generated
vendored
Normal file
240
vendor/github.com/charmbracelet/log/text.go
generated
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
const (
|
||||
separator = "="
|
||||
indentSeparator = " │ "
|
||||
)
|
||||
|
||||
func (l *Logger) writeIndent(w io.Writer, str string, indent string, newline bool, key string) {
|
||||
// kindly borrowed from hclog
|
||||
for {
|
||||
nl := strings.IndexByte(str, '\n')
|
||||
if nl == -1 {
|
||||
if str != "" {
|
||||
_, _ = w.Write([]byte(indent))
|
||||
val := escapeStringForOutput(str, false)
|
||||
if valueStyle, ok := ValueStyles[key]; ok {
|
||||
val = valueStyle.Renderer(l.re).Render(val)
|
||||
} else {
|
||||
val = ValueStyle.Renderer(l.re).Render(val)
|
||||
}
|
||||
_, _ = w.Write([]byte(val))
|
||||
if newline {
|
||||
_, _ = w.Write([]byte{'\n'})
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = w.Write([]byte(indent))
|
||||
val := escapeStringForOutput(str[:nl], false)
|
||||
val = ValueStyle.Renderer(l.re).Render(val)
|
||||
_, _ = w.Write([]byte(val))
|
||||
_, _ = w.Write([]byte{'\n'})
|
||||
str = str[nl+1:]
|
||||
}
|
||||
}
|
||||
|
||||
func needsEscaping(str string) bool {
|
||||
for _, b := range str {
|
||||
if !unicode.IsPrint(b) || b == '"' {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
const (
|
||||
lowerhex = "0123456789abcdef"
|
||||
)
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(strings.Builder)
|
||||
},
|
||||
}
|
||||
|
||||
func escapeStringForOutput(str string, escapeQuotes bool) string {
|
||||
// kindly borrowed from hclog
|
||||
if !needsEscaping(str) {
|
||||
return str
|
||||
}
|
||||
|
||||
bb := bufPool.Get().(*strings.Builder)
|
||||
bb.Reset()
|
||||
|
||||
defer bufPool.Put(bb)
|
||||
|
||||
for _, r := range str {
|
||||
if escapeQuotes && r == '"' {
|
||||
bb.WriteString(`\"`)
|
||||
} else if unicode.IsPrint(r) {
|
||||
bb.WriteRune(r)
|
||||
} else {
|
||||
switch r {
|
||||
case '\a':
|
||||
bb.WriteString(`\a`)
|
||||
case '\b':
|
||||
bb.WriteString(`\b`)
|
||||
case '\f':
|
||||
bb.WriteString(`\f`)
|
||||
case '\n':
|
||||
bb.WriteString(`\n`)
|
||||
case '\r':
|
||||
bb.WriteString(`\r`)
|
||||
case '\t':
|
||||
bb.WriteString(`\t`)
|
||||
case '\v':
|
||||
bb.WriteString(`\v`)
|
||||
default:
|
||||
switch {
|
||||
case r < ' ':
|
||||
bb.WriteString(`\x`)
|
||||
bb.WriteByte(lowerhex[byte(r)>>4])
|
||||
bb.WriteByte(lowerhex[byte(r)&0xF])
|
||||
case !utf8.ValidRune(r):
|
||||
r = 0xFFFD
|
||||
fallthrough
|
||||
case r < 0x10000:
|
||||
bb.WriteString(`\u`)
|
||||
for s := 12; s >= 0; s -= 4 {
|
||||
bb.WriteByte(lowerhex[r>>uint(s)&0xF])
|
||||
}
|
||||
default:
|
||||
bb.WriteString(`\U`)
|
||||
for s := 28; s >= 0; s -= 4 {
|
||||
bb.WriteByte(lowerhex[r>>uint(s)&0xF])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bb.String()
|
||||
}
|
||||
|
||||
// isNormal indicates if the rune is one allowed to exist as an unquoted
|
||||
// string value. This is a subset of ASCII, `-` through `~`.
|
||||
func isNormal(r rune) bool {
|
||||
return '-' <= r && r <= '~'
|
||||
}
|
||||
|
||||
// needsQuoting returns false if all the runes in string are normal, according
|
||||
// to isNormal.
|
||||
func needsQuoting(str string) bool {
|
||||
for _, r := range str {
|
||||
if !isNormal(r) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *Logger) textFormatter(keyvals ...interface{}) {
|
||||
for i := 0; i < len(keyvals); i += 2 {
|
||||
switch keyvals[i] {
|
||||
case TimestampKey:
|
||||
if t, ok := keyvals[i+1].(time.Time); ok {
|
||||
ts := t.Format(l.timeFormat)
|
||||
ts = TimestampStyle.Renderer(l.re).Render(ts)
|
||||
l.b.WriteString(ts)
|
||||
l.b.WriteByte(' ')
|
||||
}
|
||||
case LevelKey:
|
||||
if level, ok := keyvals[i+1].(Level); ok {
|
||||
lvl := levelStyle(level).Renderer(l.re).String()
|
||||
l.b.WriteString(lvl)
|
||||
l.b.WriteByte(' ')
|
||||
}
|
||||
case CallerKey:
|
||||
if caller, ok := keyvals[i+1].(string); ok {
|
||||
caller = fmt.Sprintf("<%s>", caller)
|
||||
caller = CallerStyle.Renderer(l.re).Render(caller)
|
||||
l.b.WriteString(caller)
|
||||
l.b.WriteByte(' ')
|
||||
}
|
||||
case PrefixKey:
|
||||
if prefix, ok := keyvals[i+1].(string); ok {
|
||||
prefix = PrefixStyle.Renderer(l.re).Render(prefix)
|
||||
l.b.WriteString(prefix)
|
||||
l.b.WriteByte(' ')
|
||||
}
|
||||
case MessageKey:
|
||||
if msg := keyvals[i+1]; msg != nil {
|
||||
m := fmt.Sprint(msg)
|
||||
m = MessageStyle.Renderer(l.re).Render(m)
|
||||
l.b.WriteString(m)
|
||||
}
|
||||
default:
|
||||
sep := separator
|
||||
indentSep := indentSeparator
|
||||
sep = SeparatorStyle.Renderer(l.re).Render(sep)
|
||||
indentSep = SeparatorStyle.Renderer(l.re).Render(indentSep)
|
||||
moreKeys := i < len(keyvals)-2
|
||||
key := fmt.Sprint(keyvals[i])
|
||||
val := fmt.Sprintf("%+v", keyvals[i+1])
|
||||
raw := val == ""
|
||||
if raw {
|
||||
val = `""`
|
||||
}
|
||||
if key == "" {
|
||||
continue
|
||||
}
|
||||
actualKey := key
|
||||
valueStyle := ValueStyle
|
||||
if vs, ok := ValueStyles[actualKey]; ok {
|
||||
valueStyle = vs
|
||||
}
|
||||
if keyStyle, ok := KeyStyles[key]; ok {
|
||||
key = keyStyle.Renderer(l.re).Render(key)
|
||||
} else {
|
||||
key = KeyStyle.Renderer(l.re).Render(key)
|
||||
}
|
||||
|
||||
// Values may contain multiple lines, and that format
|
||||
// is preserved, with each line prefixed with a " | "
|
||||
// to show it's part of a collection of lines.
|
||||
//
|
||||
// Values may also need quoting, if not all the runes
|
||||
// in the value string are "normal", like if they
|
||||
// contain ANSI escape sequences.
|
||||
if strings.Contains(val, "\n") {
|
||||
l.b.WriteString("\n ")
|
||||
l.b.WriteString(key)
|
||||
l.b.WriteString(sep + "\n")
|
||||
l.writeIndent(&l.b, val, indentSep, moreKeys, actualKey)
|
||||
// If there are more keyvals, separate them with a space.
|
||||
if moreKeys {
|
||||
l.b.WriteByte(' ')
|
||||
}
|
||||
} else if !raw && needsQuoting(val) {
|
||||
l.b.WriteByte(' ')
|
||||
l.b.WriteString(key)
|
||||
l.b.WriteString(sep)
|
||||
l.b.WriteString(valueStyle.Renderer(l.re).Render(fmt.Sprintf(`"%s"`,
|
||||
escapeStringForOutput(val, true))))
|
||||
} else {
|
||||
val = valueStyle.Renderer(l.re).Render(val)
|
||||
l.b.WriteByte(' ')
|
||||
l.b.WriteString(key)
|
||||
l.b.WriteString(sep)
|
||||
l.b.WriteString(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a newline to the end of the log message.
|
||||
l.b.WriteByte('\n')
|
||||
}
|
1
vendor/github.com/go-logfmt/logfmt/.gitignore
generated
vendored
Normal file
1
vendor/github.com/go-logfmt/logfmt/.gitignore
generated
vendored
Normal file
@ -0,0 +1 @@
|
||||
.vscode/
|
82
vendor/github.com/go-logfmt/logfmt/CHANGELOG.md
generated
vendored
Normal file
82
vendor/github.com/go-logfmt/logfmt/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,82 @@
|
||||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.6.0] - 2023-01-30
|
||||
|
||||
[0.6.0]: https://github.com/go-logfmt/logfmt/compare/v0.5.1...v0.6.0
|
||||
|
||||
### Added
|
||||
|
||||
- NewDecoderSize by [@alexanderjophus]
|
||||
|
||||
## [0.5.1] - 2021-08-18
|
||||
|
||||
[0.5.1]: https://github.com/go-logfmt/logfmt/compare/v0.5.0...v0.5.1
|
||||
|
||||
### Changed
|
||||
|
||||
- Update the `go.mod` file for Go 1.17 as described in the [Go 1.17 release
|
||||
notes](https://golang.org/doc/go1.17#go-command)
|
||||
|
||||
## [0.5.0] - 2020-01-03
|
||||
|
||||
[0.5.0]: https://github.com/go-logfmt/logfmt/compare/v0.4.0...v0.5.0
|
||||
|
||||
### Changed
|
||||
|
||||
- Remove the dependency on github.com/kr/logfmt by [@ChrisHines]
|
||||
- Move fuzz code to github.com/go-logfmt/fuzzlogfmt by [@ChrisHines]
|
||||
|
||||
## [0.4.0] - 2018-11-21
|
||||
|
||||
[0.4.0]: https://github.com/go-logfmt/logfmt/compare/v0.3.0...v0.4.0
|
||||
|
||||
### Added
|
||||
|
||||
- Go module support by [@ChrisHines]
|
||||
- CHANGELOG by [@ChrisHines]
|
||||
|
||||
### Changed
|
||||
|
||||
- Drop invalid runes from keys instead of returning ErrInvalidKey by [@ChrisHines]
|
||||
- On panic while printing, attempt to print panic value by [@bboreham]
|
||||
|
||||
## [0.3.0] - 2016-11-15
|
||||
|
||||
[0.3.0]: https://github.com/go-logfmt/logfmt/compare/v0.2.0...v0.3.0
|
||||
|
||||
### Added
|
||||
|
||||
- Pool buffers for quoted strings and byte slices by [@nussjustin]
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fuzz fix, quote invalid UTF-8 values by [@judwhite]
|
||||
|
||||
## [0.2.0] - 2016-05-08
|
||||
|
||||
[0.2.0]: https://github.com/go-logfmt/logfmt/compare/v0.1.0...v0.2.0
|
||||
|
||||
### Added
|
||||
|
||||
- Encoder.EncodeKeyvals by [@ChrisHines]
|
||||
|
||||
## [0.1.0] - 2016-03-28
|
||||
|
||||
[0.1.0]: https://github.com/go-logfmt/logfmt/commits/v0.1.0
|
||||
|
||||
### Added
|
||||
|
||||
- Encoder by [@ChrisHines]
|
||||
- Decoder by [@ChrisHines]
|
||||
- MarshalKeyvals by [@ChrisHines]
|
||||
|
||||
[@ChrisHines]: https://github.com/ChrisHines
|
||||
[@bboreham]: https://github.com/bboreham
|
||||
[@judwhite]: https://github.com/judwhite
|
||||
[@nussjustin]: https://github.com/nussjustin
|
||||
[@alexanderjophus]: https://github.com/alexanderjophus
|
22
vendor/github.com/go-logfmt/logfmt/LICENSE
generated
vendored
Normal file
22
vendor/github.com/go-logfmt/logfmt/LICENSE
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 go-logfmt
|
||||
|
||||
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.
|
||||
|
41
vendor/github.com/go-logfmt/logfmt/README.md
generated
vendored
Normal file
41
vendor/github.com/go-logfmt/logfmt/README.md
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
# logfmt
|
||||
|
||||
[](https://pkg.go.dev/github.com/go-logfmt/logfmt)
|
||||
[](https://goreportcard.com/report/go-logfmt/logfmt)
|
||||
[](https://github.com/go-logfmt/logfmt/actions/workflows/test.yml)
|
||||
[](https://coveralls.io/github/go-logfmt/logfmt?branch=main)
|
||||
|
||||
Package logfmt implements utilities to marshal and unmarshal data in the [logfmt
|
||||
format][fmt]. It provides an API similar to [encoding/json][json] and
|
||||
[encoding/xml][xml].
|
||||
|
||||
[fmt]: https://brandur.org/logfmt
|
||||
[json]: https://pkg.go.dev/encoding/json
|
||||
[xml]: https://pkg.go.dev/encoding/xml
|
||||
|
||||
The logfmt format was first documented by Brandur Leach in [this
|
||||
article][origin]. The format has not been formally standardized. The most
|
||||
authoritative public specification to date has been the documentation of a Go
|
||||
Language [package][parser] written by Blake Mizerany and Keith Rarick.
|
||||
|
||||
[origin]: https://brandur.org/logfmt
|
||||
[parser]: https://pkg.go.dev/github.com/kr/logfmt
|
||||
|
||||
## Goals
|
||||
|
||||
This project attempts to conform as closely as possible to the prior art, while
|
||||
also removing ambiguity where necessary to provide well behaved encoder and
|
||||
decoder implementations.
|
||||
|
||||
## Non-goals
|
||||
|
||||
This project does not attempt to formally standardize the logfmt format. In the
|
||||
event that logfmt is standardized this project would take conforming to the
|
||||
standard as a goal.
|
||||
|
||||
## Versioning
|
||||
|
||||
This project publishes releases according to the Go language guidelines for
|
||||
[developing and publishing modules][pub].
|
||||
|
||||
[pub]: https://go.dev/doc/modules/developing
|
254
vendor/github.com/go-logfmt/logfmt/decode.go
generated
vendored
Normal file
254
vendor/github.com/go-logfmt/logfmt/decode.go
generated
vendored
Normal file
@ -0,0 +1,254 @@
|
||||
package logfmt
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// A Decoder reads and decodes logfmt records from an input stream.
|
||||
type Decoder struct {
|
||||
pos int
|
||||
key []byte
|
||||
value []byte
|
||||
lineNum int
|
||||
s *bufio.Scanner
|
||||
err error
|
||||
}
|
||||
|
||||
// NewDecoder returns a new decoder that reads from r.
|
||||
//
|
||||
// The decoder introduces its own buffering and may read data from r beyond
|
||||
// the logfmt records requested.
|
||||
func NewDecoder(r io.Reader) *Decoder {
|
||||
dec := &Decoder{
|
||||
s: bufio.NewScanner(r),
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// NewDecoderSize returns a new decoder that reads from r.
|
||||
//
|
||||
// The decoder introduces its own buffering and may read data from r beyond
|
||||
// the logfmt records requested.
|
||||
// The size argument specifies the size of the initial buffer that the
|
||||
// Decoder will use to read records from r.
|
||||
// If a log line is longer than the size argument, the Decoder will return
|
||||
// a bufio.ErrTooLong error.
|
||||
func NewDecoderSize(r io.Reader, size int) *Decoder {
|
||||
scanner := bufio.NewScanner(r)
|
||||
scanner.Buffer(make([]byte, 0, size), size)
|
||||
dec := &Decoder{
|
||||
s: scanner,
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// ScanRecord advances the Decoder to the next record, which can then be
|
||||
// parsed with the ScanKeyval method. It returns false when decoding stops,
|
||||
// either by reaching the end of the input or an error. After ScanRecord
|
||||
// returns false, the Err method will return any error that occurred during
|
||||
// decoding, except that if it was io.EOF, Err will return nil.
|
||||
func (dec *Decoder) ScanRecord() bool {
|
||||
if dec.err != nil {
|
||||
return false
|
||||
}
|
||||
if !dec.s.Scan() {
|
||||
dec.err = dec.s.Err()
|
||||
return false
|
||||
}
|
||||
dec.lineNum++
|
||||
dec.pos = 0
|
||||
return true
|
||||
}
|
||||
|
||||
// ScanKeyval advances the Decoder to the next key/value pair of the current
|
||||
// record, which can then be retrieved with the Key and Value methods. It
|
||||
// returns false when decoding stops, either by reaching the end of the
|
||||
// current record or an error.
|
||||
func (dec *Decoder) ScanKeyval() bool {
|
||||
dec.key, dec.value = nil, nil
|
||||
if dec.err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
line := dec.s.Bytes()
|
||||
|
||||
// garbage
|
||||
for p, c := range line[dec.pos:] {
|
||||
if c > ' ' {
|
||||
dec.pos += p
|
||||
goto key
|
||||
}
|
||||
}
|
||||
dec.pos = len(line)
|
||||
return false
|
||||
|
||||
key:
|
||||
const invalidKeyError = "invalid key"
|
||||
|
||||
start, multibyte := dec.pos, false
|
||||
for p, c := range line[dec.pos:] {
|
||||
switch {
|
||||
case c == '=':
|
||||
dec.pos += p
|
||||
if dec.pos > start {
|
||||
dec.key = line[start:dec.pos]
|
||||
if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) {
|
||||
dec.syntaxError(invalidKeyError)
|
||||
return false
|
||||
}
|
||||
}
|
||||
if dec.key == nil {
|
||||
dec.unexpectedByte(c)
|
||||
return false
|
||||
}
|
||||
goto equal
|
||||
case c == '"':
|
||||
dec.pos += p
|
||||
dec.unexpectedByte(c)
|
||||
return false
|
||||
case c <= ' ':
|
||||
dec.pos += p
|
||||
if dec.pos > start {
|
||||
dec.key = line[start:dec.pos]
|
||||
if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) {
|
||||
dec.syntaxError(invalidKeyError)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case c >= utf8.RuneSelf:
|
||||
multibyte = true
|
||||
}
|
||||
}
|
||||
dec.pos = len(line)
|
||||
if dec.pos > start {
|
||||
dec.key = line[start:dec.pos]
|
||||
if multibyte && bytes.ContainsRune(dec.key, utf8.RuneError) {
|
||||
dec.syntaxError(invalidKeyError)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
|
||||
equal:
|
||||
dec.pos++
|
||||
if dec.pos >= len(line) {
|
||||
return true
|
||||
}
|
||||
switch c := line[dec.pos]; {
|
||||
case c <= ' ':
|
||||
return true
|
||||
case c == '"':
|
||||
goto qvalue
|
||||
}
|
||||
|
||||
// value
|
||||
start = dec.pos
|
||||
for p, c := range line[dec.pos:] {
|
||||
switch {
|
||||
case c == '=' || c == '"':
|
||||
dec.pos += p
|
||||
dec.unexpectedByte(c)
|
||||
return false
|
||||
case c <= ' ':
|
||||
dec.pos += p
|
||||
if dec.pos > start {
|
||||
dec.value = line[start:dec.pos]
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
dec.pos = len(line)
|
||||
if dec.pos > start {
|
||||
dec.value = line[start:dec.pos]
|
||||
}
|
||||
return true
|
||||
|
||||
qvalue:
|
||||
const (
|
||||
untermQuote = "unterminated quoted value"
|
||||
invalidQuote = "invalid quoted value"
|
||||
)
|
||||
|
||||
hasEsc, esc := false, false
|
||||
start = dec.pos
|
||||
for p, c := range line[dec.pos+1:] {
|
||||
switch {
|
||||
case esc:
|
||||
esc = false
|
||||
case c == '\\':
|
||||
hasEsc, esc = true, true
|
||||
case c == '"':
|
||||
dec.pos += p + 2
|
||||
if hasEsc {
|
||||
v, ok := unquoteBytes(line[start:dec.pos])
|
||||
if !ok {
|
||||
dec.syntaxError(invalidQuote)
|
||||
return false
|
||||
}
|
||||
dec.value = v
|
||||
} else {
|
||||
start++
|
||||
end := dec.pos - 1
|
||||
if end > start {
|
||||
dec.value = line[start:end]
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
dec.pos = len(line)
|
||||
dec.syntaxError(untermQuote)
|
||||
return false
|
||||
}
|
||||
|
||||
// Key returns the most recent key found by a call to ScanKeyval. The returned
|
||||
// slice may point to internal buffers and is only valid until the next call
|
||||
// to ScanRecord. It does no allocation.
|
||||
func (dec *Decoder) Key() []byte {
|
||||
return dec.key
|
||||
}
|
||||
|
||||
// Value returns the most recent value found by a call to ScanKeyval. The
|
||||
// returned slice may point to internal buffers and is only valid until the
|
||||
// next call to ScanRecord. It does no allocation when the value has no
|
||||
// escape sequences.
|
||||
func (dec *Decoder) Value() []byte {
|
||||
return dec.value
|
||||
}
|
||||
|
||||
// Err returns the first non-EOF error that was encountered by the Scanner.
|
||||
func (dec *Decoder) Err() error {
|
||||
return dec.err
|
||||
}
|
||||
|
||||
func (dec *Decoder) syntaxError(msg string) {
|
||||
dec.err = &SyntaxError{
|
||||
Msg: msg,
|
||||
Line: dec.lineNum,
|
||||
Pos: dec.pos + 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (dec *Decoder) unexpectedByte(c byte) {
|
||||
dec.err = &SyntaxError{
|
||||
Msg: fmt.Sprintf("unexpected %q", c),
|
||||
Line: dec.lineNum,
|
||||
Pos: dec.pos + 1,
|
||||
}
|
||||
}
|
||||
|
||||
// A SyntaxError represents a syntax error in the logfmt input stream.
|
||||
type SyntaxError struct {
|
||||
Msg string
|
||||
Line int
|
||||
Pos int
|
||||
}
|
||||
|
||||
func (e *SyntaxError) Error() string {
|
||||
return fmt.Sprintf("logfmt syntax error at pos %d on line %d: %s", e.Pos, e.Line, e.Msg)
|
||||
}
|
6
vendor/github.com/go-logfmt/logfmt/doc.go
generated
vendored
Normal file
6
vendor/github.com/go-logfmt/logfmt/doc.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
// Package logfmt implements utilities to marshal and unmarshal data in the
|
||||
// logfmt format. The logfmt format records key/value pairs in a way that
|
||||
// balances readability for humans and simplicity of computer parsing. It is
|
||||
// most commonly used as a more human friendly alternative to JSON for
|
||||
// structured logging.
|
||||
package logfmt
|
322
vendor/github.com/go-logfmt/logfmt/encode.go
generated
vendored
Normal file
322
vendor/github.com/go-logfmt/logfmt/encode.go
generated
vendored
Normal file
@ -0,0 +1,322 @@
|
||||
package logfmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// MarshalKeyvals returns the logfmt encoding of keyvals, a variadic sequence
|
||||
// of alternating keys and values.
|
||||
func MarshalKeyvals(keyvals ...interface{}) ([]byte, error) {
|
||||
buf := &bytes.Buffer{}
|
||||
if err := NewEncoder(buf).EncodeKeyvals(keyvals...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// An Encoder writes logfmt data to an output stream.
|
||||
type Encoder struct {
|
||||
w io.Writer
|
||||
scratch bytes.Buffer
|
||||
needSep bool
|
||||
}
|
||||
|
||||
// NewEncoder returns a new encoder that writes to w.
|
||||
func NewEncoder(w io.Writer) *Encoder {
|
||||
return &Encoder{
|
||||
w: w,
|
||||
}
|
||||
}
|
||||
|
||||
var (
|
||||
space = []byte(" ")
|
||||
equals = []byte("=")
|
||||
newline = []byte("\n")
|
||||
null = []byte("null")
|
||||
)
|
||||
|
||||
// EncodeKeyval writes the logfmt encoding of key and value to the stream. A
|
||||
// single space is written before the second and subsequent keys in a record.
|
||||
// Nothing is written if a non-nil error is returned.
|
||||
func (enc *Encoder) EncodeKeyval(key, value interface{}) error {
|
||||
enc.scratch.Reset()
|
||||
if enc.needSep {
|
||||
if _, err := enc.scratch.Write(space); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if err := writeKey(&enc.scratch, key); err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := enc.scratch.Write(equals); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := writeValue(&enc.scratch, value); err != nil {
|
||||
return err
|
||||
}
|
||||
_, err := enc.w.Write(enc.scratch.Bytes())
|
||||
enc.needSep = true
|
||||
return err
|
||||
}
|
||||
|
||||
// EncodeKeyvals writes the logfmt encoding of keyvals to the stream. Keyvals
|
||||
// is a variadic sequence of alternating keys and values. Keys of unsupported
|
||||
// type are skipped along with their corresponding value. Values of
|
||||
// unsupported type or that cause a MarshalerError are replaced by their error
|
||||
// but do not cause EncodeKeyvals to return an error. If a non-nil error is
|
||||
// returned some key/value pairs may not have be written.
|
||||
func (enc *Encoder) EncodeKeyvals(keyvals ...interface{}) error {
|
||||
if len(keyvals) == 0 {
|
||||
return nil
|
||||
}
|
||||
if len(keyvals)%2 == 1 {
|
||||
keyvals = append(keyvals, nil)
|
||||
}
|
||||
for i := 0; i < len(keyvals); i += 2 {
|
||||
k, v := keyvals[i], keyvals[i+1]
|
||||
err := enc.EncodeKeyval(k, v)
|
||||
if err == ErrUnsupportedKeyType {
|
||||
continue
|
||||
}
|
||||
if _, ok := err.(*MarshalerError); ok || err == ErrUnsupportedValueType {
|
||||
v = err
|
||||
err = enc.EncodeKeyval(k, v)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// MarshalerError represents an error encountered while marshaling a value.
|
||||
type MarshalerError struct {
|
||||
Type reflect.Type
|
||||
Err error
|
||||
}
|
||||
|
||||
func (e *MarshalerError) Error() string {
|
||||
return "error marshaling value of type " + e.Type.String() + ": " + e.Err.Error()
|
||||
}
|
||||
|
||||
// ErrNilKey is returned by Marshal functions and Encoder methods if a key is
|
||||
// a nil interface or pointer value.
|
||||
var ErrNilKey = errors.New("nil key")
|
||||
|
||||
// ErrInvalidKey is returned by Marshal functions and Encoder methods if, after
|
||||
// dropping invalid runes, a key is empty.
|
||||
var ErrInvalidKey = errors.New("invalid key")
|
||||
|
||||
// ErrUnsupportedKeyType is returned by Encoder methods if a key has an
|
||||
// unsupported type.
|
||||
var ErrUnsupportedKeyType = errors.New("unsupported key type")
|
||||
|
||||
// ErrUnsupportedValueType is returned by Encoder methods if a value has an
|
||||
// unsupported type.
|
||||
var ErrUnsupportedValueType = errors.New("unsupported value type")
|
||||
|
||||
func writeKey(w io.Writer, key interface{}) error {
|
||||
if key == nil {
|
||||
return ErrNilKey
|
||||
}
|
||||
|
||||
switch k := key.(type) {
|
||||
case string:
|
||||
return writeStringKey(w, k)
|
||||
case []byte:
|
||||
if k == nil {
|
||||
return ErrNilKey
|
||||
}
|
||||
return writeBytesKey(w, k)
|
||||
case encoding.TextMarshaler:
|
||||
kb, err := safeMarshal(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if kb == nil {
|
||||
return ErrNilKey
|
||||
}
|
||||
return writeBytesKey(w, kb)
|
||||
case fmt.Stringer:
|
||||
ks, ok := safeString(k)
|
||||
if !ok {
|
||||
return ErrNilKey
|
||||
}
|
||||
return writeStringKey(w, ks)
|
||||
default:
|
||||
rkey := reflect.ValueOf(key)
|
||||
switch rkey.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
return ErrUnsupportedKeyType
|
||||
case reflect.Ptr:
|
||||
if rkey.IsNil() {
|
||||
return ErrNilKey
|
||||
}
|
||||
return writeKey(w, rkey.Elem().Interface())
|
||||
}
|
||||
return writeStringKey(w, fmt.Sprint(k))
|
||||
}
|
||||
}
|
||||
|
||||
// keyRuneFilter returns r for all valid key runes, and -1 for all invalid key
|
||||
// runes. When used as the mapping function for strings.Map and bytes.Map
|
||||
// functions it causes them to remove invalid key runes from strings or byte
|
||||
// slices respectively.
|
||||
func keyRuneFilter(r rune) rune {
|
||||
if r <= ' ' || r == '=' || r == '"' || r == utf8.RuneError {
|
||||
return -1
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func writeStringKey(w io.Writer, key string) error {
|
||||
k := strings.Map(keyRuneFilter, key)
|
||||
if k == "" {
|
||||
return ErrInvalidKey
|
||||
}
|
||||
_, err := io.WriteString(w, k)
|
||||
return err
|
||||
}
|
||||
|
||||
func writeBytesKey(w io.Writer, key []byte) error {
|
||||
k := bytes.Map(keyRuneFilter, key)
|
||||
if len(k) == 0 {
|
||||
return ErrInvalidKey
|
||||
}
|
||||
_, err := w.Write(k)
|
||||
return err
|
||||
}
|
||||
|
||||
func writeValue(w io.Writer, value interface{}) error {
|
||||
switch v := value.(type) {
|
||||
case nil:
|
||||
return writeBytesValue(w, null)
|
||||
case string:
|
||||
return writeStringValue(w, v, true)
|
||||
case []byte:
|
||||
return writeBytesValue(w, v)
|
||||
case encoding.TextMarshaler:
|
||||
vb, err := safeMarshal(v)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if vb == nil {
|
||||
vb = null
|
||||
}
|
||||
return writeBytesValue(w, vb)
|
||||
case error:
|
||||
se, ok := safeError(v)
|
||||
return writeStringValue(w, se, ok)
|
||||
case fmt.Stringer:
|
||||
ss, ok := safeString(v)
|
||||
return writeStringValue(w, ss, ok)
|
||||
default:
|
||||
rvalue := reflect.ValueOf(value)
|
||||
switch rvalue.Kind() {
|
||||
case reflect.Array, reflect.Chan, reflect.Func, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
return ErrUnsupportedValueType
|
||||
case reflect.Ptr:
|
||||
if rvalue.IsNil() {
|
||||
return writeBytesValue(w, null)
|
||||
}
|
||||
return writeValue(w, rvalue.Elem().Interface())
|
||||
}
|
||||
return writeStringValue(w, fmt.Sprint(v), true)
|
||||
}
|
||||
}
|
||||
|
||||
func needsQuotedValueRune(r rune) bool {
|
||||
return r <= ' ' || r == '=' || r == '"' || r == utf8.RuneError
|
||||
}
|
||||
|
||||
func writeStringValue(w io.Writer, value string, ok bool) error {
|
||||
var err error
|
||||
if ok && value == "null" {
|
||||
_, err = io.WriteString(w, `"null"`)
|
||||
} else if strings.IndexFunc(value, needsQuotedValueRune) != -1 {
|
||||
_, err = writeQuotedString(w, value)
|
||||
} else {
|
||||
_, err = io.WriteString(w, value)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func writeBytesValue(w io.Writer, value []byte) error {
|
||||
var err error
|
||||
if bytes.IndexFunc(value, needsQuotedValueRune) != -1 {
|
||||
_, err = writeQuotedBytes(w, value)
|
||||
} else {
|
||||
_, err = w.Write(value)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// EndRecord writes a newline character to the stream and resets the encoder
|
||||
// to the beginning of a new record.
|
||||
func (enc *Encoder) EndRecord() error {
|
||||
_, err := enc.w.Write(newline)
|
||||
if err == nil {
|
||||
enc.needSep = false
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// Reset resets the encoder to the beginning of a new record.
|
||||
func (enc *Encoder) Reset() {
|
||||
enc.needSep = false
|
||||
}
|
||||
|
||||
func safeError(err error) (s string, ok bool) {
|
||||
defer func() {
|
||||
if panicVal := recover(); panicVal != nil {
|
||||
if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
s, ok = "null", false
|
||||
} else {
|
||||
s, ok = fmt.Sprintf("PANIC:%v", panicVal), false
|
||||
}
|
||||
}
|
||||
}()
|
||||
s, ok = err.Error(), true
|
||||
return
|
||||
}
|
||||
|
||||
func safeString(str fmt.Stringer) (s string, ok bool) {
|
||||
defer func() {
|
||||
if panicVal := recover(); panicVal != nil {
|
||||
if v := reflect.ValueOf(str); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
s, ok = "null", false
|
||||
} else {
|
||||
s, ok = fmt.Sprintf("PANIC:%v", panicVal), true
|
||||
}
|
||||
}
|
||||
}()
|
||||
s, ok = str.String(), true
|
||||
return
|
||||
}
|
||||
|
||||
func safeMarshal(tm encoding.TextMarshaler) (b []byte, err error) {
|
||||
defer func() {
|
||||
if panicVal := recover(); panicVal != nil {
|
||||
if v := reflect.ValueOf(tm); v.Kind() == reflect.Ptr && v.IsNil() {
|
||||
b, err = nil, nil
|
||||
} else {
|
||||
b, err = nil, fmt.Errorf("panic when marshalling: %s", panicVal)
|
||||
}
|
||||
}
|
||||
}()
|
||||
b, err = tm.MarshalText()
|
||||
if err != nil {
|
||||
return nil, &MarshalerError{
|
||||
Type: reflect.TypeOf(tm),
|
||||
Err: err,
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
277
vendor/github.com/go-logfmt/logfmt/jsonstring.go
generated
vendored
Normal file
277
vendor/github.com/go-logfmt/logfmt/jsonstring.go
generated
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
package logfmt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"strconv"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf16"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
// Taken from Go's encoding/json and modified for use here.
|
||||
|
||||
// Copyright 2010 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
var hex = "0123456789abcdef"
|
||||
|
||||
var bufferPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return &bytes.Buffer{}
|
||||
},
|
||||
}
|
||||
|
||||
func getBuffer() *bytes.Buffer {
|
||||
return bufferPool.Get().(*bytes.Buffer)
|
||||
}
|
||||
|
||||
func poolBuffer(buf *bytes.Buffer) {
|
||||
buf.Reset()
|
||||
bufferPool.Put(buf)
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with writeQuotedBytes below.
|
||||
func writeQuotedString(w io.Writer, s string) (int, error) {
|
||||
buf := getBuffer()
|
||||
buf.WriteByte('"')
|
||||
start := 0
|
||||
for i := 0; i < len(s); {
|
||||
if b := s[i]; b < utf8.RuneSelf {
|
||||
if 0x20 <= b && b != '\\' && b != '"' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if start < i {
|
||||
buf.WriteString(s[start:i])
|
||||
}
|
||||
switch b {
|
||||
case '\\', '"':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte(b)
|
||||
case '\n':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('n')
|
||||
case '\r':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('r')
|
||||
case '\t':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('t')
|
||||
default:
|
||||
// This encodes bytes < 0x20 except for \n, \r, and \t.
|
||||
buf.WriteString(`\u00`)
|
||||
buf.WriteByte(hex[b>>4])
|
||||
buf.WriteByte(hex[b&0xF])
|
||||
}
|
||||
i++
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
c, size := utf8.DecodeRuneInString(s[i:])
|
||||
if c == utf8.RuneError {
|
||||
if start < i {
|
||||
buf.WriteString(s[start:i])
|
||||
}
|
||||
buf.WriteString(`\ufffd`)
|
||||
i += size
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
i += size
|
||||
}
|
||||
if start < len(s) {
|
||||
buf.WriteString(s[start:])
|
||||
}
|
||||
buf.WriteByte('"')
|
||||
n, err := w.Write(buf.Bytes())
|
||||
poolBuffer(buf)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// NOTE: keep in sync with writeQuoteString above.
|
||||
func writeQuotedBytes(w io.Writer, s []byte) (int, error) {
|
||||
buf := getBuffer()
|
||||
buf.WriteByte('"')
|
||||
start := 0
|
||||
for i := 0; i < len(s); {
|
||||
if b := s[i]; b < utf8.RuneSelf {
|
||||
if 0x20 <= b && b != '\\' && b != '"' {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
if start < i {
|
||||
buf.Write(s[start:i])
|
||||
}
|
||||
switch b {
|
||||
case '\\', '"':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte(b)
|
||||
case '\n':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('n')
|
||||
case '\r':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('r')
|
||||
case '\t':
|
||||
buf.WriteByte('\\')
|
||||
buf.WriteByte('t')
|
||||
default:
|
||||
// This encodes bytes < 0x20 except for \n, \r, and \t.
|
||||
buf.WriteString(`\u00`)
|
||||
buf.WriteByte(hex[b>>4])
|
||||
buf.WriteByte(hex[b&0xF])
|
||||
}
|
||||
i++
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
c, size := utf8.DecodeRune(s[i:])
|
||||
if c == utf8.RuneError {
|
||||
if start < i {
|
||||
buf.Write(s[start:i])
|
||||
}
|
||||
buf.WriteString(`\ufffd`)
|
||||
i += size
|
||||
start = i
|
||||
continue
|
||||
}
|
||||
i += size
|
||||
}
|
||||
if start < len(s) {
|
||||
buf.Write(s[start:])
|
||||
}
|
||||
buf.WriteByte('"')
|
||||
n, err := w.Write(buf.Bytes())
|
||||
poolBuffer(buf)
|
||||
return n, err
|
||||
}
|
||||
|
||||
// getu4 decodes \uXXXX from the beginning of s, returning the hex value,
|
||||
// or it returns -1.
|
||||
func getu4(s []byte) rune {
|
||||
if len(s) < 6 || s[0] != '\\' || s[1] != 'u' {
|
||||
return -1
|
||||
}
|
||||
r, err := strconv.ParseUint(string(s[2:6]), 16, 64)
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
return rune(r)
|
||||
}
|
||||
|
||||
func unquoteBytes(s []byte) (t []byte, ok bool) {
|
||||
if len(s) < 2 || s[0] != '"' || s[len(s)-1] != '"' {
|
||||
return
|
||||
}
|
||||
s = s[1 : len(s)-1]
|
||||
|
||||
// Check for unusual characters. If there are none,
|
||||
// then no unquoting is needed, so return a slice of the
|
||||
// original bytes.
|
||||
r := 0
|
||||
for r < len(s) {
|
||||
c := s[r]
|
||||
if c == '\\' || c == '"' || c < ' ' {
|
||||
break
|
||||
}
|
||||
if c < utf8.RuneSelf {
|
||||
r++
|
||||
continue
|
||||
}
|
||||
rr, size := utf8.DecodeRune(s[r:])
|
||||
if rr == utf8.RuneError {
|
||||
break
|
||||
}
|
||||
r += size
|
||||
}
|
||||
if r == len(s) {
|
||||
return s, true
|
||||
}
|
||||
|
||||
b := make([]byte, len(s)+2*utf8.UTFMax)
|
||||
w := copy(b, s[0:r])
|
||||
for r < len(s) {
|
||||
// Out of room? Can only happen if s is full of
|
||||
// malformed UTF-8 and we're replacing each
|
||||
// byte with RuneError.
|
||||
if w >= len(b)-2*utf8.UTFMax {
|
||||
nb := make([]byte, (len(b)+utf8.UTFMax)*2)
|
||||
copy(nb, b[0:w])
|
||||
b = nb
|
||||
}
|
||||
switch c := s[r]; {
|
||||
case c == '\\':
|
||||
r++
|
||||
if r >= len(s) {
|
||||
return
|
||||
}
|
||||
switch s[r] {
|
||||
default:
|
||||
return
|
||||
case '"', '\\', '/', '\'':
|
||||
b[w] = s[r]
|
||||
r++
|
||||
w++
|
||||
case 'b':
|
||||
b[w] = '\b'
|
||||
r++
|
||||
w++
|
||||
case 'f':
|
||||
b[w] = '\f'
|
||||
r++
|
||||
w++
|
||||
case 'n':
|
||||
b[w] = '\n'
|
||||
r++
|
||||
w++
|
||||
case 'r':
|
||||
b[w] = '\r'
|
||||
r++
|
||||
w++
|
||||
case 't':
|
||||
b[w] = '\t'
|
||||
r++
|
||||
w++
|
||||
case 'u':
|
||||
r--
|
||||
rr := getu4(s[r:])
|
||||
if rr < 0 {
|
||||
return
|
||||
}
|
||||
r += 6
|
||||
if utf16.IsSurrogate(rr) {
|
||||
rr1 := getu4(s[r:])
|
||||
if dec := utf16.DecodeRune(rr, rr1); dec != unicode.ReplacementChar {
|
||||
// A valid pair; consume.
|
||||
r += 6
|
||||
w += utf8.EncodeRune(b[w:], dec)
|
||||
break
|
||||
}
|
||||
// Invalid surrogate; fall back to replacement rune.
|
||||
rr = unicode.ReplacementChar
|
||||
}
|
||||
w += utf8.EncodeRune(b[w:], rr)
|
||||
}
|
||||
|
||||
// Quote, control characters are invalid.
|
||||
case c == '"', c < ' ':
|
||||
return
|
||||
|
||||
// ASCII
|
||||
case c < utf8.RuneSelf:
|
||||
b[w] = c
|
||||
r++
|
||||
w++
|
||||
|
||||
// Coerce to well-formed UTF-8.
|
||||
default:
|
||||
rr, size := utf8.DecodeRune(s[r:])
|
||||
r += size
|
||||
w += utf8.EncodeRune(b[w:], rr)
|
||||
}
|
||||
}
|
||||
return b[0:w], true
|
||||
}
|
201
vendor/github.com/inconshreveable/mousetrap/LICENSE
generated
vendored
Normal file
201
vendor/github.com/inconshreveable/mousetrap/LICENSE
generated
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright 2022 Alan Shreve (@inconshreveable)
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
23
vendor/github.com/inconshreveable/mousetrap/README.md
generated
vendored
Normal file
23
vendor/github.com/inconshreveable/mousetrap/README.md
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
# mousetrap
|
||||
|
||||
mousetrap is a tiny library that answers a single question.
|
||||
|
||||
On a Windows machine, was the process invoked by someone double clicking on
|
||||
the executable file while browsing in explorer?
|
||||
|
||||
### Motivation
|
||||
|
||||
Windows developers unfamiliar with command line tools will often "double-click"
|
||||
the executable for a tool. Because most CLI tools print the help and then exit
|
||||
when invoked without arguments, this is often very frustrating for those users.
|
||||
|
||||
mousetrap provides a way to detect these invocations so that you can provide
|
||||
more helpful behavior and instructions on how to run the CLI tool. To see what
|
||||
this looks like, both from an organizational and a technical perspective, see
|
||||
https://inconshreveable.com/09-09-2014/sweat-the-small-stuff/
|
||||
|
||||
### The interface
|
||||
|
||||
The library exposes a single interface:
|
||||
|
||||
func StartedByExplorer() (bool)
|
15
vendor/github.com/inconshreveable/mousetrap/trap_others.go
generated
vendored
Normal file
15
vendor/github.com/inconshreveable/mousetrap/trap_others.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// +build !windows
|
||||
|
||||
package mousetrap
|
||||
|
||||
// StartedByExplorer returns true if the program was invoked by the user
|
||||
// double-clicking on the executable from explorer.exe
|
||||
//
|
||||
// It is conservative and returns false if any of the internal calls fail.
|
||||
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||
// whether it was launched from explorer.exe
|
||||
//
|
||||
// On non-Windows platforms, it always returns false.
|
||||
func StartedByExplorer() bool {
|
||||
return false
|
||||
}
|
98
vendor/github.com/inconshreveable/mousetrap/trap_windows.go
generated
vendored
Normal file
98
vendor/github.com/inconshreveable/mousetrap/trap_windows.go
generated
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
// +build windows
|
||||
// +build !go1.4
|
||||
|
||||
package mousetrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
// defined by the Win32 API
|
||||
th32cs_snapprocess uintptr = 0x2
|
||||
)
|
||||
|
||||
var (
|
||||
kernel = syscall.MustLoadDLL("kernel32.dll")
|
||||
CreateToolhelp32Snapshot = kernel.MustFindProc("CreateToolhelp32Snapshot")
|
||||
Process32First = kernel.MustFindProc("Process32FirstW")
|
||||
Process32Next = kernel.MustFindProc("Process32NextW")
|
||||
)
|
||||
|
||||
// ProcessEntry32 structure defined by the Win32 API
|
||||
type processEntry32 struct {
|
||||
dwSize uint32
|
||||
cntUsage uint32
|
||||
th32ProcessID uint32
|
||||
th32DefaultHeapID int
|
||||
th32ModuleID uint32
|
||||
cntThreads uint32
|
||||
th32ParentProcessID uint32
|
||||
pcPriClassBase int32
|
||||
dwFlags uint32
|
||||
szExeFile [syscall.MAX_PATH]uint16
|
||||
}
|
||||
|
||||
func getProcessEntry(pid int) (pe *processEntry32, err error) {
|
||||
snapshot, _, e1 := CreateToolhelp32Snapshot.Call(th32cs_snapprocess, uintptr(0))
|
||||
if snapshot == uintptr(syscall.InvalidHandle) {
|
||||
err = fmt.Errorf("CreateToolhelp32Snapshot: %v", e1)
|
||||
return
|
||||
}
|
||||
defer syscall.CloseHandle(syscall.Handle(snapshot))
|
||||
|
||||
var processEntry processEntry32
|
||||
processEntry.dwSize = uint32(unsafe.Sizeof(processEntry))
|
||||
ok, _, e1 := Process32First.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
|
||||
if ok == 0 {
|
||||
err = fmt.Errorf("Process32First: %v", e1)
|
||||
return
|
||||
}
|
||||
|
||||
for {
|
||||
if processEntry.th32ProcessID == uint32(pid) {
|
||||
pe = &processEntry
|
||||
return
|
||||
}
|
||||
|
||||
ok, _, e1 = Process32Next.Call(snapshot, uintptr(unsafe.Pointer(&processEntry)))
|
||||
if ok == 0 {
|
||||
err = fmt.Errorf("Process32Next: %v", e1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getppid() (pid int, err error) {
|
||||
pe, err := getProcessEntry(os.Getpid())
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
pid = int(pe.th32ParentProcessID)
|
||||
return
|
||||
}
|
||||
|
||||
// StartedByExplorer returns true if the program was invoked by the user double-clicking
|
||||
// on the executable from explorer.exe
|
||||
//
|
||||
// It is conservative and returns false if any of the internal calls fail.
|
||||
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||
// whether it was launched from explorer.exe
|
||||
func StartedByExplorer() bool {
|
||||
ppid, err := getppid()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
pe, err := getProcessEntry(ppid)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
|
||||
name := syscall.UTF16ToString(pe.szExeFile[:])
|
||||
return name == "explorer.exe"
|
||||
}
|
46
vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go
generated
vendored
Normal file
46
vendor/github.com/inconshreveable/mousetrap/trap_windows_1.4.go
generated
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
// +build windows
|
||||
// +build go1.4
|
||||
|
||||
package mousetrap
|
||||
|
||||
import (
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func getProcessEntry(pid int) (*syscall.ProcessEntry32, error) {
|
||||
snapshot, err := syscall.CreateToolhelp32Snapshot(syscall.TH32CS_SNAPPROCESS, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CloseHandle(snapshot)
|
||||
var procEntry syscall.ProcessEntry32
|
||||
procEntry.Size = uint32(unsafe.Sizeof(procEntry))
|
||||
if err = syscall.Process32First(snapshot, &procEntry); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for {
|
||||
if procEntry.ProcessID == uint32(pid) {
|
||||
return &procEntry, nil
|
||||
}
|
||||
err = syscall.Process32Next(snapshot, &procEntry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StartedByExplorer returns true if the program was invoked by the user double-clicking
|
||||
// on the executable from explorer.exe
|
||||
//
|
||||
// It is conservative and returns false if any of the internal calls fail.
|
||||
// It does not guarantee that the program was run from a terminal. It only can tell you
|
||||
// whether it was launched from explorer.exe
|
||||
func StartedByExplorer() bool {
|
||||
pe, err := getProcessEntry(os.Getppid())
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return "explorer.exe" == syscall.UTF16ToString(pe.ExeFile[:])
|
||||
}
|
101
vendor/github.com/lucasb-eyer/go-colorful/.gitignore
generated
vendored
Normal file
101
vendor/github.com/lucasb-eyer/go-colorful/.gitignore
generated
vendored
Normal file
@ -0,0 +1,101 @@
|
||||
# Created by https://www.toptal.com/developers/gitignore/api/code,go,linux,macos,windows
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=code,go,linux,macos,windows
|
||||
|
||||
### Code ###
|
||||
.vscode/*
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
*.code-workspace
|
||||
|
||||
### Go ###
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
### Go Patch ###
|
||||
/vendor/
|
||||
/Godeps/
|
||||
|
||||
### Linux ###
|
||||
*~
|
||||
|
||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||
.fuse_hidden*
|
||||
|
||||
# KDE directory preferences
|
||||
.directory
|
||||
|
||||
# Linux trash folder which might appear on any partition or disk
|
||||
.Trash-*
|
||||
|
||||
# .nfs files are created when an open file is removed but is still being accessed
|
||||
.nfs*
|
||||
|
||||
### macOS ###
|
||||
# General
|
||||
.DS_Store
|
||||
.AppleDouble
|
||||
.LSOverride
|
||||
|
||||
# Icon must end with two \r
|
||||
Icon
|
||||
|
||||
|
||||
# Thumbnails
|
||||
._*
|
||||
|
||||
# Files that might appear in the root of a volume
|
||||
.DocumentRevisions-V100
|
||||
.fseventsd
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.com.apple.timemachine.donotpresent
|
||||
|
||||
# Directories potentially created on remote AFP share
|
||||
.AppleDB
|
||||
.AppleDesktop
|
||||
Network Trash Folder
|
||||
Temporary Items
|
||||
.apdisk
|
||||
|
||||
### Windows ###
|
||||
# Windows thumbnail cache files
|
||||
Thumbs.db
|
||||
Thumbs.db:encryptable
|
||||
ehthumbs.db
|
||||
ehthumbs_vista.db
|
||||
|
||||
# Dump file
|
||||
*.stackdump
|
||||
|
||||
# Folder config file
|
||||
[Dd]esktop.ini
|
||||
|
||||
# Recycle Bin used on file shares
|
||||
$RECYCLE.BIN/
|
||||
|
||||
# Windows Installer files
|
||||
*.cab
|
||||
*.msi
|
||||
*.msix
|
||||
*.msm
|
||||
*.msp
|
||||
|
||||
# Windows shortcuts
|
||||
*.lnk
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/code,go,linux,macos,windows
|
42
vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md
generated
vendored
Normal file
42
vendor/github.com/lucasb-eyer/go-colorful/CHANGELOG.md
generated
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
# Changelog
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
The format of this file is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
but only releases after v1.0.3 properly adhere to it.
|
||||
|
||||
|
||||
## [1.2.0] - 2021-01-27
|
||||
### Added
|
||||
- HSLuv and HPLuv color spaces (#41, #51)
|
||||
- CIE LCh(uv) color space, called `LuvLCh` in code (#51)
|
||||
- JSON and envconfig serialization support for `HexColor` (#42)
|
||||
- `DistanceLinearRGB` (#53)
|
||||
|
||||
### Fixed
|
||||
- RGB to/from XYZ conversion is more accurate (#51)
|
||||
- A bug in `XYZToLuvWhiteRef` that only applied to very small values was fixed (#51)
|
||||
- `BlendHCL` output is clamped so that it's not invalid (#46)
|
||||
- Properly documented `DistanceCIE76` (#40)
|
||||
- Some small godoc fixes
|
||||
|
||||
|
||||
## [1.0.3] - 2019-11-11
|
||||
- Remove SQLMock dependency
|
||||
|
||||
|
||||
## [1.0.2] - 2019-04-07
|
||||
- Fixes SQLMock dependency
|
||||
|
||||
|
||||
## [1.0.1] - 2019-03-24
|
||||
- Adds support for Go Modules
|
||||
|
||||
|
||||
## [1.0.0] - 2018-05-26
|
||||
- API Breaking change in `MakeColor`: instead of `panic`ing when alpha is zero, it now returns a secondary, boolean return value indicating success. See [the color.Color interface](#the-colorcolor-interface) section and [this FAQ entry](#q-why-would-makecolor-ever-fail) for details.
|
||||
|
||||
|
||||
## [0.9.0] - 2018-05-26
|
||||
- Initial version number after having ignored versioning for a long time :)
|
7
vendor/github.com/lucasb-eyer/go-colorful/LICENSE
generated
vendored
Normal file
7
vendor/github.com/lucasb-eyer/go-colorful/LICENSE
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
Copyright (c) 2013 Lucas Beyer
|
||||
|
||||
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.
|
482
vendor/github.com/lucasb-eyer/go-colorful/README.md
generated
vendored
Normal file
482
vendor/github.com/lucasb-eyer/go-colorful/README.md
generated
vendored
Normal file
@ -0,0 +1,482 @@
|
||||
go-colorful
|
||||
===========
|
||||
|
||||
[](https://goreportcard.com/report/github.com/lucasb-eyer/go-colorful)
|
||||
|
||||
A library for playing with colors in Go. Supports Go 1.13 onwards.
|
||||
|
||||
Why?
|
||||
====
|
||||
I love games. I make games. I love detail and I get lost in detail.
|
||||
One such detail popped up during the development of [Memory Which Does Not Suck](https://github.com/lucasb-eyer/mwdns/),
|
||||
when we wanted the server to assign the players random colors. Sometimes
|
||||
two players got very similar colors, which bugged me. The very same evening,
|
||||
[I want hue](http://tools.medialab.sciences-po.fr/iwanthue/) was the top post
|
||||
on HackerNews' frontpage and showed me how to Do It Right™. Last but not
|
||||
least, there was no library for handling color spaces available in go. Colorful
|
||||
does just that and implements Go's `color.Color` interface.
|
||||
|
||||
What?
|
||||
=====
|
||||
Go-Colorful stores colors in RGB and provides methods from converting these to various color-spaces. Currently supported colorspaces are:
|
||||
|
||||
- **RGB:** All three of Red, Green and Blue in [0..1].
|
||||
- **HSL:** Hue in [0..360], Saturation and Luminance in [0..1]. For legacy reasons; please forget that it exists.
|
||||
- **HSV:** Hue in [0..360], Saturation and Value in [0..1]. You're better off using HCL, see below.
|
||||
- **Hex RGB:** The "internet" color format, as in #FF00FF.
|
||||
- **Linear RGB:** See [gamma correct rendering](http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/).
|
||||
- **CIE-XYZ:** CIE's standard color space, almost in [0..1].
|
||||
- **CIE-xyY:** encodes chromacity in x and y and luminance in Y, all in [0..1]
|
||||
- **CIE-L\*a\*b\*:** A *perceptually uniform* color space, i.e. distances are meaningful. L\* in [0..1] and a\*, b\* almost in [-1..1].
|
||||
- **CIE-L\*u\*v\*:** Very similar to CIE-L\*a\*b\*, there is [no consensus](http://en.wikipedia.org/wiki/CIELUV#Historical_background) on which one is "better".
|
||||
- **CIE-L\*C\*h° (HCL):** This is generally the [most useful](http://vis4.net/blog/posts/avoid-equidistant-hsv-colors/) one; CIE-L\*a\*b\* space in polar coordinates, i.e. a *better* HSV. H° is in [0..360], C\* almost in [-1..1] and L\* as in CIE-L\*a\*b\*.
|
||||
- **CIE LCh(uv):** Called `LuvLCh` in code, this is a cylindrical transformation of the CIE-L\*u\*v\* color space. Like HCL above: H° is in [0..360], C\* almost in [-1..1] and L\* as in CIE-L\*u\*v\*.
|
||||
- **HSLuv:** The better alternative to HSL, see [here](https://www.hsluv.org/) and [here](https://www.kuon.ch/post/2020-03-08-hsluv/). Hue in [0..360], Saturation and Luminance in [0..1].
|
||||
- **HPLuv:** A variant of HSLuv. The color space is smoother, but only pastel colors can be included. Because the valid colors are limited, it's easy to get invalid Saturation values way above 1.0, indicating the color can't be represented in HPLuv beccause it's not pastel.
|
||||
|
||||
For the colorspaces where it makes sense (XYZ, Lab, Luv, HCl), the
|
||||
[D65](http://en.wikipedia.org/wiki/Illuminant_D65) is used as reference white
|
||||
by default but methods for using your own reference white are provided.
|
||||
|
||||
A coordinate being *almost in* a range means that generally it is, but for very
|
||||
bright colors and depending on the reference white, it might overflow this
|
||||
range slightly. For example, C\* of #0000ff is 1.338.
|
||||
|
||||
Unit-tests are provided.
|
||||
|
||||
Nice, but what's it useful for?
|
||||
-------------------------------
|
||||
|
||||
- Converting color spaces. Some people like to do that.
|
||||
- Blending (interpolating) between colors in a "natural" look by using the right colorspace.
|
||||
- Generating random colors under some constraints (e.g. colors of the same shade, or shades of one color.)
|
||||
- Generating gorgeous random palettes with distinct colors of a same temperature.
|
||||
|
||||
What not (yet)?
|
||||
===============
|
||||
There are a few features which are currently missing and might be useful.
|
||||
I just haven't implemented them yet because I didn't have the need for it.
|
||||
Pull requests welcome.
|
||||
|
||||
- Sorting colors (potentially using above mentioned distances)
|
||||
|
||||
So which colorspace should I use?
|
||||
=================================
|
||||
It depends on what you want to do. I think the folks from *I want hue* are
|
||||
on-spot when they say that RGB fits to how *screens produce* color, CIE L\*a\*b\*
|
||||
fits how *humans perceive* color and HCL fits how *humans think* colors.
|
||||
|
||||
Whenever you'd use HSV, rather go for CIE-L\*C\*h°. for fixed lightness L\* and
|
||||
chroma C\* values, the hue angle h° rotates through colors of the same
|
||||
perceived brightness and intensity.
|
||||
|
||||
How?
|
||||
====
|
||||
|
||||
### Installing
|
||||
Installing the library is as easy as
|
||||
|
||||
```bash
|
||||
$ go get github.com/lucasb-eyer/go-colorful
|
||||
```
|
||||
|
||||
The package can then be used through an
|
||||
|
||||
```go
|
||||
import "github.com/lucasb-eyer/go-colorful"
|
||||
```
|
||||
|
||||
### Basic usage
|
||||
|
||||
Create a beautiful blue color using different source space:
|
||||
|
||||
```go
|
||||
// Any of the following should be the same
|
||||
c := colorful.Color{0.313725, 0.478431, 0.721569}
|
||||
c, err := colorful.Hex("#517AB8")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
c = colorful.Hsv(216.0, 0.56, 0.722)
|
||||
c = colorful.Xyz(0.189165, 0.190837, 0.480248)
|
||||
c = colorful.Xyy(0.219895, 0.221839, 0.190837)
|
||||
c = colorful.Lab(0.507850, 0.040585,-0.370945)
|
||||
c = colorful.Luv(0.507849,-0.194172,-0.567924)
|
||||
c = colorful.Hcl(276.2440, 0.373160, 0.507849)
|
||||
fmt.Printf("RGB values: %v, %v, %v", c.R, c.G, c.B)
|
||||
```
|
||||
|
||||
And then converting this color back into various color spaces:
|
||||
|
||||
```go
|
||||
hex := c.Hex()
|
||||
h, s, v := c.Hsv()
|
||||
x, y, z := c.Xyz()
|
||||
x, y, Y := c.Xyy()
|
||||
l, a, b := c.Lab()
|
||||
l, u, v := c.Luv()
|
||||
h, c, l := c.Hcl()
|
||||
```
|
||||
|
||||
Note that, because of Go's unfortunate choice of requiring an initial uppercase,
|
||||
the name of the functions relating to the xyY space are just off. If you have
|
||||
any good suggestion, please open an issue. (I don't consider XyY good.)
|
||||
|
||||
### The `color.Color` interface
|
||||
Because a `colorful.Color` implements Go's `color.Color` interface (found in the
|
||||
`image/color` package), it can be used anywhere that expects a `color.Color`.
|
||||
|
||||
Furthermore, you can convert anything that implements the `color.Color` interface
|
||||
into a `colorful.Color` using the `MakeColor` function:
|
||||
|
||||
```go
|
||||
c, ok := colorful.MakeColor(color.Gray16{12345})
|
||||
```
|
||||
|
||||
**Caveat:** Be aware that this latter conversion (using `MakeColor`) hits a
|
||||
corner-case when alpha is exactly zero. Because `color.Color` uses pre-multiplied
|
||||
alpha colors, this means the RGB values are lost (set to 0) and it's impossible
|
||||
to recover them. In such a case `MakeColor` will return `false` as its second value.
|
||||
|
||||
### Comparing colors
|
||||
In the RGB color space, the Euclidian distance between colors *doesn't* correspond
|
||||
to visual/perceptual distance. This means that two pairs of colors which have the
|
||||
same distance in RGB space can look much further apart. This is fixed by the
|
||||
CIE-L\*a\*b\*, CIE-L\*u\*v\* and CIE-L\*C\*h° color spaces.
|
||||
Thus you should only compare colors in any of these space.
|
||||
(Note that the distance in CIE-L\*a\*b\* and CIE-L\*C\*h° are the same, since it's the same space but in cylindrical coordinates)
|
||||
|
||||

|
||||
|
||||
The two colors shown on the top look much more different than the two shown on
|
||||
the bottom. Still, in RGB space, their distance is the same.
|
||||
Here is a little example program which shows the distances between the top two
|
||||
and bottom two colors in RGB, CIE-L\*a\*b\* and CIE-L\*u\*v\* space. You can find it in `doc/colordist/colordist.go`.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import "github.com/lucasb-eyer/go-colorful"
|
||||
|
||||
func main() {
|
||||
c1a := colorful.Color{150.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0}
|
||||
c1b := colorful.Color{53.0 / 255.0, 10.0 / 255.0, 150.0 / 255.0}
|
||||
c2a := colorful.Color{10.0 / 255.0, 150.0 / 255.0, 50.0 / 255.0}
|
||||
c2b := colorful.Color{99.9 / 255.0, 150.0 / 255.0, 10.0 / 255.0}
|
||||
|
||||
fmt.Printf("DistanceRgb: c1: %v\tand c2: %v\n", c1a.DistanceRgb(c1b), c2a.DistanceRgb(c2b))
|
||||
fmt.Printf("DistanceLab: c1: %v\tand c2: %v\n", c1a.DistanceLab(c1b), c2a.DistanceLab(c2b))
|
||||
fmt.Printf("DistanceLuv: c1: %v\tand c2: %v\n", c1a.DistanceLuv(c1b), c2a.DistanceLuv(c2b))
|
||||
fmt.Printf("DistanceCIE76: c1: %v\tand c2: %v\n", c1a.DistanceCIE76(c1b), c2a.DistanceCIE76(c2b))
|
||||
fmt.Printf("DistanceCIE94: c1: %v\tand c2: %v\n", c1a.DistanceCIE94(c1b), c2a.DistanceCIE94(c2b))
|
||||
fmt.Printf("DistanceCIEDE2000: c1: %v\tand c2: %v\n", c1a.DistanceCIEDE2000(c1b), c2a.DistanceCIEDE2000(c2b))
|
||||
}
|
||||
```
|
||||
|
||||
Running the above program shows that you should always prefer any of the CIE distances:
|
||||
|
||||
```bash
|
||||
$ go run colordist.go
|
||||
DistanceRgb: c1: 0.3803921568627451 and c2: 0.3858713931171159
|
||||
DistanceLab: c1: 0.32048458312798056 and c2: 0.24397151758565272
|
||||
DistanceLuv: c1: 0.5134369614199698 and c2: 0.2568692839860636
|
||||
DistanceCIE76: c1: 0.32048458312798056 and c2: 0.24397151758565272
|
||||
DistanceCIE94: c1: 0.19799168128511324 and c2: 0.12207136371167401
|
||||
DistanceCIEDE2000: c1: 0.17274551120971166 and c2: 0.10665210031428465
|
||||
```
|
||||
|
||||
It also shows that `DistanceLab` is more formally known as `DistanceCIE76` and
|
||||
has been superseded by the slightly more accurate, but much more expensive
|
||||
`DistanceCIE94` and `DistanceCIEDE2000`.
|
||||
|
||||
Note that `AlmostEqualRgb` is provided mainly for (unit-)testing purposes. Use
|
||||
it only if you really know what you're doing. It will eat your cat.
|
||||
|
||||
### Blending colors
|
||||
Blending is highly connected to distance, since it basically "walks through" the
|
||||
colorspace thus, if the colorspace maps distances well, the walk is "smooth".
|
||||
|
||||
Colorful comes with blending functions in RGB, HSV and any of the LAB spaces.
|
||||
Of course, you'd rather want to use the blending functions of the LAB spaces since
|
||||
these spaces map distances well but, just in case, here is an example showing
|
||||
you how the blendings (`#fdffcc` to `#242a42`) are done in the various spaces:
|
||||
|
||||

|
||||
|
||||
What you see is that HSV is really bad: it adds some green, which is not present
|
||||
in the original colors at all! RGB is much better, but it stays light a little
|
||||
too long. LUV and LAB both hit the right lightness but LAB has a little more
|
||||
color. HCL works in the same vein as HSV (both cylindrical interpolations) but
|
||||
it does it right in that there is no green appearing and the lighthness changes
|
||||
in a linear manner.
|
||||
|
||||
While this seems all good, you need to know one thing: When interpolating in any
|
||||
of the CIE color spaces, you might get invalid RGB colors! This is important if
|
||||
the starting and ending colors are user-input or random. An example of where this
|
||||
happens is when blending between `#eeef61` and `#1e3140`:
|
||||
|
||||

|
||||
|
||||
You can test whether a color is a valid RGB color by calling the `IsValid` method
|
||||
and indeed, calling IsValid will return false for the redish colors on the bottom.
|
||||
One way to "fix" this is to get a valid color close to the invalid one by calling
|
||||
`Clamped`, which always returns a nearby valid color. Doing this, we get the
|
||||
following result, which is satisfactory:
|
||||
|
||||

|
||||
|
||||
The following is the code creating the above three images; it can be found in `doc/colorblend/colorblend.go`
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
import "github.com/lucasb-eyer/go-colorful"
|
||||
import "image"
|
||||
import "image/draw"
|
||||
import "image/png"
|
||||
import "os"
|
||||
|
||||
func main() {
|
||||
blocks := 10
|
||||
blockw := 40
|
||||
img := image.NewRGBA(image.Rect(0,0,blocks*blockw,200))
|
||||
|
||||
c1, _ := colorful.Hex("#fdffcc")
|
||||
c2, _ := colorful.Hex("#242a42")
|
||||
|
||||
// Use these colors to get invalid RGB in the gradient.
|
||||
//c1, _ := colorful.Hex("#EEEF61")
|
||||
//c2, _ := colorful.Hex("#1E3140")
|
||||
|
||||
for i := 0 ; i < blocks ; i++ {
|
||||
draw.Draw(img, image.Rect(i*blockw, 0,(i+1)*blockw, 40), &image.Uniform{c1.BlendHsv(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
|
||||
draw.Draw(img, image.Rect(i*blockw, 40,(i+1)*blockw, 80), &image.Uniform{c1.BlendLuv(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
|
||||
draw.Draw(img, image.Rect(i*blockw, 80,(i+1)*blockw,120), &image.Uniform{c1.BlendRgb(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
|
||||
draw.Draw(img, image.Rect(i*blockw,120,(i+1)*blockw,160), &image.Uniform{c1.BlendLab(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
|
||||
draw.Draw(img, image.Rect(i*blockw,160,(i+1)*blockw,200), &image.Uniform{c1.BlendHcl(c2, float64(i)/float64(blocks-1))}, image.Point{}, draw.Src)
|
||||
|
||||
// This can be used to "fix" invalid colors in the gradient.
|
||||
//draw.Draw(img, image.Rect(i*blockw,160,(i+1)*blockw,200), &image.Uniform{c1.BlendHcl(c2, float64(i)/float64(blocks-1)).Clamped()}, image.Point{}, draw.Src)
|
||||
}
|
||||
|
||||
toimg, err := os.Create("colorblend.png")
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %v", err)
|
||||
return
|
||||
}
|
||||
defer toimg.Close()
|
||||
|
||||
png.Encode(toimg, img)
|
||||
}
|
||||
```
|
||||
|
||||
#### Generating color gradients
|
||||
A very common reason to blend colors is creating gradients. There is an example
|
||||
program in [doc/gradientgen.go](doc/gradientgen/gradientgen.go); it doesn't use any API
|
||||
which hasn't been used in the previous example code, so I won't bother pasting
|
||||
the code in here. Just look at that gorgeous gradient it generated in HCL space:
|
||||
|
||||

|
||||
|
||||
### Getting random colors
|
||||
It is sometimes necessary to generate random colors. You could simply do this
|
||||
on your own by generating colors with random values. By restricting the random
|
||||
values to a range smaller than [0..1] and using a space such as CIE-H\*C\*l° or
|
||||
HSV, you can generate both random shades of a color or random colors of a
|
||||
lightness:
|
||||
|
||||
```go
|
||||
random_blue := colorful.Hcl(180.0+rand.Float64()*50.0, 0.2+rand.Float64()*0.8, 0.3+rand.Float64()*0.7)
|
||||
random_dark := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), rand.Float64()*0.4)
|
||||
random_light := colorful.Hcl(rand.Float64()*360.0, rand.Float64(), 0.6+rand.Float64()*0.4)
|
||||
```
|
||||
|
||||
Since getting random "warm" and "happy" colors is quite a common task, there
|
||||
are some helper functions:
|
||||
|
||||
```go
|
||||
colorful.WarmColor()
|
||||
colorful.HappyColor()
|
||||
colorful.FastWarmColor()
|
||||
colorful.FastHappyColor()
|
||||
```
|
||||
|
||||
The ones prefixed by `Fast` are faster but less coherent since they use the HSV
|
||||
space as opposed to the regular ones which use CIE-L\*C\*h° space. The
|
||||
following picture shows the warm colors in the top two rows and happy colors
|
||||
in the bottom two rows. Within these, the first is the regular one and the
|
||||
second is the fast one.
|
||||
|
||||

|
||||
|
||||
Don't forget to initialize the random seed! You can see the code used for
|
||||
generating this picture in `doc/colorgens/colorgens.go`.
|
||||
|
||||
### Getting random palettes
|
||||
As soon as you need to generate more than one random color, you probably want
|
||||
them to be distinguishible. Playing against an opponent which has almost the
|
||||
same blue as I do is not fun. This is where random palettes can help.
|
||||
|
||||
These palettes are generated using an algorithm which ensures that all colors
|
||||
on the palette are as distinguishible as possible. Again, there is a `Fast`
|
||||
method which works in HSV and is less perceptually uniform and a non-`Fast`
|
||||
method which works in CIE spaces. For more theory on `SoftPalette`, check out
|
||||
[I want hue](http://tools.medialab.sciences-po.fr/iwanthue/theory.php). Yet
|
||||
again, there is a `Happy` and a `Warm` version, which do what you expect, but
|
||||
now there is an additional `Soft` version, which is more configurable: you can
|
||||
give a constraint on the color space in order to get colors within a certain *feel*.
|
||||
|
||||
Let's start with the simple methods first, all they take is the amount of
|
||||
colors to generate, which could, for example, be the player count. They return
|
||||
an array of `colorful.Color` objects:
|
||||
|
||||
```go
|
||||
pal1, err1 := colorful.WarmPalette(10)
|
||||
pal2 := colorful.FastWarmPalette(10)
|
||||
pal3, err3 := colorful.HappyPalette(10)
|
||||
pal4 := colorful.FastHappyPalette(10)
|
||||
pal5, err5 := colorful.SoftPalette(10)
|
||||
```
|
||||
|
||||
Note that the non-fast methods *may* fail if you ask for way too many colors.
|
||||
Let's move on to the advanced one, namely `SoftPaletteEx`. Besides the color
|
||||
count, this function takes a `SoftPaletteSettings` object as argument. The
|
||||
interesting part here is its `CheckColor` member, which is a boolean function
|
||||
taking three floating points as arguments: `l`, `a` and `b`. This function
|
||||
should return `true` for colors which lie within the region you want and `false`
|
||||
otherwise. The other members are `Iteration`, which should be within [5..100]
|
||||
where higher means slower but more exact palette, and `ManySamples` which you
|
||||
should set to `true` in case your `CheckColor` constraint rejects a large part
|
||||
of the color space.
|
||||
|
||||
For example, to create a palette of 10 brownish colors, you'd call it like this:
|
||||
|
||||
```go
|
||||
func isbrowny(l, a, b float64) bool {
|
||||
h, c, L := colorful.LabToHcl(l, a, b)
|
||||
return 10.0 < h && h < 50.0 && 0.1 < c && c < 0.5 && L < 0.5
|
||||
}
|
||||
// Since the above function is pretty restrictive, we set ManySamples to true.
|
||||
brownies := colorful.SoftPaletteEx(10, colorful.SoftPaletteSettings{isbrowny, 50, true})
|
||||
```
|
||||
|
||||
The following picture shows the palettes generated by all of these methods
|
||||
(sourcecode in `doc/palettegens/palettegens.go`), in the order they were presented, i.e.
|
||||
from top to bottom: `Warm`, `FastWarm`, `Happy`, `FastHappy`, `Soft`,
|
||||
`SoftEx(isbrowny)`. All of them contain some randomness, so YMMV.
|
||||
|
||||

|
||||
|
||||
Again, the code used for generating the above image is available as [doc/palettegens/palettegens.go](https://github.com/lucasb-eyer/go-colorful/blob/master/doc/palettegens/palettegens.go).
|
||||
|
||||
### Sorting colors
|
||||
TODO: Sort using dist fn.
|
||||
|
||||
### Using linear RGB for computations
|
||||
There are two methods for transforming RGB<->Linear RGB: a fast and almost precise one,
|
||||
and a slow and precise one.
|
||||
|
||||
```go
|
||||
r, g, b := colorful.Hex("#FF0000").FastLinearRgb()
|
||||
```
|
||||
|
||||
TODO: describe some more.
|
||||
|
||||
### Want to use some other reference point?
|
||||
|
||||
```go
|
||||
c := colorful.LabWhiteRef(0.507850, 0.040585,-0.370945, colorful.D50)
|
||||
l, a, b := c.LabWhiteRef(colorful.D50)
|
||||
```
|
||||
|
||||
### Reading and writing colors from databases
|
||||
|
||||
The type `HexColor` makes it easy to store colors as strings in a database. It
|
||||
implements the [https://godoc.org/database/sql#Scanner](database/sql.Scanner)
|
||||
and [database/sql/driver.Value](https://godoc.org/database/sql/driver.Value)
|
||||
interfaces which provide automatic type conversion.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
var hc HexColor
|
||||
_, err := db.QueryRow("SELECT '#ff0000';").Scan(&hc)
|
||||
// hc == HexColor{R: 1, G: 0, B: 0}; err == nil
|
||||
```
|
||||
|
||||
FAQ
|
||||
===
|
||||
|
||||
### Q: I get all f!@#ed up values! Your library sucks!
|
||||
A: You probably provided values in the wrong range. For example, RGB values are
|
||||
expected to reside between 0 and 1, *not* between 0 and 255. Normalize your colors.
|
||||
|
||||
### Q: Lab/Luv/HCl seem broken! Your library sucks!
|
||||
They look like this:
|
||||
|
||||
<img height="150" src="https://user-images.githubusercontent.com/3779568/28646900-6548040c-7264-11e7-8f12-81097a97c260.png">
|
||||
|
||||
A: You're likely trying to generate and display colors that can't be represented by RGB,
|
||||
and thus monitors. When you're trying to convert, say, `HCL(190.0, 1.0, 1.0).RGB255()`,
|
||||
you're asking for RGB values of `(-2105.254 300.680 286.185)`, which clearly don't exist,
|
||||
and the `RGB255` function just casts these numbers to `uint8`, creating wrap-around and
|
||||
what looks like a completely broken gradient. What you want to do, is either use more
|
||||
reasonable values of colors which actually exist in RGB, or just `Clamp()` the resulting
|
||||
color to its nearest existing one, living with the consequences:
|
||||
`HCL(190.0, 1.0, 1.0).Clamp().RGB255()`. It will look something like this:
|
||||
|
||||
<img height="150" src="https://user-images.githubusercontent.com/1476029/29596343-9a8c62c6-8771-11e7-9026-b8eb8852cc4a.png">
|
||||
|
||||
[Here's an issue going in-depth about this](https://github.com/lucasb-eyer/go-colorful/issues/14),
|
||||
as well as [my answer](https://github.com/lucasb-eyer/go-colorful/issues/14#issuecomment-324205385),
|
||||
both with code and pretty pictures. Also note that this was somewhat covered above in the
|
||||
["Blending colors" section](https://github.com/lucasb-eyer/go-colorful#blending-colors).
|
||||
|
||||
### Q: In a tight loop, conversion to Lab/Luv/HCl/... are slooooow!
|
||||
A: Yes, they are.
|
||||
This library aims for correctness, readability, and modularity; it wasn't written with speed in mind.
|
||||
A large part of the slowness comes from these conversions going through `LinearRgb` which uses powers.
|
||||
I implemented a fast approximation to `LinearRgb` called `FastLinearRgb` by using Taylor approximations.
|
||||
The approximation is roughly 5x faster and precise up to roughly 0.5%,
|
||||
the major caveat being that if the input values are outside the range 0-1, accuracy drops dramatically.
|
||||
You can use these in your conversions as follows:
|
||||
|
||||
```go
|
||||
col := // Get your color somehow
|
||||
l, a, b := XyzToLab(LinearRgbToXyz(col.LinearRgb()))
|
||||
```
|
||||
|
||||
If you need faster versions of `Distance*` and `Blend*` that make use of this fast approximation,
|
||||
feel free to implement them and open a pull-request, I'll happily accept.
|
||||
|
||||
The derivation of these functions can be followed in [this Jupyter notebook](doc/LinearRGB Approximations.ipynb).
|
||||
Here's the main figure showing the approximation quality:
|
||||
|
||||

|
||||
|
||||
More speed could be gained by using SIMD instructions in many places.
|
||||
You can also get more speed for specific conversions by approximating the full conversion function,
|
||||
but that is outside the scope of this library.
|
||||
Thanks to [@ZirconiumX](https://github.com/ZirconiumX) for starting this investigation,
|
||||
see [issue #18](https://github.com/lucasb-eyer/go-colorful/issues/18) for details.
|
||||
|
||||
### Q: Why would `MakeColor` ever fail!?
|
||||
A: `MakeColor` fails when the alpha channel is zero. In that case, the
|
||||
conversion is undefined. See [issue 21](https://github.com/lucasb-eyer/go-colorful/issues/21)
|
||||
as well as the short caveat note in the ["The `color.Color` interface"](README.md#the-colorcolor-interface)
|
||||
section above.
|
||||
|
||||
Who?
|
||||
====
|
||||
|
||||
This library was developed by Lucas Beyer with contributions from
|
||||
Bastien Dejean (@baskerville), Phil Kulak (@pkulak) and Christian Muehlhaeuser (@muesli).
|
||||
|
||||
It is now maintained by makeworld (@makeworld-the-better-one).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
This repo is under the MIT license, see [LICENSE](LICENSE) for details.
|
55
vendor/github.com/lucasb-eyer/go-colorful/colorgens.go
generated
vendored
Normal file
55
vendor/github.com/lucasb-eyer/go-colorful/colorgens.go
generated
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
// Various ways to generate single random colors
|
||||
|
||||
package colorful
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// Creates a random dark, "warm" color through a restricted HSV space.
|
||||
func FastWarmColor() Color {
|
||||
return Hsv(
|
||||
rand.Float64()*360.0,
|
||||
0.5+rand.Float64()*0.3,
|
||||
0.3+rand.Float64()*0.3)
|
||||
}
|
||||
|
||||
// Creates a random dark, "warm" color through restricted HCL space.
|
||||
// This is slower than FastWarmColor but will likely give you colors which have
|
||||
// the same "warmness" if you run it many times.
|
||||
func WarmColor() (c Color) {
|
||||
for c = randomWarm(); !c.IsValid(); c = randomWarm() {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func randomWarm() Color {
|
||||
return Hcl(
|
||||
rand.Float64()*360.0,
|
||||
0.1+rand.Float64()*0.3,
|
||||
0.2+rand.Float64()*0.3)
|
||||
}
|
||||
|
||||
// Creates a random bright, "pimpy" color through a restricted HSV space.
|
||||
func FastHappyColor() Color {
|
||||
return Hsv(
|
||||
rand.Float64()*360.0,
|
||||
0.7+rand.Float64()*0.3,
|
||||
0.6+rand.Float64()*0.3)
|
||||
}
|
||||
|
||||
// Creates a random bright, "pimpy" color through restricted HCL space.
|
||||
// This is slower than FastHappyColor but will likely give you colors which
|
||||
// have the same "brightness" if you run it many times.
|
||||
func HappyColor() (c Color) {
|
||||
for c = randomPimp(); !c.IsValid(); c = randomPimp() {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func randomPimp() Color {
|
||||
return Hcl(
|
||||
rand.Float64()*360.0,
|
||||
0.5+rand.Float64()*0.3,
|
||||
0.5+rand.Float64()*0.3)
|
||||
}
|
979
vendor/github.com/lucasb-eyer/go-colorful/colors.go
generated
vendored
Normal file
979
vendor/github.com/lucasb-eyer/go-colorful/colors.go
generated
vendored
Normal file
@ -0,0 +1,979 @@
|
||||
// The colorful package provides all kinds of functions for working with colors.
|
||||
package colorful
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"image/color"
|
||||
"math"
|
||||
)
|
||||
|
||||
// A color is stored internally using sRGB (standard RGB) values in the range 0-1
|
||||
type Color struct {
|
||||
R, G, B float64
|
||||
}
|
||||
|
||||
// Implement the Go color.Color interface.
|
||||
func (col Color) RGBA() (r, g, b, a uint32) {
|
||||
r = uint32(col.R*65535.0 + 0.5)
|
||||
g = uint32(col.G*65535.0 + 0.5)
|
||||
b = uint32(col.B*65535.0 + 0.5)
|
||||
a = 0xFFFF
|
||||
return
|
||||
}
|
||||
|
||||
// Constructs a colorful.Color from something implementing color.Color
|
||||
func MakeColor(col color.Color) (Color, bool) {
|
||||
r, g, b, a := col.RGBA()
|
||||
if a == 0 {
|
||||
return Color{0, 0, 0}, false
|
||||
}
|
||||
|
||||
// Since color.Color is alpha pre-multiplied, we need to divide the
|
||||
// RGB values by alpha again in order to get back the original RGB.
|
||||
r *= 0xffff
|
||||
r /= a
|
||||
g *= 0xffff
|
||||
g /= a
|
||||
b *= 0xffff
|
||||
b /= a
|
||||
|
||||
return Color{float64(r) / 65535.0, float64(g) / 65535.0, float64(b) / 65535.0}, true
|
||||
}
|
||||
|
||||
// Might come in handy sometimes to reduce boilerplate code.
|
||||
func (col Color) RGB255() (r, g, b uint8) {
|
||||
r = uint8(col.R*255.0 + 0.5)
|
||||
g = uint8(col.G*255.0 + 0.5)
|
||||
b = uint8(col.B*255.0 + 0.5)
|
||||
return
|
||||
}
|
||||
|
||||
// Used to simplify HSLuv testing.
|
||||
func (col Color) values() (float64, float64, float64) {
|
||||
return col.R, col.G, col.B
|
||||
}
|
||||
|
||||
// This is the tolerance used when comparing colors using AlmostEqualRgb.
|
||||
const Delta = 1.0 / 255.0
|
||||
|
||||
// This is the default reference white point.
|
||||
var D65 = [3]float64{0.95047, 1.00000, 1.08883}
|
||||
|
||||
// And another one.
|
||||
var D50 = [3]float64{0.96422, 1.00000, 0.82521}
|
||||
|
||||
// Checks whether the color exists in RGB space, i.e. all values are in [0..1]
|
||||
func (c Color) IsValid() bool {
|
||||
return 0.0 <= c.R && c.R <= 1.0 &&
|
||||
0.0 <= c.G && c.G <= 1.0 &&
|
||||
0.0 <= c.B && c.B <= 1.0
|
||||
}
|
||||
|
||||
// clamp01 clamps from 0 to 1.
|
||||
func clamp01(v float64) float64 {
|
||||
return math.Max(0.0, math.Min(v, 1.0))
|
||||
}
|
||||
|
||||
// Returns Clamps the color into valid range, clamping each value to [0..1]
|
||||
// If the color is valid already, this is a no-op.
|
||||
func (c Color) Clamped() Color {
|
||||
return Color{clamp01(c.R), clamp01(c.G), clamp01(c.B)}
|
||||
}
|
||||
|
||||
func sq(v float64) float64 {
|
||||
return v * v
|
||||
}
|
||||
|
||||
func cub(v float64) float64 {
|
||||
return v * v * v
|
||||
}
|
||||
|
||||
// DistanceRgb computes the distance between two colors in RGB space.
|
||||
// This is not a good measure! Rather do it in Lab space.
|
||||
func (c1 Color) DistanceRgb(c2 Color) float64 {
|
||||
return math.Sqrt(sq(c1.R-c2.R) + sq(c1.G-c2.G) + sq(c1.B-c2.B))
|
||||
}
|
||||
|
||||
// DistanceLinearRGB computes the distance between two colors in linear RGB
|
||||
// space. This is not useful for measuring how humans perceive color, but
|
||||
// might be useful for other things, like dithering.
|
||||
func (c1 Color) DistanceLinearRGB(c2 Color) float64 {
|
||||
r1, g1, b1 := c1.LinearRgb()
|
||||
r2, g2, b2 := c2.LinearRgb()
|
||||
return math.Sqrt(sq(r1-r2) + sq(g1-g2) + sq(b1-b2))
|
||||
}
|
||||
|
||||
// Check for equality between colors within the tolerance Delta (1/255).
|
||||
func (c1 Color) AlmostEqualRgb(c2 Color) bool {
|
||||
return math.Abs(c1.R-c2.R)+
|
||||
math.Abs(c1.G-c2.G)+
|
||||
math.Abs(c1.B-c2.B) < 3.0*Delta
|
||||
}
|
||||
|
||||
// You don't really want to use this, do you? Go for BlendLab, BlendLuv or BlendHcl.
|
||||
func (c1 Color) BlendRgb(c2 Color, t float64) Color {
|
||||
return Color{c1.R + t*(c2.R-c1.R),
|
||||
c1.G + t*(c2.G-c1.G),
|
||||
c1.B + t*(c2.B-c1.B)}
|
||||
}
|
||||
|
||||
// Utility used by Hxx color-spaces for interpolating between two angles in [0,360].
|
||||
func interp_angle(a0, a1, t float64) float64 {
|
||||
// Based on the answer here: http://stackoverflow.com/a/14498790/2366315
|
||||
// With potential proof that it works here: http://math.stackexchange.com/a/2144499
|
||||
delta := math.Mod(math.Mod(a1-a0, 360.0)+540, 360.0) - 180.0
|
||||
return math.Mod(a0+t*delta+360.0, 360.0)
|
||||
}
|
||||
|
||||
/// HSV ///
|
||||
///////////
|
||||
// From http://en.wikipedia.org/wiki/HSL_and_HSV
|
||||
// Note that h is in [0..360] and s,v in [0..1]
|
||||
|
||||
// Hsv returns the Hue [0..360], Saturation and Value [0..1] of the color.
|
||||
func (col Color) Hsv() (h, s, v float64) {
|
||||
min := math.Min(math.Min(col.R, col.G), col.B)
|
||||
v = math.Max(math.Max(col.R, col.G), col.B)
|
||||
C := v - min
|
||||
|
||||
s = 0.0
|
||||
if v != 0.0 {
|
||||
s = C / v
|
||||
}
|
||||
|
||||
h = 0.0 // We use 0 instead of undefined as in wp.
|
||||
if min != v {
|
||||
if v == col.R {
|
||||
h = math.Mod((col.G-col.B)/C, 6.0)
|
||||
}
|
||||
if v == col.G {
|
||||
h = (col.B-col.R)/C + 2.0
|
||||
}
|
||||
if v == col.B {
|
||||
h = (col.R-col.G)/C + 4.0
|
||||
}
|
||||
h *= 60.0
|
||||
if h < 0.0 {
|
||||
h += 360.0
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Hsv creates a new Color given a Hue in [0..360], a Saturation and a Value in [0..1]
|
||||
func Hsv(H, S, V float64) Color {
|
||||
Hp := H / 60.0
|
||||
C := V * S
|
||||
X := C * (1.0 - math.Abs(math.Mod(Hp, 2.0)-1.0))
|
||||
|
||||
m := V - C
|
||||
r, g, b := 0.0, 0.0, 0.0
|
||||
|
||||
switch {
|
||||
case 0.0 <= Hp && Hp < 1.0:
|
||||
r = C
|
||||
g = X
|
||||
case 1.0 <= Hp && Hp < 2.0:
|
||||
r = X
|
||||
g = C
|
||||
case 2.0 <= Hp && Hp < 3.0:
|
||||
g = C
|
||||
b = X
|
||||
case 3.0 <= Hp && Hp < 4.0:
|
||||
g = X
|
||||
b = C
|
||||
case 4.0 <= Hp && Hp < 5.0:
|
||||
r = X
|
||||
b = C
|
||||
case 5.0 <= Hp && Hp < 6.0:
|
||||
r = C
|
||||
b = X
|
||||
}
|
||||
|
||||
return Color{m + r, m + g, m + b}
|
||||
}
|
||||
|
||||
// You don't really want to use this, do you? Go for BlendLab, BlendLuv or BlendHcl.
|
||||
func (c1 Color) BlendHsv(c2 Color, t float64) Color {
|
||||
h1, s1, v1 := c1.Hsv()
|
||||
h2, s2, v2 := c2.Hsv()
|
||||
|
||||
// We know that h are both in [0..360]
|
||||
return Hsv(interp_angle(h1, h2, t), s1+t*(s2-s1), v1+t*(v2-v1))
|
||||
}
|
||||
|
||||
/// HSL ///
|
||||
///////////
|
||||
|
||||
// Hsl returns the Hue [0..360], Saturation [0..1], and Luminance (lightness) [0..1] of the color.
|
||||
func (col Color) Hsl() (h, s, l float64) {
|
||||
min := math.Min(math.Min(col.R, col.G), col.B)
|
||||
max := math.Max(math.Max(col.R, col.G), col.B)
|
||||
|
||||
l = (max + min) / 2
|
||||
|
||||
if min == max {
|
||||
s = 0
|
||||
h = 0
|
||||
} else {
|
||||
if l < 0.5 {
|
||||
s = (max - min) / (max + min)
|
||||
} else {
|
||||
s = (max - min) / (2.0 - max - min)
|
||||
}
|
||||
|
||||
if max == col.R {
|
||||
h = (col.G - col.B) / (max - min)
|
||||
} else if max == col.G {
|
||||
h = 2.0 + (col.B-col.R)/(max-min)
|
||||
} else {
|
||||
h = 4.0 + (col.R-col.G)/(max-min)
|
||||
}
|
||||
|
||||
h *= 60
|
||||
|
||||
if h < 0 {
|
||||
h += 360
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Hsl creates a new Color given a Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1]
|
||||
func Hsl(h, s, l float64) Color {
|
||||
if s == 0 {
|
||||
return Color{l, l, l}
|
||||
}
|
||||
|
||||
var r, g, b float64
|
||||
var t1 float64
|
||||
var t2 float64
|
||||
var tr float64
|
||||
var tg float64
|
||||
var tb float64
|
||||
|
||||
if l < 0.5 {
|
||||
t1 = l * (1.0 + s)
|
||||
} else {
|
||||
t1 = l + s - l*s
|
||||
}
|
||||
|
||||
t2 = 2*l - t1
|
||||
h /= 360
|
||||
tr = h + 1.0/3.0
|
||||
tg = h
|
||||
tb = h - 1.0/3.0
|
||||
|
||||
if tr < 0 {
|
||||
tr++
|
||||
}
|
||||
if tr > 1 {
|
||||
tr--
|
||||
}
|
||||
if tg < 0 {
|
||||
tg++
|
||||
}
|
||||
if tg > 1 {
|
||||
tg--
|
||||
}
|
||||
if tb < 0 {
|
||||
tb++
|
||||
}
|
||||
if tb > 1 {
|
||||
tb--
|
||||
}
|
||||
|
||||
// Red
|
||||
if 6*tr < 1 {
|
||||
r = t2 + (t1-t2)*6*tr
|
||||
} else if 2*tr < 1 {
|
||||
r = t1
|
||||
} else if 3*tr < 2 {
|
||||
r = t2 + (t1-t2)*(2.0/3.0-tr)*6
|
||||
} else {
|
||||
r = t2
|
||||
}
|
||||
|
||||
// Green
|
||||
if 6*tg < 1 {
|
||||
g = t2 + (t1-t2)*6*tg
|
||||
} else if 2*tg < 1 {
|
||||
g = t1
|
||||
} else if 3*tg < 2 {
|
||||
g = t2 + (t1-t2)*(2.0/3.0-tg)*6
|
||||
} else {
|
||||
g = t2
|
||||
}
|
||||
|
||||
// Blue
|
||||
if 6*tb < 1 {
|
||||
b = t2 + (t1-t2)*6*tb
|
||||
} else if 2*tb < 1 {
|
||||
b = t1
|
||||
} else if 3*tb < 2 {
|
||||
b = t2 + (t1-t2)*(2.0/3.0-tb)*6
|
||||
} else {
|
||||
b = t2
|
||||
}
|
||||
|
||||
return Color{r, g, b}
|
||||
}
|
||||
|
||||
/// Hex ///
|
||||
///////////
|
||||
|
||||
// Hex returns the hex "html" representation of the color, as in #ff0080.
|
||||
func (col Color) Hex() string {
|
||||
// Add 0.5 for rounding
|
||||
return fmt.Sprintf("#%02x%02x%02x", uint8(col.R*255.0+0.5), uint8(col.G*255.0+0.5), uint8(col.B*255.0+0.5))
|
||||
}
|
||||
|
||||
// Hex parses a "html" hex color-string, either in the 3 "#f0c" or 6 "#ff1034" digits form.
|
||||
func Hex(scol string) (Color, error) {
|
||||
format := "#%02x%02x%02x"
|
||||
factor := 1.0 / 255.0
|
||||
if len(scol) == 4 {
|
||||
format = "#%1x%1x%1x"
|
||||
factor = 1.0 / 15.0
|
||||
}
|
||||
|
||||
var r, g, b uint8
|
||||
n, err := fmt.Sscanf(scol, format, &r, &g, &b)
|
||||
if err != nil {
|
||||
return Color{}, err
|
||||
}
|
||||
if n != 3 {
|
||||
return Color{}, fmt.Errorf("color: %v is not a hex-color", scol)
|
||||
}
|
||||
|
||||
return Color{float64(r) * factor, float64(g) * factor, float64(b) * factor}, nil
|
||||
}
|
||||
|
||||
/// Linear ///
|
||||
//////////////
|
||||
// http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/
|
||||
// http://www.brucelindbloom.com/Eqn_RGB_to_XYZ.html
|
||||
|
||||
func linearize(v float64) float64 {
|
||||
if v <= 0.04045 {
|
||||
return v / 12.92
|
||||
}
|
||||
return math.Pow((v+0.055)/1.055, 2.4)
|
||||
}
|
||||
|
||||
// LinearRgb converts the color into the linear RGB space (see http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/).
|
||||
func (col Color) LinearRgb() (r, g, b float64) {
|
||||
r = linearize(col.R)
|
||||
g = linearize(col.G)
|
||||
b = linearize(col.B)
|
||||
return
|
||||
}
|
||||
|
||||
// A much faster and still quite precise linearization using a 6th-order Taylor approximation.
|
||||
// See the accompanying Jupyter notebook for derivation of the constants.
|
||||
func linearize_fast(v float64) float64 {
|
||||
v1 := v - 0.5
|
||||
v2 := v1 * v1
|
||||
v3 := v2 * v1
|
||||
v4 := v2 * v2
|
||||
//v5 := v3*v2
|
||||
return -0.248750514614486 + 0.925583310193438*v + 1.16740237321695*v2 + 0.280457026598666*v3 - 0.0757991963780179*v4 //+ 0.0437040411548932*v5
|
||||
}
|
||||
|
||||
// FastLinearRgb is much faster than and almost as accurate as LinearRgb.
|
||||
// BUT it is important to NOTE that they only produce good results for valid colors r,g,b in [0,1].
|
||||
func (col Color) FastLinearRgb() (r, g, b float64) {
|
||||
r = linearize_fast(col.R)
|
||||
g = linearize_fast(col.G)
|
||||
b = linearize_fast(col.B)
|
||||
return
|
||||
}
|
||||
|
||||
func delinearize(v float64) float64 {
|
||||
if v <= 0.0031308 {
|
||||
return 12.92 * v
|
||||
}
|
||||
return 1.055*math.Pow(v, 1.0/2.4) - 0.055
|
||||
}
|
||||
|
||||
// LinearRgb creates an sRGB color out of the given linear RGB color (see http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/).
|
||||
func LinearRgb(r, g, b float64) Color {
|
||||
return Color{delinearize(r), delinearize(g), delinearize(b)}
|
||||
}
|
||||
|
||||
func delinearize_fast(v float64) float64 {
|
||||
// This function (fractional root) is much harder to linearize, so we need to split.
|
||||
if v > 0.2 {
|
||||
v1 := v - 0.6
|
||||
v2 := v1 * v1
|
||||
v3 := v2 * v1
|
||||
v4 := v2 * v2
|
||||
v5 := v3 * v2
|
||||
return 0.442430344268235 + 0.592178981271708*v - 0.287864782562636*v2 + 0.253214392068985*v3 - 0.272557158129811*v4 + 0.325554383321718*v5
|
||||
} else if v > 0.03 {
|
||||
v1 := v - 0.115
|
||||
v2 := v1 * v1
|
||||
v3 := v2 * v1
|
||||
v4 := v2 * v2
|
||||
v5 := v3 * v2
|
||||
return 0.194915592891669 + 1.55227076330229*v - 3.93691860257828*v2 + 18.0679839248761*v3 - 101.468750302746*v4 + 632.341487393927*v5
|
||||
} else {
|
||||
v1 := v - 0.015
|
||||
v2 := v1 * v1
|
||||
v3 := v2 * v1
|
||||
v4 := v2 * v2
|
||||
v5 := v3 * v2
|
||||
// You can clearly see from the involved constants that the low-end is highly nonlinear.
|
||||
return 0.0519565234928877 + 5.09316778537561*v - 99.0338180489702*v2 + 3484.52322764895*v3 - 150028.083412663*v4 + 7168008.42971613*v5
|
||||
}
|
||||
}
|
||||
|
||||
// FastLinearRgb is much faster than and almost as accurate as LinearRgb.
|
||||
// BUT it is important to NOTE that they only produce good results for valid inputs r,g,b in [0,1].
|
||||
func FastLinearRgb(r, g, b float64) Color {
|
||||
return Color{delinearize_fast(r), delinearize_fast(g), delinearize_fast(b)}
|
||||
}
|
||||
|
||||
// XyzToLinearRgb converts from CIE XYZ-space to Linear RGB space.
|
||||
func XyzToLinearRgb(x, y, z float64) (r, g, b float64) {
|
||||
r = 3.2409699419045214*x - 1.5373831775700935*y - 0.49861076029300328*z
|
||||
g = -0.96924363628087983*x + 1.8759675015077207*y + 0.041555057407175613*z
|
||||
b = 0.055630079696993609*x - 0.20397695888897657*y + 1.0569715142428786*z
|
||||
return
|
||||
}
|
||||
|
||||
func LinearRgbToXyz(r, g, b float64) (x, y, z float64) {
|
||||
x = 0.41239079926595948*r + 0.35758433938387796*g + 0.18048078840183429*b
|
||||
y = 0.21263900587151036*r + 0.71516867876775593*g + 0.072192315360733715*b
|
||||
z = 0.019330818715591851*r + 0.11919477979462599*g + 0.95053215224966058*b
|
||||
return
|
||||
}
|
||||
|
||||
/// XYZ ///
|
||||
///////////
|
||||
// http://www.sjbrown.co.uk/2004/05/14/gamma-correct-rendering/
|
||||
|
||||
func (col Color) Xyz() (x, y, z float64) {
|
||||
return LinearRgbToXyz(col.LinearRgb())
|
||||
}
|
||||
|
||||
func Xyz(x, y, z float64) Color {
|
||||
return LinearRgb(XyzToLinearRgb(x, y, z))
|
||||
}
|
||||
|
||||
/// xyY ///
|
||||
///////////
|
||||
// http://www.brucelindbloom.com/Eqn_XYZ_to_xyY.html
|
||||
|
||||
// Well, the name is bad, since it's xyY but Golang needs me to start with a
|
||||
// capital letter to make the method public.
|
||||
func XyzToXyy(X, Y, Z float64) (x, y, Yout float64) {
|
||||
return XyzToXyyWhiteRef(X, Y, Z, D65)
|
||||
}
|
||||
|
||||
func XyzToXyyWhiteRef(X, Y, Z float64, wref [3]float64) (x, y, Yout float64) {
|
||||
Yout = Y
|
||||
N := X + Y + Z
|
||||
if math.Abs(N) < 1e-14 {
|
||||
// When we have black, Bruce Lindbloom recommends to use
|
||||
// the reference white's chromacity for x and y.
|
||||
x = wref[0] / (wref[0] + wref[1] + wref[2])
|
||||
y = wref[1] / (wref[0] + wref[1] + wref[2])
|
||||
} else {
|
||||
x = X / N
|
||||
y = Y / N
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func XyyToXyz(x, y, Y float64) (X, Yout, Z float64) {
|
||||
Yout = Y
|
||||
|
||||
if -1e-14 < y && y < 1e-14 {
|
||||
X = 0.0
|
||||
Z = 0.0
|
||||
} else {
|
||||
X = Y / y * x
|
||||
Z = Y / y * (1.0 - x - y)
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Converts the given color to CIE xyY space using D65 as reference white.
|
||||
// (Note that the reference white is only used for black input.)
|
||||
// x, y and Y are in [0..1]
|
||||
func (col Color) Xyy() (x, y, Y float64) {
|
||||
return XyzToXyy(col.Xyz())
|
||||
}
|
||||
|
||||
// Converts the given color to CIE xyY space, taking into account
|
||||
// a given reference white. (i.e. the monitor's white)
|
||||
// (Note that the reference white is only used for black input.)
|
||||
// x, y and Y are in [0..1]
|
||||
func (col Color) XyyWhiteRef(wref [3]float64) (x, y, Y float64) {
|
||||
X, Y2, Z := col.Xyz()
|
||||
return XyzToXyyWhiteRef(X, Y2, Z, wref)
|
||||
}
|
||||
|
||||
// Generates a color by using data given in CIE xyY space.
|
||||
// x, y and Y are in [0..1]
|
||||
func Xyy(x, y, Y float64) Color {
|
||||
return Xyz(XyyToXyz(x, y, Y))
|
||||
}
|
||||
|
||||
/// L*a*b* ///
|
||||
//////////////
|
||||
// http://en.wikipedia.org/wiki/Lab_color_space#CIELAB-CIEXYZ_conversions
|
||||
// For L*a*b*, we need to L*a*b*<->XYZ->RGB and the first one is device dependent.
|
||||
|
||||
func lab_f(t float64) float64 {
|
||||
if t > 6.0/29.0*6.0/29.0*6.0/29.0 {
|
||||
return math.Cbrt(t)
|
||||
}
|
||||
return t/3.0*29.0/6.0*29.0/6.0 + 4.0/29.0
|
||||
}
|
||||
|
||||
func XyzToLab(x, y, z float64) (l, a, b float64) {
|
||||
// Use D65 white as reference point by default.
|
||||
// http://www.fredmiranda.com/forum/topic/1035332
|
||||
// http://en.wikipedia.org/wiki/Standard_illuminant
|
||||
return XyzToLabWhiteRef(x, y, z, D65)
|
||||
}
|
||||
|
||||
func XyzToLabWhiteRef(x, y, z float64, wref [3]float64) (l, a, b float64) {
|
||||
fy := lab_f(y / wref[1])
|
||||
l = 1.16*fy - 0.16
|
||||
a = 5.0 * (lab_f(x/wref[0]) - fy)
|
||||
b = 2.0 * (fy - lab_f(z/wref[2]))
|
||||
return
|
||||
}
|
||||
|
||||
func lab_finv(t float64) float64 {
|
||||
if t > 6.0/29.0 {
|
||||
return t * t * t
|
||||
}
|
||||
return 3.0 * 6.0 / 29.0 * 6.0 / 29.0 * (t - 4.0/29.0)
|
||||
}
|
||||
|
||||
func LabToXyz(l, a, b float64) (x, y, z float64) {
|
||||
// D65 white (see above).
|
||||
return LabToXyzWhiteRef(l, a, b, D65)
|
||||
}
|
||||
|
||||
func LabToXyzWhiteRef(l, a, b float64, wref [3]float64) (x, y, z float64) {
|
||||
l2 := (l + 0.16) / 1.16
|
||||
x = wref[0] * lab_finv(l2+a/5.0)
|
||||
y = wref[1] * lab_finv(l2)
|
||||
z = wref[2] * lab_finv(l2-b/2.0)
|
||||
return
|
||||
}
|
||||
|
||||
// Converts the given color to CIE L*a*b* space using D65 as reference white.
|
||||
func (col Color) Lab() (l, a, b float64) {
|
||||
return XyzToLab(col.Xyz())
|
||||
}
|
||||
|
||||
// Converts the given color to CIE L*a*b* space, taking into account
|
||||
// a given reference white. (i.e. the monitor's white)
|
||||
func (col Color) LabWhiteRef(wref [3]float64) (l, a, b float64) {
|
||||
x, y, z := col.Xyz()
|
||||
return XyzToLabWhiteRef(x, y, z, wref)
|
||||
}
|
||||
|
||||
// Generates a color by using data given in CIE L*a*b* space using D65 as reference white.
|
||||
// WARNING: many combinations of `l`, `a`, and `b` values do not have corresponding
|
||||
// valid RGB values, check the FAQ in the README if you're unsure.
|
||||
func Lab(l, a, b float64) Color {
|
||||
return Xyz(LabToXyz(l, a, b))
|
||||
}
|
||||
|
||||
// Generates a color by using data given in CIE L*a*b* space, taking
|
||||
// into account a given reference white. (i.e. the monitor's white)
|
||||
func LabWhiteRef(l, a, b float64, wref [3]float64) Color {
|
||||
return Xyz(LabToXyzWhiteRef(l, a, b, wref))
|
||||
}
|
||||
|
||||
// DistanceLab is a good measure of visual similarity between two colors!
|
||||
// A result of 0 would mean identical colors, while a result of 1 or higher
|
||||
// means the colors differ a lot.
|
||||
func (c1 Color) DistanceLab(c2 Color) float64 {
|
||||
l1, a1, b1 := c1.Lab()
|
||||
l2, a2, b2 := c2.Lab()
|
||||
return math.Sqrt(sq(l1-l2) + sq(a1-a2) + sq(b1-b2))
|
||||
}
|
||||
|
||||
// DistanceCIE76 is the same as DistanceLab.
|
||||
func (c1 Color) DistanceCIE76(c2 Color) float64 {
|
||||
return c1.DistanceLab(c2)
|
||||
}
|
||||
|
||||
// Uses the CIE94 formula to calculate color distance. More accurate than
|
||||
// DistanceLab, but also more work.
|
||||
func (cl Color) DistanceCIE94(cr Color) float64 {
|
||||
l1, a1, b1 := cl.Lab()
|
||||
l2, a2, b2 := cr.Lab()
|
||||
|
||||
// NOTE: Since all those formulas expect L,a,b values 100x larger than we
|
||||
// have them in this library, we either need to adjust all constants
|
||||
// in the formula, or convert the ranges of L,a,b before, and then
|
||||
// scale the distances down again. The latter is less error-prone.
|
||||
l1, a1, b1 = l1*100.0, a1*100.0, b1*100.0
|
||||
l2, a2, b2 = l2*100.0, a2*100.0, b2*100.0
|
||||
|
||||
kl := 1.0 // 2.0 for textiles
|
||||
kc := 1.0
|
||||
kh := 1.0
|
||||
k1 := 0.045 // 0.048 for textiles
|
||||
k2 := 0.015 // 0.014 for textiles.
|
||||
|
||||
deltaL := l1 - l2
|
||||
c1 := math.Sqrt(sq(a1) + sq(b1))
|
||||
c2 := math.Sqrt(sq(a2) + sq(b2))
|
||||
deltaCab := c1 - c2
|
||||
|
||||
// Not taking Sqrt here for stability, and it's unnecessary.
|
||||
deltaHab2 := sq(a1-a2) + sq(b1-b2) - sq(deltaCab)
|
||||
sl := 1.0
|
||||
sc := 1.0 + k1*c1
|
||||
sh := 1.0 + k2*c1
|
||||
|
||||
vL2 := sq(deltaL / (kl * sl))
|
||||
vC2 := sq(deltaCab / (kc * sc))
|
||||
vH2 := deltaHab2 / sq(kh*sh)
|
||||
|
||||
return math.Sqrt(vL2+vC2+vH2) * 0.01 // See above.
|
||||
}
|
||||
|
||||
// DistanceCIEDE2000 uses the Delta E 2000 formula to calculate color
|
||||
// distance. It is more expensive but more accurate than both DistanceLab
|
||||
// and DistanceCIE94.
|
||||
func (cl Color) DistanceCIEDE2000(cr Color) float64 {
|
||||
return cl.DistanceCIEDE2000klch(cr, 1.0, 1.0, 1.0)
|
||||
}
|
||||
|
||||
// DistanceCIEDE2000klch uses the Delta E 2000 formula with custom values
|
||||
// for the weighting factors kL, kC, and kH.
|
||||
func (cl Color) DistanceCIEDE2000klch(cr Color, kl, kc, kh float64) float64 {
|
||||
l1, a1, b1 := cl.Lab()
|
||||
l2, a2, b2 := cr.Lab()
|
||||
|
||||
// As with CIE94, we scale up the ranges of L,a,b beforehand and scale
|
||||
// them down again afterwards.
|
||||
l1, a1, b1 = l1*100.0, a1*100.0, b1*100.0
|
||||
l2, a2, b2 = l2*100.0, a2*100.0, b2*100.0
|
||||
|
||||
cab1 := math.Sqrt(sq(a1) + sq(b1))
|
||||
cab2 := math.Sqrt(sq(a2) + sq(b2))
|
||||
cabmean := (cab1 + cab2) / 2
|
||||
|
||||
g := 0.5 * (1 - math.Sqrt(math.Pow(cabmean, 7)/(math.Pow(cabmean, 7)+math.Pow(25, 7))))
|
||||
ap1 := (1 + g) * a1
|
||||
ap2 := (1 + g) * a2
|
||||
cp1 := math.Sqrt(sq(ap1) + sq(b1))
|
||||
cp2 := math.Sqrt(sq(ap2) + sq(b2))
|
||||
|
||||
hp1 := 0.0
|
||||
if b1 != ap1 || ap1 != 0 {
|
||||
hp1 = math.Atan2(b1, ap1)
|
||||
if hp1 < 0 {
|
||||
hp1 += math.Pi * 2
|
||||
}
|
||||
hp1 *= 180 / math.Pi
|
||||
}
|
||||
hp2 := 0.0
|
||||
if b2 != ap2 || ap2 != 0 {
|
||||
hp2 = math.Atan2(b2, ap2)
|
||||
if hp2 < 0 {
|
||||
hp2 += math.Pi * 2
|
||||
}
|
||||
hp2 *= 180 / math.Pi
|
||||
}
|
||||
|
||||
deltaLp := l2 - l1
|
||||
deltaCp := cp2 - cp1
|
||||
dhp := 0.0
|
||||
cpProduct := cp1 * cp2
|
||||
if cpProduct != 0 {
|
||||
dhp = hp2 - hp1
|
||||
if dhp > 180 {
|
||||
dhp -= 360
|
||||
} else if dhp < -180 {
|
||||
dhp += 360
|
||||
}
|
||||
}
|
||||
deltaHp := 2 * math.Sqrt(cpProduct) * math.Sin(dhp/2*math.Pi/180)
|
||||
|
||||
lpmean := (l1 + l2) / 2
|
||||
cpmean := (cp1 + cp2) / 2
|
||||
hpmean := hp1 + hp2
|
||||
if cpProduct != 0 {
|
||||
hpmean /= 2
|
||||
if math.Abs(hp1-hp2) > 180 {
|
||||
if hp1+hp2 < 360 {
|
||||
hpmean += 180
|
||||
} else {
|
||||
hpmean -= 180
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
t := 1 - 0.17*math.Cos((hpmean-30)*math.Pi/180) + 0.24*math.Cos(2*hpmean*math.Pi/180) + 0.32*math.Cos((3*hpmean+6)*math.Pi/180) - 0.2*math.Cos((4*hpmean-63)*math.Pi/180)
|
||||
deltaTheta := 30 * math.Exp(-sq((hpmean-275)/25))
|
||||
rc := 2 * math.Sqrt(math.Pow(cpmean, 7)/(math.Pow(cpmean, 7)+math.Pow(25, 7)))
|
||||
sl := 1 + (0.015*sq(lpmean-50))/math.Sqrt(20+sq(lpmean-50))
|
||||
sc := 1 + 0.045*cpmean
|
||||
sh := 1 + 0.015*cpmean*t
|
||||
rt := -math.Sin(2*deltaTheta*math.Pi/180) * rc
|
||||
|
||||
return math.Sqrt(sq(deltaLp/(kl*sl))+sq(deltaCp/(kc*sc))+sq(deltaHp/(kh*sh))+rt*(deltaCp/(kc*sc))*(deltaHp/(kh*sh))) * 0.01
|
||||
}
|
||||
|
||||
// BlendLab blends two colors in the L*a*b* color-space, which should result in a smoother blend.
|
||||
// t == 0 results in c1, t == 1 results in c2
|
||||
func (c1 Color) BlendLab(c2 Color, t float64) Color {
|
||||
l1, a1, b1 := c1.Lab()
|
||||
l2, a2, b2 := c2.Lab()
|
||||
return Lab(l1+t*(l2-l1),
|
||||
a1+t*(a2-a1),
|
||||
b1+t*(b2-b1))
|
||||
}
|
||||
|
||||
/// L*u*v* ///
|
||||
//////////////
|
||||
// http://en.wikipedia.org/wiki/CIELUV#XYZ_.E2.86.92_CIELUV_and_CIELUV_.E2.86.92_XYZ_conversions
|
||||
// For L*u*v*, we need to L*u*v*<->XYZ<->RGB and the first one is device dependent.
|
||||
|
||||
func XyzToLuv(x, y, z float64) (l, a, b float64) {
|
||||
// Use D65 white as reference point by default.
|
||||
// http://www.fredmiranda.com/forum/topic/1035332
|
||||
// http://en.wikipedia.org/wiki/Standard_illuminant
|
||||
return XyzToLuvWhiteRef(x, y, z, D65)
|
||||
}
|
||||
|
||||
func XyzToLuvWhiteRef(x, y, z float64, wref [3]float64) (l, u, v float64) {
|
||||
if y/wref[1] <= 6.0/29.0*6.0/29.0*6.0/29.0 {
|
||||
l = y / wref[1] * (29.0 / 3.0 * 29.0 / 3.0 * 29.0 / 3.0) / 100.0
|
||||
} else {
|
||||
l = 1.16*math.Cbrt(y/wref[1]) - 0.16
|
||||
}
|
||||
ubis, vbis := xyz_to_uv(x, y, z)
|
||||
un, vn := xyz_to_uv(wref[0], wref[1], wref[2])
|
||||
u = 13.0 * l * (ubis - un)
|
||||
v = 13.0 * l * (vbis - vn)
|
||||
return
|
||||
}
|
||||
|
||||
// For this part, we do as R's graphics.hcl does, not as wikipedia does.
|
||||
// Or is it the same?
|
||||
func xyz_to_uv(x, y, z float64) (u, v float64) {
|
||||
denom := x + 15.0*y + 3.0*z
|
||||
if denom == 0.0 {
|
||||
u, v = 0.0, 0.0
|
||||
} else {
|
||||
u = 4.0 * x / denom
|
||||
v = 9.0 * y / denom
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func LuvToXyz(l, u, v float64) (x, y, z float64) {
|
||||
// D65 white (see above).
|
||||
return LuvToXyzWhiteRef(l, u, v, D65)
|
||||
}
|
||||
|
||||
func LuvToXyzWhiteRef(l, u, v float64, wref [3]float64) (x, y, z float64) {
|
||||
//y = wref[1] * lab_finv((l + 0.16) / 1.16)
|
||||
if l <= 0.08 {
|
||||
y = wref[1] * l * 100.0 * 3.0 / 29.0 * 3.0 / 29.0 * 3.0 / 29.0
|
||||
} else {
|
||||
y = wref[1] * cub((l+0.16)/1.16)
|
||||
}
|
||||
un, vn := xyz_to_uv(wref[0], wref[1], wref[2])
|
||||
if l != 0.0 {
|
||||
ubis := u/(13.0*l) + un
|
||||
vbis := v/(13.0*l) + vn
|
||||
x = y * 9.0 * ubis / (4.0 * vbis)
|
||||
z = y * (12.0 - 3.0*ubis - 20.0*vbis) / (4.0 * vbis)
|
||||
} else {
|
||||
x, y = 0.0, 0.0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Converts the given color to CIE L*u*v* space using D65 as reference white.
|
||||
// L* is in [0..1] and both u* and v* are in about [-1..1]
|
||||
func (col Color) Luv() (l, u, v float64) {
|
||||
return XyzToLuv(col.Xyz())
|
||||
}
|
||||
|
||||
// Converts the given color to CIE L*u*v* space, taking into account
|
||||
// a given reference white. (i.e. the monitor's white)
|
||||
// L* is in [0..1] and both u* and v* are in about [-1..1]
|
||||
func (col Color) LuvWhiteRef(wref [3]float64) (l, u, v float64) {
|
||||
x, y, z := col.Xyz()
|
||||
return XyzToLuvWhiteRef(x, y, z, wref)
|
||||
}
|
||||
|
||||
// Generates a color by using data given in CIE L*u*v* space using D65 as reference white.
|
||||
// L* is in [0..1] and both u* and v* are in about [-1..1]
|
||||
// WARNING: many combinations of `l`, `u`, and `v` values do not have corresponding
|
||||
// valid RGB values, check the FAQ in the README if you're unsure.
|
||||
func Luv(l, u, v float64) Color {
|
||||
return Xyz(LuvToXyz(l, u, v))
|
||||
}
|
||||
|
||||
// Generates a color by using data given in CIE L*u*v* space, taking
|
||||
// into account a given reference white. (i.e. the monitor's white)
|
||||
// L* is in [0..1] and both u* and v* are in about [-1..1]
|
||||
func LuvWhiteRef(l, u, v float64, wref [3]float64) Color {
|
||||
return Xyz(LuvToXyzWhiteRef(l, u, v, wref))
|
||||
}
|
||||
|
||||
// DistanceLuv is a good measure of visual similarity between two colors!
|
||||
// A result of 0 would mean identical colors, while a result of 1 or higher
|
||||
// means the colors differ a lot.
|
||||
func (c1 Color) DistanceLuv(c2 Color) float64 {
|
||||
l1, u1, v1 := c1.Luv()
|
||||
l2, u2, v2 := c2.Luv()
|
||||
return math.Sqrt(sq(l1-l2) + sq(u1-u2) + sq(v1-v2))
|
||||
}
|
||||
|
||||
// BlendLuv blends two colors in the CIE-L*u*v* color-space, which should result in a smoother blend.
|
||||
// t == 0 results in c1, t == 1 results in c2
|
||||
func (c1 Color) BlendLuv(c2 Color, t float64) Color {
|
||||
l1, u1, v1 := c1.Luv()
|
||||
l2, u2, v2 := c2.Luv()
|
||||
return Luv(l1+t*(l2-l1),
|
||||
u1+t*(u2-u1),
|
||||
v1+t*(v2-v1))
|
||||
}
|
||||
|
||||
/// HCL ///
|
||||
///////////
|
||||
// HCL is nothing else than L*a*b* in cylindrical coordinates!
|
||||
// (this was wrong on English wikipedia, I fixed it, let's hope the fix stays.)
|
||||
// But it is widely popular since it is a "correct HSV"
|
||||
// http://www.hunterlab.com/appnotes/an09_96a.pdf
|
||||
|
||||
// Converts the given color to HCL space using D65 as reference white.
|
||||
// H values are in [0..360], C and L values are in [0..1] although C can overshoot 1.0
|
||||
func (col Color) Hcl() (h, c, l float64) {
|
||||
return col.HclWhiteRef(D65)
|
||||
}
|
||||
|
||||
func LabToHcl(L, a, b float64) (h, c, l float64) {
|
||||
// Oops, floating point workaround necessary if a ~= b and both are very small (i.e. almost zero).
|
||||
if math.Abs(b-a) > 1e-4 && math.Abs(a) > 1e-4 {
|
||||
h = math.Mod(57.29577951308232087721*math.Atan2(b, a)+360.0, 360.0) // Rad2Deg
|
||||
} else {
|
||||
h = 0.0
|
||||
}
|
||||
c = math.Sqrt(sq(a) + sq(b))
|
||||
l = L
|
||||
return
|
||||
}
|
||||
|
||||
// Converts the given color to HCL space, taking into account
|
||||
// a given reference white. (i.e. the monitor's white)
|
||||
// H values are in [0..360], C and L values are in [0..1]
|
||||
func (col Color) HclWhiteRef(wref [3]float64) (h, c, l float64) {
|
||||
L, a, b := col.LabWhiteRef(wref)
|
||||
return LabToHcl(L, a, b)
|
||||
}
|
||||
|
||||
// Generates a color by using data given in HCL space using D65 as reference white.
|
||||
// H values are in [0..360], C and L values are in [0..1]
|
||||
// WARNING: many combinations of `h`, `c`, and `l` values do not have corresponding
|
||||
// valid RGB values, check the FAQ in the README if you're unsure.
|
||||
func Hcl(h, c, l float64) Color {
|
||||
return HclWhiteRef(h, c, l, D65)
|
||||
}
|
||||
|
||||
func HclToLab(h, c, l float64) (L, a, b float64) {
|
||||
H := 0.01745329251994329576 * h // Deg2Rad
|
||||
a = c * math.Cos(H)
|
||||
b = c * math.Sin(H)
|
||||
L = l
|
||||
return
|
||||
}
|
||||
|
||||
// Generates a color by using data given in HCL space, taking
|
||||
// into account a given reference white. (i.e. the monitor's white)
|
||||
// H values are in [0..360], C and L values are in [0..1]
|
||||
func HclWhiteRef(h, c, l float64, wref [3]float64) Color {
|
||||
L, a, b := HclToLab(h, c, l)
|
||||
return LabWhiteRef(L, a, b, wref)
|
||||
}
|
||||
|
||||
// BlendHcl blends two colors in the CIE-L*C*h° color-space, which should result in a smoother blend.
|
||||
// t == 0 results in c1, t == 1 results in c2
|
||||
func (col1 Color) BlendHcl(col2 Color, t float64) Color {
|
||||
h1, c1, l1 := col1.Hcl()
|
||||
h2, c2, l2 := col2.Hcl()
|
||||
|
||||
// We know that h are both in [0..360]
|
||||
return Hcl(interp_angle(h1, h2, t), c1+t*(c2-c1), l1+t*(l2-l1)).Clamped()
|
||||
}
|
||||
|
||||
// LuvLch
|
||||
|
||||
// Converts the given color to LuvLCh space using D65 as reference white.
|
||||
// h values are in [0..360], C and L values are in [0..1] although C can overshoot 1.0
|
||||
func (col Color) LuvLCh() (l, c, h float64) {
|
||||
return col.LuvLChWhiteRef(D65)
|
||||
}
|
||||
|
||||
func LuvToLuvLCh(L, u, v float64) (l, c, h float64) {
|
||||
// Oops, floating point workaround necessary if u ~= v and both are very small (i.e. almost zero).
|
||||
if math.Abs(v-u) > 1e-4 && math.Abs(u) > 1e-4 {
|
||||
h = math.Mod(57.29577951308232087721*math.Atan2(v, u)+360.0, 360.0) // Rad2Deg
|
||||
} else {
|
||||
h = 0.0
|
||||
}
|
||||
l = L
|
||||
c = math.Sqrt(sq(u) + sq(v))
|
||||
return
|
||||
}
|
||||
|
||||
// Converts the given color to LuvLCh space, taking into account
|
||||
// a given reference white. (i.e. the monitor's white)
|
||||
// h values are in [0..360], c and l values are in [0..1]
|
||||
func (col Color) LuvLChWhiteRef(wref [3]float64) (l, c, h float64) {
|
||||
return LuvToLuvLCh(col.LuvWhiteRef(wref))
|
||||
}
|
||||
|
||||
// Generates a color by using data given in LuvLCh space using D65 as reference white.
|
||||
// h values are in [0..360], C and L values are in [0..1]
|
||||
// WARNING: many combinations of `l`, `c`, and `h` values do not have corresponding
|
||||
// valid RGB values, check the FAQ in the README if you're unsure.
|
||||
func LuvLCh(l, c, h float64) Color {
|
||||
return LuvLChWhiteRef(l, c, h, D65)
|
||||
}
|
||||
|
||||
func LuvLChToLuv(l, c, h float64) (L, u, v float64) {
|
||||
H := 0.01745329251994329576 * h // Deg2Rad
|
||||
u = c * math.Cos(H)
|
||||
v = c * math.Sin(H)
|
||||
L = l
|
||||
return
|
||||
}
|
||||
|
||||
// Generates a color by using data given in LuvLCh space, taking
|
||||
// into account a given reference white. (i.e. the monitor's white)
|
||||
// h values are in [0..360], C and L values are in [0..1]
|
||||
func LuvLChWhiteRef(l, c, h float64, wref [3]float64) Color {
|
||||
L, u, v := LuvLChToLuv(l, c, h)
|
||||
return LuvWhiteRef(L, u, v, wref)
|
||||
}
|
||||
|
||||
// BlendLuvLCh blends two colors in the cylindrical CIELUV color space.
|
||||
// t == 0 results in c1, t == 1 results in c2
|
||||
func (col1 Color) BlendLuvLCh(col2 Color, t float64) Color {
|
||||
l1, c1, h1 := col1.LuvLCh()
|
||||
l2, c2, h2 := col2.LuvLCh()
|
||||
|
||||
// We know that h are both in [0..360]
|
||||
return LuvLCh(l1+t*(l2-l1), c1+t*(c2-c1), interp_angle(h1, h2, t))
|
||||
}
|
25
vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go
generated
vendored
Normal file
25
vendor/github.com/lucasb-eyer/go-colorful/happy_palettegen.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package colorful
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// Uses the HSV color space to generate colors with similar S,V but distributed
|
||||
// evenly along their Hue. This is fast but not always pretty.
|
||||
// If you've got time to spare, use Lab (the non-fast below).
|
||||
func FastHappyPalette(colorsCount int) (colors []Color) {
|
||||
colors = make([]Color, colorsCount)
|
||||
|
||||
for i := 0; i < colorsCount; i++ {
|
||||
colors[i] = Hsv(float64(i)*(360.0/float64(colorsCount)), 0.8+rand.Float64()*0.2, 0.65+rand.Float64()*0.2)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func HappyPalette(colorsCount int) ([]Color, error) {
|
||||
pimpy := func(l, a, b float64) bool {
|
||||
_, c, _ := LabToHcl(l, a, b)
|
||||
return 0.3 <= c && 0.4 <= l && l <= 0.8
|
||||
}
|
||||
return SoftPaletteEx(colorsCount, SoftPaletteSettings{pimpy, 50, true})
|
||||
}
|
67
vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go
generated
vendored
Normal file
67
vendor/github.com/lucasb-eyer/go-colorful/hexcolor.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
package colorful
|
||||
|
||||
import (
|
||||
"database/sql/driver"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// A HexColor is a Color stored as a hex string "#rrggbb". It implements the
|
||||
// database/sql.Scanner, database/sql/driver.Value,
|
||||
// encoding/json.Unmarshaler and encoding/json.Marshaler interfaces.
|
||||
type HexColor Color
|
||||
|
||||
type errUnsupportedType struct {
|
||||
got interface{}
|
||||
want reflect.Type
|
||||
}
|
||||
|
||||
func (hc *HexColor) Scan(value interface{}) error {
|
||||
s, ok := value.(string)
|
||||
if !ok {
|
||||
return errUnsupportedType{got: reflect.TypeOf(value), want: reflect.TypeOf("")}
|
||||
}
|
||||
c, err := Hex(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*hc = HexColor(c)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hc *HexColor) Value() (driver.Value, error) {
|
||||
return Color(*hc).Hex(), nil
|
||||
}
|
||||
|
||||
func (e errUnsupportedType) Error() string {
|
||||
return fmt.Sprintf("unsupported type: got %v, want a %s", e.got, e.want)
|
||||
}
|
||||
|
||||
func (hc *HexColor) UnmarshalJSON(data []byte) error {
|
||||
var hexCode string
|
||||
if err := json.Unmarshal(data, &hexCode); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var col, err = Hex(hexCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*hc = HexColor(col)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (hc HexColor) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(Color(hc).Hex())
|
||||
}
|
||||
|
||||
// Decode - deserialize function for https://github.com/kelseyhightower/envconfig
|
||||
func (hc *HexColor) Decode(hexCode string) error {
|
||||
var col, err = Hex(hexCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*hc = HexColor(col)
|
||||
return nil
|
||||
}
|
1
vendor/github.com/lucasb-eyer/go-colorful/hsluv-snapshot-rev4.json
generated
vendored
Normal file
1
vendor/github.com/lucasb-eyer/go-colorful/hsluv-snapshot-rev4.json
generated
vendored
Normal file
File diff suppressed because one or more lines are too long
207
vendor/github.com/lucasb-eyer/go-colorful/hsluv.go
generated
vendored
Normal file
207
vendor/github.com/lucasb-eyer/go-colorful/hsluv.go
generated
vendored
Normal file
@ -0,0 +1,207 @@
|
||||
package colorful
|
||||
|
||||
import "math"
|
||||
|
||||
// Source: https://github.com/hsluv/hsluv-go
|
||||
// Under MIT License
|
||||
// Modified so that Saturation and Luminance are in [0..1] instead of [0..100].
|
||||
|
||||
// HSLuv uses a rounded version of the D65. This has no impact on the final RGB
|
||||
// values, but to keep high levels of accuracy for internal operations and when
|
||||
// comparing to the test values, this modified white reference is used internally.
|
||||
//
|
||||
// See this GitHub thread for details on these values:
|
||||
// https://github.com/hsluv/hsluv/issues/79
|
||||
var hSLuvD65 = [3]float64{0.95045592705167, 1.0, 1.089057750759878}
|
||||
|
||||
func LuvLChToHSLuv(l, c, h float64) (float64, float64, float64) {
|
||||
// [-1..1] but the code expects it to be [-100..100]
|
||||
c *= 100.0
|
||||
l *= 100.0
|
||||
|
||||
var s, max float64
|
||||
if l > 99.9999999 || l < 0.00000001 {
|
||||
s = 0.0
|
||||
} else {
|
||||
max = maxChromaForLH(l, h)
|
||||
s = c / max * 100.0
|
||||
}
|
||||
return h, clamp01(s / 100.0), clamp01(l / 100.0)
|
||||
}
|
||||
|
||||
func HSLuvToLuvLCh(h, s, l float64) (float64, float64, float64) {
|
||||
l *= 100.0
|
||||
s *= 100.0
|
||||
|
||||
var c, max float64
|
||||
if l > 99.9999999 || l < 0.00000001 {
|
||||
c = 0.0
|
||||
} else {
|
||||
max = maxChromaForLH(l, h)
|
||||
c = max / 100.0 * s
|
||||
}
|
||||
|
||||
// c is [-100..100], but for LCh it's supposed to be almost [-1..1]
|
||||
return clamp01(l / 100.0), c / 100.0, h
|
||||
}
|
||||
|
||||
func LuvLChToHPLuv(l, c, h float64) (float64, float64, float64) {
|
||||
// [-1..1] but the code expects it to be [-100..100]
|
||||
c *= 100.0
|
||||
l *= 100.0
|
||||
|
||||
var s, max float64
|
||||
if l > 99.9999999 || l < 0.00000001 {
|
||||
s = 0.0
|
||||
} else {
|
||||
max = maxSafeChromaForL(l)
|
||||
s = c / max * 100.0
|
||||
}
|
||||
return h, s / 100.0, l / 100.0
|
||||
}
|
||||
|
||||
func HPLuvToLuvLCh(h, s, l float64) (float64, float64, float64) {
|
||||
// [-1..1] but the code expects it to be [-100..100]
|
||||
l *= 100.0
|
||||
s *= 100.0
|
||||
|
||||
var c, max float64
|
||||
if l > 99.9999999 || l < 0.00000001 {
|
||||
c = 0.0
|
||||
} else {
|
||||
max = maxSafeChromaForL(l)
|
||||
c = max / 100.0 * s
|
||||
}
|
||||
return l / 100.0, c / 100.0, h
|
||||
}
|
||||
|
||||
// HSLuv creates a new Color from values in the HSLuv color space.
|
||||
// Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1].
|
||||
//
|
||||
// The returned color values are clamped (using .Clamped), so this will never output
|
||||
// an invalid color.
|
||||
func HSLuv(h, s, l float64) Color {
|
||||
// HSLuv -> LuvLCh -> CIELUV -> CIEXYZ -> Linear RGB -> sRGB
|
||||
l, u, v := LuvLChToLuv(HSLuvToLuvLCh(h, s, l))
|
||||
return LinearRgb(XyzToLinearRgb(LuvToXyzWhiteRef(l, u, v, hSLuvD65))).Clamped()
|
||||
}
|
||||
|
||||
// HPLuv creates a new Color from values in the HPLuv color space.
|
||||
// Hue in [0..360], a Saturation [0..1], and a Luminance (lightness) in [0..1].
|
||||
//
|
||||
// The returned color values are clamped (using .Clamped), so this will never output
|
||||
// an invalid color.
|
||||
func HPLuv(h, s, l float64) Color {
|
||||
// HPLuv -> LuvLCh -> CIELUV -> CIEXYZ -> Linear RGB -> sRGB
|
||||
l, u, v := LuvLChToLuv(HPLuvToLuvLCh(h, s, l))
|
||||
return LinearRgb(XyzToLinearRgb(LuvToXyzWhiteRef(l, u, v, hSLuvD65))).Clamped()
|
||||
}
|
||||
|
||||
// HSLuv returns the Hue, Saturation and Luminance of the color in the HSLuv
|
||||
// color space. Hue in [0..360], a Saturation [0..1], and a Luminance
|
||||
// (lightness) in [0..1].
|
||||
func (col Color) HSLuv() (h, s, l float64) {
|
||||
// sRGB -> Linear RGB -> CIEXYZ -> CIELUV -> LuvLCh -> HSLuv
|
||||
return LuvLChToHSLuv(col.LuvLChWhiteRef(hSLuvD65))
|
||||
}
|
||||
|
||||
// HPLuv returns the Hue, Saturation and Luminance of the color in the HSLuv
|
||||
// color space. Hue in [0..360], a Saturation [0..1], and a Luminance
|
||||
// (lightness) in [0..1].
|
||||
//
|
||||
// Note that HPLuv can only represent pastel colors, and so the Saturation
|
||||
// value could be much larger than 1 for colors it can't represent.
|
||||
func (col Color) HPLuv() (h, s, l float64) {
|
||||
return LuvLChToHPLuv(col.LuvLChWhiteRef(hSLuvD65))
|
||||
}
|
||||
|
||||
// DistanceHSLuv calculates Euclidan distance in the HSLuv colorspace. No idea
|
||||
// how useful this is.
|
||||
//
|
||||
// The Hue value is divided by 100 before the calculation, so that H, S, and L
|
||||
// have the same relative ranges.
|
||||
func (c1 Color) DistanceHSLuv(c2 Color) float64 {
|
||||
h1, s1, l1 := c1.HSLuv()
|
||||
h2, s2, l2 := c2.HSLuv()
|
||||
return math.Sqrt(sq((h1-h2)/100.0) + sq(s1-s2) + sq(l1-l2))
|
||||
}
|
||||
|
||||
// DistanceHPLuv calculates Euclidean distance in the HPLuv colorspace. No idea
|
||||
// how useful this is.
|
||||
//
|
||||
// The Hue value is divided by 100 before the calculation, so that H, S, and L
|
||||
// have the same relative ranges.
|
||||
func (c1 Color) DistanceHPLuv(c2 Color) float64 {
|
||||
h1, s1, l1 := c1.HPLuv()
|
||||
h2, s2, l2 := c2.HPLuv()
|
||||
return math.Sqrt(sq((h1-h2)/100.0) + sq(s1-s2) + sq(l1-l2))
|
||||
}
|
||||
|
||||
var m = [3][3]float64{
|
||||
{3.2409699419045214, -1.5373831775700935, -0.49861076029300328},
|
||||
{-0.96924363628087983, 1.8759675015077207, 0.041555057407175613},
|
||||
{0.055630079696993609, -0.20397695888897657, 1.0569715142428786},
|
||||
}
|
||||
|
||||
const kappa = 903.2962962962963
|
||||
const epsilon = 0.0088564516790356308
|
||||
|
||||
func maxChromaForLH(l, h float64) float64 {
|
||||
hRad := h / 360.0 * math.Pi * 2.0
|
||||
minLength := math.MaxFloat64
|
||||
for _, line := range getBounds(l) {
|
||||
length := lengthOfRayUntilIntersect(hRad, line[0], line[1])
|
||||
if length > 0.0 && length < minLength {
|
||||
minLength = length
|
||||
}
|
||||
}
|
||||
return minLength
|
||||
}
|
||||
|
||||
func getBounds(l float64) [6][2]float64 {
|
||||
var sub2 float64
|
||||
var ret [6][2]float64
|
||||
sub1 := math.Pow(l+16.0, 3.0) / 1560896.0
|
||||
if sub1 > epsilon {
|
||||
sub2 = sub1
|
||||
} else {
|
||||
sub2 = l / kappa
|
||||
}
|
||||
for i := range m {
|
||||
for k := 0; k < 2; k++ {
|
||||
top1 := (284517.0*m[i][0] - 94839.0*m[i][2]) * sub2
|
||||
top2 := (838422.0*m[i][2]+769860.0*m[i][1]+731718.0*m[i][0])*l*sub2 - 769860.0*float64(k)*l
|
||||
bottom := (632260.0*m[i][2]-126452.0*m[i][1])*sub2 + 126452.0*float64(k)
|
||||
ret[i*2+k][0] = top1 / bottom
|
||||
ret[i*2+k][1] = top2 / bottom
|
||||
}
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func lengthOfRayUntilIntersect(theta, x, y float64) (length float64) {
|
||||
length = y / (math.Sin(theta) - x*math.Cos(theta))
|
||||
return
|
||||
}
|
||||
|
||||
func maxSafeChromaForL(l float64) float64 {
|
||||
minLength := math.MaxFloat64
|
||||
for _, line := range getBounds(l) {
|
||||
m1 := line[0]
|
||||
b1 := line[1]
|
||||
x := intersectLineLine(m1, b1, -1.0/m1, 0.0)
|
||||
dist := distanceFromPole(x, b1+x*m1)
|
||||
if dist < minLength {
|
||||
minLength = dist
|
||||
}
|
||||
}
|
||||
return minLength
|
||||
}
|
||||
|
||||
func intersectLineLine(x1, y1, x2, y2 float64) float64 {
|
||||
return (y1 - y2) / (x2 - x1)
|
||||
}
|
||||
|
||||
func distanceFromPole(x, y float64) float64 {
|
||||
return math.Sqrt(math.Pow(x, 2.0) + math.Pow(y, 2.0))
|
||||
}
|
185
vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go
generated
vendored
Normal file
185
vendor/github.com/lucasb-eyer/go-colorful/soft_palettegen.go
generated
vendored
Normal file
@ -0,0 +1,185 @@
|
||||
// Largely inspired by the descriptions in http://lab.medialab.sciences-po.fr/iwanthue/
|
||||
// but written from scratch.
|
||||
|
||||
package colorful
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// The algorithm works in L*a*b* color space and converts to RGB in the end.
|
||||
// L* in [0..1], a* and b* in [-1..1]
|
||||
type lab_t struct {
|
||||
L, A, B float64
|
||||
}
|
||||
|
||||
type SoftPaletteSettings struct {
|
||||
// A function which can be used to restrict the allowed color-space.
|
||||
CheckColor func(l, a, b float64) bool
|
||||
|
||||
// The higher, the better quality but the slower. Usually two figures.
|
||||
Iterations int
|
||||
|
||||
// Use up to 160000 or 8000 samples of the L*a*b* space (and thus calls to CheckColor).
|
||||
// Set this to true only if your CheckColor shapes the Lab space weirdly.
|
||||
ManySamples bool
|
||||
}
|
||||
|
||||
// Yeah, windows-stype Foo, FooEx, screw you golang...
|
||||
// Uses K-means to cluster the color-space and return the means of the clusters
|
||||
// as a new palette of distinctive colors. Falls back to K-medoid if the mean
|
||||
// happens to fall outside of the color-space, which can only happen if you
|
||||
// specify a CheckColor function.
|
||||
func SoftPaletteEx(colorsCount int, settings SoftPaletteSettings) ([]Color, error) {
|
||||
|
||||
// Checks whether it's a valid RGB and also fulfills the potentially provided constraint.
|
||||
check := func(col lab_t) bool {
|
||||
c := Lab(col.L, col.A, col.B)
|
||||
return c.IsValid() && (settings.CheckColor == nil || settings.CheckColor(col.L, col.A, col.B))
|
||||
}
|
||||
|
||||
// Sample the color space. These will be the points k-means is run on.
|
||||
dl := 0.05
|
||||
dab := 0.1
|
||||
if settings.ManySamples {
|
||||
dl = 0.01
|
||||
dab = 0.05
|
||||
}
|
||||
|
||||
samples := make([]lab_t, 0, int(1.0/dl*2.0/dab*2.0/dab))
|
||||
for l := 0.0; l <= 1.0; l += dl {
|
||||
for a := -1.0; a <= 1.0; a += dab {
|
||||
for b := -1.0; b <= 1.0; b += dab {
|
||||
if check(lab_t{l, a, b}) {
|
||||
samples = append(samples, lab_t{l, a, b})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// That would cause some infinite loops down there...
|
||||
if len(samples) < colorsCount {
|
||||
return nil, fmt.Errorf("palettegen: more colors requested (%v) than samples available (%v). Your requested color count may be wrong, you might want to use many samples or your constraint function makes the valid color space too small", colorsCount, len(samples))
|
||||
} else if len(samples) == colorsCount {
|
||||
return labs2cols(samples), nil // Oops?
|
||||
}
|
||||
|
||||
// We take the initial means out of the samples, so they are in fact medoids.
|
||||
// This helps us avoid infinite loops or arbitrary cutoffs with too restrictive constraints.
|
||||
means := make([]lab_t, colorsCount)
|
||||
for i := 0; i < colorsCount; i++ {
|
||||
for means[i] = samples[rand.Intn(len(samples))]; in(means, i, means[i]); means[i] = samples[rand.Intn(len(samples))] {
|
||||
}
|
||||
}
|
||||
|
||||
clusters := make([]int, len(samples))
|
||||
samples_used := make([]bool, len(samples))
|
||||
|
||||
// The actual k-means/medoid iterations
|
||||
for i := 0; i < settings.Iterations; i++ {
|
||||
// Reassing the samples to clusters, i.e. to their closest mean.
|
||||
// By the way, also check if any sample is used as a medoid and if so, mark that.
|
||||
for isample, sample := range samples {
|
||||
samples_used[isample] = false
|
||||
mindist := math.Inf(+1)
|
||||
for imean, mean := range means {
|
||||
dist := lab_dist(sample, mean)
|
||||
if dist < mindist {
|
||||
mindist = dist
|
||||
clusters[isample] = imean
|
||||
}
|
||||
|
||||
// Mark samples which are used as a medoid.
|
||||
if lab_eq(sample, mean) {
|
||||
samples_used[isample] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compute new means according to the samples.
|
||||
for imean := range means {
|
||||
// The new mean is the average of all samples belonging to it..
|
||||
nsamples := 0
|
||||
newmean := lab_t{0.0, 0.0, 0.0}
|
||||
for isample, sample := range samples {
|
||||
if clusters[isample] == imean {
|
||||
nsamples++
|
||||
newmean.L += sample.L
|
||||
newmean.A += sample.A
|
||||
newmean.B += sample.B
|
||||
}
|
||||
}
|
||||
if nsamples > 0 {
|
||||
newmean.L /= float64(nsamples)
|
||||
newmean.A /= float64(nsamples)
|
||||
newmean.B /= float64(nsamples)
|
||||
} else {
|
||||
// That mean doesn't have any samples? Get a new mean from the sample list!
|
||||
var inewmean int
|
||||
for inewmean = rand.Intn(len(samples_used)); samples_used[inewmean]; inewmean = rand.Intn(len(samples_used)) {
|
||||
}
|
||||
newmean = samples[inewmean]
|
||||
samples_used[inewmean] = true
|
||||
}
|
||||
|
||||
// But now we still need to check whether the new mean is an allowed color.
|
||||
if nsamples > 0 && check(newmean) {
|
||||
// It does, life's good (TM)
|
||||
means[imean] = newmean
|
||||
} else {
|
||||
// New mean isn't an allowed color or doesn't have any samples!
|
||||
// Switch to medoid mode and pick the closest (unused) sample.
|
||||
// This should always find something thanks to len(samples) >= colorsCount
|
||||
mindist := math.Inf(+1)
|
||||
for isample, sample := range samples {
|
||||
if !samples_used[isample] {
|
||||
dist := lab_dist(sample, newmean)
|
||||
if dist < mindist {
|
||||
mindist = dist
|
||||
newmean = sample
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return labs2cols(means), nil
|
||||
}
|
||||
|
||||
// A wrapper which uses common parameters.
|
||||
func SoftPalette(colorsCount int) ([]Color, error) {
|
||||
return SoftPaletteEx(colorsCount, SoftPaletteSettings{nil, 50, false})
|
||||
}
|
||||
|
||||
func in(haystack []lab_t, upto int, needle lab_t) bool {
|
||||
for i := 0; i < upto && i < len(haystack); i++ {
|
||||
if haystack[i] == needle {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
const LAB_DELTA = 1e-6
|
||||
|
||||
func lab_eq(lab1, lab2 lab_t) bool {
|
||||
return math.Abs(lab1.L-lab2.L) < LAB_DELTA &&
|
||||
math.Abs(lab1.A-lab2.A) < LAB_DELTA &&
|
||||
math.Abs(lab1.B-lab2.B) < LAB_DELTA
|
||||
}
|
||||
|
||||
// That's faster than using colorful's DistanceLab since we would have to
|
||||
// convert back and forth for that. Here is no conversion.
|
||||
func lab_dist(lab1, lab2 lab_t) float64 {
|
||||
return math.Sqrt(sq(lab1.L-lab2.L) + sq(lab1.A-lab2.A) + sq(lab1.B-lab2.B))
|
||||
}
|
||||
|
||||
func labs2cols(labs []lab_t) (cols []Color) {
|
||||
cols = make([]Color, len(labs))
|
||||
for k, v := range labs {
|
||||
cols[k] = Lab(v.L, v.A, v.B)
|
||||
}
|
||||
return cols
|
||||
}
|
25
vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go
generated
vendored
Normal file
25
vendor/github.com/lucasb-eyer/go-colorful/warm_palettegen.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package colorful
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// Uses the HSV color space to generate colors with similar S,V but distributed
|
||||
// evenly along their Hue. This is fast but not always pretty.
|
||||
// If you've got time to spare, use Lab (the non-fast below).
|
||||
func FastWarmPalette(colorsCount int) (colors []Color) {
|
||||
colors = make([]Color, colorsCount)
|
||||
|
||||
for i := 0; i < colorsCount; i++ {
|
||||
colors[i] = Hsv(float64(i)*(360.0/float64(colorsCount)), 0.55+rand.Float64()*0.2, 0.35+rand.Float64()*0.2)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func WarmPalette(colorsCount int) ([]Color, error) {
|
||||
warmy := func(l, a, b float64) bool {
|
||||
_, c, _ := LabToHcl(l, a, b)
|
||||
return 0.1 <= c && c <= 0.4 && 0.2 <= l && l <= 0.5
|
||||
}
|
||||
return SoftPaletteEx(colorsCount, SoftPaletteSettings{warmy, 50, true})
|
||||
}
|
24
vendor/github.com/matryer/is/.gitignore
generated
vendored
Normal file
24
vendor/github.com/matryer/is/.gitignore
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
40
vendor/github.com/matryer/is/.travis.yml
generated
vendored
Normal file
40
vendor/github.com/matryer/is/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
os:
|
||||
- linux
|
||||
- osx
|
||||
- windows
|
||||
|
||||
language: go
|
||||
|
||||
sudo: required
|
||||
|
||||
go:
|
||||
# "1.x" always refers to the latest Go version, inc. the patch release.
|
||||
# e.g. "1.x" is 1.13 until 1.13.1 is available.
|
||||
- 1.x
|
||||
- 1.6.x
|
||||
- 1.7.x
|
||||
- 1.8.x
|
||||
- 1.9.x
|
||||
- 1.10.x
|
||||
- 1.11.x
|
||||
- 1.12.x
|
||||
- 1.13.x
|
||||
- 1.14.x
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- os: windows
|
||||
go: tip
|
||||
exclude:
|
||||
# OSX 1.6.4 is not present in travis.
|
||||
# https://github.com/travis-ci/travis-ci/issues/10309
|
||||
- go: 1.6.x
|
||||
os: osx
|
||||
|
||||
install:
|
||||
- go get -d -v ./...
|
||||
|
||||
script:
|
||||
- go build -v ./...
|
||||
- go test ./...
|
21
vendor/github.com/matryer/is/LICENSE
generated
vendored
Normal file
21
vendor/github.com/matryer/is/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2018 Mat Ryer
|
||||
|
||||
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.
|
43
vendor/github.com/matryer/is/README.md
generated
vendored
Normal file
43
vendor/github.com/matryer/is/README.md
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# is [](http://godoc.org/github.com/matryer/is) [](https://goreportcard.com/report/github.com/matryer/is) [](https://travis-ci.org/matryer/is)
|
||||
Professional lightweight testing mini-framework for Go.
|
||||
|
||||
* Easy to write and read
|
||||
* [Beautifully simple API](https://pkg.go.dev/github.com/matryer/is) with everything you need: `is.Equal`, `is.True`, `is.NoErr`, and `is.Fail`
|
||||
* Use comments to add descriptions (which show up when tests fail)
|
||||
|
||||
Failures are very easy to read:
|
||||
|
||||

|
||||
|
||||
### Usage
|
||||
|
||||
The following code shows a range of useful ways you can use
|
||||
the helper methods:
|
||||
|
||||
```go
|
||||
func Test(t *testing.T) {
|
||||
|
||||
is := is.New(t)
|
||||
|
||||
signedin, err := isSignedIn(ctx)
|
||||
is.NoErr(err) // isSignedIn error
|
||||
is.Equal(signedin, true) // must be signed in
|
||||
|
||||
body := readBody(r)
|
||||
is.True(strings.Contains(body, "Hi there"))
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Color
|
||||
|
||||
To turn off the colors, run `go test` with the `-nocolor` flag,
|
||||
or with the env var [`NO_COLOR` (with any value)](https://no-color.org).
|
||||
|
||||
```
|
||||
go test -nocolor
|
||||
```
|
||||
|
||||
```
|
||||
NO_COLOR=1 go test
|
||||
```
|
64
vendor/github.com/matryer/is/is-1.7.go
generated
vendored
Normal file
64
vendor/github.com/matryer/is/is-1.7.go
generated
vendored
Normal file
@ -0,0 +1,64 @@
|
||||
// +build go1.7
|
||||
|
||||
package is
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// Helper marks the calling function as a test helper function.
|
||||
// When printing file and line information, that function will be skipped.
|
||||
//
|
||||
// Available with Go 1.7 and later.
|
||||
func (is *I) Helper() {
|
||||
is.helpers[callerName(1)] = struct{}{}
|
||||
}
|
||||
|
||||
// callerName gives the function name (qualified with a package path)
|
||||
// for the caller after skip frames (where 0 means the current function).
|
||||
func callerName(skip int) string {
|
||||
// Make room for the skip PC.
|
||||
var pc [1]uintptr
|
||||
n := runtime.Callers(skip+2, pc[:]) // skip + runtime.Callers + callerName
|
||||
if n == 0 {
|
||||
panic("is: zero callers found")
|
||||
}
|
||||
frames := runtime.CallersFrames(pc[:n])
|
||||
frame, _ := frames.Next()
|
||||
return frame.Function
|
||||
}
|
||||
|
||||
// The maximum number of stack frames to go through when skipping helper functions for
|
||||
// the purpose of decorating log messages.
|
||||
const maxStackLen = 50
|
||||
|
||||
var reIsSourceFile = regexp.MustCompile(`is(-1.7)?\.go$`)
|
||||
|
||||
func (is *I) callerinfo() (path string, line int, ok bool) {
|
||||
var pc [maxStackLen]uintptr
|
||||
// Skip two extra frames to account for this function
|
||||
// and runtime.Callers itself.
|
||||
n := runtime.Callers(2, pc[:])
|
||||
if n == 0 {
|
||||
panic("is: zero callers found")
|
||||
}
|
||||
frames := runtime.CallersFrames(pc[:n])
|
||||
var firstFrame, frame runtime.Frame
|
||||
for more := true; more; {
|
||||
frame, more = frames.Next()
|
||||
if reIsSourceFile.MatchString(frame.File) {
|
||||
continue
|
||||
}
|
||||
if firstFrame.PC == 0 {
|
||||
firstFrame = frame
|
||||
}
|
||||
if _, ok := is.helpers[frame.Function]; ok {
|
||||
// Frame is inside a helper function.
|
||||
continue
|
||||
}
|
||||
return frame.File, frame.Line, true
|
||||
}
|
||||
// If no "non-helper" frame is found, the first non is frame is returned.
|
||||
return firstFrame.File, firstFrame.Line, true
|
||||
}
|
23
vendor/github.com/matryer/is/is-before-1.7.go
generated
vendored
Normal file
23
vendor/github.com/matryer/is/is-before-1.7.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
// +build !go1.7
|
||||
|
||||
package is
|
||||
|
||||
import (
|
||||
"regexp"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var reIsSourceFile = regexp.MustCompile("is(-before-1.7)?\\.go$")
|
||||
|
||||
func (is *I) callerinfo() (path string, line int, ok bool) {
|
||||
for i := 0; ; i++ {
|
||||
_, path, line, ok = runtime.Caller(i)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if reIsSourceFile.MatchString(path) {
|
||||
continue
|
||||
}
|
||||
return path, line, true
|
||||
}
|
||||
}
|
403
vendor/github.com/matryer/is/is.go
generated
vendored
Normal file
403
vendor/github.com/matryer/is/is.go
generated
vendored
Normal file
@ -0,0 +1,403 @@
|
||||
// Package is provides a lightweight extension to the
|
||||
// standard library's testing capabilities.
|
||||
//
|
||||
// Comments on the assertion lines are used to add
|
||||
// a description.
|
||||
//
|
||||
// The following failing test:
|
||||
//
|
||||
// func Test(t *testing.T) {
|
||||
// is := is.New(t)
|
||||
// a, b := 1, 2
|
||||
// is.Equal(a, b) // expect to be the same
|
||||
// }
|
||||
//
|
||||
// Will output:
|
||||
//
|
||||
// your_test.go:123: 1 != 2 // expect to be the same
|
||||
//
|
||||
// Usage
|
||||
//
|
||||
// The following code shows a range of useful ways you can use
|
||||
// the helper methods:
|
||||
//
|
||||
// func Test(t *testing.T) {
|
||||
// // always start tests with this
|
||||
// is := is.New(t)
|
||||
//
|
||||
// signedin, err := isSignedIn(ctx)
|
||||
// is.NoErr(err) // isSignedIn error
|
||||
// is.Equal(signedin, true) // must be signed in
|
||||
//
|
||||
// body := readBody(r)
|
||||
// is.True(strings.Contains(body, "Hi there"))
|
||||
// }
|
||||
package is
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// T reports when failures occur.
|
||||
// testing.T implements this interface.
|
||||
type T interface {
|
||||
// Fail indicates that the test has failed but
|
||||
// allowed execution to continue.
|
||||
// Fail is called in relaxed mode (via NewRelaxed).
|
||||
Fail()
|
||||
// FailNow indicates that the test has failed and
|
||||
// aborts the test.
|
||||
// FailNow is called in strict mode (via New).
|
||||
FailNow()
|
||||
}
|
||||
|
||||
// I is the test helper harness.
|
||||
type I struct {
|
||||
t T
|
||||
fail func()
|
||||
out io.Writer
|
||||
colorful bool
|
||||
|
||||
helpers map[string]struct{} // functions to be skipped when writing file/line info
|
||||
}
|
||||
|
||||
var noColorFlag bool
|
||||
|
||||
func init() {
|
||||
var envNoColor bool
|
||||
|
||||
// prefer https://no-color.org (with any value)
|
||||
if _, ok := os.LookupEnv("NO_COLOR"); ok {
|
||||
envNoColor = true
|
||||
}
|
||||
|
||||
if v, ok := os.LookupEnv("IS_NO_COLOR"); ok {
|
||||
if b, err := strconv.ParseBool(v); err == nil {
|
||||
envNoColor = b
|
||||
}
|
||||
}
|
||||
|
||||
flag.BoolVar(&noColorFlag, "nocolor", envNoColor, "turns off colors")
|
||||
}
|
||||
|
||||
// New makes a new testing helper using the specified
|
||||
// T through which failures will be reported.
|
||||
// In strict mode, failures call T.FailNow causing the test
|
||||
// to be aborted. See NewRelaxed for alternative behavior.
|
||||
func New(t T) *I {
|
||||
return &I{t, t.FailNow, os.Stdout, !noColorFlag, map[string]struct{}{}}
|
||||
}
|
||||
|
||||
// NewRelaxed makes a new testing helper using the specified
|
||||
// T through which failures will be reported.
|
||||
// In relaxed mode, failures call T.Fail allowing
|
||||
// multiple failures per test.
|
||||
func NewRelaxed(t T) *I {
|
||||
return &I{t, t.Fail, os.Stdout, !noColorFlag, map[string]struct{}{}}
|
||||
}
|
||||
|
||||
func (is *I) log(args ...interface{}) {
|
||||
s := is.decorate(fmt.Sprint(args...))
|
||||
fmt.Fprintf(is.out, s)
|
||||
is.fail()
|
||||
}
|
||||
|
||||
func (is *I) logf(format string, args ...interface{}) {
|
||||
is.log(fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Fail immediately fails the test.
|
||||
//
|
||||
// func Test(t *testing.T) {
|
||||
// is := is.New(t)
|
||||
// is.Fail() // TODO: write this test
|
||||
// }
|
||||
//
|
||||
// In relaxed mode, execution will continue after a call to
|
||||
// Fail, but that test will still fail.
|
||||
func (is *I) Fail() {
|
||||
is.log("failed")
|
||||
}
|
||||
|
||||
// True asserts that the expression is true. The expression
|
||||
// code itself will be reported if the assertion fails.
|
||||
//
|
||||
// func Test(t *testing.T) {
|
||||
// is := is.New(t)
|
||||
// val := method()
|
||||
// is.True(val != nil) // val should never be nil
|
||||
// }
|
||||
//
|
||||
// Will output:
|
||||
//
|
||||
// your_test.go:123: not true: val != nil
|
||||
func (is *I) True(expression bool) {
|
||||
if !expression {
|
||||
is.log("not true: $ARGS")
|
||||
}
|
||||
}
|
||||
|
||||
// Equal asserts that a and b are equal.
|
||||
//
|
||||
// func Test(t *testing.T) {
|
||||
// is := is.New(t)
|
||||
// a := greet("Mat")
|
||||
// is.Equal(a, "Hi Mat") // greeting
|
||||
// }
|
||||
//
|
||||
// Will output:
|
||||
//
|
||||
// your_test.go:123: Hey Mat != Hi Mat // greeting
|
||||
func (is *I) Equal(a, b interface{}) {
|
||||
if areEqual(a, b) {
|
||||
return
|
||||
}
|
||||
if isNil(a) || isNil(b) {
|
||||
is.logf("%s != %s", is.valWithType(a), is.valWithType(b))
|
||||
} else if reflect.ValueOf(a).Type() == reflect.ValueOf(b).Type() {
|
||||
is.logf("%v != %v", a, b)
|
||||
} else {
|
||||
is.logf("%s != %s", is.valWithType(a), is.valWithType(b))
|
||||
}
|
||||
}
|
||||
|
||||
// New is a method wrapper around the New function.
|
||||
// It allows you to write subtests using a similar
|
||||
// pattern:
|
||||
//
|
||||
// func Test(t *testing.T) {
|
||||
// is := is.New(t)
|
||||
// t.Run("sub", func(t *testing.T) {
|
||||
// is := is.New(t)
|
||||
// // TODO: test
|
||||
// })
|
||||
// }
|
||||
func (is *I) New(t T) *I {
|
||||
return New(t)
|
||||
}
|
||||
|
||||
// NewRelaxed is a method wrapper around the NewRelaxed
|
||||
// method. It allows you to write subtests using a similar
|
||||
// pattern:
|
||||
//
|
||||
// func Test(t *testing.T) {
|
||||
// is := is.NewRelaxed(t)
|
||||
// t.Run("sub", func(t *testing.T) {
|
||||
// is := is.NewRelaxed(t)
|
||||
// // TODO: test
|
||||
// })
|
||||
// }
|
||||
func (is *I) NewRelaxed(t T) *I {
|
||||
return NewRelaxed(t)
|
||||
}
|
||||
|
||||
func (is *I) valWithType(v interface{}) string {
|
||||
if isNil(v) {
|
||||
return "<nil>"
|
||||
}
|
||||
if is.colorful {
|
||||
return fmt.Sprintf("%[1]s%[3]T(%[2]s%[3]v%[1]s)%[2]s", colorType, colorNormal, v)
|
||||
}
|
||||
return fmt.Sprintf("%[1]T(%[1]v)", v)
|
||||
}
|
||||
|
||||
// NoErr asserts that err is nil.
|
||||
//
|
||||
// func Test(t *testing.T) {
|
||||
// is := is.New(t)
|
||||
// val, err := getVal()
|
||||
// is.NoErr(err) // getVal error
|
||||
// is.True(len(val) > 10) // val cannot be short
|
||||
// }
|
||||
//
|
||||
// Will output:
|
||||
//
|
||||
// your_test.go:123: err: not found // getVal error
|
||||
func (is *I) NoErr(err error) {
|
||||
if err != nil {
|
||||
is.logf("err: %s", err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// isNil gets whether the object is nil or not.
|
||||
func isNil(object interface{}) bool {
|
||||
if object == nil {
|
||||
return true
|
||||
}
|
||||
value := reflect.ValueOf(object)
|
||||
kind := value.Kind()
|
||||
if kind >= reflect.Chan && kind <= reflect.Slice && value.IsNil() {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// areEqual gets whether a equals b or not.
|
||||
func areEqual(a, b interface{}) bool {
|
||||
if isNil(a) && isNil(b) {
|
||||
return true
|
||||
}
|
||||
if isNil(a) || isNil(b) {
|
||||
return false
|
||||
}
|
||||
if reflect.DeepEqual(a, b) {
|
||||
return true
|
||||
}
|
||||
aValue := reflect.ValueOf(a)
|
||||
bValue := reflect.ValueOf(b)
|
||||
return aValue == bValue
|
||||
}
|
||||
|
||||
// loadComment gets the Go comment from the specified line
|
||||
// in the specified file.
|
||||
func loadComment(path string, line int) (string, bool) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
defer f.Close()
|
||||
s := bufio.NewScanner(f)
|
||||
i := 1
|
||||
for s.Scan() {
|
||||
if i != line {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
|
||||
text := s.Text()
|
||||
commentI := strings.Index(text, "// ")
|
||||
if commentI == -1 {
|
||||
return "", false // no comment
|
||||
}
|
||||
text = text[commentI+2:]
|
||||
text = strings.TrimSpace(text)
|
||||
return text, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// loadArguments gets the arguments from the function call
|
||||
// on the specified line of the file.
|
||||
func loadArguments(path string, line int) (string, bool) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
defer f.Close()
|
||||
s := bufio.NewScanner(f)
|
||||
i := 1
|
||||
for s.Scan() {
|
||||
if i != line {
|
||||
i++
|
||||
continue
|
||||
}
|
||||
text := s.Text()
|
||||
braceI := strings.Index(text, "(")
|
||||
if braceI == -1 {
|
||||
return "", false
|
||||
}
|
||||
text = text[braceI+1:]
|
||||
cs := bufio.NewScanner(strings.NewReader(text))
|
||||
cs.Split(bufio.ScanBytes)
|
||||
j := 0
|
||||
c := 1
|
||||
for cs.Scan() {
|
||||
switch cs.Text() {
|
||||
case ")":
|
||||
c--
|
||||
case "(":
|
||||
c++
|
||||
}
|
||||
if c == 0 {
|
||||
break
|
||||
}
|
||||
j++
|
||||
}
|
||||
text = text[:j]
|
||||
return text, true
|
||||
}
|
||||
return "", false
|
||||
}
|
||||
|
||||
// decorate prefixes the string with the file and line of the call site
|
||||
// and inserts the final newline if needed and indentation tabs for formatting.
|
||||
// this function was copied from the testing framework and modified.
|
||||
func (is *I) decorate(s string) string {
|
||||
path, lineNumber, ok := is.callerinfo() // decorate + log + public function.
|
||||
file := filepath.Base(path)
|
||||
if ok {
|
||||
// Truncate file name at last file name separator.
|
||||
if index := strings.LastIndex(file, "/"); index >= 0 {
|
||||
file = file[index+1:]
|
||||
} else if index = strings.LastIndex(file, "\\"); index >= 0 {
|
||||
file = file[index+1:]
|
||||
}
|
||||
} else {
|
||||
file = "???"
|
||||
lineNumber = 1
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
// Every line is indented at least one tab.
|
||||
buf.WriteByte('\t')
|
||||
if is.colorful {
|
||||
buf.WriteString(colorFile)
|
||||
}
|
||||
fmt.Fprintf(buf, "%s:%d: ", file, lineNumber)
|
||||
if is.colorful {
|
||||
buf.WriteString(colorNormal)
|
||||
}
|
||||
|
||||
s = escapeFormatString(s)
|
||||
|
||||
lines := strings.Split(s, "\n")
|
||||
if l := len(lines); l > 1 && lines[l-1] == "" {
|
||||
lines = lines[:l-1]
|
||||
}
|
||||
for i, line := range lines {
|
||||
if i > 0 {
|
||||
// Second and subsequent lines are indented an extra tab.
|
||||
buf.WriteString("\n\t\t")
|
||||
}
|
||||
// expand arguments (if $ARGS is present)
|
||||
if strings.Contains(line, "$ARGS") {
|
||||
args, _ := loadArguments(path, lineNumber)
|
||||
line = strings.Replace(line, "$ARGS", args, -1)
|
||||
}
|
||||
buf.WriteString(line)
|
||||
}
|
||||
comment, ok := loadComment(path, lineNumber)
|
||||
if ok {
|
||||
if is.colorful {
|
||||
buf.WriteString(colorComment)
|
||||
}
|
||||
buf.WriteString(" // ")
|
||||
comment = escapeFormatString(comment)
|
||||
buf.WriteString(comment)
|
||||
if is.colorful {
|
||||
buf.WriteString(colorNormal)
|
||||
}
|
||||
}
|
||||
buf.WriteString("\n")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// escapeFormatString escapes strings for use in formatted functions like Sprintf.
|
||||
func escapeFormatString(fmt string) string {
|
||||
return strings.Replace(fmt, "%", "%%", -1)
|
||||
}
|
||||
|
||||
const (
|
||||
colorNormal = "\u001b[39m"
|
||||
colorComment = "\u001b[31m"
|
||||
colorFile = "\u001b[90m"
|
||||
colorType = "\u001b[90m"
|
||||
)
|
9
vendor/github.com/mattn/go-isatty/LICENSE
generated
vendored
Normal file
9
vendor/github.com/mattn/go-isatty/LICENSE
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
|
||||
|
||||
MIT License (Expat)
|
||||
|
||||
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.
|
50
vendor/github.com/mattn/go-isatty/README.md
generated
vendored
Normal file
50
vendor/github.com/mattn/go-isatty/README.md
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
# go-isatty
|
||||
|
||||
[](http://godoc.org/github.com/mattn/go-isatty)
|
||||
[](https://codecov.io/gh/mattn/go-isatty)
|
||||
[](https://coveralls.io/github/mattn/go-isatty?branch=master)
|
||||
[](https://goreportcard.com/report/mattn/go-isatty)
|
||||
|
||||
isatty for golang
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mattn/go-isatty"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
fmt.Println("Is Terminal")
|
||||
} else if isatty.IsCygwinTerminal(os.Stdout.Fd()) {
|
||||
fmt.Println("Is Cygwin/MSYS2 Terminal")
|
||||
} else {
|
||||
fmt.Println("Is Not Terminal")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ go get github.com/mattn/go-isatty
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
## Author
|
||||
|
||||
Yasuhiro Matsumoto (a.k.a mattn)
|
||||
|
||||
## Thanks
|
||||
|
||||
* k-takata: base idea for IsCygwinTerminal
|
||||
|
||||
https://github.com/k-takata/go-iscygpty
|
2
vendor/github.com/mattn/go-isatty/doc.go
generated
vendored
Normal file
2
vendor/github.com/mattn/go-isatty/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package isatty implements interface to isatty
|
||||
package isatty
|
12
vendor/github.com/mattn/go-isatty/go.test.sh
generated
vendored
Normal file
12
vendor/github.com/mattn/go-isatty/go.test.sh
generated
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -e
|
||||
echo "" > coverage.txt
|
||||
|
||||
for d in $(go list ./... | grep -v vendor); do
|
||||
go test -race -coverprofile=profile.out -covermode=atomic "$d"
|
||||
if [ -f profile.out ]; then
|
||||
cat profile.out >> coverage.txt
|
||||
rm profile.out
|
||||
fi
|
||||
done
|
19
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
Normal file
19
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
//go:build (darwin || freebsd || openbsd || netbsd || dragonfly || hurd) && !appengine
|
||||
// +build darwin freebsd openbsd netbsd dragonfly hurd
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
_, err := unix.IoctlGetTermios(int(fd), unix.TIOCGETA)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
16
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
Normal file
16
vendor/github.com/mattn/go-isatty/isatty_others.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
//go:build appengine || js || nacl || wasm
|
||||
// +build appengine js nacl wasm
|
||||
|
||||
package isatty
|
||||
|
||||
// IsTerminal returns true if the file descriptor is terminal which
|
||||
// is always false on js and appengine classic which is a sandboxed PaaS.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
23
vendor/github.com/mattn/go-isatty/isatty_plan9.go
generated
vendored
Normal file
23
vendor/github.com/mattn/go-isatty/isatty_plan9.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
//go:build plan9
|
||||
// +build plan9
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
path, err := syscall.Fd2path(int(fd))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return path == "/dev/cons" || path == "/mnt/term/dev/cons"
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
21
vendor/github.com/mattn/go-isatty/isatty_solaris.go
generated
vendored
Normal file
21
vendor/github.com/mattn/go-isatty/isatty_solaris.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
//go:build solaris && !appengine
|
||||
// +build solaris,!appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
// see: https://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libc/port/gen/isatty.c
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
_, err := unix.IoctlGetTermio(int(fd), unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
19
vendor/github.com/mattn/go-isatty/isatty_tcgets.go
generated
vendored
Normal file
19
vendor/github.com/mattn/go-isatty/isatty_tcgets.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
//go:build (linux || aix || zos) && !appengine
|
||||
// +build linux aix zos
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import "golang.org/x/sys/unix"
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
_, err := unix.IoctlGetTermios(int(fd), unix.TCGETS)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// IsCygwinTerminal return true if the file descriptor is a cygwin or msys2
|
||||
// terminal. This is also always false on this environment.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
125
vendor/github.com/mattn/go-isatty/isatty_windows.go
generated
vendored
Normal file
125
vendor/github.com/mattn/go-isatty/isatty_windows.go
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
//go:build windows && !appengine
|
||||
// +build windows,!appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unicode/utf16"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
objectNameInfo uintptr = 1
|
||||
fileNameInfo = 2
|
||||
fileTypePipe = 3
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
ntdll = syscall.NewLazyDLL("ntdll.dll")
|
||||
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
procGetFileInformationByHandleEx = kernel32.NewProc("GetFileInformationByHandleEx")
|
||||
procGetFileType = kernel32.NewProc("GetFileType")
|
||||
procNtQueryObject = ntdll.NewProc("NtQueryObject")
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Check if GetFileInformationByHandleEx is available.
|
||||
if procGetFileInformationByHandleEx.Find() != nil {
|
||||
procGetFileInformationByHandleEx = nil
|
||||
}
|
||||
}
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
}
|
||||
|
||||
// Check pipe name is used for cygwin/msys2 pty.
|
||||
// Cygwin/MSYS2 PTY has a name like:
|
||||
// \{cygwin,msys}-XXXXXXXXXXXXXXXX-ptyN-{from,to}-master
|
||||
func isCygwinPipeName(name string) bool {
|
||||
token := strings.Split(name, "-")
|
||||
if len(token) < 5 {
|
||||
return false
|
||||
}
|
||||
|
||||
if token[0] != `\msys` &&
|
||||
token[0] != `\cygwin` &&
|
||||
token[0] != `\Device\NamedPipe\msys` &&
|
||||
token[0] != `\Device\NamedPipe\cygwin` {
|
||||
return false
|
||||
}
|
||||
|
||||
if token[1] == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(token[2], "pty") {
|
||||
return false
|
||||
}
|
||||
|
||||
if token[3] != `from` && token[3] != `to` {
|
||||
return false
|
||||
}
|
||||
|
||||
if token[4] != "master" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// getFileNameByHandle use the undocomented ntdll NtQueryObject to get file full name from file handler
|
||||
// since GetFileInformationByHandleEx is not available under windows Vista and still some old fashion
|
||||
// guys are using Windows XP, this is a workaround for those guys, it will also work on system from
|
||||
// Windows vista to 10
|
||||
// see https://stackoverflow.com/a/18792477 for details
|
||||
func getFileNameByHandle(fd uintptr) (string, error) {
|
||||
if procNtQueryObject == nil {
|
||||
return "", errors.New("ntdll.dll: NtQueryObject not supported")
|
||||
}
|
||||
|
||||
var buf [4 + syscall.MAX_PATH]uint16
|
||||
var result int
|
||||
r, _, e := syscall.Syscall6(procNtQueryObject.Addr(), 5,
|
||||
fd, objectNameInfo, uintptr(unsafe.Pointer(&buf)), uintptr(2*len(buf)), uintptr(unsafe.Pointer(&result)), 0)
|
||||
if r != 0 {
|
||||
return "", e
|
||||
}
|
||||
return string(utf16.Decode(buf[4 : 4+buf[0]/2])), nil
|
||||
}
|
||||
|
||||
// IsCygwinTerminal() return true if the file descriptor is a cygwin or msys2
|
||||
// terminal.
|
||||
func IsCygwinTerminal(fd uintptr) bool {
|
||||
if procGetFileInformationByHandleEx == nil {
|
||||
name, err := getFileNameByHandle(fd)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return isCygwinPipeName(name)
|
||||
}
|
||||
|
||||
// Cygwin/msys's pty is a pipe.
|
||||
ft, _, e := syscall.Syscall(procGetFileType.Addr(), 1, fd, 0, 0)
|
||||
if ft != fileTypePipe || e != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
var buf [2 + syscall.MAX_PATH]uint16
|
||||
r, _, e := syscall.Syscall6(procGetFileInformationByHandleEx.Addr(),
|
||||
4, fd, fileNameInfo, uintptr(unsafe.Pointer(&buf)),
|
||||
uintptr(len(buf)*2), 0, 0)
|
||||
if r == 0 || e != 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
l := *(*uint32)(unsafe.Pointer(&buf))
|
||||
return isCygwinPipeName(string(utf16.Decode(buf[2 : 2+l/2])))
|
||||
}
|
21
vendor/github.com/mattn/go-runewidth/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mattn/go-runewidth/LICENSE
generated
vendored
Normal file
@ -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.
|
27
vendor/github.com/mattn/go-runewidth/README.md
generated
vendored
Normal file
27
vendor/github.com/mattn/go-runewidth/README.md
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
go-runewidth
|
||||
============
|
||||
|
||||
[](https://github.com/mattn/go-runewidth/actions?query=workflow%3Atest)
|
||||
[](https://codecov.io/gh/mattn/go-runewidth)
|
||||
[](http://godoc.org/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
|
358
vendor/github.com/mattn/go-runewidth/runewidth.go
generated
vendored
Normal file
358
vendor/github.com/mattn/go-runewidth/runewidth.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
9
vendor/github.com/mattn/go-runewidth/runewidth_appengine.go
generated
vendored
Normal file
9
vendor/github.com/mattn/go-runewidth/runewidth_appengine.go
generated
vendored
Normal file
@ -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
|
||||
}
|
9
vendor/github.com/mattn/go-runewidth/runewidth_js.go
generated
vendored
Normal file
9
vendor/github.com/mattn/go-runewidth/runewidth_js.go
generated
vendored
Normal file
@ -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
|
||||
}
|
81
vendor/github.com/mattn/go-runewidth/runewidth_posix.go
generated
vendored
Normal file
81
vendor/github.com/mattn/go-runewidth/runewidth_posix.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
439
vendor/github.com/mattn/go-runewidth/runewidth_table.go
generated
vendored
Normal file
439
vendor/github.com/mattn/go-runewidth/runewidth_table.go
generated
vendored
Normal file
@ -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},
|
||||
}
|
28
vendor/github.com/mattn/go-runewidth/runewidth_windows.go
generated
vendored
Normal file
28
vendor/github.com/mattn/go-runewidth/runewidth_windows.go
generated
vendored
Normal file
@ -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
|
||||
}
|
26
vendor/github.com/mohae/deepcopy/.gitignore
generated
vendored
Normal file
26
vendor/github.com/mohae/deepcopy/.gitignore
generated
vendored
Normal file
@ -0,0 +1,26 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*~
|
||||
*.out
|
||||
*.log
|
11
vendor/github.com/mohae/deepcopy/.travis.yml
generated
vendored
Normal file
11
vendor/github.com/mohae/deepcopy/.travis.yml
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- tip
|
||||
|
||||
matrix:
|
||||
allow_failures:
|
||||
- go: tip
|
||||
|
||||
script:
|
||||
- go test ./...
|
21
vendor/github.com/mohae/deepcopy/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mohae/deepcopy/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Joel
|
||||
|
||||
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.
|
8
vendor/github.com/mohae/deepcopy/README.md
generated
vendored
Normal file
8
vendor/github.com/mohae/deepcopy/README.md
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
deepCopy
|
||||
========
|
||||
[](https://godoc.org/github.com/mohae/deepcopy)[](https://travis-ci.org/mohae/deepcopy)
|
||||
|
||||
DeepCopy makes deep copies of things: unexported field values are not copied.
|
||||
|
||||
## Usage
|
||||
cpy := deepcopy.Copy(orig)
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user