forked from toolshed/abra
Compare commits
67
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0dd314b53c | ||
|
|
e9f51526f6 | ||
|
|
ae2080fd9d | ||
|
|
8f6ebac2ce | ||
|
|
23d67677c6 | ||
|
|
00d64556bc | ||
|
|
70c0accf7f | ||
|
|
82ae4cb10f | ||
|
|
99d3cc87b2 | ||
|
|
232d6f39e4 | ||
|
|
26089f0886 | ||
|
|
a1de95a8ce | ||
|
|
f4861bf3c1 | ||
|
|
7fb3b2f591 | ||
|
|
0dec68294f | ||
|
|
69ba548dab | ||
|
|
cfa9ff15bb | ||
|
|
ddf7441152 | ||
|
|
20b4a3fb84 | ||
|
|
81f4420a67 | ||
|
|
b13b10be9a | ||
|
|
8137e56d1e | ||
|
|
f3943d25d1 | ||
|
|
9859ff46f5 | ||
|
|
3109a0da82 | ||
|
|
b1146a707e | ||
|
|
6d13d14d75 | ||
|
|
427ec22cab | ||
|
|
1111b69f12 | ||
|
|
1541e6aa6a | ||
|
|
14ee80582a | ||
|
|
e5c65b8fa0 | ||
|
|
e1b10f6020 | ||
|
|
8e4ed7b689
|
||
|
|
a44fde2df2
|
||
|
|
e623f55852 | ||
|
|
11e6f28a60 | ||
|
|
acec067d76 | ||
|
|
f50a57af7a | ||
|
|
776693acc0 | ||
|
|
5dea5f7746 | ||
|
|
1d9a289888 | ||
|
|
c7bd55e371 | ||
|
|
4276337b0f | ||
|
|
90ca856b64 | ||
|
|
f2dd65491d | ||
|
|
0e902ed897 | ||
|
|
db001c1ba4 | ||
|
|
e4215c09aa | ||
|
|
e0e6dcb710 | ||
|
|
e7ddb74a08 | ||
|
|
24a5e6334f | ||
|
|
9d8eb2317e | ||
|
|
5945ea8e1b | ||
|
|
e170d1c971 | ||
|
|
5eba3abb1b | ||
|
|
df5a38e887 | ||
|
|
1e80d111e6
|
||
|
|
7a079b78de | ||
|
|
7524a785ce | ||
|
|
1013f669bb | ||
|
|
aae20f07cc
|
||
|
|
6ef8e1ff52
|
||
|
|
7fb9675b1e
|
||
|
|
d88b478503
|
||
|
|
7a735043cd
|
||
|
|
e610f32c35
|
+15
-13
@@ -3,17 +3,22 @@ kind: pipeline
|
||||
name: coopcloud.tech/abra
|
||||
steps:
|
||||
- name: make check
|
||||
image: golang:1.24
|
||||
image: golang:1.27
|
||||
commands:
|
||||
- make check
|
||||
|
||||
- name: xgettext-go
|
||||
image: git.coopcloud.tech/toolshed/drone-xgettext-go:latest
|
||||
settings:
|
||||
keyword: i18n.G
|
||||
keyword_ctx: i18n.GC
|
||||
out: pkg/i18n/locales/abra.pot
|
||||
comments_tag: translators
|
||||
image: golang:1.27
|
||||
environment:
|
||||
GOPRIVATE: coopcloud.tech
|
||||
commands:
|
||||
- go run git.coopcloud.tech/toolshed/xgettext-go@latest
|
||||
-o pkg/i18n/locales/abra.pot
|
||||
--keyword=i18n.G
|
||||
--keyword-ctx=i18n.GC
|
||||
--sort-output
|
||||
--add-comments-tag="translators"
|
||||
$(find . -name "*.go" -not -path "*vendor*" | sort)
|
||||
depends_on:
|
||||
- make check
|
||||
when:
|
||||
@@ -22,7 +27,7 @@ steps:
|
||||
- tag
|
||||
|
||||
- name: xgettext-go status
|
||||
image: golang:1.24-alpine3.22
|
||||
image: golang:1.26-alpine3.22
|
||||
commands:
|
||||
- apk add patchutils git make
|
||||
- cd /drone/src
|
||||
@@ -38,13 +43,10 @@ steps:
|
||||
- tag
|
||||
|
||||
- name: make test
|
||||
image: golang:1.24
|
||||
image: golang:1.27
|
||||
environment:
|
||||
ABRA_DIR: $HOME/.abra
|
||||
CATL_URL: https://git.coopcloud.tech/toolshed/recipes-catalogue-json.git
|
||||
ABRA_DIR: /root/.abra_test
|
||||
commands:
|
||||
- mkdir -p $HOME/.abra
|
||||
- git clone $CATL_URL $HOME/.abra/catalogue
|
||||
- make test
|
||||
depends_on:
|
||||
- make check
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
# Build image
|
||||
FROM golang:1.24-alpine AS build
|
||||
FROM golang:1.27-alpine AS build
|
||||
|
||||
ENV GOPRIVATE=coopcloud.tech
|
||||
|
||||
@@ -15,8 +15,7 @@ WORKDIR /app
|
||||
|
||||
RUN CGO_ENABLED=0 make build
|
||||
|
||||
# Release image ("slim")
|
||||
FROM alpine:3.19.1
|
||||
FROM alpine:3.24
|
||||
|
||||
RUN apk add --no-cache \
|
||||
ca-certificates \
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
ABRA := ./cmd/abra
|
||||
XGETTEXT := ./bin/xgettext-go
|
||||
XGETTEXT := go run git.coopcloud.tech/toolshed/xgettext-go@latest
|
||||
COMMIT := $(shell git rev-list -1 HEAD)
|
||||
GOPATH := $(shell go env GOPATH)
|
||||
GOVERSION := 1.24
|
||||
GOVERSION := 1.26
|
||||
LDFLAGS := "-X 'main.Commit=$(COMMIT)'"
|
||||
DIST_LDFLAGS := $(LDFLAGS)" -s -w"
|
||||
BFLAGS := -v -trimpath
|
||||
@@ -40,7 +40,7 @@ check:
|
||||
(echo "gofmt: formatting issue - run 'make format' to resolve" && exit 1)
|
||||
|
||||
test:
|
||||
@go test ./... -cover -v
|
||||
@go test ./... -cover -v -p 1
|
||||
|
||||
find-tests:
|
||||
@find . -name "*_test.go"
|
||||
@@ -62,8 +62,8 @@ update-po:
|
||||
done
|
||||
|
||||
.PHONY: update-pot
|
||||
update-pot: $(XGETTEXT)
|
||||
@${XGETTEXT} \
|
||||
update-pot:
|
||||
@$(XGETTEXT) \
|
||||
-o pkg/i18n/locales/$(DOMAIN).pot \
|
||||
--keyword=i18n.G \
|
||||
--keyword-ctx=i18n.GC \
|
||||
@@ -71,11 +71,6 @@ update-pot: $(XGETTEXT)
|
||||
--add-comments-tag="translators" \
|
||||
$$(find . -name "*.go" -not -path "*vendor*" | sort)
|
||||
|
||||
${XGETTEXT}:
|
||||
@mkdir -p ./bin && \
|
||||
wget -O ./bin/xgettext-go https://git.coopcloud.tech/toolshed/xgettext-go/raw/branch/main/xgettext-go && \
|
||||
chmod +x ./bin/xgettext-go
|
||||
|
||||
.PHONY: update-pot-po-metadata
|
||||
update-pot-po-metadata:
|
||||
@sed -i "s/charset=CHARSET/charset=UTF-8/g" pkg/i18n/locales/*.po pkg/i18n/locales/*.pot
|
||||
|
||||
+6
-5
@@ -151,11 +151,12 @@ checkout as-is. Recipe commit hashes are also supported as values for
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
SendRegistryAuth: true,
|
||||
}
|
||||
compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env)
|
||||
if err != nil {
|
||||
|
||||
+5
-1
@@ -112,7 +112,11 @@ var AppNewCommand = &cobra.Command{
|
||||
}
|
||||
}
|
||||
|
||||
if len(recipeVersions) > 0 {
|
||||
if recipeVersion != "" {
|
||||
if _, err := recipe.EnsureVersion(recipeVersion); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
} else if len(recipeVersions) > 0 {
|
||||
latest := recipeVersions[len(recipeVersions)-1]
|
||||
for tag := range latest {
|
||||
recipeVersion = tag
|
||||
|
||||
+6
-5
@@ -166,11 +166,12 @@ beforehand. See "abra app backup" for more.`),
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
SendRegistryAuth: true,
|
||||
}
|
||||
|
||||
compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env)
|
||||
|
||||
+7
-6
@@ -178,11 +178,12 @@ beforehand. See "abra app backup" for more.`),
|
||||
|
||||
stackName := app.StackName()
|
||||
deployOpts := stack.Deploy{
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
Composefiles: composeFiles,
|
||||
Namespace: stackName,
|
||||
Prune: false,
|
||||
ResolveImage: stack.ResolveImageAlways,
|
||||
Detach: false,
|
||||
SendRegistryAuth: true,
|
||||
}
|
||||
|
||||
compose, err := appPkg.GetAppComposeConfig(app.Name, deployOpts, app.Env)
|
||||
@@ -238,7 +239,7 @@ beforehand. See "abra app backup" for more.`),
|
||||
if upgradeReleaseNotes == "" {
|
||||
upgradeWarnMessages = append(
|
||||
upgradeWarnMessages,
|
||||
fmt.Sprintf("no release notes available for %s", chosenUpgrade),
|
||||
i18n.G("no release notes for upgrading from %s to %s", deployMeta.Version, chosenUpgrade),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Generated
+61
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1778443072,
|
||||
"narHash": "sha256-zi7/fsqM/kFdNuED//4WOCUtezGtKKqRNORjMvfwjnA=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "da5ad661ba4e5ef59ba743f0d112cbc30e474f32",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
{
|
||||
description = "The Co-op Cloud utility belt";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
self,
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
packages = rec {
|
||||
abra = pkgs.callPackage ./package.nix { };
|
||||
default = abra;
|
||||
};
|
||||
apps = rec {
|
||||
abra = flake-utils.lib.mkApp { drv = self.packages.${system}.abra; };
|
||||
default = abra;
|
||||
};
|
||||
devShells.default = pkgs.mkShell {
|
||||
packages = with pkgs; [
|
||||
go_1_26
|
||||
gnumake
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -1,28 +1,26 @@
|
||||
module coopcloud.tech/abra
|
||||
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.1
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca
|
||||
coopcloud.tech/tagcmp v0.0.0-20260515102403-c26951b55977
|
||||
git.coopcloud.tech/toolshed/godotenv v1.5.2-0.20250103171850-4d0ca41daa5c
|
||||
github.com/AlecAivazis/survey/v2 v2.3.7
|
||||
github.com/charmbracelet/bubbletea v1.3.10
|
||||
github.com/charmbracelet/lipgloss v1.1.0
|
||||
github.com/charmbracelet/log v0.4.2
|
||||
github.com/charmbracelet/log v1.0.0
|
||||
github.com/distribution/reference v0.6.0
|
||||
github.com/docker/cli v28.4.0+incompatible
|
||||
github.com/docker/docker v28.5.2+incompatible
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/go-git/go-git/v5 v5.19.2
|
||||
github.com/google/go-cmp v0.7.0
|
||||
github.com/leonelquinteros/gotext v1.7.2
|
||||
github.com/moby/sys/signal v0.7.1
|
||||
github.com/moby/term v0.5.2
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/schollz/progressbar/v3 v3.18.0
|
||||
golang.org/x/term v0.35.0
|
||||
github.com/schollz/progressbar/v3 v3.19.1
|
||||
golang.org/x/term v0.45.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gotest.tools/v3 v3.5.2
|
||||
)
|
||||
@@ -30,28 +28,28 @@ require (
|
||||
require (
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/BurntSushi/toml v1.5.0 // indirect
|
||||
github.com/BurntSushi/toml v1.6.0 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.3.0 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.4.1 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.3.2 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.10.2 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.4.3 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.11.6 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15 // indirect
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2 // indirect
|
||||
github.com/clipperhouse/displaywidth v0.11.0 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.5.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
|
||||
github.com/docker/distribution v2.8.3+incompatible // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
@@ -61,26 +59,26 @@ require (
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.9.0 // indirect
|
||||
github.com/go-logfmt/logfmt v0.6.1 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
|
||||
github.com/kevinburke/ssh_config v1.4.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.6.0 // indirect
|
||||
github.com/klauspost/compress v1.18.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-isatty v0.0.22 // indirect
|
||||
github.com/mattn/go-localereader v0.0.1 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.21 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/miekg/pkcs11 v1.1.1 // indirect
|
||||
github.com/mitchellh/colorstring v0.0.0-20190213212951-d06e56a500db // indirect
|
||||
@@ -89,7 +87,7 @@ require (
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/morikuni/aec v1.1.0 // indirect
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
|
||||
github.com/muesli/cancelreader v0.2.2 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
@@ -97,43 +95,42 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/runc v1.1.13 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.1.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.5.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/pjbgf/sha1cd v0.6.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.66.1 // indirect
|
||||
github.com/prometheus/procfs v0.17.0 // indirect
|
||||
github.com/prometheus/common v0.67.5 // indirect
|
||||
github.com/prometheus/procfs v0.20.1 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/sirupsen/logrus v1.9.4 // indirect
|
||||
github.com/skeema/knownhosts v1.3.2 // indirect
|
||||
github.com/spf13/pflag v1.0.10 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
|
||||
go.opentelemetry.io/otel v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.3 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
golang.org/x/crypto v0.42.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
golang.org/x/time v0.13.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 // indirect
|
||||
google.golang.org/grpc v1.75.1 // indirect
|
||||
google.golang.org/protobuf v1.36.9 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.42.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.5 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f // indirect
|
||||
golang.org/x/net v0.56.0 // indirect
|
||||
golang.org/x/text v0.39.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 // indirect
|
||||
google.golang.org/grpc v1.80.0 // indirect
|
||||
google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
)
|
||||
@@ -142,23 +139,23 @@ require (
|
||||
github.com/containers/image v3.0.2+incompatible
|
||||
github.com/containers/storage v1.38.2 // indirect
|
||||
github.com/decentral1se/passgen v1.0.1
|
||||
github.com/docker/docker-credential-helpers v0.9.3 // indirect
|
||||
github.com/docker/docker-credential-helpers v0.9.5 // indirect
|
||||
github.com/fvbommel/sortorder v1.1.0 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/gorilla/mux v1.8.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/patternmatcher v0.6.1 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/prometheus/client_golang v1.23.2 // indirect
|
||||
github.com/sergi/go-diff v1.4.0 // indirect
|
||||
github.com/spf13/cobra v1.10.1
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/stretchr/testify v1.12.1
|
||||
github.com/theupdateframework/notary v0.7.0 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
golang.org/x/sys v0.36.0
|
||||
golang.org/x/sys v0.47.0
|
||||
)
|
||||
|
||||
replace github.com/docker/cli v28.4.0+incompatible => git.coopcloud.tech/toolshed/docker-cli v28.5.3-0.20260202112816-30df2d0b3a00+incompatible
|
||||
|
||||
replace github.com/spf13/cobra => github.com/decentral1se/cobra v1.10.2-i18n
|
||||
replace github.com/spf13/cobra => github.com/decentral1se/cobra v1.10.2
|
||||
|
||||
@@ -22,8 +22,8 @@ cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIA
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca h1:gSD53tBAsbIGq4SnFfq+mEep6foekQ2a5ea7b38qkm0=
|
||||
coopcloud.tech/tagcmp v0.0.0-20250818180036-0ec1b205b5ca/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ=
|
||||
coopcloud.tech/tagcmp v0.0.0-20260515102403-c26951b55977 h1:J7I0HFjwVAj/kkX6lwSTHmlXDRjQRsdIFNUUqu55ADY=
|
||||
coopcloud.tech/tagcmp v0.0.0-20260515102403-c26951b55977/go.mod h1:ESVm0wQKcbcFi06jItF3rI7enf4Jt2PvbkWpDDHk1DQ=
|
||||
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
|
||||
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
@@ -51,8 +51,8 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ
|
||||
github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.0.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=
|
||||
github.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
|
||||
github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
|
||||
@@ -81,8 +81,8 @@ github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb0
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2 h1:+vx7roKuyA63nhn5WAunQHLTznkw5W8b1Xc0dNjp83s=
|
||||
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBiRGFrw=
|
||||
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
|
||||
github.com/ProtonMail/go-crypto v1.4.1 h1:9RfcZHqEQUvP8RzecWEUafnZVtEvrBVL9BiF67IQOfM=
|
||||
github.com/ProtonMail/go-crypto v1.4.1/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo=
|
||||
github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
@@ -136,20 +136,20 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
|
||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/bubbletea v1.3.10 h1:otUDHWMMzQSB0Pkc87rm691KZ3SWa4KUlvF9nRvCICw=
|
||||
github.com/charmbracelet/bubbletea v1.3.10/go.mod h1:ORQfo0fk8U+po9VaNvnV95UPWA1BitP1E0N6xJPlHr4=
|
||||
github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI=
|
||||
github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI=
|
||||
github.com/charmbracelet/colorprofile v0.4.3 h1:QPa1IWkYI+AOB+fE+mg/5/4HRMZcaXex9t5KX76i20Q=
|
||||
github.com/charmbracelet/colorprofile v0.4.3/go.mod h1:/zT4BhpD5aGFpqQQqw7a+VtHCzu+zrQtt1zhMt9mR4Q=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
|
||||
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
|
||||
github.com/charmbracelet/x/ansi v0.10.2 h1:ith2ArZS0CJG30cIUfID1LXN7ZFXRCww6RUvAPA+Pzw=
|
||||
github.com/charmbracelet/x/ansi v0.10.2/go.mod h1:HbLdJjQH4UH4AqA2HpRWuWNluRE6zxJH/yteYEYCFa8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/log v1.0.0 h1:HVVVMmfOorfj3BA9i8X8UL69Hoz9lI0PYwXfJvOdRc4=
|
||||
github.com/charmbracelet/log v1.0.0/go.mod h1:uYgY3SmLpwJWxmlrPwXvzVYujxis1vAKRV/0VQB7yWA=
|
||||
github.com/charmbracelet/x/ansi v0.11.6 h1:GhV21SiDz/45W9AnV2R61xZMRri5NlLnl6CVF7ihZW8=
|
||||
github.com/charmbracelet/x/ansi v0.11.6/go.mod h1:2JNYLgQUsyqaiLovhU2Rv/pb8r6ydXKS3NIttu3VGZQ=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15 h1:ur3pZy0o6z/R7EylET877CBxaiE1Sp1GMxoFPAIztPI=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.15/go.mod h1:J1YVbR7MUuEGIFPCaaZ96KDl5NoS0DAWkskup+mOY+Q=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
|
||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
|
||||
github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
|
||||
github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
|
||||
@@ -165,11 +165,13 @@ github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJ
|
||||
github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
|
||||
github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 h1:ChwIKnQN3kcZteTXMgb1wztSgaU+ZemkgWdohwgs8tY=
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/clipperhouse/displaywidth v0.11.0 h1:lBc6kY44VFw+TDx4I8opi/EtL9m20WSEFgwIwO+UVM8=
|
||||
github.com/clipperhouse/displaywidth v0.11.0/go.mod h1:bkrFNkf81G8HyVqmKGxsPufD3JhNl3dSqnGhOoSD/o0=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0 h1:+gs4oBZ2gPfVrKPthwbMzWZDaAFPGYK72F0NJv2v7Vk=
|
||||
github.com/clipperhouse/uax29/v2 v2.7.0/go.mod h1:EFJ2TJMRUaplDxHKj1qAEhCtQPW2tJSwu5BF98AuoVM=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/cloudflare/circl v1.6.1 h1:zqIqSPIndyBh1bjLVVDHMPpVKqp8Su/V+6MeDzzQBQ0=
|
||||
github.com/cloudflare/circl v1.6.1/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
@@ -297,17 +299,16 @@ github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
|
||||
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
|
||||
github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
|
||||
github.com/cyphar/filepath-securejoin v0.5.0 h1:hIAhkRBMQ8nIeuVwcAoymp7MY4oherZdAxD+m0u9zaw=
|
||||
github.com/cyphar/filepath-securejoin v0.5.0/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1 h1:5CeZ1jPXEiYt3+Z6zqprSAgSWiggmpVyciv8syjIpVE=
|
||||
github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1AX0a9kM5XL+NwKoYSc=
|
||||
github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
|
||||
github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
|
||||
github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
|
||||
github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
|
||||
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/decentral1se/cobra v1.10.2-i18n h1:XR+6AHHfnf4k5NM9f09oLMrEVwz3rkQIAIcqgL8R08g=
|
||||
github.com/decentral1se/cobra v1.10.2-i18n/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/decentral1se/cobra v1.10.2 h1:MZ8Ifi/jRels9sZrpSccDbUlK++3b2HlBODfv0Bh6x0=
|
||||
github.com/decentral1se/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=
|
||||
github.com/decentral1se/passgen v1.0.1 h1:j2AxK/kHKxDHWZZfkJj8Wgae9+O+DYEqR5sjKthIYKA=
|
||||
github.com/decentral1se/passgen v1.0.1/go.mod h1:530V+lNoPhKtkrX2fIVsIfLhkl47CuiOM7HRgi7C+SU=
|
||||
github.com/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
|
||||
@@ -327,8 +328,8 @@ github.com/docker/docker v1.4.2-0.20190924003213-a8608b5b67c7/go.mod h1:eEKB0N0r
|
||||
github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM=
|
||||
github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
|
||||
github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8=
|
||||
github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo=
|
||||
github.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=
|
||||
github.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0=
|
||||
github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q=
|
||||
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
|
||||
@@ -387,12 +388,12 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
|
||||
github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
|
||||
github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
|
||||
github.com/go-git/go-billy/v5 v5.9.0 h1:jItGXszUDRtR/AlferWPTMN4j38BQ88XnXKbilmmBPA=
|
||||
github.com/go-git/go-billy/v5 v5.9.0/go.mod h1:jCnQMLj9eUgGU7+ludSTYoZL/GGmii14RxKFj7ROgHw=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
|
||||
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
|
||||
github.com/go-git/go-git/v5 v5.16.2 h1:fT6ZIOjE5iEnkzKyxTHK1W4HGAsPhqEqiSAssSO77hM=
|
||||
github.com/go-git/go-git/v5 v5.16.2/go.mod h1:4Ge4alE/5gPs30F2H1esi2gPd69R0C39lolkucHBOp8=
|
||||
github.com/go-git/go-git/v5 v5.19.2 h1:wkfn7vOlUBu8ivAWKBWisTiwJK4jYHzTF8Ndv1LyGqY=
|
||||
github.com/go-git/go-git/v5 v5.19.2/go.mod h1:QqCBE1EFN5ddFmrliLQ3/ntRCUjZU3EJuwuB/jWEHjk=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@@ -401,8 +402,8 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
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/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
|
||||
github.com/go-logfmt/logfmt v0.6.1/go.mod h1:EV2pOAQoZaT1ZXZbqDl5hrymndi4SY9ED9/z6CO0XAk=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
@@ -423,8 +424,8 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
|
||||
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
|
||||
github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
|
||||
@@ -524,8 +525,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=
|
||||
github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4=
|
||||
github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
@@ -573,8 +574,8 @@ github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVE
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
|
||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
|
||||
github.com/kevinburke/ssh_config v1.4.0 h1:6xxtP5bZ2E4NF5tuQulISpTO2z8XbtH8cg1PWkxoFkQ=
|
||||
github.com/kevinburke/ssh_config v1.4.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
||||
github.com/kevinburke/ssh_config v1.6.0 h1:J1FBfmuVosPHf5GRdltRLhPJtJpTlMdKTBjRgTaQBFY=
|
||||
github.com/kevinburke/ssh_config v1.6.0/go.mod h1:q2RIzfka+BXARoNexmF9gkxEX7DmvbW9P4hIVx2Kg4M=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@@ -582,8 +583,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.14.2/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/klauspost/compress v1.18.5 h1:/h1gH5Ce+VWNLSWqPzOVn6XBO+vJbCNGvjoaGBFW2IE=
|
||||
github.com/klauspost/compress v1.18.5/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
|
||||
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
||||
github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
@@ -607,8 +608,8 @@ github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+
|
||||
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
|
||||
github.com/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3/go.mod h1:3r6x7q95whyfWQpmGZTu3gk3v2YkMi05HEzl7Tf7YEo=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0 h1:UtrWVfLdarDgc44HcS7pYloGHJUjHV/4FwW4TvVgFr4=
|
||||
github.com/lucasb-eyer/go-colorful v1.4.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
@@ -621,13 +622,13 @@ github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHP
|
||||
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.22 h1:j8l17JJ9i6VGPUFUYoTUKPSgKe/83EYU2zBC7YNKMw4=
|
||||
github.com/mattn/go-isatty v0.0.22/go.mod h1:ZXfXG4SQHsB/w3ZeOYbR0PrPwLy+n6xiMrJlRFqopa4=
|
||||
github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4=
|
||||
github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw=
|
||||
github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-runewidth v0.0.21 h1:jJKAZiQH+2mIinzCJIaIG9Be1+0NR+5sz/lYEEjdM8w=
|
||||
github.com/mattn/go-runewidth v0.0.21/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs=
|
||||
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-shellwords v1.0.6/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
|
||||
github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
|
||||
@@ -653,8 +654,8 @@ github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6U
|
||||
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
|
||||
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
|
||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
|
||||
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/patternmatcher v0.6.1 h1:qlhtafmr6kgMIJjKJMDmMWq7WLkKIo23hsrpR3x084U=
|
||||
github.com/moby/patternmatcher v0.6.1/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
|
||||
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
|
||||
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
|
||||
github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
|
||||
@@ -679,8 +680,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ=
|
||||
github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D264iyp3TiX5OmNcI5cIARiQI=
|
||||
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6/go.mod h1:CJlz5H+gyd6CUWT45Oy4q24RdLyn7Md9Vj2/ldJBSIo=
|
||||
@@ -750,14 +752,13 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pjbgf/sha1cd v0.5.0 h1:a+UkboSi1znleCDUNT3M5YxjOnN1fz2FhN48FlwCxs0=
|
||||
github.com/pjbgf/sha1cd v0.5.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||
github.com/pjbgf/sha1cd v0.6.0 h1:3WJ8Wz8gvDz29quX1OcEmkAlUg9diU4GxJHqs0/XiwU=
|
||||
github.com/pjbgf/sha1cd v0.6.0/go.mod h1:lhpGlyHLpQZoxMv8HcgXvZEhcGs0PG/vsZnEJ7H0iCM=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
|
||||
github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@@ -779,8 +780,8 @@ github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7q
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.66.1 h1:h5E0h5/Y8niHc5DlaLlWLArTQI7tMrsfQjHV+d9ZoGs=
|
||||
github.com/prometheus/common v0.66.1/go.mod h1:gcaUsgf3KfRSwHY4dIMXLPV0K/Wg1oZ8+SbZk/HH/dA=
|
||||
github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
|
||||
github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
|
||||
github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
@@ -791,8 +792,8 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
|
||||
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
|
||||
github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
|
||||
github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
@@ -805,8 +806,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
|
||||
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
||||
github.com/schollz/progressbar/v3 v3.18.0 h1:uXdoHABRFmNIjUfte/Ex7WtuyVslrw2wVPQmCN62HpA=
|
||||
github.com/schollz/progressbar/v3 v3.18.0/go.mod h1:IsO3lpbaGuzh8zIMzgY3+J8l4C8GjO0Y9S69eFvNsec=
|
||||
github.com/schollz/progressbar/v3 v3.19.1 h1:iv8BgwOvdML/S3p84uBpy/IMigv4U9594vPZYa2EdrU=
|
||||
github.com/schollz/progressbar/v3 v3.19.1/go.mod h1:LFL7jqimKxfhero4K1eCkUr/6R39AgQeiPCJtlTWIW8=
|
||||
github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U=
|
||||
github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
|
||||
github.com/seccomp/libseccomp-golang v0.9.2-0.20210429002308-3879420cc921/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
|
||||
@@ -821,10 +822,10 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/skeema/knownhosts v1.3.1 h1:X2osQ+RAjK76shCbvhHHHVl3ZlgDm8apHEHFqRjnBY8=
|
||||
github.com/skeema/knownhosts v1.3.1/go.mod h1:r7KTdC8l4uxWRyK2TpQZ/1o5HaSzh06ePQNxPwTcfiY=
|
||||
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||
github.com/skeema/knownhosts v1.3.2 h1:EDL9mgf4NzwMXCTfaxSD/o/a5fxDw/xL9nkU28JjdBg=
|
||||
github.com/skeema/knownhosts v1.3.2/go.mod h1:bEg3iQAuw+jyiw+484wwFJoKSLwcfd7fqRy+N0QTiow=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
@@ -854,8 +855,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE=
|
||||
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
|
||||
github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
|
||||
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI=
|
||||
@@ -912,39 +913,40 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0 h1:vl9obrcoWVKp/lwl8tRE33853I8Xru9HFbw/skNeLs8=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.38.0/go.mod h1:GAXRxmLJcVM3u22IjTg74zWBrRCKq8BnOqUVLodpcpw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
|
||||
go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
|
||||
go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0 h1:MdKucPl/HbzckWWEisiNqMPhRrAOQX8r4jTuGr636gk=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.42.0/go.mod h1:RolT8tWtfHcjajEH5wFIZ4Dgh5jpPdFXYV9pTAk/qjc=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0/go.mod h1:2qXPNBX1OVRC0IwOnfo1ljoid+RD0QK3443EaqVlsOU=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
|
||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||
go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
|
||||
go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
|
||||
go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
|
||||
go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
|
||||
go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE=
|
||||
go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0 h1:IQRWgT5srOCYfiWnpqUYz9CVmbO8bFmKcwYxpuCSL2g=
|
||||
go.opentelemetry.io/proto/otlp v1.10.0/go.mod h1:/CV4QoCR/S9yaPj8utp3lvQPoqMtxXdzn7ozvvozVqk=
|
||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0=
|
||||
go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8=
|
||||
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
|
||||
go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
|
||||
go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
|
||||
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
|
||||
go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
|
||||
golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
@@ -963,8 +965,8 @@ golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWP
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
|
||||
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -975,8 +977,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f h1:W3F4c+6OLc6H2lb//N1q4WpJkhzJCK5J6kUi1NTVXfM=
|
||||
golang.org/x/exp v0.0.0-20260410095643-746e56fc9e2f/go.mod h1:J1xhfL/vlindoeF/aINzNzt2Bket5bjo9sdOYzOsU80=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -1040,8 +1042,8 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -1136,14 +1138,13 @@ golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
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.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ=
|
||||
golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA=
|
||||
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@@ -1153,16 +1154,16 @@ golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
|
||||
golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.13.0 h1:eUlYslOIt32DgYD6utsuUeHs4d7AsEYLuIAdg7FlYgI=
|
||||
golang.org/x/time v0.13.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -1212,8 +1213,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
|
||||
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
|
||||
google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
@@ -1258,10 +1259,10 @@ google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfG
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200527145253-8367513e4ece/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4 h1:8XJ4pajGwOlasW+L13MnEGA8W4115jJySQtVfS2/IBU=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4/go.mod h1:NnuHhy+bxcg30o7FnVAZbXsPHUDQ9qKWAQKCD7VxFtk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4 h1:i8QOKZfYg6AbGVZzUAY3LrNWCKF8O6zFisU9Wl9RER4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250929231259-57b25ae835d4/go.mod h1:HSkG/KdJWusxU1F6CNrwNDjBMgisKxGnc5dAZfT0mjQ=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9 h1:VPWxll4HlMw1Vs/qXtN7BvhZqsS9cdAittCNvVENElA=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:7QBABkRtR8z+TEnmXTqIqwJLlzrZKVfAUm7tY3yGv0M=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9 h1:m8qni9SQFH0tJc1X0vmnpw/0t+AImlSvp30sEupozUg=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260401024825-9d38bb4040a9/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
@@ -1281,8 +1282,8 @@ google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTp
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
|
||||
google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI=
|
||||
google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
|
||||
google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
@@ -1296,8 +1297,8 @@ google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlba
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU=
|
||||
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
{
|
||||
buildGo126Module,
|
||||
fetchgit,
|
||||
lib,
|
||||
installShellFiles,
|
||||
}:
|
||||
|
||||
buildGo126Module rec {
|
||||
pname = "abra";
|
||||
version = "0.13.0-beta";
|
||||
rev = "06a57ded025a43c80f94d4e65299add8a31830dc";
|
||||
|
||||
src = fetchgit {
|
||||
url = "https://git.coopcloud.tech/toolshed/abra.git";
|
||||
tag = version;
|
||||
hash = "sha256-rgoK0TY0WLSQ39lPvVM80zW/qJF40VFBSxYDOaKXZQo=";
|
||||
};
|
||||
|
||||
vendorHash = null;
|
||||
|
||||
nativeBuildInputs = [
|
||||
installShellFiles
|
||||
];
|
||||
|
||||
env.CGO_ENABLED = 0;
|
||||
buildPhase = ''
|
||||
runHook preBuild
|
||||
go build -ldflags="-s -w -X 'main.Commit=${rev}' -X 'main.Version=${version}'" ./cmd/abra
|
||||
runHook postBuild
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
install -D abra $out/bin/abra
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
postInstall = ''
|
||||
export ABRA_DIR="$out"
|
||||
$out/bin/abra autocomplete bash >abra.bash
|
||||
$out/bin/abra autocomplete fish >abra.fish
|
||||
$out/bin/abra autocomplete zsh >abra.zsh
|
||||
installShellCompletion abra.{bash,fish,zsh}
|
||||
'';
|
||||
|
||||
meta = with lib; {
|
||||
description = "The Co-op Cloud utility belt";
|
||||
homepage = "https://docs.coopcloud.tech/abra";
|
||||
changelog = "https://git.coopcloud.tech/toolshed/abra/releases/tag/${version}";
|
||||
mainProgram = "abra";
|
||||
license = licenses.gpl3Plus;
|
||||
maintainers = "devydave";
|
||||
};
|
||||
}
|
||||
+67
-18
@@ -10,46 +10,83 @@ import (
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/envfile"
|
||||
"coopcloud.tech/abra/pkg/recipe"
|
||||
testPkg "coopcloud.tech/abra/pkg/test"
|
||||
recipePkg "coopcloud.tech/abra/pkg/recipe"
|
||||
"coopcloud.tech/abra/pkg/test"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
expectedAppEnv = envfile.AppEnv{
|
||||
"DOMAIN": test.AppName,
|
||||
"RECIPE": test.RecipeName,
|
||||
}
|
||||
|
||||
expectedApp = appPkg.App{
|
||||
Name: test.AppName,
|
||||
Recipe: recipePkg.Get(expectedAppEnv["RECIPE"]),
|
||||
Domain: expectedAppEnv["DOMAIN"],
|
||||
Env: expectedAppEnv,
|
||||
Path: expectedAppFile.Path,
|
||||
Server: expectedAppFile.Server,
|
||||
}
|
||||
|
||||
expectedAppFile = appPkg.AppFile{
|
||||
Path: test.AppEnvPath,
|
||||
Server: test.ServerName,
|
||||
}
|
||||
|
||||
expectedAppFiles = map[string]appPkg.AppFile{
|
||||
test.AppName: expectedAppFile,
|
||||
}
|
||||
)
|
||||
|
||||
func TestNewApp(t *testing.T) {
|
||||
app, err := appPkg.NewApp(testPkg.ExpectedAppEnv, testPkg.AppName, testPkg.ExpectedAppFile)
|
||||
app, err := appPkg.NewApp(expectedAppEnv, test.AppName, expectedAppFile)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(app, testPkg.ExpectedApp) {
|
||||
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, testPkg.ExpectedApp)
|
||||
|
||||
if !reflect.DeepEqual(app, expectedApp) {
|
||||
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, expectedApp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadAppEnvFile(t *testing.T) {
|
||||
app, err := appPkg.ReadAppEnvFile(testPkg.ExpectedAppFile, testPkg.AppName)
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
app, err := appPkg.ReadAppEnvFile(expectedAppFile, test.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(app, testPkg.ExpectedApp) {
|
||||
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, testPkg.ExpectedApp)
|
||||
|
||||
if !reflect.DeepEqual(app, expectedApp) {
|
||||
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, expectedApp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetApp(t *testing.T) {
|
||||
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
app, err := appPkg.GetApp(expectedAppFiles, test.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(app, testPkg.ExpectedApp) {
|
||||
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, testPkg.ExpectedApp)
|
||||
|
||||
if !reflect.DeepEqual(app, expectedApp) {
|
||||
t.Fatalf("did not get expected app type. Expected: %s; Got: %s", app, expectedApp)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetComposeFiles(t *testing.T) {
|
||||
r := recipe.Get("abra-test-recipe")
|
||||
err := r.EnsureExists()
|
||||
if err != nil {
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
r := recipe.Get(test.AbraTestRecipe)
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -94,7 +131,10 @@ func TestGetComposeFiles(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetComposeFilesError(t *testing.T) {
|
||||
r := recipe.Get("abra-test-recipe")
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
r := recipe.Get(test.AbraTestRecipe)
|
||||
err := r.EnsureExists()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -186,26 +226,32 @@ func TestFilters(t *testing.T) {
|
||||
|
||||
func compareFilter(t *testing.T, f1 filters.Args, f2 map[string]map[string]bool) {
|
||||
t.Helper()
|
||||
|
||||
j1, err := f1.MarshalJSON()
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
j2, err := json.Marshal(f2)
|
||||
if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
if diff := cmp.Diff(string(j2), string(j1)); diff != "" {
|
||||
t.Errorf("filters mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWriteRecipeVersionOverwrite(t *testing.T) {
|
||||
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
app, err := appPkg.GetApp(expectedAppFiles, test.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer t.Cleanup(func() {
|
||||
t.Cleanup(func() {
|
||||
if err := app.WipeRecipeVersion(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -217,7 +263,7 @@ func TestWriteRecipeVersionOverwrite(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
app, err = appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
|
||||
app, err = appPkg.GetApp(expectedAppFiles, test.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -226,7 +272,10 @@ func TestWriteRecipeVersionOverwrite(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWriteRecipeVersionUnknown(t *testing.T) {
|
||||
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
app, err := appPkg.GetApp(expectedAppFiles, test.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"testing"
|
||||
|
||||
appPkg "coopcloud.tech/abra/pkg/app"
|
||||
"coopcloud.tech/abra/pkg/test"
|
||||
testPkg "coopcloud.tech/abra/pkg/test"
|
||||
stack "coopcloud.tech/abra/pkg/upstream/stack"
|
||||
|
||||
@@ -11,8 +12,8 @@ import (
|
||||
)
|
||||
|
||||
func TestGetTimeoutFromLabel(t *testing.T) {
|
||||
testPkg.MkServerAppRecipe()
|
||||
defer testPkg.RmServerAppRecipe()
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
tests := []struct {
|
||||
configuredTimeout string
|
||||
@@ -25,7 +26,7 @@ func TestGetTimeoutFromLabel(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
|
||||
app, err := appPkg.GetApp(expectedAppFiles, testPkg.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -10,8 +10,9 @@ import (
|
||||
func TestFindAbraConfig(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
Dir string
|
||||
Config string
|
||||
@@ -51,8 +52,9 @@ func TestFindAbraConfig(t *testing.T) {
|
||||
func TestLoadAbraConfigGetAbraDir(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Setenv("ABRA_DIR", "")
|
||||
|
||||
t.Run("default", func(t *testing.T) {
|
||||
@@ -67,7 +69,7 @@ func TestLoadAbraConfigGetAbraDir(t *testing.T) {
|
||||
t.Cleanup(func() { os.Chdir(wd) })
|
||||
err = os.Chdir(filepath.Join(wd, "testdata/abraconfig1"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfg := LoadAbraConfig()
|
||||
@@ -81,7 +83,7 @@ func TestLoadAbraConfigGetAbraDir(t *testing.T) {
|
||||
t.Cleanup(func() { os.Chdir(wd) })
|
||||
err := os.Chdir(filepath.Join(wd, "testdata/abraconfig2"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfg := LoadAbraConfig()
|
||||
@@ -104,8 +106,9 @@ func TestLoadAbraConfigGetAbraDir(t *testing.T) {
|
||||
func TestLoadAbraConfigServersDir(t *testing.T) {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Setenv("ABRA_DIR", "")
|
||||
|
||||
t.Run("default", func(t *testing.T) {
|
||||
@@ -120,7 +123,7 @@ func TestLoadAbraConfigServersDir(t *testing.T) {
|
||||
t.Cleanup(func() { os.Chdir(wd) })
|
||||
err = os.Chdir(filepath.Join(wd, "testdata/abraconfig1"))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cfg := LoadAbraConfig()
|
||||
|
||||
+67
-29
@@ -10,48 +10,73 @@ import (
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/envfile"
|
||||
"coopcloud.tech/abra/pkg/recipe"
|
||||
testPkg "coopcloud.tech/abra/pkg/test"
|
||||
"coopcloud.tech/abra/pkg/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
var (
|
||||
expectedAppEnv = envfile.AppEnv{
|
||||
"DOMAIN": test.AppName,
|
||||
"RECIPE": test.RecipeName,
|
||||
}
|
||||
|
||||
expectedAppFile = appPkg.AppFile{
|
||||
Path: test.AppEnvPath,
|
||||
Server: test.ServerName,
|
||||
}
|
||||
|
||||
expectedAppFiles = map[string]appPkg.AppFile{
|
||||
test.AppName: expectedAppFile,
|
||||
}
|
||||
)
|
||||
|
||||
func TestGetAllFoldersInDirectory(t *testing.T) {
|
||||
folders, err := config.GetAllFoldersInDirectory(testPkg.TestDir)
|
||||
folders, err := config.GetAllFoldersInDirectory(test.TestDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(folders, testPkg.TFolders) {
|
||||
t.Fatalf("did not get expected folders. Expected: (%s), Got: (%s)", strings.Join(testPkg.TFolders, ","), strings.Join(folders, ","))
|
||||
|
||||
if !reflect.DeepEqual(folders, test.TFolders) {
|
||||
t.Fatalf("did not get expected folders. Expected: (%s), Got: (%s)", strings.Join(test.TFolders, ","), strings.Join(folders, ","))
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAllFilesInDirectory(t *testing.T) {
|
||||
files, err := config.GetAllFilesInDirectory(testPkg.TestDir)
|
||||
files, err := config.GetAllFilesInDirectory(test.TestDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
var fileNames []string
|
||||
for _, file := range files {
|
||||
fileNames = append(fileNames, file.Name())
|
||||
}
|
||||
if !reflect.DeepEqual(fileNames, testPkg.TFiles) {
|
||||
t.Fatalf("did not get expected files. Expected: (%s), Got: (%s)", strings.Join(testPkg.TFiles, ","), strings.Join(fileNames, ","))
|
||||
|
||||
if !reflect.DeepEqual(fileNames, test.TFiles) {
|
||||
t.Fatalf("did not get expected files. Expected: (%s), Got: (%s)", strings.Join(test.TFiles, ","), strings.Join(fileNames, ","))
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadEnv(t *testing.T) {
|
||||
env, err := envfile.ReadEnv(testPkg.ExpectedAppFile.Path)
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
env, err := envfile.ReadEnv(expectedAppFile.Path)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(env, testPkg.ExpectedAppEnv) {
|
||||
|
||||
if !reflect.DeepEqual(env, expectedAppEnv) {
|
||||
t.Fatal("did not get expected application settings")
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadAbraShEnvVars(t *testing.T) {
|
||||
r := recipe.Get("abra-test-recipe")
|
||||
err := r.EnsureExists()
|
||||
if err != nil {
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
r := recipe.Get(test.AbraTestRecipe)
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -78,9 +103,11 @@ func TestReadAbraShEnvVars(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestReadAbraShCmdNames(t *testing.T) {
|
||||
r := recipe.Get("abra-test-recipe")
|
||||
err := r.EnsureExists()
|
||||
if err != nil {
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
r := recipe.Get(test.AbraTestRecipe)
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -102,9 +129,11 @@ func TestReadAbraShCmdNames(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckEnv(t *testing.T) {
|
||||
r := recipe.Get("abra-test-recipe")
|
||||
err := r.EnsureExists()
|
||||
if err != nil {
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
r := recipe.Get(test.AbraTestRecipe)
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -135,9 +164,11 @@ func TestCheckEnv(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCheckEnvError(t *testing.T) {
|
||||
r := recipe.Get("abra-test-recipe")
|
||||
err := r.EnsureExists()
|
||||
if err != nil {
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
r := recipe.Get(test.AbraTestRecipe)
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -170,9 +201,11 @@ func TestCheckEnvError(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnvVarCommentsRemoved(t *testing.T) {
|
||||
r := recipe.Get("abra-test-recipe")
|
||||
err := r.EnsureExists()
|
||||
if err != nil {
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
r := recipe.Get(test.AbraTestRecipe)
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -201,9 +234,11 @@ func TestEnvVarCommentsRemoved(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestEnvVarModifiersIncluded(t *testing.T) {
|
||||
r := recipe.Get("abra-test-recipe")
|
||||
err := r.EnsureExists()
|
||||
if err != nil {
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
r := recipe.Get(test.AbraTestRecipe)
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -225,7 +260,10 @@ func TestEnvVarModifiersIncluded(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestNoOverwriteNonVersionEnvVars(t *testing.T) {
|
||||
app, err := appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
app, err := appPkg.GetApp(expectedAppFiles, test.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -234,7 +272,7 @@ func TestNoOverwriteNonVersionEnvVars(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
app, err = appPkg.GetApp(testPkg.ExpectedAppFiles, testPkg.AppName)
|
||||
app, err = appPkg.GetApp(expectedAppFiles, test.AppName)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
+39
-1
@@ -8,11 +8,44 @@ import (
|
||||
"testing"
|
||||
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
)
|
||||
|
||||
func setup() {
|
||||
teardown()
|
||||
|
||||
if err := os.Mkdir(os.ExpandEnv("$ABRA_DIR"), 0764); err != nil {
|
||||
if !os.IsExist(err) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Mkdir(os.ExpandEnv("$ABRA_DIR/recipes"), 0764); err != nil {
|
||||
if !os.IsExist(err) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func teardown() {
|
||||
abraDir := os.ExpandEnv("$ABRA_DIR")
|
||||
if abraDir == fmt.Sprintf("%s/.abra", os.ExpandEnv("$HOME")) {
|
||||
log.Fatal("set $ABRA_DIR before running the test suite")
|
||||
}
|
||||
|
||||
if err := os.RemoveAll(abraDir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestClone(t *testing.T) {
|
||||
setup()
|
||||
t.Cleanup(func() { teardown() })
|
||||
|
||||
dir := path.Join(config.RECIPES_DIR, "gitea")
|
||||
os.RemoveAll(dir)
|
||||
if err := os.RemoveAll(dir); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
gitURL := fmt.Sprintf("%s/%s.git", config.REPOS_BASE_URL, "gitea")
|
||||
if err := Clone(dir, gitURL); err != nil {
|
||||
@@ -25,6 +58,11 @@ func TestClone(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestCancelGitClone(t *testing.T) {
|
||||
t.Skip("https://git.coopcloud.tech/toolshed/abra/issues/814")
|
||||
|
||||
setup()
|
||||
t.Cleanup(func() { teardown() })
|
||||
|
||||
dir := path.Join(config.RECIPES_DIR, "gitea")
|
||||
os.RemoveAll(dir)
|
||||
|
||||
|
||||
+87
-82
@@ -7,7 +7,7 @@
|
||||
msgid ""
|
||||
msgstr "Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-03-03 01:02+0100\n"
|
||||
"POT-Creation-Date: 2026-06-14 17:56+0200\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
@@ -214,7 +214,7 @@ msgstr ""
|
||||
msgid "%s already exists?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:202
|
||||
#: ./cli/app/new.go:206
|
||||
#, c-format
|
||||
msgid "%s created (version: %s)"
|
||||
msgstr ""
|
||||
@@ -299,7 +299,7 @@ msgstr ""
|
||||
msgid "%s has no published versions?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:311
|
||||
#: ./cli/app/new.go:315
|
||||
#, c-format
|
||||
msgid "%s has no secrets to generate, skipping..."
|
||||
msgstr ""
|
||||
@@ -339,17 +339,17 @@ msgstr ""
|
||||
msgid "%s is missing the TYPE env var?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/rollback.go:309 ./cli/app/rollback.go:313
|
||||
#: ./cli/app/rollback.go:310 ./cli/app/rollback.go:314
|
||||
#, c-format
|
||||
msgid "%s is not a downgrade for %s?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:429 ./cli/app/upgrade.go:433
|
||||
#: ./cli/app/upgrade.go:430 ./cli/app/upgrade.go:434
|
||||
#, c-format
|
||||
msgid "%s is not an upgrade for %s?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/env.go:146 ./cli/app/logs.go:65 ./cli/app/ps.go:62 ./cli/app/restart.go:100 ./cli/app/services.go:55 ./cli/app/undeploy.go:66 ./cli/app/upgrade.go:450
|
||||
#: ./cli/app/env.go:146 ./cli/app/logs.go:65 ./cli/app/ps.go:62 ./cli/app/restart.go:100 ./cli/app/services.go:55 ./cli/app/undeploy.go:66 ./cli/app/upgrade.go:451
|
||||
#, c-format
|
||||
msgid "%s is not deployed?"
|
||||
msgstr ""
|
||||
@@ -369,7 +369,7 @@ msgstr ""
|
||||
msgid "%s missing context, run \"abra server add %s\"?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:194 ./cli/app/upgrade.go:210
|
||||
#: ./cli/app/deploy.go:195 ./cli/app/upgrade.go:211
|
||||
#, c-format
|
||||
msgid "%s missing from %s.env"
|
||||
msgstr ""
|
||||
@@ -404,17 +404,17 @@ msgstr ""
|
||||
msgid "%s removed from pass store"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:220
|
||||
#: ./cli/app/new.go:224
|
||||
#, c-format
|
||||
msgid "%s requires secret generation before deploy, run \"abra app secret generate %s --all\""
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:224
|
||||
#: ./cli/app/new.go:228
|
||||
#, c-format
|
||||
msgid "%s requires secret insertion before deploy (#generate=false)"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:151
|
||||
#: ./cli/app/new.go:155
|
||||
#, c-format
|
||||
msgid "%s sanitised as %s for new app"
|
||||
msgstr ""
|
||||
@@ -549,12 +549,12 @@ msgstr ""
|
||||
msgid "%s: waiting %d seconds before next retry"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:424
|
||||
#: ./cli/app/upgrade.go:425
|
||||
#, c-format
|
||||
msgid "'%s' is not a known version"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/rollback.go:304 ./cli/app/upgrade.go:419
|
||||
#: ./cli/app/rollback.go:305 ./cli/app/upgrade.go:420
|
||||
#, c-format
|
||||
msgid "'%s' is not a known version for %s"
|
||||
msgstr ""
|
||||
@@ -626,7 +626,7 @@ msgstr ""
|
||||
msgid "Both local recipe and live deployment labels are shown."
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/backup.go:319 ./cli/app/backup.go:335 ./cli/app/check.go:95 ./cli/app/cmd.go:285 ./cli/app/cp.go:385 ./cli/app/deploy.go:414 ./cli/app/labels.go:143 ./cli/app/list.go:335 ./cli/app/new.go:407 ./cli/app/ps.go:213 ./cli/app/restart.go:163 ./cli/app/restore.go:138 ./cli/app/secret.go:569 ./cli/app/secret.go:609 ./cli/app/secret.go:633 ./cli/app/secret.go:641 ./cli/catalogue/catalogue.go:318 ./cli/recipe/lint.go:137
|
||||
#: ./cli/app/backup.go:319 ./cli/app/backup.go:335 ./cli/app/check.go:95 ./cli/app/cmd.go:285 ./cli/app/cp.go:385 ./cli/app/deploy.go:415 ./cli/app/labels.go:143 ./cli/app/list.go:335 ./cli/app/new.go:411 ./cli/app/ps.go:213 ./cli/app/restart.go:163 ./cli/app/restore.go:138 ./cli/app/secret.go:569 ./cli/app/secret.go:609 ./cli/app/secret.go:633 ./cli/app/secret.go:641 ./cli/catalogue/catalogue.go:318 ./cli/recipe/lint.go:137
|
||||
msgid "C"
|
||||
msgstr ""
|
||||
|
||||
@@ -767,7 +767,7 @@ msgid "Creates a new app from a default recipe.\n"
|
||||
"on your $PATH."
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:430 ./cli/app/new.go:383 ./cli/app/rollback.go:361 ./cli/app/upgrade.go:470
|
||||
#: ./cli/app/deploy.go:431 ./cli/app/new.go:387 ./cli/app/rollback.go:362 ./cli/app/upgrade.go:471
|
||||
msgid "D"
|
||||
msgstr ""
|
||||
|
||||
@@ -878,7 +878,7 @@ msgid "Generate a report of all managed apps.\n"
|
||||
"Use \"--status/-S\" flag to query all servers for the live deployment status."
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:317
|
||||
#: ./cli/app/new.go:321
|
||||
msgid "Generate app secrets?"
|
||||
msgstr ""
|
||||
|
||||
@@ -1257,7 +1257,7 @@ msgstr ""
|
||||
msgid "Run app commands"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/backup.go:303 ./cli/app/list.go:296 ./cli/app/logs.go:109 ./cli/app/new.go:399
|
||||
#: ./cli/app/backup.go:303 ./cli/app/list.go:296 ./cli/app/logs.go:109 ./cli/app/new.go:403
|
||||
msgid "S"
|
||||
msgstr ""
|
||||
|
||||
@@ -1265,7 +1265,7 @@ msgstr ""
|
||||
msgid "SECRETS"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:234
|
||||
#: ./cli/app/new.go:238
|
||||
msgid "SECRETS OVERVIEW"
|
||||
msgstr ""
|
||||
|
||||
@@ -1298,7 +1298,7 @@ msgstr ""
|
||||
msgid "STATUS"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:342
|
||||
#: ./cli/app/new.go:346
|
||||
msgid "Select app server:"
|
||||
msgstr ""
|
||||
|
||||
@@ -1330,7 +1330,7 @@ msgstr ""
|
||||
msgid "Specify a server name"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:293
|
||||
#: ./cli/app/new.go:297
|
||||
msgid "Specify app domain"
|
||||
msgstr ""
|
||||
|
||||
@@ -1462,7 +1462,7 @@ msgid "To load completions:\n"
|
||||
" # and source this file from your PowerShell profile."
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:454 ./cli/app/rollback.go:377 ./cli/app/upgrade.go:494
|
||||
#: ./cli/app/deploy.go:455 ./cli/app/rollback.go:378 ./cli/app/upgrade.go:495
|
||||
msgid "U"
|
||||
msgstr ""
|
||||
|
||||
@@ -1835,7 +1835,7 @@ msgstr ""
|
||||
msgid "attempting to run %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:283 ./cli/app/upgrade.go:296
|
||||
#: ./cli/app/deploy.go:284 ./cli/app/upgrade.go:297
|
||||
#, c-format
|
||||
msgid "attempting to run post deploy commands, saw: %s"
|
||||
msgstr ""
|
||||
@@ -1865,7 +1865,7 @@ msgstr ""
|
||||
msgid "autocomplete failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:401
|
||||
#: ./cli/app/new.go:405
|
||||
msgid "automatically generate secrets"
|
||||
msgstr ""
|
||||
|
||||
@@ -1915,7 +1915,7 @@ msgstr ""
|
||||
#. no spaces in between
|
||||
#. translators: `abra app cp` aliases. use a comma separated list of aliases with
|
||||
#. no spaces in between
|
||||
#: ./cli/app/backup.go:148 ./cli/app/cp.go:30 ./cli/app/deploy.go:438 ./cli/app/rollback.go:369 ./cli/app/upgrade.go:478
|
||||
#: ./cli/app/backup.go:148 ./cli/app/cp.go:30 ./cli/app/deploy.go:439 ./cli/app/rollback.go:370 ./cli/app/upgrade.go:479
|
||||
msgid "c"
|
||||
msgstr ""
|
||||
|
||||
@@ -1971,7 +1971,7 @@ msgstr ""
|
||||
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:388
|
||||
#: ./cli/app/deploy.go:389
|
||||
#, c-format
|
||||
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?\n"
|
||||
" to return to a regular release, specify a release tag, commit SHA or use \"--latest\""
|
||||
@@ -1990,7 +1990,7 @@ msgstr ""
|
||||
msgid "cannot use '[secret] [version]' and '--all' together"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:330
|
||||
#: ./cli/app/deploy.go:331
|
||||
msgid "cannot use --chaos and --latest together"
|
||||
msgstr ""
|
||||
|
||||
@@ -2014,11 +2014,11 @@ msgstr ""
|
||||
msgid "cannot use [service] and --all-services/-a together"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:322 ./cli/app/new.go:80
|
||||
#: ./cli/app/deploy.go:323 ./cli/app/new.go:80
|
||||
msgid "cannot use [version] and --chaos together"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:326
|
||||
#: ./cli/app/deploy.go:327
|
||||
msgid "cannot use [version] and --latest together"
|
||||
msgstr ""
|
||||
|
||||
@@ -2054,7 +2054,7 @@ msgstr ""
|
||||
msgid "cfg"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/backup.go:318 ./cli/app/backup.go:334 ./cli/app/check.go:94 ./cli/app/cmd.go:284 ./cli/app/cp.go:384 ./cli/app/deploy.go:413 ./cli/app/labels.go:142 ./cli/app/list.go:334 ./cli/app/new.go:406 ./cli/app/ps.go:212 ./cli/app/restart.go:162 ./cli/app/restore.go:137 ./cli/app/secret.go:568 ./cli/app/secret.go:608 ./cli/app/secret.go:632 ./cli/app/secret.go:640 ./cli/catalogue/catalogue.go:317 ./cli/recipe/lint.go:136
|
||||
#: ./cli/app/backup.go:318 ./cli/app/backup.go:334 ./cli/app/check.go:94 ./cli/app/cmd.go:284 ./cli/app/cp.go:384 ./cli/app/deploy.go:414 ./cli/app/labels.go:142 ./cli/app/list.go:334 ./cli/app/new.go:410 ./cli/app/ps.go:212 ./cli/app/restart.go:162 ./cli/app/restore.go:137 ./cli/app/secret.go:568 ./cli/app/secret.go:608 ./cli/app/secret.go:632 ./cli/app/secret.go:640 ./cli/catalogue/catalogue.go:317 ./cli/recipe/lint.go:136
|
||||
msgid "chaos"
|
||||
msgstr ""
|
||||
|
||||
@@ -2068,7 +2068,7 @@ msgstr ""
|
||||
msgid "check <domain> [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:95 ./cli/app/undeploy.go:58 ./cli/app/upgrade.go:442
|
||||
#: ./cli/app/deploy.go:95 ./cli/app/undeploy.go:58 ./cli/app/upgrade.go:443
|
||||
#, c-format
|
||||
msgid "checking whether %s is already deployed"
|
||||
msgstr ""
|
||||
@@ -2371,7 +2371,7 @@ msgstr ""
|
||||
msgid "critical errors present in %s config"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/rollback.go:299
|
||||
#: ./cli/app/rollback.go:300
|
||||
#, c-format
|
||||
msgid "current deployment '%s' is not a known version for %s"
|
||||
msgstr ""
|
||||
@@ -2422,7 +2422,7 @@ msgstr ""
|
||||
msgid "deploy labels stanza present"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:448
|
||||
#: ./cli/app/deploy.go:449
|
||||
msgid "deploy latest recipe version"
|
||||
msgstr ""
|
||||
|
||||
@@ -2524,11 +2524,11 @@ msgstr ""
|
||||
msgid "dirty: %v, "
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:440 ./cli/app/rollback.go:371 ./cli/app/upgrade.go:480
|
||||
#: ./cli/app/deploy.go:441 ./cli/app/rollback.go:372 ./cli/app/upgrade.go:481
|
||||
msgid "disable converge logic checks"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:432 ./cli/app/rollback.go:363 ./cli/app/upgrade.go:472
|
||||
#: ./cli/app/deploy.go:433 ./cli/app/rollback.go:364 ./cli/app/upgrade.go:473
|
||||
msgid "disable public DNS checks"
|
||||
msgstr ""
|
||||
|
||||
@@ -2544,11 +2544,11 @@ msgstr ""
|
||||
msgid "docker: is the daemon running / your user has docker permissions?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:382
|
||||
#: ./cli/app/new.go:386
|
||||
msgid "domain"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:385
|
||||
#: ./cli/app/new.go:389
|
||||
msgid "domain name for app"
|
||||
msgstr ""
|
||||
|
||||
@@ -2737,7 +2737,7 @@ msgstr ""
|
||||
|
||||
#. translators: `abra recipe fetch` aliases. use a comma separated list of aliases
|
||||
#. with no spaces in between
|
||||
#: ./cli/app/deploy.go:422 ./cli/app/env.go:325 ./cli/app/remove.go:163 ./cli/app/rollback.go:353 ./cli/app/secret.go:593 ./cli/app/upgrade.go:462 ./cli/app/volume.go:217 ./cli/recipe/fetch.go:20 ./cli/recipe/fetch.go:138
|
||||
#: ./cli/app/deploy.go:423 ./cli/app/env.go:325 ./cli/app/remove.go:163 ./cli/app/rollback.go:354 ./cli/app/secret.go:593 ./cli/app/upgrade.go:463 ./cli/app/volume.go:217 ./cli/recipe/fetch.go:20 ./cli/recipe/fetch.go:138
|
||||
msgid "f"
|
||||
msgstr ""
|
||||
|
||||
@@ -2898,7 +2898,7 @@ msgstr ""
|
||||
msgid "failed to resize tty, using default size"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:134
|
||||
#: ./cli/app/new.go:138
|
||||
#, c-format
|
||||
msgid "failed to retrieve latest commit for %s: %s"
|
||||
msgstr ""
|
||||
@@ -2988,7 +2988,7 @@ msgstr ""
|
||||
msgid "final merged env values for %s are: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:421 ./cli/app/env.go:324 ./cli/app/remove.go:162 ./cli/app/rollback.go:352 ./cli/app/upgrade.go:461 ./cli/app/volume.go:216 ./cli/recipe/fetch.go:137
|
||||
#: ./cli/app/deploy.go:422 ./cli/app/env.go:324 ./cli/app/remove.go:162 ./cli/app/rollback.go:353 ./cli/app/upgrade.go:462 ./cli/app/volume.go:216 ./cli/recipe/fetch.go:137
|
||||
msgid "force"
|
||||
msgstr ""
|
||||
|
||||
@@ -3202,7 +3202,7 @@ msgstr ""
|
||||
msgid "id: %s, "
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/backup.go:321 ./cli/app/backup.go:337 ./cli/app/check.go:97 ./cli/app/cmd.go:287 ./cli/app/cp.go:387 ./cli/app/deploy.go:416 ./cli/app/labels.go:145 ./cli/app/list.go:337 ./cli/app/new.go:409 ./cli/app/ps.go:215 ./cli/app/restart.go:165 ./cli/app/restore.go:140 ./cli/app/secret.go:571 ./cli/app/secret.go:611 ./cli/app/secret.go:635 ./cli/app/secret.go:643 ./cli/catalogue/catalogue.go:320 ./cli/recipe/lint.go:139
|
||||
#: ./cli/app/backup.go:321 ./cli/app/backup.go:337 ./cli/app/check.go:97 ./cli/app/cmd.go:287 ./cli/app/cp.go:387 ./cli/app/deploy.go:417 ./cli/app/labels.go:145 ./cli/app/list.go:337 ./cli/app/new.go:413 ./cli/app/ps.go:215 ./cli/app/restart.go:165 ./cli/app/restore.go:140 ./cli/app/secret.go:571 ./cli/app/secret.go:611 ./cli/app/secret.go:635 ./cli/app/secret.go:643 ./cli/catalogue/catalogue.go:320 ./cli/recipe/lint.go:139
|
||||
msgid "ignore uncommitted recipes changes"
|
||||
msgstr ""
|
||||
|
||||
@@ -3400,7 +3400,7 @@ msgstr ""
|
||||
#. no spaces in between
|
||||
#. translators: `abra recipe lint` aliases. use a comma separated list of
|
||||
#. aliases with no spaces in between
|
||||
#: ./cli/app/cmd.go:261 ./cli/app/deploy.go:446 ./cli/app/logs.go:20 ./cli/recipe/lint.go:17 ./cli/server/add.go:207
|
||||
#: ./cli/app/cmd.go:261 ./cli/app/deploy.go:447 ./cli/app/logs.go:20 ./cli/recipe/lint.go:17 ./cli/server/add.go:207
|
||||
msgid "l"
|
||||
msgstr ""
|
||||
|
||||
@@ -3415,7 +3415,7 @@ msgstr ""
|
||||
msgid "labels <domain> [flags]"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:445 ./cli/app/list.go:186
|
||||
#: ./cli/app/deploy.go:446 ./cli/app/list.go:186
|
||||
msgid "latest"
|
||||
msgstr ""
|
||||
|
||||
@@ -3752,7 +3752,7 @@ msgstr ""
|
||||
msgid "no containers matching the %v filter found?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:302 ./cli/internal/validate.go:129
|
||||
#: ./cli/app/new.go:306 ./cli/internal/validate.go:129
|
||||
msgid "no domain provided"
|
||||
msgstr ""
|
||||
|
||||
@@ -3779,6 +3779,11 @@ msgstr ""
|
||||
msgid "no recipe name provided"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:242
|
||||
#, c-format
|
||||
msgid "no release notes for upgrading from %s to %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/secret.go:234
|
||||
#, c-format
|
||||
msgid "no secret %s available for recipe %s?"
|
||||
@@ -3805,7 +3810,7 @@ msgstr ""
|
||||
msgid "no secrets to remove?"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:351 ./cli/internal/validate.go:167
|
||||
#: ./cli/app/new.go:355 ./cli/internal/validate.go:167
|
||||
msgid "no server provided"
|
||||
msgstr ""
|
||||
|
||||
@@ -3863,11 +3868,11 @@ msgstr ""
|
||||
msgid "no volumes to remove"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:437 ./cli/app/rollback.go:368 ./cli/app/upgrade.go:477
|
||||
#: ./cli/app/deploy.go:438 ./cli/app/rollback.go:369 ./cli/app/upgrade.go:478
|
||||
msgid "no-converge-checks"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:429 ./cli/app/rollback.go:360 ./cli/app/upgrade.go:469
|
||||
#: ./cli/app/deploy.go:430 ./cli/app/rollback.go:361 ./cli/app/upgrade.go:470
|
||||
msgid "no-domain-checks"
|
||||
msgstr ""
|
||||
|
||||
@@ -3935,7 +3940,7 @@ msgstr ""
|
||||
msgid "only show errors"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:488
|
||||
#: ./cli/app/upgrade.go:489
|
||||
msgid "only show release notes"
|
||||
msgstr ""
|
||||
|
||||
@@ -3947,7 +3952,7 @@ msgstr ""
|
||||
#. with no spaces in between
|
||||
#. translators: `abra server prune` aliases. use a comma separated list of
|
||||
#. aliases with no spaces in between
|
||||
#: ./cli/app/backup.go:295 ./cli/app/new.go:391 ./cli/app/ps.go:29 ./cli/app/secret.go:561 ./cli/app/secret.go:585 ./cli/app/secret.go:625 ./cli/app/undeploy.go:169 ./cli/catalogue/catalogue.go:294 ./cli/recipe/list.go:112 ./cli/server/prune.go:18
|
||||
#: ./cli/app/backup.go:295 ./cli/app/new.go:395 ./cli/app/ps.go:29 ./cli/app/secret.go:561 ./cli/app/secret.go:585 ./cli/app/secret.go:625 ./cli/app/undeploy.go:169 ./cli/catalogue/catalogue.go:294 ./cli/recipe/list.go:112 ./cli/server/prune.go:18
|
||||
msgid "p"
|
||||
msgstr ""
|
||||
|
||||
@@ -3966,27 +3971,27 @@ msgstr ""
|
||||
msgid "parsed following command arguments: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:345
|
||||
#: ./cli/app/upgrade.go:346
|
||||
#, c-format
|
||||
msgid "parsing chosen upgrade version failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:389
|
||||
#: ./cli/app/upgrade.go:390
|
||||
#, c-format
|
||||
msgid "parsing deployed version failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:350
|
||||
#: ./cli/app/upgrade.go:351
|
||||
#, c-format
|
||||
msgid "parsing deployment version failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:356 ./cli/app/upgrade.go:395
|
||||
#: ./cli/app/upgrade.go:357 ./cli/app/upgrade.go:396
|
||||
#, c-format
|
||||
msgid "parsing recipe version failed: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:390 ./cli/app/secret.go:560 ./cli/app/secret.go:584 ./cli/app/secret.go:624
|
||||
#: ./cli/app/new.go:394 ./cli/app/secret.go:560 ./cli/app/secret.go:584 ./cli/app/secret.go:624
|
||||
msgid "pass"
|
||||
msgstr ""
|
||||
|
||||
@@ -4006,7 +4011,7 @@ msgstr ""
|
||||
msgid "pattern"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:424 ./cli/app/env.go:327 ./cli/app/remove.go:165 ./cli/app/rollback.go:355 ./cli/app/upgrade.go:464 ./cli/app/volume.go:219
|
||||
#: ./cli/app/deploy.go:425 ./cli/app/env.go:327 ./cli/app/remove.go:165 ./cli/app/rollback.go:356 ./cli/app/upgrade.go:465 ./cli/app/volume.go:219
|
||||
msgid "perform action without further prompt"
|
||||
msgstr ""
|
||||
|
||||
@@ -4016,22 +4021,22 @@ msgstr ""
|
||||
msgid "pl,p"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/rollback.go:267
|
||||
#: ./cli/app/rollback.go:268
|
||||
#, c-format
|
||||
msgid "please select a downgrade (version: %s):"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/rollback.go:272
|
||||
#: ./cli/app/rollback.go:273
|
||||
#, c-format
|
||||
msgid "please select a downgrade (version: %s, chaos: %s):"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:312
|
||||
#: ./cli/app/upgrade.go:313
|
||||
#, c-format
|
||||
msgid "please select an upgrade (version: %s):"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:317
|
||||
#: ./cli/app/upgrade.go:318
|
||||
#, c-format
|
||||
msgid "please select an upgrade (version: %s, chaos: %s):"
|
||||
msgstr ""
|
||||
@@ -4114,7 +4119,7 @@ msgstr ""
|
||||
#. with no spaces in between
|
||||
#. translators: `abra recipe` aliases. use a comma separated list of aliases
|
||||
#. with no spaces in between
|
||||
#: ./cli/app/backup.go:327 ./cli/app/list.go:304 ./cli/app/move.go:350 ./cli/app/run.go:23 ./cli/app/upgrade.go:486 ./cli/catalogue/catalogue.go:302 ./cli/recipe/recipe.go:12 ./cli/recipe/release.go:624
|
||||
#: ./cli/app/backup.go:327 ./cli/app/list.go:304 ./cli/app/move.go:350 ./cli/app/run.go:23 ./cli/app/upgrade.go:487 ./cli/catalogue/catalogue.go:302 ./cli/recipe/recipe.go:12 ./cli/recipe/release.go:624
|
||||
msgid "r"
|
||||
msgstr ""
|
||||
|
||||
@@ -4234,7 +4239,7 @@ msgstr ""
|
||||
msgid "release failed. any changes made have been reverted"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/upgrade.go:485
|
||||
#: ./cli/app/upgrade.go:486
|
||||
msgid "releasenotes"
|
||||
msgstr ""
|
||||
|
||||
@@ -4538,7 +4543,7 @@ msgstr ""
|
||||
msgid "run command locally"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:281 ./cli/app/upgrade.go:293
|
||||
#: ./cli/app/deploy.go:282 ./cli/app/upgrade.go:294
|
||||
#, c-format
|
||||
msgid "run the following post-deploy commands: %s"
|
||||
msgstr ""
|
||||
@@ -4579,7 +4584,7 @@ msgstr ""
|
||||
#. no spaces in between
|
||||
#. translators: `abra server` aliases. use a comma separated list of aliases
|
||||
#. with no spaces in between
|
||||
#: ./cli/app/backup.go:198 ./cli/app/backup.go:263 ./cli/app/backup.go:287 ./cli/app/env.go:333 ./cli/app/list.go:327 ./cli/app/logs.go:101 ./cli/app/new.go:368 ./cli/app/restore.go:114 ./cli/app/secret.go:535 ./cli/catalogue/catalogue.go:27 ./cli/catalogue/catalogue.go:310 ./cli/recipe/fetch.go:130 ./cli/server/server.go:12
|
||||
#: ./cli/app/backup.go:198 ./cli/app/backup.go:263 ./cli/app/backup.go:287 ./cli/app/env.go:333 ./cli/app/list.go:327 ./cli/app/logs.go:101 ./cli/app/new.go:372 ./cli/app/restore.go:114 ./cli/app/secret.go:535 ./cli/catalogue/catalogue.go:27 ./cli/catalogue/catalogue.go:310 ./cli/recipe/fetch.go:130 ./cli/server/server.go:12
|
||||
msgid "s"
|
||||
msgstr ""
|
||||
|
||||
@@ -4621,12 +4626,12 @@ msgstr ""
|
||||
msgid "secret not found: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:358
|
||||
#: ./cli/app/deploy.go:359
|
||||
#, c-format
|
||||
msgid "secret not generated: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:356
|
||||
#: ./cli/app/deploy.go:357
|
||||
#, c-format
|
||||
msgid "secret not inserted (#generate=false): %s"
|
||||
msgstr ""
|
||||
@@ -4636,27 +4641,27 @@ msgstr ""
|
||||
msgid "secret: %s removed"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/backup.go:302 ./cli/app/new.go:398
|
||||
#: ./cli/app/backup.go:302 ./cli/app/new.go:402
|
||||
msgid "secrets"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:238
|
||||
#: ./cli/app/new.go:242
|
||||
#, c-format
|
||||
msgid "secrets are %s shown again, please save them %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:306
|
||||
#: ./cli/app/deploy.go:307
|
||||
#, c-format
|
||||
msgid "selected latest recipe version: %s (from %d available versions)"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:121
|
||||
#: ./cli/app/new.go:125
|
||||
#, c-format
|
||||
msgid "selected recipe version: %s (from %d available versions)"
|
||||
msgstr ""
|
||||
|
||||
#. translators: `abra server` command for autocompletion
|
||||
#: ./cli/app/env.go:332 ./cli/app/env.go:339 ./cli/app/list.go:326 ./cli/app/list.go:341 ./cli/app/new.go:367 ./cli/app/new.go:374 ./cli/run.go:101
|
||||
#: ./cli/app/env.go:332 ./cli/app/env.go:339 ./cli/app/list.go:326 ./cli/app/list.go:341 ./cli/app/new.go:371 ./cli/app/new.go:378 ./cli/run.go:101
|
||||
msgid "server"
|
||||
msgstr ""
|
||||
|
||||
@@ -4770,7 +4775,7 @@ msgstr ""
|
||||
msgid "severity"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:456 ./cli/app/rollback.go:379 ./cli/app/upgrade.go:496
|
||||
#: ./cli/app/deploy.go:457 ./cli/app/rollback.go:380 ./cli/app/upgrade.go:497
|
||||
msgid "show all configs & images, including unchanged ones"
|
||||
msgstr ""
|
||||
|
||||
@@ -4794,7 +4799,7 @@ msgstr ""
|
||||
msgid "show debug messages"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:453 ./cli/app/rollback.go:376 ./cli/app/upgrade.go:493
|
||||
#: ./cli/app/deploy.go:454 ./cli/app/rollback.go:377 ./cli/app/upgrade.go:494
|
||||
msgid "show-unchanged"
|
||||
msgstr ""
|
||||
|
||||
@@ -4802,7 +4807,7 @@ msgstr ""
|
||||
msgid "since"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:336
|
||||
#: ./cli/app/new.go:340
|
||||
#, c-format
|
||||
msgid "single server detected, choosing %s automatically"
|
||||
msgstr ""
|
||||
@@ -4842,11 +4847,11 @@ msgstr ""
|
||||
msgid "skipping converge logic checks"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:208
|
||||
#: ./cli/app/deploy.go:209
|
||||
msgid "skipping domain checks"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:205
|
||||
#: ./cli/app/deploy.go:206
|
||||
msgid "skipping domain checks, no DOMAIN=... configured"
|
||||
msgstr ""
|
||||
|
||||
@@ -4902,7 +4907,7 @@ msgstr ""
|
||||
msgid "specify secret value"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:370
|
||||
#: ./cli/app/new.go:374
|
||||
msgid "specify server for new app"
|
||||
msgstr ""
|
||||
|
||||
@@ -4960,7 +4965,7 @@ msgstr ""
|
||||
msgid "store generated secrets in a local pass store"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:393
|
||||
#: ./cli/app/new.go:397
|
||||
msgid "store secrets in a local pass store"
|
||||
msgstr ""
|
||||
|
||||
@@ -5110,7 +5115,7 @@ msgstr ""
|
||||
msgid "trim input"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/new.go:263 ./pkg/app/app.go:141
|
||||
#: ./cli/app/new.go:267 ./pkg/app/app.go:141
|
||||
#, c-format
|
||||
msgid "trimming %s to %s to avoid runtime limits"
|
||||
msgstr ""
|
||||
@@ -5648,27 +5653,27 @@ msgstr ""
|
||||
msgid "version wiped from %s.env"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:372
|
||||
#: ./cli/app/deploy.go:373
|
||||
#, c-format
|
||||
msgid "version: taking chaos version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:398
|
||||
#: ./cli/app/deploy.go:399
|
||||
#, c-format
|
||||
msgid "version: taking deployed version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:403
|
||||
#: ./cli/app/deploy.go:404
|
||||
#, c-format
|
||||
msgid "version: taking new recipe version: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:392
|
||||
#: ./cli/app/deploy.go:393
|
||||
#, c-format
|
||||
msgid "version: taking version from .env file: %s"
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:378
|
||||
#: ./cli/app/deploy.go:379
|
||||
#, c-format
|
||||
msgid "version: taking version from cli arg: %s"
|
||||
msgstr ""
|
||||
@@ -5796,7 +5801,7 @@ msgstr ""
|
||||
msgid "writer: %v, "
|
||||
msgstr ""
|
||||
|
||||
#: ./cli/app/deploy.go:288 ./cli/app/new.go:251 ./cli/app/rollback.go:256 ./cli/app/undeploy.go:120 ./cli/app/upgrade.go:301
|
||||
#: ./cli/app/deploy.go:289 ./cli/app/new.go:255 ./cli/app/rollback.go:257 ./cli/app/undeploy.go:120 ./cli/app/upgrade.go:302
|
||||
#, c-format
|
||||
msgid "writing recipe version failed: %s"
|
||||
msgstr ""
|
||||
|
||||
Binary file not shown.
+100
-147
@@ -1,17 +1,5 @@
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: EMAIL\n"
|
||||
"POT-Creation-Date: 2026-03-03 01:02+0100\n"
|
||||
"PO-Revision-Date: 2026-02-28 13:52+0000\n"
|
||||
"Last-Translator: chasqui <chasqui@cryptolab.net>\n"
|
||||
"Language-Team: Spanish <https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/>\n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 5.12.2\n"
|
||||
msgstr "Project-Id-Version: \nReport-Msgid-Bugs-To: EMAIL\nPOT-Creation-Date: 2026-06-14 17:56+0200\nPO-Revision-Date: 2026-08-11 06:51+0000\nLast-Translator: chasqui <chasqui@cryptolab.net>\nLanguage-Team: Spanish <https://translate.coopcloud.tech/projects/co-op-cloud/abra/es/>\nLanguage: es\nMIME-Version: 1.0\nContent-Type: text/plain; charset=UTF-8\nContent-Transfer-Encoding: 8bit\nPlural-Forms: nplurals=2; plural=n != 1;\nX-Generator: Weblate 5.12.2\n"
|
||||
|
||||
#: cli/app/cp.go:38
|
||||
msgid ""
|
||||
@@ -112,18 +100,7 @@ msgid ""
|
||||
"\n"
|
||||
" # list apps of all servers which match a specific recipe\n"
|
||||
" abra app ls -r gitea"
|
||||
msgstr ""
|
||||
" # Listar las aplicaciones instaladas\n"
|
||||
" abra aplicacion listar\n"
|
||||
"\n"
|
||||
" # Listar las aplicaciones instaladas en un servidor específico\n"
|
||||
" abra aplicacion listar -s 1312.net\n"
|
||||
"\n"
|
||||
" # Listar aplicaciones instaladas en servidor específico con detalles\n"
|
||||
" abra aplicacion listar -s 1312.net -S\n"
|
||||
"\n"
|
||||
" # Listar en qué servidores está desplegada la aplicación \"gitea\"\n"
|
||||
" abra aplicacion listar -r gitea"
|
||||
msgstr " # Listar todas las aplicaciones instaladas con mayor detalle\n abra aplicacion listar --estado\n\n # Listar todas las aplicaciones \"wordpress\" (puedes probar otras recetas)\n abra aplicacion listar --receta wordpress\n\n # Listar todas las aplicaciones \"wordpress\" con mayor detalle\n abra aplicacion listar --receta wordpress --estado\n\n # Listar todas las aplicaciones instaladas con mayor detalle usando los comandos abreviados\n abra app ls -S"
|
||||
|
||||
#: cli/app/move.go:59
|
||||
msgid ""
|
||||
@@ -318,7 +295,7 @@ msgstr "%s ya existe"
|
||||
msgid "%s already exists?"
|
||||
msgstr "%s ¿ya existe?"
|
||||
|
||||
#: cli/app/new.go:202
|
||||
#: cli/app/new.go:206
|
||||
#, c-format
|
||||
msgid "%s created (version: %s)"
|
||||
msgstr "%s creado (versión: %s)"
|
||||
@@ -403,7 +380,7 @@ msgstr "¿%s no tiene una aplicación principal?"
|
||||
msgid "%s has no published versions?"
|
||||
msgstr "¿%s no tiene versiones publicadas?"
|
||||
|
||||
#: cli/app/new.go:311
|
||||
#: cli/app/new.go:315
|
||||
#, c-format
|
||||
msgid "%s has no secrets to generate, skipping..."
|
||||
msgstr "%s no tiene secretos para generar, omitiendo..."
|
||||
@@ -443,19 +420,19 @@ msgstr "¿%s no tiene un archivo compose.yml o compose.*.yml?"
|
||||
msgid "%s is missing the TYPE env var?"
|
||||
msgstr "¿A %s le falta la variable de entorno TYPE?"
|
||||
|
||||
#: cli/app/rollback.go:309 cli/app/rollback.go:313
|
||||
#: cli/app/rollback.go:310 cli/app/rollback.go:314
|
||||
#, c-format
|
||||
msgid "%s is not a downgrade for %s?"
|
||||
msgstr "¿%s no es un downgrade para %s?"
|
||||
|
||||
#: cli/app/upgrade.go:429 cli/app/upgrade.go:433
|
||||
#: cli/app/upgrade.go:430 cli/app/upgrade.go:434
|
||||
#, c-format
|
||||
msgid "%s is not an upgrade for %s?"
|
||||
msgstr "¿%s no es una actualización para %s?"
|
||||
|
||||
#: cli/app/env.go:146 cli/app/logs.go:65 cli/app/ps.go:62
|
||||
#: cli/app/restart.go:100 cli/app/services.go:55 cli/app/undeploy.go:66
|
||||
#: cli/app/upgrade.go:450
|
||||
#: cli/app/upgrade.go:451
|
||||
#, c-format
|
||||
msgid "%s is not deployed?"
|
||||
msgstr "¿%s no está desplegado?"
|
||||
@@ -475,7 +452,7 @@ msgstr "%s todavía está desplegado. Ejecuta \"abra aplicacion plegar %s\""
|
||||
msgid "%s missing context, run \"abra server add %s\"?"
|
||||
msgstr "falta contexto %s, ¿ejecutar \"abra servidor agregar %s\"?"
|
||||
|
||||
#: cli/app/deploy.go:194 cli/app/upgrade.go:210
|
||||
#: cli/app/deploy.go:195 cli/app/upgrade.go:211
|
||||
#, c-format
|
||||
msgid "%s missing from %s.env"
|
||||
msgstr "falta %s de %s.env"
|
||||
@@ -510,17 +487,17 @@ msgstr "notas de la versión %s:"
|
||||
msgid "%s removed from pass store"
|
||||
msgstr "%s eliminado del almacén de contraseñas"
|
||||
|
||||
#: cli/app/new.go:220
|
||||
#: cli/app/new.go:224
|
||||
#, c-format
|
||||
msgid "%s requires secret generation before deploy, run \"abra app secret generate %s --all\""
|
||||
msgstr "%s requiere generación de secretos antes del despliegue, ejecuta \"abra aplicacion secreto generar %s --all\""
|
||||
msgstr "%s requiere generación de secretos antes del despliegue, ejecuta \"abra aplicacion secreto generar %s --todos\""
|
||||
|
||||
#: cli/app/new.go:224
|
||||
#: cli/app/new.go:228
|
||||
#, c-format
|
||||
msgid "%s requires secret insertion before deploy (#generate=false)"
|
||||
msgstr "%s requiere inserción de secretos antes del despliegue (#generate=false)"
|
||||
|
||||
#: cli/app/new.go:151
|
||||
#: cli/app/new.go:155
|
||||
#, c-format
|
||||
msgid "%s sanitised as %s for new app"
|
||||
msgstr "%s sanitisado como %s para nueva aplicación"
|
||||
@@ -655,12 +632,12 @@ msgstr "%s: no se puede resolver la dirección IPv4: %s"
|
||||
msgid "%s: waiting %d seconds before next retry"
|
||||
msgstr "%s: esperando %d segundos antes del siguiente reintento"
|
||||
|
||||
#: cli/app/upgrade.go:424
|
||||
#: cli/app/upgrade.go:425
|
||||
#, c-format
|
||||
msgid "'%s' is not a known version"
|
||||
msgstr "'%s' no es una versión conocida"
|
||||
|
||||
#: cli/app/rollback.go:304 cli/app/upgrade.go:419
|
||||
#: cli/app/rollback.go:305 cli/app/upgrade.go:420
|
||||
#, c-format
|
||||
msgid "'%s' is not a known version for %s"
|
||||
msgstr "'%s' no es una versión conocida para %s"
|
||||
@@ -759,8 +736,8 @@ msgid "Both local recipe and live deployment labels are shown."
|
||||
msgstr "Se muestran tanto la receta local como las etiquetas del despliegue en vivo."
|
||||
|
||||
#: cli/app/backup.go:319 cli/app/backup.go:335 cli/app/check.go:95
|
||||
#: cli/app/cmd.go:285 cli/app/cp.go:385 cli/app/deploy.go:414
|
||||
#: cli/app/labels.go:143 cli/app/list.go:335 cli/app/new.go:407
|
||||
#: cli/app/cmd.go:285 cli/app/cp.go:385 cli/app/deploy.go:415
|
||||
#: cli/app/labels.go:143 cli/app/list.go:335 cli/app/new.go:411
|
||||
#: cli/app/ps.go:213 cli/app/restart.go:163 cli/app/restore.go:138
|
||||
#: cli/app/secret.go:569 cli/app/secret.go:609 cli/app/secret.go:633
|
||||
#: cli/app/secret.go:641 cli/catalogue/catalogue.go:318 cli/recipe/lint.go:137
|
||||
@@ -959,8 +936,8 @@ msgstr ""
|
||||
"en un almacén pass (ver passwordstore.org). El comando pass debe estar \n"
|
||||
"disponible en tu $PATH."
|
||||
|
||||
#: cli/app/deploy.go:430 cli/app/new.go:383 cli/app/rollback.go:361
|
||||
#: cli/app/upgrade.go:470
|
||||
#: cli/app/deploy.go:431 cli/app/new.go:387 cli/app/rollback.go:362
|
||||
#: cli/app/upgrade.go:471
|
||||
msgid "D"
|
||||
msgstr ""
|
||||
|
||||
@@ -1107,12 +1084,9 @@ msgid ""
|
||||
"Generate a report of all managed apps.\n"
|
||||
"\n"
|
||||
"Use \"--status/-S\" flag to query all servers for the live deployment status."
|
||||
msgstr ""
|
||||
"Genera un informe de todas las aplicaciones gestionadas.\n"
|
||||
"\n"
|
||||
"Usa la opción \"--estado/-S\" para consultar en todos los servidores sobre el estado de despliegue."
|
||||
msgstr "BIENVENIDE AL MANUAL DE USO COMANDO \"LISTAR\"\n\nEl comando \"abra aplicacion listar\" permite consultar de forma rápida qué aplicaciones están siendo gestionadas. \nAl ejecutarlo, Abra genera un informe con la lista de aplicaciones disponibles y la información básica de cada una.\nPuedes imaginar el comando listar como una forma de decirle a Abra: «muéstrame qué aplicaciones tengo»\n\nLos comandos pueden aceptar opciones y flags que permiten modificar o ampliar la información que muestran, \nen este caso es bastante útil el uso de \"--estado\". Estas opciones se escriben después del comando siguiendo \nla sintaxis detallada más adelante."
|
||||
|
||||
#: cli/app/new.go:317
|
||||
#: cli/app/new.go:321
|
||||
msgid "Generate app secrets?"
|
||||
msgstr "¿Generar secretos de la aplicación?"
|
||||
|
||||
@@ -1579,7 +1553,7 @@ msgid "Run app commands"
|
||||
msgstr "Ejecutar 💻 comando dentro una 🚀aplicación"
|
||||
|
||||
#: cli/app/backup.go:303 cli/app/list.go:296 cli/app/logs.go:109
|
||||
#: cli/app/new.go:399
|
||||
#: cli/app/new.go:403
|
||||
msgid "S"
|
||||
msgstr ""
|
||||
|
||||
@@ -1587,7 +1561,7 @@ msgstr ""
|
||||
msgid "SECRETS"
|
||||
msgstr "🥷 SECRETOS"
|
||||
|
||||
#: cli/app/new.go:234
|
||||
#: cli/app/new.go:238
|
||||
msgid "SECRETS OVERVIEW"
|
||||
msgstr "RESUMEN DE LOS 🥷 SECRETOS"
|
||||
|
||||
@@ -1621,7 +1595,7 @@ msgstr ""
|
||||
msgid "STATUS"
|
||||
msgstr "🩻 ESTADO"
|
||||
|
||||
#: cli/app/new.go:342
|
||||
#: cli/app/new.go:346
|
||||
msgid "Select app server:"
|
||||
msgstr "Seleccionar servidor para la aplicación:"
|
||||
|
||||
@@ -1659,7 +1633,7 @@ msgstr "Especifica un 🌐 nombre de dominio (DNS)"
|
||||
msgid "Specify a server name"
|
||||
msgstr "Especifica un nombre de servidor"
|
||||
|
||||
#: cli/app/new.go:293
|
||||
#: cli/app/new.go:297
|
||||
msgid "Specify app domain"
|
||||
msgstr "Especifica un 🌐 nombre de dominio (DNS) para la aplicación"
|
||||
|
||||
@@ -1881,7 +1855,7 @@ msgstr ""
|
||||
" PS> abra autocomplete powershell > abra.ps1\n"
|
||||
" # y fuente (source) ese archivo desde tu perfil de PowerShell."
|
||||
|
||||
#: cli/app/deploy.go:454 cli/app/rollback.go:377 cli/app/upgrade.go:494
|
||||
#: cli/app/deploy.go:455 cli/app/rollback.go:378 cli/app/upgrade.go:495
|
||||
msgid "U"
|
||||
msgstr ""
|
||||
|
||||
@@ -2024,33 +1998,7 @@ msgid ""
|
||||
" {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}\n"
|
||||
"\n"
|
||||
"Use \"{{.CommandPath}} [command] --help\" for more information about a command.{{end}}\n"
|
||||
msgstr ""
|
||||
"Uso:{{if .Runnable}}\n"
|
||||
" {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}\n"
|
||||
" {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}\n"
|
||||
"\n"
|
||||
"Comando:\n"
|
||||
" {{.NameAndAliases}}{{end}}{{if .HasExample}}\n"
|
||||
"\n"
|
||||
"Ejemplos:\n"
|
||||
" # Nota: \"1312.net\" es solo un ejemplo de nombre de aplicación.\n"
|
||||
"# Reemplázalo por el dominio o nombre real de tu aplicación.\n"
|
||||
"\n"
|
||||
"{{.Example}}{{end}}{{if .HasAvailableSubCommands}}\n"
|
||||
"\n"
|
||||
"Comandos disponibles:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name \"help\"))}}\n"
|
||||
" {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\n"
|
||||
"\n"
|
||||
"Opciones:\n"
|
||||
"{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\n"
|
||||
"\n"
|
||||
"Opciones globales:\n"
|
||||
"{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}\n"
|
||||
"\n"
|
||||
"Temas de ayuda adicionales:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}\n"
|
||||
" {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}\n"
|
||||
"\n"
|
||||
"Use \"{{.CommandPath}} [command] --help\" para más información sobre un comando.{{end}}\n"
|
||||
msgstr "Sintaxis del comando:{{if .Runnable}}\n {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}}\n {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}}\n\nComando y comando abreviado:\n {{.NameAndAliases}}{{end}}{{if .HasExample}}\n\nUSO DE\"OPCIONES\" y \"FLAGS\":\nImportante: 1312.net es únicamente un ejemplo utilizado para mostrar cómo debe configurarse este campo. No es necesario utilizar este nombre.\nAl ejecutar el comando, reemplaza 1312.net por el nombre o dominio real de tu aplicación.\n\n{{.Example}}{{end}}{{if .HasAvailableSubCommands}}\n\nComandos disponibles:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name \"help\"))}}\n {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}\n\nOpciones:\n{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}}\n\nOpciones globales:\n{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}}\n\nTemas de ayuda adicionales:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}\n {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}\n\nUse \"{{.CommandPath}} [command] --help\" para más información sobre un comando.{{end}}\n"
|
||||
|
||||
#: cli/recipe/fetch.go:28
|
||||
msgid "Using \"--force/-f\" Git syncs an existing recipe. It does not erase unstaged changes."
|
||||
@@ -2368,7 +2316,7 @@ msgstr "Intentando generar y almacenar %s en %s"
|
||||
msgid "attempting to run %s"
|
||||
msgstr "Intentando ejecutar %s"
|
||||
|
||||
#: cli/app/deploy.go:283 cli/app/upgrade.go:296
|
||||
#: cli/app/deploy.go:284 cli/app/upgrade.go:297
|
||||
#, c-format
|
||||
msgid "attempting to run post deploy commands, saw: %s"
|
||||
msgstr "Intentando ejecutar los comandos posteriores al despliegue: %s"
|
||||
@@ -2404,7 +2352,7 @@ msgstr "autocompletar [bash|zsh|fish|powershell]"
|
||||
msgid "autocomplete failed: %s"
|
||||
msgstr "autocompletar falló: %s"
|
||||
|
||||
#: cli/app/new.go:401
|
||||
#: cli/app/new.go:405
|
||||
msgid "automatically generate secrets"
|
||||
msgstr "generar secretos automáticamente"
|
||||
|
||||
@@ -2454,8 +2402,8 @@ msgstr "enlace symlink roto en tus carpetas de configuración de abra: %s"
|
||||
#. no spaces in between
|
||||
#. translators: `abra app cp` aliases. use a comma separated list of aliases with
|
||||
#. no spaces in between
|
||||
#: cli/app/backup.go:148 cli/app/cp.go:30 cli/app/deploy.go:438
|
||||
#: cli/app/rollback.go:369 cli/app/upgrade.go:478
|
||||
#: cli/app/backup.go:148 cli/app/cp.go:30 cli/app/deploy.go:439
|
||||
#: cli/app/rollback.go:370 cli/app/upgrade.go:479
|
||||
msgid "c"
|
||||
msgstr ""
|
||||
|
||||
@@ -2511,7 +2459,7 @@ msgstr "no se puede obtener la etiqueta %s para %s"
|
||||
msgid "cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?"
|
||||
msgstr "no se puede redeplegar la versión anterior de caos (%s), ¿Era tu intención usar \"--caos\"?"
|
||||
|
||||
#: cli/app/deploy.go:388
|
||||
#: cli/app/deploy.go:389
|
||||
#, c-format
|
||||
msgid ""
|
||||
"cannot redeploy previous chaos version (%s), did you mean to use \"--chaos\"?\n"
|
||||
@@ -2533,7 +2481,7 @@ msgstr "no se puede especificar una etiqueta y un tipo de incremento al mismo ti
|
||||
msgid "cannot use '[secret] [version]' and '--all' together"
|
||||
msgstr "no se puede usar '[secreto] [versión]' y '--todos' juntos"
|
||||
|
||||
#: cli/app/deploy.go:330
|
||||
#: cli/app/deploy.go:331
|
||||
msgid "cannot use --chaos and --latest together"
|
||||
msgstr "no se puede usar --caos y --latest juntos"
|
||||
|
||||
@@ -2557,11 +2505,11 @@ msgstr "no se puede usar [server] y --local al mismo tiempo"
|
||||
msgid "cannot use [service] and --all-services/-a together"
|
||||
msgstr "no se puede usar [servicio] y --todos-los-servicios/-a juntos"
|
||||
|
||||
#: cli/app/deploy.go:322 cli/app/new.go:80
|
||||
#: cli/app/deploy.go:323 cli/app/new.go:80
|
||||
msgid "cannot use [version] and --chaos together"
|
||||
msgstr "no se puede usar [versión] y --caos juntos"
|
||||
|
||||
#: cli/app/deploy.go:326
|
||||
#: cli/app/deploy.go:327
|
||||
msgid "cannot use [version] and --latest together"
|
||||
msgstr "no se puede usar [versión] y --latest juntos"
|
||||
|
||||
@@ -2598,8 +2546,8 @@ msgid "cfg"
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/backup.go:318 cli/app/backup.go:334 cli/app/check.go:94
|
||||
#: cli/app/cmd.go:284 cli/app/cp.go:384 cli/app/deploy.go:413
|
||||
#: cli/app/labels.go:142 cli/app/list.go:334 cli/app/new.go:406
|
||||
#: cli/app/cmd.go:284 cli/app/cp.go:384 cli/app/deploy.go:414
|
||||
#: cli/app/labels.go:142 cli/app/list.go:334 cli/app/new.go:410
|
||||
#: cli/app/ps.go:212 cli/app/restart.go:162 cli/app/restore.go:137
|
||||
#: cli/app/secret.go:568 cli/app/secret.go:608 cli/app/secret.go:632
|
||||
#: cli/app/secret.go:640 cli/catalogue/catalogue.go:317 cli/recipe/lint.go:136
|
||||
@@ -2616,7 +2564,7 @@ msgstr ""
|
||||
msgid "check <domain> [flags]"
|
||||
msgstr "verificar <aplicacion> [opciones]"
|
||||
|
||||
#: cli/app/deploy.go:95 cli/app/undeploy.go:58 cli/app/upgrade.go:442
|
||||
#: cli/app/deploy.go:95 cli/app/undeploy.go:58 cli/app/upgrade.go:443
|
||||
#, c-format
|
||||
msgid "checking whether %s is already deployed"
|
||||
msgstr "verificando si %s ya está desplegado"
|
||||
@@ -2919,7 +2867,7 @@ msgstr "crítico"
|
||||
msgid "critical errors present in %s config"
|
||||
msgstr "errores críticos presentes en la configuración de %s"
|
||||
|
||||
#: cli/app/rollback.go:299
|
||||
#: cli/app/rollback.go:300
|
||||
#, c-format
|
||||
msgid "current deployment '%s' is not a known version for %s"
|
||||
msgstr "el despliegue actual '%s' no es una versión conocida para %s"
|
||||
@@ -2971,7 +2919,7 @@ msgstr "despliegue en proceso 🟠"
|
||||
msgid "deploy labels stanza present"
|
||||
msgstr "stanza de etiquetas de despliegue presente"
|
||||
|
||||
#: cli/app/deploy.go:448
|
||||
#: cli/app/deploy.go:449
|
||||
msgid "deploy latest recipe version"
|
||||
msgstr "desplegar la última versión de la receta"
|
||||
|
||||
@@ -3073,11 +3021,11 @@ msgstr "el directorio está vacío: %s"
|
||||
msgid "dirty: %v, "
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/deploy.go:440 cli/app/rollback.go:371 cli/app/upgrade.go:480
|
||||
#: cli/app/deploy.go:441 cli/app/rollback.go:372 cli/app/upgrade.go:481
|
||||
msgid "disable converge logic checks"
|
||||
msgstr "desactivar comprobaciones de lógica de convergencia"
|
||||
|
||||
#: cli/app/deploy.go:432 cli/app/rollback.go:363 cli/app/upgrade.go:472
|
||||
#: cli/app/deploy.go:433 cli/app/rollback.go:364 cli/app/upgrade.go:473
|
||||
msgid "disable public DNS checks"
|
||||
msgstr "desactivar comprobaciones de DNS público"
|
||||
|
||||
@@ -3093,11 +3041,11 @@ msgstr "no solicitar un TTY"
|
||||
msgid "docker: is the daemon running / your user has docker permissions?"
|
||||
msgstr "docker: ¿está corriendo el daemon / tu usuario tiene permisos de Docker?"
|
||||
|
||||
#: cli/app/new.go:382
|
||||
#: cli/app/new.go:386
|
||||
msgid "domain"
|
||||
msgstr "dominio"
|
||||
|
||||
#: cli/app/new.go:385
|
||||
#: cli/app/new.go:389
|
||||
msgid "domain name for app"
|
||||
msgstr "nombre de dominio para la aplicación"
|
||||
|
||||
@@ -3288,8 +3236,8 @@ msgstr "extrayendo secreto %s en %s"
|
||||
|
||||
#. translators: `abra recipe fetch` aliases. use a comma separated list of aliases
|
||||
#. with no spaces in between
|
||||
#: cli/app/deploy.go:422 cli/app/env.go:325 cli/app/remove.go:163
|
||||
#: cli/app/rollback.go:353 cli/app/secret.go:593 cli/app/upgrade.go:462
|
||||
#: cli/app/deploy.go:423 cli/app/env.go:325 cli/app/remove.go:163
|
||||
#: cli/app/rollback.go:354 cli/app/secret.go:593 cli/app/upgrade.go:463
|
||||
#: cli/app/volume.go:217 cli/recipe/fetch.go:20 cli/recipe/fetch.go:138
|
||||
msgid "f"
|
||||
msgstr ""
|
||||
@@ -3451,7 +3399,7 @@ msgstr "🛑 No se pudo eliminar algunos recursos del stock: %s"
|
||||
msgid "failed to resize tty, using default size"
|
||||
msgstr "🛑 No se pudo redimensionar el TTY; se usará el tamaño predeterminado"
|
||||
|
||||
#: cli/app/new.go:134
|
||||
#: cli/app/new.go:138
|
||||
#, c-format
|
||||
msgid "failed to retrieve latest commit for %s: %s"
|
||||
msgstr "🛑 No se pudo obtener el último commit de %s: %s"
|
||||
@@ -3541,8 +3489,8 @@ msgstr "Filtrar por receta"
|
||||
msgid "final merged env values for %s are: %s"
|
||||
msgstr "Los valores finales combinados de entorno para %s son: %s"
|
||||
|
||||
#: cli/app/deploy.go:421 cli/app/env.go:324 cli/app/remove.go:162
|
||||
#: cli/app/rollback.go:352 cli/app/upgrade.go:461 cli/app/volume.go:216
|
||||
#: cli/app/deploy.go:422 cli/app/env.go:324 cli/app/remove.go:162
|
||||
#: cli/app/rollback.go:353 cli/app/upgrade.go:462 cli/app/volume.go:216
|
||||
#: cli/recipe/fetch.go:137
|
||||
msgid "force"
|
||||
msgstr "forzar"
|
||||
@@ -3758,8 +3706,8 @@ msgid "id: %s, "
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/backup.go:321 cli/app/backup.go:337 cli/app/check.go:97
|
||||
#: cli/app/cmd.go:287 cli/app/cp.go:387 cli/app/deploy.go:416
|
||||
#: cli/app/labels.go:145 cli/app/list.go:337 cli/app/new.go:409
|
||||
#: cli/app/cmd.go:287 cli/app/cp.go:387 cli/app/deploy.go:417
|
||||
#: cli/app/labels.go:145 cli/app/list.go:337 cli/app/new.go:413
|
||||
#: cli/app/ps.go:215 cli/app/restart.go:165 cli/app/restore.go:140
|
||||
#: cli/app/secret.go:571 cli/app/secret.go:611 cli/app/secret.go:635
|
||||
#: cli/app/secret.go:643 cli/catalogue/catalogue.go:320 cli/recipe/lint.go:139
|
||||
@@ -3960,7 +3908,7 @@ msgstr "versión %s especificada inválida"
|
||||
#. no spaces in between
|
||||
#. translators: `abra recipe lint` aliases. use a comma separated list of
|
||||
#. aliases with no spaces in between
|
||||
#: cli/app/cmd.go:261 cli/app/deploy.go:446 cli/app/logs.go:20
|
||||
#: cli/app/cmd.go:261 cli/app/deploy.go:447 cli/app/logs.go:20
|
||||
#: cli/recipe/lint.go:17 cli/server/add.go:207
|
||||
msgid "l"
|
||||
msgstr ""
|
||||
@@ -3976,7 +3924,7 @@ msgstr ""
|
||||
msgid "labels <domain> [flags]"
|
||||
msgstr "etiquetas <aplicacion> [opciones]"
|
||||
|
||||
#: cli/app/deploy.go:445 cli/app/list.go:186
|
||||
#: cli/app/deploy.go:446 cli/app/list.go:186
|
||||
msgid "latest"
|
||||
msgstr "ultima"
|
||||
|
||||
@@ -4325,7 +4273,7 @@ msgstr "no hay configuraciones para eliminar"
|
||||
msgid "no containers matching the %v filter found?"
|
||||
msgstr "no se encontraron contenedores que coincidan con el filtro %v?"
|
||||
|
||||
#: cli/app/new.go:302 cli/internal/validate.go:129
|
||||
#: cli/app/new.go:306 cli/internal/validate.go:129
|
||||
msgid "no domain provided"
|
||||
msgstr "no se proporcionó dominio"
|
||||
|
||||
@@ -4352,6 +4300,11 @@ msgstr "¿no existe la receta '%s'?"
|
||||
msgid "no recipe name provided"
|
||||
msgstr "no se proporcionó el nombre de la receta"
|
||||
|
||||
#: cli/app/upgrade.go:242
|
||||
#, fuzzy, c-format
|
||||
msgid "no release notes for upgrading from %s to %s"
|
||||
msgstr "ejecución de prueba: mover la nota de la versión de 'next' a %s"
|
||||
|
||||
#: cli/app/secret.go:234
|
||||
#, c-format
|
||||
msgid "no secret %s available for recipe %s?"
|
||||
@@ -4378,7 +4331,7 @@ msgstr "no hay secretos para eliminar"
|
||||
msgid "no secrets to remove?"
|
||||
msgstr "¿No hay secretos para eliminar?"
|
||||
|
||||
#: cli/app/new.go:351 cli/internal/validate.go:167
|
||||
#: cli/app/new.go:355 cli/internal/validate.go:167
|
||||
msgid "no server provided"
|
||||
msgstr "no se proporcionó servidor"
|
||||
|
||||
@@ -4436,11 +4389,11 @@ msgstr "no se eliminaron volúmenes"
|
||||
msgid "no volumes to remove"
|
||||
msgstr "no hay volúmenes para eliminar"
|
||||
|
||||
#: cli/app/deploy.go:437 cli/app/rollback.go:368 cli/app/upgrade.go:477
|
||||
#: cli/app/deploy.go:438 cli/app/rollback.go:369 cli/app/upgrade.go:478
|
||||
msgid "no-converge-checks"
|
||||
msgstr "sin-verificaciones-de-convergencia"
|
||||
|
||||
#: cli/app/deploy.go:429 cli/app/rollback.go:360 cli/app/upgrade.go:469
|
||||
#: cli/app/deploy.go:430 cli/app/rollback.go:361 cli/app/upgrade.go:470
|
||||
msgid "no-domain-checks"
|
||||
msgstr "sin-verificaciones-de-dominio"
|
||||
|
||||
@@ -4508,7 +4461,7 @@ msgstr "solo se usaron etiquetas anotadas para la versión de la receta"
|
||||
msgid "only show errors"
|
||||
msgstr "mostrar solo errores"
|
||||
|
||||
#: cli/app/upgrade.go:488
|
||||
#: cli/app/upgrade.go:489
|
||||
msgid "only show release notes"
|
||||
msgstr "solo mostrar notas de la versión"
|
||||
|
||||
@@ -4520,7 +4473,7 @@ msgstr "solo cola de stderr"
|
||||
#. with no spaces in between
|
||||
#. translators: `abra server prune` aliases. use a comma separated list of
|
||||
#. aliases with no spaces in between
|
||||
#: cli/app/backup.go:295 cli/app/new.go:391 cli/app/ps.go:29
|
||||
#: cli/app/backup.go:295 cli/app/new.go:395 cli/app/ps.go:29
|
||||
#: cli/app/secret.go:561 cli/app/secret.go:585 cli/app/secret.go:625
|
||||
#: cli/app/undeploy.go:169 cli/catalogue/catalogue.go:294
|
||||
#: cli/recipe/list.go:112 cli/server/prune.go:18
|
||||
@@ -4542,27 +4495,27 @@ msgstr "parseado %s de %s"
|
||||
msgid "parsed following command arguments: %s"
|
||||
msgstr "parseados los siguientes argumentos del comando: %s"
|
||||
|
||||
#: cli/app/upgrade.go:345
|
||||
#: cli/app/upgrade.go:346
|
||||
#, c-format
|
||||
msgid "parsing chosen upgrade version failed: %s"
|
||||
msgstr "falló la actualización de versión elegida para el parsing: %s"
|
||||
|
||||
#: cli/app/upgrade.go:389
|
||||
#: cli/app/upgrade.go:390
|
||||
#, c-format
|
||||
msgid "parsing deployed version failed: %s"
|
||||
msgstr "parsing fallido de la versión desplegada: %s"
|
||||
|
||||
#: cli/app/upgrade.go:350
|
||||
#: cli/app/upgrade.go:351
|
||||
#, c-format
|
||||
msgid "parsing deployment version failed: %s"
|
||||
msgstr "parsing fallido de la versión de despliegue: %s"
|
||||
|
||||
#: cli/app/upgrade.go:356 cli/app/upgrade.go:395
|
||||
#: cli/app/upgrade.go:357 cli/app/upgrade.go:396
|
||||
#, c-format
|
||||
msgid "parsing recipe version failed: %s"
|
||||
msgstr "parsing fallido de la versión de la receta: %s"
|
||||
|
||||
#: cli/app/new.go:390 cli/app/secret.go:560 cli/app/secret.go:584
|
||||
#: cli/app/new.go:394 cli/app/secret.go:560 cli/app/secret.go:584
|
||||
#: cli/app/secret.go:624
|
||||
msgid "pass"
|
||||
msgstr ""
|
||||
@@ -4585,8 +4538,8 @@ msgstr "ruta"
|
||||
msgid "pattern"
|
||||
msgstr "patrón"
|
||||
|
||||
#: cli/app/deploy.go:424 cli/app/env.go:327 cli/app/remove.go:165
|
||||
#: cli/app/rollback.go:355 cli/app/upgrade.go:464 cli/app/volume.go:219
|
||||
#: cli/app/deploy.go:425 cli/app/env.go:327 cli/app/remove.go:165
|
||||
#: cli/app/rollback.go:356 cli/app/upgrade.go:465 cli/app/volume.go:219
|
||||
msgid "perform action without further prompt"
|
||||
msgstr "realizar acción sin más avisos"
|
||||
|
||||
@@ -4596,22 +4549,22 @@ msgstr "realizar acción sin más avisos"
|
||||
msgid "pl,p"
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/rollback.go:267
|
||||
#: cli/app/rollback.go:268
|
||||
#, c-format
|
||||
msgid "please select a downgrade (version: %s):"
|
||||
msgstr "por favor, selecciona una versión anterior (downgrade) (versión: %s):"
|
||||
|
||||
#: cli/app/rollback.go:272
|
||||
#: cli/app/rollback.go:273
|
||||
#, c-format
|
||||
msgid "please select a downgrade (version: %s, chaos: %s):"
|
||||
msgstr "por favor, selecciona una versión anterior (downgrade) (versión: %s, caos: %s):"
|
||||
|
||||
#: cli/app/upgrade.go:312
|
||||
#: cli/app/upgrade.go:313
|
||||
#, c-format
|
||||
msgid "please select an upgrade (version: %s):"
|
||||
msgstr "por favor, selecciona una actualización (versión: %s):"
|
||||
|
||||
#: cli/app/upgrade.go:317
|
||||
#: cli/app/upgrade.go:318
|
||||
#, c-format
|
||||
msgid "please select an upgrade (version: %s, chaos: %s):"
|
||||
msgstr "por favor, selecciona una actualización (versión: %s, caos: %s):"
|
||||
@@ -4697,7 +4650,7 @@ msgstr "consultando servidores remotos..."
|
||||
#. translators: `abra recipe` aliases. use a comma separated list of aliases
|
||||
#. with no spaces in between
|
||||
#: cli/app/backup.go:327 cli/app/list.go:304 cli/app/move.go:350
|
||||
#: cli/app/run.go:23 cli/app/upgrade.go:486 cli/catalogue/catalogue.go:302
|
||||
#: cli/app/run.go:23 cli/app/upgrade.go:487 cli/catalogue/catalogue.go:302
|
||||
#: cli/recipe/recipe.go:12 cli/recipe/release.go:624
|
||||
msgid "r"
|
||||
msgstr ""
|
||||
@@ -4822,7 +4775,7 @@ msgstr "publicar <receta> [version] [opciones]"
|
||||
msgid "release failed. any changes made have been reverted"
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/upgrade.go:485
|
||||
#: cli/app/upgrade.go:486
|
||||
msgid "releasenotes"
|
||||
msgstr "notas de la versión"
|
||||
|
||||
@@ -5127,7 +5080,7 @@ msgstr "ejecutar comando como usuario"
|
||||
msgid "run command locally"
|
||||
msgstr "ejecutar comando localmente"
|
||||
|
||||
#: cli/app/deploy.go:281 cli/app/upgrade.go:293
|
||||
#: cli/app/deploy.go:282 cli/app/upgrade.go:294
|
||||
#, c-format
|
||||
msgid "run the following post-deploy commands: %s"
|
||||
msgstr "Ejecutar los siguientes comandos después del despliegue: %s"
|
||||
@@ -5170,7 +5123,7 @@ msgstr "ejecutando el comando posterior '%s %s' en el contenedor %s"
|
||||
#. with no spaces in between
|
||||
#: cli/app/backup.go:198 cli/app/backup.go:263 cli/app/backup.go:287
|
||||
#: cli/app/env.go:333 cli/app/list.go:327 cli/app/logs.go:101
|
||||
#: cli/app/new.go:368 cli/app/restore.go:114 cli/app/secret.go:535
|
||||
#: cli/app/new.go:372 cli/app/restore.go:114 cli/app/secret.go:535
|
||||
#: cli/catalogue/catalogue.go:27 cli/catalogue/catalogue.go:310
|
||||
#: cli/recipe/fetch.go:130 cli/server/server.go:12
|
||||
msgid "s"
|
||||
@@ -5214,12 +5167,12 @@ msgstr "datos del secreto no proporcionados en la línea de comandos o stdin, so
|
||||
msgid "secret not found: %s"
|
||||
msgstr "secreto no encontrado: %s"
|
||||
|
||||
#: cli/app/deploy.go:358
|
||||
#: cli/app/deploy.go:359
|
||||
#, c-format
|
||||
msgid "secret not generated: %s"
|
||||
msgstr "secreto no generado: %s"
|
||||
|
||||
#: cli/app/deploy.go:356
|
||||
#: cli/app/deploy.go:357
|
||||
#, c-format
|
||||
msgid "secret not inserted (#generate=false): %s"
|
||||
msgstr "secreto no insertado (#generate=false): %s"
|
||||
@@ -5229,28 +5182,28 @@ msgstr "secreto no insertado (#generate=false): %s"
|
||||
msgid "secret: %s removed"
|
||||
msgstr "secreto: %s eliminado"
|
||||
|
||||
#: cli/app/backup.go:302 cli/app/new.go:398
|
||||
#: cli/app/backup.go:302 cli/app/new.go:402
|
||||
msgid "secrets"
|
||||
msgstr "secretos"
|
||||
|
||||
#: cli/app/new.go:238
|
||||
#: cli/app/new.go:242
|
||||
#, c-format
|
||||
msgid "secrets are %s shown again, please save them %s"
|
||||
msgstr "los secretos se muestran %s nuevamente, por favor guárdalos %s"
|
||||
|
||||
#: cli/app/deploy.go:306
|
||||
#: cli/app/deploy.go:307
|
||||
#, c-format
|
||||
msgid "selected latest recipe version: %s (from %d available versions)"
|
||||
msgstr "versión de receta más reciente seleccionada: %s (de %d versiones disponibles)"
|
||||
|
||||
#: cli/app/new.go:121
|
||||
#: cli/app/new.go:125
|
||||
#, c-format
|
||||
msgid "selected recipe version: %s (from %d available versions)"
|
||||
msgstr "versión de receta seleccionada: %s (de %d versiones disponibles)"
|
||||
|
||||
#. translators: `abra server` command for autocompletion
|
||||
#: cli/app/env.go:332 cli/app/env.go:339 cli/app/list.go:326
|
||||
#: cli/app/list.go:341 cli/app/new.go:367 cli/app/new.go:374 cli/run.go:101
|
||||
#: cli/app/list.go:341 cli/app/new.go:371 cli/app/new.go:378 cli/run.go:101
|
||||
msgid "server"
|
||||
msgstr "servidor"
|
||||
|
||||
@@ -5364,7 +5317,7 @@ msgstr "establecer referencia: %s"
|
||||
msgid "severity"
|
||||
msgstr "gravedad"
|
||||
|
||||
#: cli/app/deploy.go:456 cli/app/rollback.go:379 cli/app/upgrade.go:496
|
||||
#: cli/app/deploy.go:457 cli/app/rollback.go:380 cli/app/upgrade.go:497
|
||||
msgid "show all configs & images, including unchanged ones"
|
||||
msgstr "mostrar todas las configuraciones e imágenes, incluidas las no cambiadas"
|
||||
|
||||
@@ -5388,7 +5341,7 @@ msgstr "mostrar aplicaciones de un servidor específico"
|
||||
msgid "show debug messages"
|
||||
msgstr "mostrar mensajes para el debugeo"
|
||||
|
||||
#: cli/app/deploy.go:453 cli/app/rollback.go:376 cli/app/upgrade.go:493
|
||||
#: cli/app/deploy.go:454 cli/app/rollback.go:377 cli/app/upgrade.go:494
|
||||
msgid "show-unchanged"
|
||||
msgstr "mostrar-sin-cambios"
|
||||
|
||||
@@ -5396,7 +5349,7 @@ msgstr "mostrar-sin-cambios"
|
||||
msgid "since"
|
||||
msgstr "desde"
|
||||
|
||||
#: cli/app/new.go:336
|
||||
#: cli/app/new.go:340
|
||||
#, c-format
|
||||
msgid "single server detected, choosing %s automatically"
|
||||
msgstr "se ha detectado un único servidor, eligiendo %s automáticamente"
|
||||
@@ -5436,11 +5389,11 @@ msgstr "omitiendo según lo solicitado, plegado aún está en progreso 🟠"
|
||||
msgid "skipping converge logic checks"
|
||||
msgstr "omitiendo comprobaciones de lógica de convergencia"
|
||||
|
||||
#: cli/app/deploy.go:208
|
||||
#: cli/app/deploy.go:209
|
||||
msgid "skipping domain checks"
|
||||
msgstr "omitiendo comprobaciones de dominio"
|
||||
|
||||
#: cli/app/deploy.go:205
|
||||
#: cli/app/deploy.go:206
|
||||
msgid "skipping domain checks, no DOMAIN=... configured"
|
||||
msgstr "omitiendo comprobaciones de dominio, no DOMAIN=... configurado"
|
||||
|
||||
@@ -5496,7 +5449,7 @@ msgstr "especificar archivo de secreto"
|
||||
msgid "specify secret value"
|
||||
msgstr "especificar valor de secreto"
|
||||
|
||||
#: cli/app/new.go:370
|
||||
#: cli/app/new.go:374
|
||||
msgid "specify server for new app"
|
||||
msgstr "especificar servidor para la nueva aplicación"
|
||||
|
||||
@@ -5554,7 +5507,7 @@ msgstr ""
|
||||
msgid "store generated secrets in a local pass store"
|
||||
msgstr "almacenar secretos generados en un almacén de contraseñas local"
|
||||
|
||||
#: cli/app/new.go:393
|
||||
#: cli/app/new.go:397
|
||||
msgid "store secrets in a local pass store"
|
||||
msgstr "almacenar secretos en un almacén de contraseñas local"
|
||||
|
||||
@@ -5705,7 +5658,7 @@ msgstr "recortar"
|
||||
msgid "trim input"
|
||||
msgstr "recortar entrada"
|
||||
|
||||
#: cli/app/new.go:263 pkg/app/app.go:141
|
||||
#: cli/app/new.go:267 pkg/app/app.go:141
|
||||
#, c-format
|
||||
msgid "trimming %s to %s to avoid runtime limits"
|
||||
msgstr "recortando %s a %s para evitar límites de tiempo de ejecución"
|
||||
@@ -6250,27 +6203,27 @@ msgstr "la versión parece inválida: %s"
|
||||
msgid "version wiped from %s.env"
|
||||
msgstr "versión eliminada de %s.env"
|
||||
|
||||
#: cli/app/deploy.go:372
|
||||
#: cli/app/deploy.go:373
|
||||
#, c-format
|
||||
msgid "version: taking chaos version: %s"
|
||||
msgstr "versión: tomando la versión de caos: %s"
|
||||
|
||||
#: cli/app/deploy.go:398
|
||||
#: cli/app/deploy.go:399
|
||||
#, c-format
|
||||
msgid "version: taking deployed version: %s"
|
||||
msgstr "versión: tomando la versión desplegada: %s"
|
||||
|
||||
#: cli/app/deploy.go:403
|
||||
#: cli/app/deploy.go:404
|
||||
#, c-format
|
||||
msgid "version: taking new recipe version: %s"
|
||||
msgstr "versión: tomando la nueva versión de la receta: %s"
|
||||
|
||||
#: cli/app/deploy.go:392
|
||||
#: cli/app/deploy.go:393
|
||||
#, c-format
|
||||
msgid "version: taking version from .env file: %s"
|
||||
msgstr "versión: tomando la versión desde el archivo .env: %s"
|
||||
|
||||
#: cli/app/deploy.go:378
|
||||
#: cli/app/deploy.go:379
|
||||
#, c-format
|
||||
msgid "version: taking version from cli arg: %s"
|
||||
msgstr "versión: tomando la versión desde el argumento de la línea de comandos: %s"
|
||||
@@ -6401,8 +6354,8 @@ msgstr "el directorio de trabajo no está limpio en %s, abortando"
|
||||
msgid "writer: %v, "
|
||||
msgstr ""
|
||||
|
||||
#: cli/app/deploy.go:288 cli/app/new.go:251 cli/app/rollback.go:256
|
||||
#: cli/app/undeploy.go:120 cli/app/upgrade.go:301
|
||||
#: cli/app/deploy.go:289 cli/app/new.go:255 cli/app/rollback.go:257
|
||||
#: cli/app/undeploy.go:120 cli/app/upgrade.go:302
|
||||
#, c-format
|
||||
msgid "writing recipe version failed: %s"
|
||||
msgstr "escritura de la versión de la receta fallida: %s"
|
||||
|
||||
@@ -5,12 +5,15 @@ import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"coopcloud.tech/abra/pkg/test"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestIsDirty(t *testing.T) {
|
||||
r := Get("abra-test-recipe")
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
r := Get(test.RecipeName)
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -22,10 +25,9 @@ func TestIsDirty(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
defer f.Close()
|
||||
defer t.Cleanup(func() {
|
||||
os.Remove(fpath)
|
||||
})
|
||||
t.Cleanup(func() { os.Remove(fpath) })
|
||||
|
||||
dirty, err := r.IsDirty()
|
||||
if err != nil {
|
||||
|
||||
+12
-11
@@ -5,6 +5,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"coopcloud.tech/abra/pkg/config"
|
||||
"coopcloud.tech/abra/pkg/test"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -101,24 +102,24 @@ func TestGet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetVersionLabelLocalDoesNotUseTimeoutLabel(t *testing.T) {
|
||||
r := Get("traefik")
|
||||
test.Setup()
|
||||
t.Cleanup(func() { test.Teardown() })
|
||||
|
||||
r := Get(test.RecipeName)
|
||||
if err := r.EnsureExists(); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 1; i < 50; i++ {
|
||||
timeout := "120"
|
||||
if err := test.AddEnv("TIMEOUT", timeout); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := 1; i < 3; i++ {
|
||||
label, err := r.GetVersionLabelLocal()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// NOTE(d1): this is potentially quite a brittle unit test as it needs to
|
||||
// hardcode the default timeout label to ensure that the label parser never
|
||||
// returns it. hopefully this won't fail too often! if you're here because
|
||||
// of a failure, just update the `defaultTimeoutLabel` value & permalink
|
||||
// below
|
||||
// https://git.coopcloud.tech/coop-cloud/traefik/src/commit/ac3a47fe8ca3ef92db84f64cfedfbb348000faee/.env.sample#L2
|
||||
defaultTimeoutLabel := "300"
|
||||
assert.NotEqual(t, label, defaultTimeoutLabel)
|
||||
assert.NotEqual(t, label, timeout)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: nginx:1.21.0
|
||||
image: nginx:1.31.4
|
||||
secrets:
|
||||
- test_pass_one
|
||||
- test_pass_two
|
||||
|
||||
+54
-40
@@ -2,57 +2,50 @@ package test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
appPkg "coopcloud.tech/abra/pkg/app"
|
||||
"coopcloud.tech/abra/pkg/envfile"
|
||||
"coopcloud.tech/abra/pkg/log"
|
||||
"coopcloud.tech/abra/pkg/recipe"
|
||||
gitPkg "coopcloud.tech/abra/pkg/git"
|
||||
"git.coopcloud.tech/toolshed/godotenv"
|
||||
)
|
||||
|
||||
var (
|
||||
AppName = "test_app.example.com"
|
||||
ServerName = "test_server"
|
||||
TFiles = []string{"bar.env", "foo.env"}
|
||||
TFolders = []string{"dir1", "dir2"}
|
||||
TestServer = os.ExpandEnv("$PWD/../../tests/resources/test_server")
|
||||
TestDir = os.ExpandEnv("$PWD/../../tests/resources/test_dir")
|
||||
RecipeName = "test_recipe"
|
||||
|
||||
ExpectedAppEnv = envfile.AppEnv{
|
||||
"DOMAIN": "test_app.example.com",
|
||||
"RECIPE": "test_recipe",
|
||||
}
|
||||
TFiles = []string{"bar.env", "foo.env"}
|
||||
TFolders = []string{"dir1", "dir2"}
|
||||
|
||||
ExpectedApp = appPkg.App{
|
||||
Name: AppName,
|
||||
Recipe: recipe.Get(ExpectedAppEnv["RECIPE"]),
|
||||
Domain: ExpectedAppEnv["DOMAIN"],
|
||||
Env: ExpectedAppEnv,
|
||||
Path: ExpectedAppFile.Path,
|
||||
Server: ExpectedAppFile.Server,
|
||||
}
|
||||
ServerDir = os.ExpandEnv("$ABRA_DIR/servers/test_server")
|
||||
RecipeDir = os.ExpandEnv("$ABRA_DIR/recipes/test_recipe")
|
||||
TestDir = os.ExpandEnv("$PWD/../../tests/resources/test_dir")
|
||||
|
||||
ExpectedAppFile = appPkg.AppFile{
|
||||
Path: path.Join(TestServer, fmt.Sprintf("%s.env", AppName)),
|
||||
Server: ServerName,
|
||||
}
|
||||
AppEnvPath = path.Join(ServerDir, fmt.Sprintf("%s.env", AppName))
|
||||
|
||||
ExpectedAppFiles = map[string]appPkg.AppFile{
|
||||
AppName: ExpectedAppFile,
|
||||
}
|
||||
AbraTestRecipe = "abra-test-recipe"
|
||||
)
|
||||
|
||||
func RmServerAppRecipe() {
|
||||
testAppLink := os.ExpandEnv("$ABRA_DIR/servers/test_server")
|
||||
os.Remove(testAppLink)
|
||||
func Teardown() {
|
||||
abraDir := os.ExpandEnv("$ABRA_DIR")
|
||||
if abraDir == fmt.Sprintf("%s/.abra", os.ExpandEnv("$HOME")) {
|
||||
log.Fatal("set $ABRA_DIR before running the test suite")
|
||||
}
|
||||
|
||||
testRecipeLink := os.ExpandEnv("$ABRA_DIR/recipes/test_recipe")
|
||||
os.Remove(testRecipeLink)
|
||||
if err := os.RemoveAll(abraDir); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func MkServerAppRecipe() {
|
||||
RmServerAppRecipe()
|
||||
func Setup() {
|
||||
Teardown()
|
||||
|
||||
if err := os.Mkdir(os.ExpandEnv("$ABRA_DIR"), 0764); err != nil {
|
||||
if !os.IsExist(err) {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Mkdir(os.ExpandEnv("$ABRA_DIR/servers"), 0700); err != nil {
|
||||
if !os.IsExist(err) {
|
||||
@@ -66,15 +59,36 @@ func MkServerAppRecipe() {
|
||||
}
|
||||
}
|
||||
|
||||
testAppDir := os.ExpandEnv("$PWD/../../tests/resources/test_server")
|
||||
testAppLink := os.ExpandEnv("$ABRA_DIR/servers/test_server")
|
||||
if err := os.Symlink(testAppDir, testAppLink); err != nil {
|
||||
serverSrcDir := os.ExpandEnv("$PWD/../../tests/resources/test_server")
|
||||
serverDestDir := os.ExpandEnv("$ABRA_DIR/servers/test_server")
|
||||
if err := os.CopyFS(serverDestDir, os.DirFS(serverSrcDir)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
testRecipeDir := os.ExpandEnv("$PWD/../../tests/resources/test_recipe")
|
||||
testRecipeLink := os.ExpandEnv("$ABRA_DIR/recipes/test_recipe")
|
||||
if err := os.Symlink(testRecipeDir, testRecipeLink); err != nil {
|
||||
recipeSrcDir := os.ExpandEnv("$PWD/../../tests/resources/test_recipe")
|
||||
recipeDestDir := os.ExpandEnv("$ABRA_DIR/recipes/test_recipe")
|
||||
if err := os.CopyFS(recipeDestDir, os.DirFS(recipeSrcDir)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
if err := gitPkg.Init(recipeDestDir, true, "tester", "helo@coopcloud.tech"); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func AddEnv(envKey, envValue string) error {
|
||||
filePath := os.ExpandEnv(fmt.Sprintf("$ABRA_DIR/servers/%s/%s.env", ServerName, AppName))
|
||||
|
||||
envVars, _, err := godotenv.Read(filePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
envVars[envKey] = envValue
|
||||
|
||||
if err := godotenv.Write(envVars, filePath); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+2
-2
@@ -8,7 +8,7 @@
|
||||
"gomodTidy"
|
||||
],
|
||||
"ignoreDeps": [
|
||||
"github.com/urfave/cli",
|
||||
"goreleaser/goreleaser"
|
||||
"github.com/docker/cli",
|
||||
"github.com/spf13/cobra"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ echo "========================================================================"
|
||||
echo "========================================================================"
|
||||
echo "BUILDING ABRA"
|
||||
echo "========================================================================"
|
||||
export PATH="/usr/lib/go-1.21/bin:$PATH"
|
||||
export PATH="$PATH:/usr/local/go/bin"
|
||||
make build
|
||||
echo "========================================================================"
|
||||
|
||||
|
||||
@@ -61,6 +61,16 @@ teardown(){
|
||||
run grep -q "TYPE=$TEST_RECIPE:0.3.0+1.21.0" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
# 0.3.0-only
|
||||
run grep -q "NETWORK_WITH_COMMENT=BAZ" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
# latest-only
|
||||
run grep -q "TIMEOUT=120" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "ensure recipe is up-to-date" {
|
||||
@@ -100,6 +110,16 @@ teardown(){
|
||||
run grep -q "TYPE=$TEST_RECIPE:${tagHash}" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
# 0.3.0-only
|
||||
run grep -q "NETWORK_WITH_COMMENT=BAZ" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_success
|
||||
|
||||
# latest-only
|
||||
run grep -q "TIMEOUT=120" \
|
||||
"$ABRA_DIR/servers/$TEST_SERVER/$TEST_APP_DOMAIN.env"
|
||||
assert_failure
|
||||
}
|
||||
|
||||
@test "does not overwrite existing env files" {
|
||||
|
||||
@@ -63,26 +63,23 @@ teardown() {
|
||||
}
|
||||
|
||||
@test "release minor bump" {
|
||||
run $ABRA recipe upgrade "$TEST_RECIPE" --no-input --minor --commit
|
||||
assert_success
|
||||
|
||||
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" show
|
||||
assert_success
|
||||
assert_output --regexp 'image: nginx:1.2.*'
|
||||
|
||||
# NOTE(d1): ensure the latest tag is the one we expect
|
||||
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" checkout 0.3.0+1.21.0
|
||||
_remove_tags
|
||||
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag \
|
||||
-a "0.3.0+1.21.0" -m "fake: 0.3.0+1.21.0"
|
||||
assert_success
|
||||
|
||||
run $ABRA recipe upgrade "$TEST_RECIPE" --no-input --minor --commit
|
||||
assert_success
|
||||
|
||||
run $ABRA recipe release "$TEST_RECIPE" --no-input --minor
|
||||
assert_success
|
||||
assert_output --partial 'INFO new release published:'
|
||||
|
||||
run git -C "$ABRA_DIR/recipes/$TEST_RECIPE" tag --list
|
||||
assert_success
|
||||
assert_output --regexp '0\.4\.0\+1\.2.*'
|
||||
assert_output --regexp '0\.4\.0\+1\.(.*)'
|
||||
}
|
||||
|
||||
@test "release with unstaged changes" {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
RECIPE=test_recipe
|
||||
DOMAIN=test_app.example.com
|
||||
|
||||
# NOTE(d1): ensure commented out TIMEOUT doesn't get included
|
||||
# see TestReadEnv in ./pkg/envfile
|
||||
# TIMEOUT=120
|
||||
#
|
||||
# NOTE(d1): for new changes, you *MUST* also update ../test_server/test_app.example.com.env
|
||||
#
|
||||
|
||||
# NOTE(d1): TestReadEnv
|
||||
# FOO=BAR
|
||||
|
||||
@@ -3,11 +3,12 @@ version: "3.8"
|
||||
|
||||
services:
|
||||
app:
|
||||
image: nginx:1.29.0
|
||||
image: nginx:1.31.4
|
||||
networks:
|
||||
- proxy
|
||||
deploy:
|
||||
labels:
|
||||
- "coop-cloud.${STACK_NAME}.version=0.1.0+0.1.0"
|
||||
- "coop-cloud.${STACK_NAME}.timeout=${TIMEOUT}"
|
||||
|
||||
networks:
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
RECIPE=test_recipe
|
||||
DOMAIN=test_app.example.com
|
||||
|
||||
# NOTE(d1): ensure commented out TIMEOUT doesn't get included
|
||||
# see TestReadEnv in ./pkg/envfile
|
||||
# TIMEOUT=120
|
||||
#
|
||||
# NOTE(d1): for new changes, you *MUST* also update ../test_recipe/.env.sample
|
||||
#
|
||||
|
||||
# NOTE(d1): TestReadEnv
|
||||
# FOO=BAR
|
||||
|
||||
+3
-3
@@ -3,16 +3,16 @@ kind: pipeline
|
||||
name: coopcloud.tech/tagcmp
|
||||
steps:
|
||||
- name: gofmt
|
||||
image: golang:1.21
|
||||
image: golang:1.27
|
||||
commands:
|
||||
- test -z "$(gofmt -l .)"
|
||||
|
||||
- name: go build
|
||||
image: golang:1.21
|
||||
image: golang:1.27
|
||||
commands:
|
||||
- go build -v .
|
||||
|
||||
- name: go test
|
||||
image: golang:1.21
|
||||
image: golang:1.27
|
||||
commands:
|
||||
- go test . -cover
|
||||
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
]
|
||||
}
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
TOML stands for Tom's Obvious, Minimal Language. This Go package provides a
|
||||
reflection interface similar to Go's standard library `json` and `xml` packages.
|
||||
|
||||
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
|
||||
Compatible with TOML version [v1.1.0](https://toml.io/en/v1.1.0).
|
||||
|
||||
Documentation: https://pkg.go.dev/github.com/BurntSushi/toml
|
||||
|
||||
|
||||
+8
-1
@@ -206,6 +206,13 @@ func markDecodedRecursive(md *MetaData, tmap map[string]any) {
|
||||
markDecodedRecursive(md, tmap)
|
||||
md.context = md.context[0 : len(md.context)-1]
|
||||
}
|
||||
if tarr, ok := tmap[key].([]map[string]any); ok {
|
||||
for _, elm := range tarr {
|
||||
md.context = append(md.context, key)
|
||||
markDecodedRecursive(md, elm)
|
||||
md.context = md.context[0 : len(md.context)-1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -423,7 +430,7 @@ func (md *MetaData) unifyString(data any, rv reflect.Value) error {
|
||||
if i, ok := data.(int64); ok {
|
||||
rv.SetString(strconv.FormatInt(i, 10))
|
||||
} else if f, ok := data.(float64); ok {
|
||||
rv.SetString(strconv.FormatFloat(f, 'f', -1, 64))
|
||||
rv.SetString(strconv.FormatFloat(f, 'g', -1, 64))
|
||||
} else {
|
||||
return md.badtype("string", data)
|
||||
}
|
||||
|
||||
+46
-33
@@ -228,9 +228,9 @@ func (enc *Encoder) eElement(rv reflect.Value) {
|
||||
}
|
||||
switch v.Location() {
|
||||
default:
|
||||
enc.wf(v.Format(format))
|
||||
enc.write(v.Format(format))
|
||||
case internal.LocalDatetime, internal.LocalDate, internal.LocalTime:
|
||||
enc.wf(v.In(time.UTC).Format(format))
|
||||
enc.write(v.In(time.UTC).Format(format))
|
||||
}
|
||||
return
|
||||
case Marshaler:
|
||||
@@ -279,40 +279,40 @@ func (enc *Encoder) eElement(rv reflect.Value) {
|
||||
case reflect.String:
|
||||
enc.writeQuoted(rv.String())
|
||||
case reflect.Bool:
|
||||
enc.wf(strconv.FormatBool(rv.Bool()))
|
||||
enc.write(strconv.FormatBool(rv.Bool()))
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
enc.wf(strconv.FormatInt(rv.Int(), 10))
|
||||
enc.write(strconv.FormatInt(rv.Int(), 10))
|
||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||
enc.wf(strconv.FormatUint(rv.Uint(), 10))
|
||||
enc.write(strconv.FormatUint(rv.Uint(), 10))
|
||||
case reflect.Float32:
|
||||
f := rv.Float()
|
||||
if math.IsNaN(f) {
|
||||
if math.Signbit(f) {
|
||||
enc.wf("-")
|
||||
enc.write("-")
|
||||
}
|
||||
enc.wf("nan")
|
||||
enc.write("nan")
|
||||
} else if math.IsInf(f, 0) {
|
||||
if math.Signbit(f) {
|
||||
enc.wf("-")
|
||||
enc.write("-")
|
||||
}
|
||||
enc.wf("inf")
|
||||
enc.write("inf")
|
||||
} else {
|
||||
enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 32)))
|
||||
enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 32)))
|
||||
}
|
||||
case reflect.Float64:
|
||||
f := rv.Float()
|
||||
if math.IsNaN(f) {
|
||||
if math.Signbit(f) {
|
||||
enc.wf("-")
|
||||
enc.write("-")
|
||||
}
|
||||
enc.wf("nan")
|
||||
enc.write("nan")
|
||||
} else if math.IsInf(f, 0) {
|
||||
if math.Signbit(f) {
|
||||
enc.wf("-")
|
||||
enc.write("-")
|
||||
}
|
||||
enc.wf("inf")
|
||||
enc.write("inf")
|
||||
} else {
|
||||
enc.wf(floatAddDecimal(strconv.FormatFloat(f, 'f', -1, 64)))
|
||||
enc.write(floatAddDecimal(strconv.FormatFloat(f, 'g', -1, 64)))
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
enc.eArrayOrSliceElement(rv)
|
||||
@@ -330,27 +330,32 @@ func (enc *Encoder) eElement(rv reflect.Value) {
|
||||
// By the TOML spec, all floats must have a decimal with at least one number on
|
||||
// either side.
|
||||
func floatAddDecimal(fstr string) string {
|
||||
if !strings.Contains(fstr, ".") {
|
||||
return fstr + ".0"
|
||||
for _, c := range fstr {
|
||||
if c == 'e' { // Exponent syntax
|
||||
return fstr
|
||||
}
|
||||
if c == '.' {
|
||||
return fstr
|
||||
}
|
||||
}
|
||||
return fstr
|
||||
return fstr + ".0"
|
||||
}
|
||||
|
||||
func (enc *Encoder) writeQuoted(s string) {
|
||||
enc.wf("\"%s\"", dblQuotedReplacer.Replace(s))
|
||||
enc.write(`"` + dblQuotedReplacer.Replace(s) + `"`)
|
||||
}
|
||||
|
||||
func (enc *Encoder) eArrayOrSliceElement(rv reflect.Value) {
|
||||
length := rv.Len()
|
||||
enc.wf("[")
|
||||
enc.write("[")
|
||||
for i := 0; i < length; i++ {
|
||||
elem := eindirect(rv.Index(i))
|
||||
enc.eElement(elem)
|
||||
if i != length-1 {
|
||||
enc.wf(", ")
|
||||
enc.write(", ")
|
||||
}
|
||||
}
|
||||
enc.wf("]")
|
||||
enc.write("]")
|
||||
}
|
||||
|
||||
func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
||||
@@ -363,7 +368,7 @@ func (enc *Encoder) eArrayOfTables(key Key, rv reflect.Value) {
|
||||
continue
|
||||
}
|
||||
enc.newline()
|
||||
enc.wf("%s[[%s]]", enc.indentStr(key), key)
|
||||
enc.writef("%s[[%s]]", enc.indentStr(key), key)
|
||||
enc.newline()
|
||||
enc.eMapOrStruct(key, trv, false)
|
||||
}
|
||||
@@ -376,7 +381,7 @@ func (enc *Encoder) eTable(key Key, rv reflect.Value) {
|
||||
enc.newline()
|
||||
}
|
||||
if len(key) > 0 {
|
||||
enc.wf("%s[%s]", enc.indentStr(key), key)
|
||||
enc.writef("%s[%s]", enc.indentStr(key), key)
|
||||
enc.newline()
|
||||
}
|
||||
enc.eMapOrStruct(key, rv, false)
|
||||
@@ -422,7 +427,7 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
|
||||
if inline {
|
||||
enc.writeKeyValue(Key{mapKey.String()}, val, true)
|
||||
if trailC || i != len(mapKeys)-1 {
|
||||
enc.wf(", ")
|
||||
enc.write(", ")
|
||||
}
|
||||
} else {
|
||||
enc.encode(key.add(mapKey.String()), val)
|
||||
@@ -431,12 +436,12 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
|
||||
}
|
||||
|
||||
if inline {
|
||||
enc.wf("{")
|
||||
enc.write("{")
|
||||
}
|
||||
writeMapKeys(mapKeysDirect, len(mapKeysSub) > 0)
|
||||
writeMapKeys(mapKeysSub, false)
|
||||
if inline {
|
||||
enc.wf("}")
|
||||
enc.write("}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -534,7 +539,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
|
||||
if inline {
|
||||
enc.writeKeyValue(Key{keyName}, fieldVal, true)
|
||||
if fieldIndex[0] != totalFields-1 {
|
||||
enc.wf(", ")
|
||||
enc.write(", ")
|
||||
}
|
||||
} else {
|
||||
enc.encode(key.add(keyName), fieldVal)
|
||||
@@ -543,14 +548,14 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
|
||||
}
|
||||
|
||||
if inline {
|
||||
enc.wf("{")
|
||||
enc.write("{")
|
||||
}
|
||||
|
||||
l := len(fieldsDirect) + len(fieldsSub)
|
||||
writeFields(fieldsDirect, l)
|
||||
writeFields(fieldsSub, l)
|
||||
if inline {
|
||||
enc.wf("}")
|
||||
enc.write("}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -700,7 +705,7 @@ func isEmpty(rv reflect.Value) bool {
|
||||
|
||||
func (enc *Encoder) newline() {
|
||||
if enc.hasWritten {
|
||||
enc.wf("\n")
|
||||
enc.write("\n")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -722,14 +727,22 @@ func (enc *Encoder) writeKeyValue(key Key, val reflect.Value, inline bool) {
|
||||
enc.eElement(val)
|
||||
return
|
||||
}
|
||||
enc.wf("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
|
||||
enc.writef("%s%s = ", enc.indentStr(key), key.maybeQuoted(len(key)-1))
|
||||
enc.eElement(val)
|
||||
if !inline {
|
||||
enc.newline()
|
||||
}
|
||||
}
|
||||
|
||||
func (enc *Encoder) wf(format string, v ...any) {
|
||||
func (enc *Encoder) write(s string) {
|
||||
_, err := enc.w.WriteString(s)
|
||||
if err != nil {
|
||||
encPanic(err)
|
||||
}
|
||||
enc.hasWritten = true
|
||||
}
|
||||
|
||||
func (enc *Encoder) writef(format string, v ...any) {
|
||||
_, err := fmt.Fprintf(enc.w, format, v...)
|
||||
if err != nil {
|
||||
encPanic(err)
|
||||
|
||||
+53
-77
@@ -13,7 +13,6 @@ type itemType int
|
||||
|
||||
const (
|
||||
itemError itemType = iota
|
||||
itemNIL // used in the parser to indicate no type
|
||||
itemEOF
|
||||
itemText
|
||||
itemString
|
||||
@@ -47,14 +46,13 @@ func (p Position) String() string {
|
||||
}
|
||||
|
||||
type lexer struct {
|
||||
input string
|
||||
start int
|
||||
pos int
|
||||
line int
|
||||
state stateFn
|
||||
items chan item
|
||||
tomlNext bool
|
||||
esc bool
|
||||
input string
|
||||
start int
|
||||
pos int
|
||||
line int
|
||||
state stateFn
|
||||
items chan item
|
||||
esc bool
|
||||
|
||||
// Allow for backing up up to 4 runes. This is necessary because TOML
|
||||
// contains 3-rune tokens (""" and ''').
|
||||
@@ -90,14 +88,13 @@ func (lx *lexer) nextItem() item {
|
||||
}
|
||||
}
|
||||
|
||||
func lex(input string, tomlNext bool) *lexer {
|
||||
func lex(input string) *lexer {
|
||||
lx := &lexer{
|
||||
input: input,
|
||||
state: lexTop,
|
||||
items: make(chan item, 10),
|
||||
stack: make([]stateFn, 0, 10),
|
||||
line: 1,
|
||||
tomlNext: tomlNext,
|
||||
input: input,
|
||||
state: lexTop,
|
||||
items: make(chan item, 10),
|
||||
stack: make([]stateFn, 0, 10),
|
||||
line: 1,
|
||||
}
|
||||
return lx
|
||||
}
|
||||
@@ -108,7 +105,7 @@ func (lx *lexer) push(state stateFn) {
|
||||
|
||||
func (lx *lexer) pop() stateFn {
|
||||
if len(lx.stack) == 0 {
|
||||
return lx.errorf("BUG in lexer: no states to pop")
|
||||
panic("BUG in lexer: no states to pop")
|
||||
}
|
||||
last := lx.stack[len(lx.stack)-1]
|
||||
lx.stack = lx.stack[0 : len(lx.stack)-1]
|
||||
@@ -305,6 +302,8 @@ func lexTop(lx *lexer) stateFn {
|
||||
return lexTableStart
|
||||
case eof:
|
||||
if lx.pos > lx.start {
|
||||
// TODO: never reached? I think this can only occur on a bug in the
|
||||
// lexer(?)
|
||||
return lx.errorf("unexpected EOF")
|
||||
}
|
||||
lx.emit(itemEOF)
|
||||
@@ -392,8 +391,6 @@ func lexTableNameStart(lx *lexer) stateFn {
|
||||
func lexTableNameEnd(lx *lexer) stateFn {
|
||||
lx.skip(isWhitespace)
|
||||
switch r := lx.next(); {
|
||||
case isWhitespace(r):
|
||||
return lexTableNameEnd
|
||||
case r == '.':
|
||||
lx.ignore()
|
||||
return lexTableNameStart
|
||||
@@ -412,7 +409,7 @@ func lexTableNameEnd(lx *lexer) stateFn {
|
||||
// Lexes only one part, e.g. only 'a' inside 'a.b'.
|
||||
func lexBareName(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
if isBareKeyChar(r, lx.tomlNext) {
|
||||
if isBareKeyChar(r) {
|
||||
return lexBareName
|
||||
}
|
||||
lx.backup()
|
||||
@@ -420,23 +417,23 @@ func lexBareName(lx *lexer) stateFn {
|
||||
return lx.pop()
|
||||
}
|
||||
|
||||
// lexBareName lexes one part of a key or table.
|
||||
//
|
||||
// It assumes that at least one valid character for the table has already been
|
||||
// read.
|
||||
// lexQuotedName lexes one part of a quoted key or table name. It assumes that
|
||||
// it starts lexing at the quote itself (" or ').
|
||||
//
|
||||
// Lexes only one part, e.g. only '"a"' inside '"a".b'.
|
||||
func lexQuotedName(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexValue)
|
||||
case r == '"':
|
||||
lx.ignore() // ignore the '"'
|
||||
return lexString
|
||||
case r == '\'':
|
||||
lx.ignore() // ignore the "'"
|
||||
return lexRawString
|
||||
|
||||
// TODO: I don't think any of the below conditions can ever be reached?
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexValue)
|
||||
case r == eof:
|
||||
return lx.errorf("unexpected EOF; expected value")
|
||||
default:
|
||||
@@ -464,17 +461,19 @@ func lexKeyStart(lx *lexer) stateFn {
|
||||
func lexKeyNameStart(lx *lexer) stateFn {
|
||||
lx.skip(isWhitespace)
|
||||
switch r := lx.peek(); {
|
||||
case r == '=' || r == eof:
|
||||
return lx.errorf("unexpected '='")
|
||||
case r == '.':
|
||||
return lx.errorf("unexpected '.'")
|
||||
default:
|
||||
lx.push(lexKeyEnd)
|
||||
return lexBareName
|
||||
case r == '"' || r == '\'':
|
||||
lx.ignore()
|
||||
lx.push(lexKeyEnd)
|
||||
return lexQuotedName
|
||||
default:
|
||||
lx.push(lexKeyEnd)
|
||||
return lexBareName
|
||||
|
||||
// TODO: I think these can never be reached?
|
||||
case r == '=' || r == eof:
|
||||
return lx.errorf("unexpected '='")
|
||||
case r == '.':
|
||||
return lx.errorf("unexpected '.'")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -485,7 +484,7 @@ func lexKeyEnd(lx *lexer) stateFn {
|
||||
switch r := lx.next(); {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexKeyEnd)
|
||||
case r == eof:
|
||||
case r == eof: // TODO: never reached
|
||||
return lx.errorf("unexpected EOF; expected key separator '='")
|
||||
case r == '.':
|
||||
lx.ignore()
|
||||
@@ -628,10 +627,7 @@ func lexInlineTableValue(lx *lexer) stateFn {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexInlineTableValue)
|
||||
case isNL(r):
|
||||
if lx.tomlNext {
|
||||
return lexSkip(lx, lexInlineTableValue)
|
||||
}
|
||||
return lx.errorPrevLine(errLexInlineTableNL{})
|
||||
return lexSkip(lx, lexInlineTableValue)
|
||||
case r == '#':
|
||||
lx.push(lexInlineTableValue)
|
||||
return lexCommentStart
|
||||
@@ -653,10 +649,7 @@ func lexInlineTableValueEnd(lx *lexer) stateFn {
|
||||
case isWhitespace(r):
|
||||
return lexSkip(lx, lexInlineTableValueEnd)
|
||||
case isNL(r):
|
||||
if lx.tomlNext {
|
||||
return lexSkip(lx, lexInlineTableValueEnd)
|
||||
}
|
||||
return lx.errorPrevLine(errLexInlineTableNL{})
|
||||
return lexSkip(lx, lexInlineTableValueEnd)
|
||||
case r == '#':
|
||||
lx.push(lexInlineTableValueEnd)
|
||||
return lexCommentStart
|
||||
@@ -664,10 +657,7 @@ func lexInlineTableValueEnd(lx *lexer) stateFn {
|
||||
lx.ignore()
|
||||
lx.skip(isWhitespace)
|
||||
if lx.peek() == '}' {
|
||||
if lx.tomlNext {
|
||||
return lexInlineTableValueEnd
|
||||
}
|
||||
return lx.errorf("trailing comma not allowed in inline tables")
|
||||
return lexInlineTableValueEnd
|
||||
}
|
||||
return lexInlineTableValue
|
||||
case r == '}':
|
||||
@@ -855,9 +845,6 @@ func lexStringEscape(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch r {
|
||||
case 'e':
|
||||
if !lx.tomlNext {
|
||||
return lx.error(errLexEscape{r})
|
||||
}
|
||||
fallthrough
|
||||
case 'b':
|
||||
fallthrough
|
||||
@@ -878,9 +865,6 @@ func lexStringEscape(lx *lexer) stateFn {
|
||||
case '\\':
|
||||
return lx.pop()
|
||||
case 'x':
|
||||
if !lx.tomlNext {
|
||||
return lx.error(errLexEscape{r})
|
||||
}
|
||||
return lexHexEscape
|
||||
case 'u':
|
||||
return lexShortUnicodeEscape
|
||||
@@ -928,19 +912,9 @@ func lexLongUnicodeEscape(lx *lexer) stateFn {
|
||||
// lexBaseNumberOrDate can differentiate base prefixed integers from other
|
||||
// types.
|
||||
func lexNumberOrDateStart(lx *lexer) stateFn {
|
||||
r := lx.next()
|
||||
switch r {
|
||||
case '0':
|
||||
if lx.next() == '0' {
|
||||
return lexBaseNumberOrDate
|
||||
}
|
||||
|
||||
if !isDigit(r) {
|
||||
// The only way to reach this state is if the value starts
|
||||
// with a digit, so specifically treat anything else as an
|
||||
// error.
|
||||
return lx.errorf("expected a digit but got %q", r)
|
||||
}
|
||||
|
||||
return lexNumberOrDate
|
||||
}
|
||||
|
||||
@@ -1196,13 +1170,13 @@ func lexSkip(lx *lexer, nextState stateFn) stateFn {
|
||||
}
|
||||
|
||||
func (s stateFn) String() string {
|
||||
if s == nil {
|
||||
return "<nil>"
|
||||
}
|
||||
name := runtime.FuncForPC(reflect.ValueOf(s).Pointer()).Name()
|
||||
if i := strings.LastIndexByte(name, '.'); i > -1 {
|
||||
name = name[i+1:]
|
||||
}
|
||||
if s == nil {
|
||||
name = "<nil>"
|
||||
}
|
||||
return name + "()"
|
||||
}
|
||||
|
||||
@@ -1210,8 +1184,6 @@ func (itype itemType) String() string {
|
||||
switch itype {
|
||||
case itemError:
|
||||
return "Error"
|
||||
case itemNIL:
|
||||
return "NIL"
|
||||
case itemEOF:
|
||||
return "EOF"
|
||||
case itemText:
|
||||
@@ -1226,18 +1198,22 @@ func (itype itemType) String() string {
|
||||
return "Float"
|
||||
case itemDatetime:
|
||||
return "DateTime"
|
||||
case itemTableStart:
|
||||
return "TableStart"
|
||||
case itemTableEnd:
|
||||
return "TableEnd"
|
||||
case itemKeyStart:
|
||||
return "KeyStart"
|
||||
case itemKeyEnd:
|
||||
return "KeyEnd"
|
||||
case itemArray:
|
||||
return "Array"
|
||||
case itemArrayEnd:
|
||||
return "ArrayEnd"
|
||||
case itemTableStart:
|
||||
return "TableStart"
|
||||
case itemTableEnd:
|
||||
return "TableEnd"
|
||||
case itemArrayTableStart:
|
||||
return "ArrayTableStart"
|
||||
case itemArrayTableEnd:
|
||||
return "ArrayTableEnd"
|
||||
case itemKeyStart:
|
||||
return "KeyStart"
|
||||
case itemKeyEnd:
|
||||
return "KeyEnd"
|
||||
case itemCommentStart:
|
||||
return "CommentStart"
|
||||
case itemInlineTableStart:
|
||||
@@ -1266,7 +1242,7 @@ func isDigit(r rune) bool { return r >= '0' && r <= '9' }
|
||||
func isBinary(r rune) bool { return r == '0' || r == '1' }
|
||||
func isOctal(r rune) bool { return r >= '0' && r <= '7' }
|
||||
func isHex(r rune) bool { return (r >= '0' && r <= '9') || (r|0x20 >= 'a' && r|0x20 <= 'f') }
|
||||
func isBareKeyChar(r rune, tomlNext bool) bool {
|
||||
func isBareKeyChar(r rune) bool {
|
||||
return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') ||
|
||||
(r >= '0' && r <= '9') || r == '_' || r == '-'
|
||||
}
|
||||
|
||||
+18
-28
@@ -3,7 +3,6 @@ package toml
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -17,7 +16,6 @@ type parser struct {
|
||||
context Key // Full key for the current hash in scope.
|
||||
currentKey string // Base key name for everything except hashes.
|
||||
pos Position // Current position in the TOML file.
|
||||
tomlNext bool
|
||||
|
||||
ordered []Key // List of keys in the order that they appear in the TOML data.
|
||||
|
||||
@@ -32,8 +30,6 @@ type keyInfo struct {
|
||||
}
|
||||
|
||||
func parse(data string) (p *parser, err error) {
|
||||
_, tomlNext := os.LookupEnv("BURNTSUSHI_TOML_110")
|
||||
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if pErr, ok := r.(ParseError); ok {
|
||||
@@ -73,10 +69,9 @@ func parse(data string) (p *parser, err error) {
|
||||
p = &parser{
|
||||
keyInfo: make(map[string]keyInfo),
|
||||
mapping: make(map[string]any),
|
||||
lx: lex(data, tomlNext),
|
||||
lx: lex(data),
|
||||
ordered: make([]Key, 0),
|
||||
implicits: make(map[string]struct{}),
|
||||
tomlNext: tomlNext,
|
||||
}
|
||||
for {
|
||||
item := p.next()
|
||||
@@ -350,17 +345,14 @@ func (p *parser) valueFloat(it item) (any, tomlType) {
|
||||
var dtTypes = []struct {
|
||||
fmt string
|
||||
zone *time.Location
|
||||
next bool
|
||||
}{
|
||||
{time.RFC3339Nano, time.Local, false},
|
||||
{"2006-01-02T15:04:05.999999999", internal.LocalDatetime, false},
|
||||
{"2006-01-02", internal.LocalDate, false},
|
||||
{"15:04:05.999999999", internal.LocalTime, false},
|
||||
|
||||
// tomlNext
|
||||
{"2006-01-02T15:04Z07:00", time.Local, true},
|
||||
{"2006-01-02T15:04", internal.LocalDatetime, true},
|
||||
{"15:04", internal.LocalTime, true},
|
||||
{time.RFC3339Nano, time.Local},
|
||||
{"2006-01-02T15:04:05.999999999", internal.LocalDatetime},
|
||||
{"2006-01-02", internal.LocalDate},
|
||||
{"15:04:05.999999999", internal.LocalTime},
|
||||
{"2006-01-02T15:04Z07:00", time.Local},
|
||||
{"2006-01-02T15:04", internal.LocalDatetime},
|
||||
{"15:04", internal.LocalTime},
|
||||
}
|
||||
|
||||
func (p *parser) valueDatetime(it item) (any, tomlType) {
|
||||
@@ -371,9 +363,6 @@ func (p *parser) valueDatetime(it item) (any, tomlType) {
|
||||
err error
|
||||
)
|
||||
for _, dt := range dtTypes {
|
||||
if dt.next && !p.tomlNext {
|
||||
continue
|
||||
}
|
||||
t, err = time.ParseInLocation(dt.fmt, it.val, dt.zone)
|
||||
if err == nil {
|
||||
if missingLeadingZero(it.val, dt.fmt) {
|
||||
@@ -644,6 +633,11 @@ func (p *parser) setValue(key string, value any) {
|
||||
// Note that since it has already been defined (as a hash), we don't
|
||||
// want to overwrite it. So our business is done.
|
||||
if p.isArray(keyContext) {
|
||||
if !p.isImplicit(keyContext) {
|
||||
if _, ok := hash[key]; ok {
|
||||
p.panicf("Key '%s' has already been defined.", keyContext)
|
||||
}
|
||||
}
|
||||
p.removeImplicit(keyContext)
|
||||
hash[key] = value
|
||||
return
|
||||
@@ -802,10 +796,8 @@ func (p *parser) replaceEscapes(it item, str string) string {
|
||||
b.WriteByte(0x0d)
|
||||
skip = 1
|
||||
case 'e':
|
||||
if p.tomlNext {
|
||||
b.WriteByte(0x1b)
|
||||
skip = 1
|
||||
}
|
||||
b.WriteByte(0x1b)
|
||||
skip = 1
|
||||
case '"':
|
||||
b.WriteByte(0x22)
|
||||
skip = 1
|
||||
@@ -815,11 +807,9 @@ func (p *parser) replaceEscapes(it item, str string) string {
|
||||
// The lexer guarantees the correct number of characters are present;
|
||||
// don't need to check here.
|
||||
case 'x':
|
||||
if p.tomlNext {
|
||||
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+4])
|
||||
b.WriteRune(escaped)
|
||||
skip = 3
|
||||
}
|
||||
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+4])
|
||||
b.WriteRune(escaped)
|
||||
skip = 3
|
||||
case 'u':
|
||||
escaped := p.asciiEscapeToUnicode(it, str[i+2:i+6])
|
||||
b.WriteRune(escaped)
|
||||
|
||||
+2
@@ -69,6 +69,8 @@ func (l *lineReader) Read(p []byte) (n int, err error) {
|
||||
if isPrefix {
|
||||
return 0, ArmorCorrupt
|
||||
}
|
||||
// Trim the line to remove any whitespace
|
||||
line = bytes.TrimSpace(line)
|
||||
|
||||
if bytes.HasPrefix(line, armorEnd) {
|
||||
l.eof = true
|
||||
|
||||
+8
-2
@@ -125,7 +125,10 @@ func (c *curve25519) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecr
|
||||
// "VB = convert point V to the octet string"
|
||||
// sharedPoint corresponds to `VB`.
|
||||
var sharedPoint x25519lib.Key
|
||||
x25519lib.Shared(&sharedPoint, &ephemeralPrivate, &pubKey)
|
||||
ok := x25519lib.Shared(&sharedPoint, &ephemeralPrivate, &pubKey)
|
||||
if !ok {
|
||||
return nil, nil, errors.KeyInvalidError("ecc: the public key is a low order point")
|
||||
}
|
||||
|
||||
return ephemeralPublic[:], sharedPoint[:], nil
|
||||
}
|
||||
@@ -146,7 +149,10 @@ func (c *curve25519) Decaps(vsG, secret []byte) (sharedSecret []byte, err error)
|
||||
// RFC6637 §8: "Note that the recipient obtains the shared secret by calculating
|
||||
// S = rV = rvG, where (r,R) is the recipient's key pair."
|
||||
// sharedPoint corresponds to `S`.
|
||||
x25519lib.Shared(&sharedPoint, &decodedPrivate, &ephemeralPublic)
|
||||
ok := x25519lib.Shared(&sharedPoint, &decodedPrivate, &ephemeralPublic)
|
||||
if !ok {
|
||||
return nil, errors.KeyInvalidError("ecc: the public key is a low order point")
|
||||
}
|
||||
|
||||
return sharedPoint[:], nil
|
||||
}
|
||||
|
||||
+4
-1
@@ -78,7 +78,7 @@ func (c *genericCurve) GenerateECDSA(rand io.Reader) (x, y, secret *big.Int, err
|
||||
func (c *genericCurve) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSecret []byte, err error) {
|
||||
xP, yP := elliptic.Unmarshal(c.Curve, point)
|
||||
if xP == nil {
|
||||
panic("invalid point")
|
||||
return nil, nil, errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", c.Curve.Params().Name))
|
||||
}
|
||||
|
||||
d, x, y, err := elliptic.GenerateKey(c.Curve, rand)
|
||||
@@ -99,6 +99,9 @@ func (c *genericCurve) Encaps(rand io.Reader, point []byte) (ephemeral, sharedSe
|
||||
|
||||
func (c *genericCurve) Decaps(ephemeral, secret []byte) (sharedSecret []byte, err error) {
|
||||
x, y := elliptic.Unmarshal(c.Curve, ephemeral)
|
||||
if x == nil {
|
||||
return nil, errors.KeyInvalidError(fmt.Sprintf("ecc (%s): invalid point", c.Curve.Params().Name))
|
||||
}
|
||||
zbBig, _ := c.Curve.ScalarMult(x, y, secret)
|
||||
byteLen := (c.Curve.Params().BitSize + 7) >> 3
|
||||
zb := make([]byte, byteLen)
|
||||
|
||||
+26
@@ -178,6 +178,18 @@ type Config struct {
|
||||
// When set to true, a key without flags is treated as if all flags are enabled.
|
||||
// This behavior is consistent with GPG.
|
||||
InsecureAllowAllKeyFlagsWhenMissing bool
|
||||
// InsecureGenerateNonCriticalKeyFlags causes the "Key Flags" signature subpacket
|
||||
// to be non-critical in newly generated signatures.
|
||||
// This may be needed for keys to be accepted by older clients who do not recognize
|
||||
// the subpacket.
|
||||
// For example, rpm 4.14.3-150400.59.3.1 in OpenSUSE Leap 15.4 does not recognize it.
|
||||
InsecureGenerateNonCriticalKeyFlags bool
|
||||
// InsecureGenerateNonCriticalSignatureCreationTime causes the "Signature Creation Time" signature subpacket
|
||||
// to be non-critical in newly generated signatures.
|
||||
// This may be needed for keys to be accepted by older clients who do not recognize
|
||||
// the subpacket.
|
||||
// For example, yum 3.4.3-168 in CentOS 7 and yum 3.4.3-158 in Amazon Linux 2 do not recognize it.
|
||||
InsecureGenerateNonCriticalSignatureCreationTime bool
|
||||
|
||||
// MaxDecompressedMessageSize specifies the maximum number of bytes that can be
|
||||
// read from a compressed packet. This serves as an upper limit to prevent
|
||||
@@ -420,6 +432,20 @@ func (c *Config) AllowAllKeyFlagsWhenMissing() bool {
|
||||
return c.InsecureAllowAllKeyFlagsWhenMissing
|
||||
}
|
||||
|
||||
func (c *Config) GenerateNonCriticalKeyFlags() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.InsecureGenerateNonCriticalKeyFlags
|
||||
}
|
||||
|
||||
func (c *Config) GenerateNonCriticalSignatureCreationTime() bool {
|
||||
if c == nil {
|
||||
return false
|
||||
}
|
||||
return c.InsecureGenerateNonCriticalSignatureCreationTime
|
||||
}
|
||||
|
||||
func (c *Config) DecompressedMessageSizeLimit() *int64 {
|
||||
if c == nil {
|
||||
return nil
|
||||
|
||||
+4
-4
@@ -933,7 +933,7 @@ func (sig *Signature) Sign(h hash.Hash, priv *PrivateKey, config *Config) (err e
|
||||
}
|
||||
sig.Notations = append(sig.Notations, ¬ation)
|
||||
}
|
||||
sig.outSubpackets, err = sig.buildSubpackets(priv.PublicKey)
|
||||
sig.outSubpackets, err = sig.buildSubpackets(priv.PublicKey, config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1254,11 +1254,11 @@ type outputSubpacket struct {
|
||||
contents []byte
|
||||
}
|
||||
|
||||
func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubpacket, err error) {
|
||||
func (sig *Signature) buildSubpackets(issuer PublicKey, config *Config) (subpackets []outputSubpacket, err error) {
|
||||
creationTime := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(creationTime, uint32(sig.CreationTime.Unix()))
|
||||
// Signature Creation Time
|
||||
subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, true, creationTime})
|
||||
subpackets = append(subpackets, outputSubpacket{true, creationTimeSubpacket, !config.GenerateNonCriticalSignatureCreationTime(), creationTime})
|
||||
// Signature Expiration Time
|
||||
if sig.SigLifetimeSecs != nil && *sig.SigLifetimeSecs != 0 {
|
||||
sigLifetime := make([]byte, 4)
|
||||
@@ -1357,7 +1357,7 @@ func (sig *Signature) buildSubpackets(issuer PublicKey) (subpackets []outputSubp
|
||||
if sig.FlagGroupKey {
|
||||
flags |= KeyFlagGroupKey
|
||||
}
|
||||
subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, true, []byte{flags}})
|
||||
subpackets = append(subpackets, outputSubpacket{true, keyFlagsSubpacket, !config.GenerateNonCriticalKeyFlags(), []byte{flags}})
|
||||
}
|
||||
// Signer's User ID
|
||||
if sig.SignerUserId != nil {
|
||||
|
||||
+3
@@ -33,6 +33,9 @@ linters:
|
||||
generated: lax
|
||||
presets:
|
||||
- common-false-positives
|
||||
settings:
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
+24
-23
@@ -2,6 +2,7 @@ package colorprofile
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
@@ -83,8 +84,8 @@ func colorProfile(isatty bool, env environ) (p Profile) {
|
||||
}
|
||||
|
||||
if envNoColor(env) && isatty {
|
||||
if p > Ascii {
|
||||
p = Ascii
|
||||
if p > ASCII {
|
||||
p = ASCII
|
||||
}
|
||||
return //nolint:nakedret
|
||||
}
|
||||
@@ -153,29 +154,29 @@ func envColorProfile(env environ) (p Profile) {
|
||||
p = ANSI
|
||||
}
|
||||
|
||||
parts := strings.Split(term, "-")
|
||||
switch parts[0] {
|
||||
case "alacritty",
|
||||
"contour",
|
||||
"foot",
|
||||
"ghostty",
|
||||
"kitty",
|
||||
"rio",
|
||||
"st",
|
||||
"wezterm":
|
||||
switch {
|
||||
case strings.Contains(term, "alacritty"),
|
||||
strings.Contains(term, "contour"),
|
||||
strings.Contains(term, "foot"),
|
||||
strings.Contains(term, "ghostty"),
|
||||
strings.Contains(term, "kitty"),
|
||||
strings.Contains(term, "rio"),
|
||||
strings.Contains(term, "st"),
|
||||
strings.Contains(term, "wezterm"):
|
||||
return TrueColor
|
||||
case "xterm":
|
||||
if len(parts) > 1 {
|
||||
switch parts[1] {
|
||||
case "ghostty", "kitty":
|
||||
// These terminals can be defined as xterm-TERMNAME
|
||||
return TrueColor
|
||||
}
|
||||
}
|
||||
case "tmux", "screen":
|
||||
case strings.HasPrefix(term, "tmux"), strings.HasPrefix(term, "screen"):
|
||||
if p < ANSI256 {
|
||||
p = ANSI256
|
||||
}
|
||||
case strings.HasPrefix(term, "xterm"):
|
||||
if p < ANSI {
|
||||
p = ANSI
|
||||
}
|
||||
}
|
||||
|
||||
if len(env["WT_SESSION"]) > 0 {
|
||||
// Windows Terminal supports TrueColor
|
||||
return TrueColor
|
||||
}
|
||||
|
||||
if isCloudShell, _ := strconv.ParseBool(env.get("GOOGLE_CLOUD_SHELL")); isCloudShell {
|
||||
@@ -244,13 +245,13 @@ func tmux(env environ) (p Profile) {
|
||||
// Check if tmux has either Tc or RGB capabilities. Otherwise, return
|
||||
// ANSI256.
|
||||
p = ANSI256
|
||||
cmd := exec.Command("tmux", "info")
|
||||
cmd := exec.CommandContext(context.Background(), "tmux", "info")
|
||||
out, err := cmd.Output()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, line := range bytes.Split(out, []byte("\n")) {
|
||||
for line := range bytes.SplitSeq(out, []byte("\n")) {
|
||||
if (bytes.Contains(line, []byte("Tc")) || bytes.Contains(line, []byte("RGB"))) &&
|
||||
bytes.Contains(line, []byte("true")) {
|
||||
return TrueColor
|
||||
|
||||
-5
@@ -14,11 +14,6 @@ func windowsColorProfile(env map[string]string) (Profile, bool) {
|
||||
return TrueColor, true
|
||||
}
|
||||
|
||||
if len(env["WT_SESSION"]) > 0 {
|
||||
// Windows Terminal supports TrueColor
|
||||
return TrueColor, true
|
||||
}
|
||||
|
||||
major, _, build := windows.RtlGetNtVersionNumbers()
|
||||
if build < 10586 || major < 10 {
|
||||
// No ANSI support before WindowsNT 10 build 10586
|
||||
|
||||
+17
-9
@@ -11,10 +11,12 @@ import (
|
||||
type Profile byte
|
||||
|
||||
const (
|
||||
// Unknown is a profile that represents the absence of a profile.
|
||||
Unknown Profile = iota
|
||||
// NoTTY is a profile with no terminal support.
|
||||
NoTTY Profile = iota
|
||||
// Ascii is a profile with no color support.
|
||||
Ascii //nolint:revive
|
||||
NoTTY
|
||||
// ASCII is a profile with no color support.
|
||||
ASCII
|
||||
// ANSI is a profile with 16 colors (4-bit).
|
||||
ANSI
|
||||
// ANSI256 is a profile with 256 colors (8-bit).
|
||||
@@ -23,6 +25,9 @@ const (
|
||||
TrueColor
|
||||
)
|
||||
|
||||
// Ascii is an alias for the [ASCII] profile for backwards compatibility.
|
||||
const Ascii = ASCII //nolint:revive
|
||||
|
||||
// String returns the string representation of a Profile.
|
||||
func (p Profile) String() string {
|
||||
switch p {
|
||||
@@ -32,12 +37,13 @@ func (p Profile) String() string {
|
||||
return "ANSI256"
|
||||
case ANSI:
|
||||
return "ANSI"
|
||||
case Ascii:
|
||||
case ASCII:
|
||||
return "Ascii"
|
||||
case NoTTY:
|
||||
return "NoTTY"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -50,7 +56,7 @@ var (
|
||||
|
||||
// Convert transforms a given Color to a Color supported within the Profile.
|
||||
func (p Profile) Convert(c color.Color) (cc color.Color) {
|
||||
if p <= Ascii {
|
||||
if p <= ASCII {
|
||||
return nil
|
||||
}
|
||||
if p == TrueColor {
|
||||
@@ -90,11 +96,13 @@ func (p Profile) Convert(c color.Color) (cc color.Color) {
|
||||
return c
|
||||
|
||||
default:
|
||||
if p == ANSI256 {
|
||||
switch p {
|
||||
case ANSI256:
|
||||
return ansi.Convert256(c)
|
||||
} else if p == ANSI {
|
||||
case ANSI:
|
||||
return ansi.Convert16(c)
|
||||
default:
|
||||
return c
|
||||
}
|
||||
return c
|
||||
}
|
||||
}
|
||||
|
||||
+11
-9
@@ -36,13 +36,15 @@ type Writer struct {
|
||||
|
||||
// Write writes the given text to the underlying writer.
|
||||
func (w *Writer) Write(p []byte) (int, error) {
|
||||
switch w.Profile {
|
||||
case TrueColor:
|
||||
switch {
|
||||
case w.Profile == TrueColor:
|
||||
return w.Forward.Write(p) //nolint:wrapcheck
|
||||
case NoTTY:
|
||||
return io.WriteString(w.Forward, ansi.Strip(string(p))) //nolint:wrapcheck
|
||||
case Ascii, ANSI, ANSI256:
|
||||
return w.downsample(p)
|
||||
case w.Profile <= NoTTY:
|
||||
_, err := io.WriteString(w.Forward, ansi.Strip(string(p)))
|
||||
return len(p), err
|
||||
case w.Profile == ASCII, w.Profile == ANSI, w.Profile == ANSI256:
|
||||
_, err := w.downsample(p)
|
||||
return len(p), err
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid profile: %v", w.Profile)
|
||||
}
|
||||
@@ -112,7 +114,7 @@ func handleSgr(w *Writer, p *ansi.Parser, buf *bytes.Buffer) {
|
||||
if w.Profile < ANSI {
|
||||
continue
|
||||
}
|
||||
style = style.DefaultForegroundColor()
|
||||
style = style.ForegroundColor(nil)
|
||||
case 40, 41, 42, 43, 44, 45, 46, 47: // 8-bit background color
|
||||
if w.Profile < ANSI {
|
||||
continue
|
||||
@@ -132,7 +134,7 @@ func handleSgr(w *Writer, p *ansi.Parser, buf *bytes.Buffer) {
|
||||
if w.Profile < ANSI {
|
||||
continue
|
||||
}
|
||||
style = style.DefaultBackgroundColor()
|
||||
style = style.BackgroundColor(nil)
|
||||
case 58: // 16 or 24-bit underline color
|
||||
var c color.Color
|
||||
if n := ansi.ReadStyleColor(params[i:], &c); n > 0 {
|
||||
@@ -146,7 +148,7 @@ func handleSgr(w *Writer, p *ansi.Parser, buf *bytes.Buffer) {
|
||||
if w.Profile < ANSI {
|
||||
continue
|
||||
}
|
||||
style = style.DefaultUnderlineColor()
|
||||
style = style.UnderlineColor(nil)
|
||||
case 90, 91, 92, 93, 94, 95, 96, 97: // 8-bit bright foreground color
|
||||
if w.Profile < ANSI {
|
||||
continue
|
||||
|
||||
+7
-1
@@ -7,7 +7,6 @@ linters:
|
||||
- exhaustive
|
||||
- goconst
|
||||
- godot
|
||||
- godox
|
||||
- gomoddirectives
|
||||
- goprintffuncname
|
||||
- gosec
|
||||
@@ -27,9 +26,16 @@ linters:
|
||||
- whitespace
|
||||
- wrapcheck
|
||||
exclusions:
|
||||
rules:
|
||||
- text: '(slog|log)\.\w+'
|
||||
linters:
|
||||
- noctx
|
||||
generated: lax
|
||||
presets:
|
||||
- common-false-positives
|
||||
settings:
|
||||
exhaustive:
|
||||
default-signifies-exhaustive: true
|
||||
issues:
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
func (l *Logger) jsonFormatter(keyvals ...interface{}) {
|
||||
func (l *Logger) jsonFormatter(keyvals ...any) {
|
||||
jw := &jsonWriter{w: &l.b}
|
||||
jw.start()
|
||||
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/go-logfmt/logfmt"
|
||||
)
|
||||
|
||||
func (l *Logger) logfmtFormatter(keyvals ...interface{}) {
|
||||
func (l *Logger) logfmtFormatter(keyvals ...any) {
|
||||
e := logfmt.NewEncoder(&l.b)
|
||||
|
||||
for i := 0; i < len(keyvals); i += 2 {
|
||||
|
||||
+19
-19
@@ -41,19 +41,19 @@ type Logger struct {
|
||||
reportCaller bool
|
||||
reportTimestamp bool
|
||||
|
||||
fields []interface{}
|
||||
fields []any
|
||||
|
||||
helpers *sync.Map
|
||||
styles *Styles
|
||||
}
|
||||
|
||||
// Logf logs a message with formatting.
|
||||
func (l *Logger) Logf(level Level, format string, args ...interface{}) {
|
||||
func (l *Logger) Logf(level Level, format string, args ...any) {
|
||||
l.Log(level, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Log logs the given message with the given keyvals for the given level.
|
||||
func (l *Logger) Log(level Level, msg interface{}, keyvals ...interface{}) {
|
||||
func (l *Logger) Log(level Level, msg any, keyvals ...any) {
|
||||
if atomic.LoadUint32(&l.isDiscard) != 0 {
|
||||
return
|
||||
}
|
||||
@@ -81,8 +81,8 @@ func (l *Logger) Log(level Level, msg interface{}, keyvals ...interface{}) {
|
||||
l.handle(level, l.timeFunc(time.Now()), []runtime.Frame{frame}, msg, keyvals...)
|
||||
}
|
||||
|
||||
func (l *Logger) handle(level Level, ts time.Time, frames []runtime.Frame, msg interface{}, keyvals ...interface{}) {
|
||||
var kvs []interface{}
|
||||
func (l *Logger) handle(level Level, ts time.Time, frames []runtime.Frame, msg any, keyvals ...any) {
|
||||
var kvs []any
|
||||
if l.reportTimestamp && !ts.IsZero() {
|
||||
kvs = append(kvs, TimestampKey, ts)
|
||||
}
|
||||
@@ -327,7 +327,7 @@ func (l *Logger) SetStyles(s *Styles) {
|
||||
}
|
||||
|
||||
// With returns a new logger with the given keyvals added.
|
||||
func (l *Logger) With(keyvals ...interface{}) *Logger {
|
||||
func (l *Logger) With(keyvals ...any) *Logger {
|
||||
var st Styles
|
||||
l.mu.Lock()
|
||||
sl := *l
|
||||
@@ -336,7 +336,7 @@ func (l *Logger) With(keyvals ...interface{}) *Logger {
|
||||
sl.b = bytes.Buffer{}
|
||||
sl.mu = &sync.RWMutex{}
|
||||
sl.helpers = &sync.Map{}
|
||||
sl.fields = append(make([]interface{}, 0, len(l.fields)+len(keyvals)), l.fields...)
|
||||
sl.fields = append(make([]any, 0, len(l.fields)+len(keyvals)), l.fields...)
|
||||
sl.fields = append(sl.fields, keyvals...)
|
||||
sl.styles = &st
|
||||
return &sl
|
||||
@@ -350,63 +350,63 @@ func (l *Logger) WithPrefix(prefix string) *Logger {
|
||||
}
|
||||
|
||||
// Debug prints a debug message.
|
||||
func (l *Logger) Debug(msg interface{}, keyvals ...interface{}) {
|
||||
func (l *Logger) Debug(msg any, keyvals ...any) {
|
||||
l.Log(DebugLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Info prints an info message.
|
||||
func (l *Logger) Info(msg interface{}, keyvals ...interface{}) {
|
||||
func (l *Logger) Info(msg any, keyvals ...any) {
|
||||
l.Log(InfoLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Warn prints a warning message.
|
||||
func (l *Logger) Warn(msg interface{}, keyvals ...interface{}) {
|
||||
func (l *Logger) Warn(msg any, keyvals ...any) {
|
||||
l.Log(WarnLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Error prints an error message.
|
||||
func (l *Logger) Error(msg interface{}, keyvals ...interface{}) {
|
||||
func (l *Logger) Error(msg any, keyvals ...any) {
|
||||
l.Log(ErrorLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Fatal prints a fatal message and exits.
|
||||
func (l *Logger) Fatal(msg interface{}, keyvals ...interface{}) {
|
||||
func (l *Logger) Fatal(msg any, keyvals ...any) {
|
||||
l.Log(FatalLevel, msg, keyvals...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Print prints a message with no level.
|
||||
func (l *Logger) Print(msg interface{}, keyvals ...interface{}) {
|
||||
func (l *Logger) Print(msg any, keyvals ...any) {
|
||||
l.Log(noLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Debugf prints a debug message with formatting.
|
||||
func (l *Logger) Debugf(format string, args ...interface{}) {
|
||||
func (l *Logger) Debugf(format string, args ...any) {
|
||||
l.Log(DebugLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Infof prints an info message with formatting.
|
||||
func (l *Logger) Infof(format string, args ...interface{}) {
|
||||
func (l *Logger) Infof(format string, args ...any) {
|
||||
l.Log(InfoLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Warnf prints a warning message with formatting.
|
||||
func (l *Logger) Warnf(format string, args ...interface{}) {
|
||||
func (l *Logger) Warnf(format string, args ...any) {
|
||||
l.Log(WarnLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Errorf prints an error message with formatting.
|
||||
func (l *Logger) Errorf(format string, args ...interface{}) {
|
||||
func (l *Logger) Errorf(format string, args ...any) {
|
||||
l.Log(ErrorLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Fatalf prints a fatal message with formatting and exits.
|
||||
func (l *Logger) Fatalf(format string, args ...interface{}) {
|
||||
func (l *Logger) Fatalf(format string, args ...any) {
|
||||
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{}) {
|
||||
func (l *Logger) Printf(format string, args ...any) {
|
||||
l.Log(noLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ func (l *Logger) Handle(ctx context.Context, record slog.Record) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
fields := make([]interface{}, 0, record.NumAttrs()*2)
|
||||
fields := make([]any, 0, record.NumAttrs()*2)
|
||||
record.Attrs(func(a slog.Attr) bool {
|
||||
fields = append(fields, a.Key, a.Value)
|
||||
return true
|
||||
@@ -52,7 +52,7 @@ func (l *Logger) Handle(ctx context.Context, record slog.Record) error {
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
fields := make([]interface{}, 0, len(attrs)*2)
|
||||
fields := make([]any, 0, len(attrs)*2)
|
||||
for _, attr := range attrs {
|
||||
fields = append(fields, attr.Key, attr.Value)
|
||||
}
|
||||
|
||||
+2
-2
@@ -33,7 +33,7 @@ func (l *Logger) Enabled(_ context.Context, level slog.Level) bool {
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) Handle(_ context.Context, record slog.Record) error {
|
||||
fields := make([]interface{}, 0, record.NumAttrs()*2)
|
||||
fields := make([]any, 0, record.NumAttrs()*2)
|
||||
record.Attrs(func(a slog.Attr) bool {
|
||||
fields = append(fields, a.Key, a.Value)
|
||||
return true
|
||||
@@ -49,7 +49,7 @@ func (l *Logger) Handle(_ context.Context, record slog.Record) error {
|
||||
//
|
||||
// Implements slog.Handler.
|
||||
func (l *Logger) WithAttrs(attrs []slog.Attr) slog.Handler {
|
||||
fields := make([]interface{}, 0, len(attrs)*2)
|
||||
fields := make([]any, 0, len(attrs)*2)
|
||||
for _, attr := range attrs {
|
||||
fields = append(fields, attr.Key, attr.Value)
|
||||
}
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ type Options struct {
|
||||
// CallerOffset is the caller format for the logger. The default is 0.
|
||||
CallerOffset int
|
||||
// Fields is the fields for the logger. The default is no fields.
|
||||
Fields []interface{}
|
||||
Fields []any
|
||||
// Formatter is the formatter for the logger. The default is TextFormatter.
|
||||
Formatter Formatter
|
||||
}
|
||||
|
||||
+15
-15
@@ -155,7 +155,7 @@ func GetPrefix() string {
|
||||
}
|
||||
|
||||
// With returns a new logger with the given keyvals.
|
||||
func With(keyvals ...interface{}) *Logger {
|
||||
func With(keyvals ...any) *Logger {
|
||||
return Default().With(keyvals...)
|
||||
}
|
||||
|
||||
@@ -172,74 +172,74 @@ func Helper() {
|
||||
}
|
||||
|
||||
// Log logs a message with the given level.
|
||||
func Log(level Level, msg interface{}, keyvals ...interface{}) {
|
||||
func Log(level Level, msg any, keyvals ...any) {
|
||||
Default().Log(level, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Debug logs a debug message.
|
||||
func Debug(msg interface{}, keyvals ...interface{}) {
|
||||
func Debug(msg any, keyvals ...any) {
|
||||
Default().Log(DebugLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Info logs an info message.
|
||||
func Info(msg interface{}, keyvals ...interface{}) {
|
||||
func Info(msg any, keyvals ...any) {
|
||||
Default().Log(InfoLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Warn logs a warning message.
|
||||
func Warn(msg interface{}, keyvals ...interface{}) {
|
||||
func Warn(msg any, keyvals ...any) {
|
||||
Default().Log(WarnLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Error logs an error message.
|
||||
func Error(msg interface{}, keyvals ...interface{}) {
|
||||
func Error(msg any, keyvals ...any) {
|
||||
Default().Log(ErrorLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Fatal logs a fatal message and exit.
|
||||
func Fatal(msg interface{}, keyvals ...interface{}) {
|
||||
func Fatal(msg any, keyvals ...any) {
|
||||
Default().Log(FatalLevel, msg, keyvals...)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Print logs a message with no level.
|
||||
func Print(msg interface{}, keyvals ...interface{}) {
|
||||
func Print(msg any, keyvals ...any) {
|
||||
Default().Log(noLevel, msg, keyvals...)
|
||||
}
|
||||
|
||||
// Logf logs a message with formatting and level.
|
||||
func Logf(level Level, format string, args ...interface{}) {
|
||||
func Logf(level Level, format string, args ...any) {
|
||||
Default().Logf(level, format, args...)
|
||||
}
|
||||
|
||||
// Debugf logs a debug message with formatting.
|
||||
func Debugf(format string, args ...interface{}) {
|
||||
func Debugf(format string, args ...any) {
|
||||
Default().Log(DebugLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Infof logs an info message with formatting.
|
||||
func Infof(format string, args ...interface{}) {
|
||||
func Infof(format string, args ...any) {
|
||||
Default().Log(InfoLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Warnf logs a warning message with formatting.
|
||||
func Warnf(format string, args ...interface{}) {
|
||||
func Warnf(format string, args ...any) {
|
||||
Default().Log(WarnLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Errorf logs an error message with formatting.
|
||||
func Errorf(format string, args ...interface{}) {
|
||||
func Errorf(format string, args ...any) {
|
||||
Default().Log(ErrorLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// Fatalf logs a fatal message with formatting and exit.
|
||||
func Fatalf(format string, args ...interface{}) {
|
||||
func Fatalf(format string, args ...any) {
|
||||
Default().Log(FatalLevel, fmt.Sprintf(format, args...))
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// Printf logs a message with formatting and no level.
|
||||
func Printf(format string, args ...interface{}) {
|
||||
func Printf(format string, args ...any) {
|
||||
Default().Log(noLevel, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -62,7 +62,7 @@ const (
|
||||
)
|
||||
|
||||
var bufPool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
New: func() any {
|
||||
return new(strings.Builder)
|
||||
},
|
||||
}
|
||||
@@ -164,7 +164,7 @@ func writeSpace(w io.Writer, first bool) {
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) textFormatter(keyvals ...interface{}) {
|
||||
func (l *Logger) textFormatter(keyvals ...any) {
|
||||
st := l.styles
|
||||
lenKeyvals := len(keyvals)
|
||||
|
||||
|
||||
+4
-2
@@ -261,7 +261,7 @@ func CHA(col int) string {
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/CUP.html
|
||||
func CursorPosition(col, row int) string {
|
||||
if row <= 0 && col <= 0 {
|
||||
if row <= 1 && col <= 1 {
|
||||
return CursorHomePosition
|
||||
}
|
||||
|
||||
@@ -281,7 +281,9 @@ func CUP(col, row int) string {
|
||||
}
|
||||
|
||||
// CursorHomePosition is a sequence for moving the cursor to the upper left
|
||||
// corner of the scrolling region. This is equivalent to `CursorPosition(1, 1)`.
|
||||
// corner of the scrolling region.
|
||||
//
|
||||
// This is equivalent to [CursorPosition](1, 1).
|
||||
const CursorHomePosition = "\x1b[H"
|
||||
|
||||
// SetCursorPosition (CUP) returns a sequence for setting the cursor to the
|
||||
|
||||
+24
@@ -1,5 +1,29 @@
|
||||
package ansi
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/clipperhouse/displaywidth"
|
||||
"github.com/mattn/go-runewidth"
|
||||
)
|
||||
|
||||
var wcOptions = &runewidth.Condition{
|
||||
EastAsianWidth: false,
|
||||
StrictEmojiNeutral: true,
|
||||
}
|
||||
|
||||
var dwOptions = &displaywidth.Options{
|
||||
EastAsianWidth: false,
|
||||
}
|
||||
|
||||
func init() {
|
||||
if ea, err := strconv.ParseBool(os.Getenv("RUNEWIDTH_EASTASIAN")); err == nil && ea {
|
||||
wcOptions.EastAsianWidth = true
|
||||
dwOptions.EastAsianWidth = true
|
||||
}
|
||||
}
|
||||
|
||||
// Method is a type that represents the how the renderer should calculate the
|
||||
// display width of cells.
|
||||
type Method uint8
|
||||
|
||||
+154
-311
@@ -108,7 +108,7 @@ func DECRST(modes ...Mode) string {
|
||||
|
||||
func setMode(reset bool, modes ...Mode) (s string) {
|
||||
if len(modes) == 0 {
|
||||
return //nolint:nakedret
|
||||
return s
|
||||
}
|
||||
|
||||
cmd := "h"
|
||||
@@ -142,7 +142,7 @@ func setMode(reset bool, modes ...Mode) (s string) {
|
||||
if len(dec) > 0 {
|
||||
s += seq + "?" + strings.Join(dec, ";") + cmd
|
||||
}
|
||||
return //nolint:nakedret
|
||||
return s
|
||||
}
|
||||
|
||||
// RequestMode (DECRQM) returns a sequence to request a mode from the terminal.
|
||||
@@ -228,12 +228,12 @@ func (m DECMode) Mode() int {
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/KAM.html
|
||||
const (
|
||||
KeyboardActionMode = ANSIMode(2)
|
||||
KAM = KeyboardActionMode
|
||||
ModeKeyboardAction = ANSIMode(2)
|
||||
KAM = ModeKeyboardAction
|
||||
|
||||
SetKeyboardActionMode = "\x1b[2h"
|
||||
ResetKeyboardActionMode = "\x1b[2l"
|
||||
RequestKeyboardActionMode = "\x1b[2$p"
|
||||
SetModeKeyboardAction = "\x1b[2h"
|
||||
ResetModeKeyboardAction = "\x1b[2l"
|
||||
RequestModeKeyboardAction = "\x1b[2$p"
|
||||
)
|
||||
|
||||
// Insert/Replace Mode (IRM) is a mode that determines whether characters are
|
||||
@@ -245,12 +245,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/IRM.html
|
||||
const (
|
||||
InsertReplaceMode = ANSIMode(4)
|
||||
IRM = InsertReplaceMode
|
||||
ModeInsertReplace = ANSIMode(4)
|
||||
IRM = ModeInsertReplace
|
||||
|
||||
SetInsertReplaceMode = "\x1b[4h"
|
||||
ResetInsertReplaceMode = "\x1b[4l"
|
||||
RequestInsertReplaceMode = "\x1b[4$p"
|
||||
SetModeInsertReplace = "\x1b[4h"
|
||||
ResetModeInsertReplace = "\x1b[4l"
|
||||
RequestModeInsertReplace = "\x1b[4$p"
|
||||
)
|
||||
|
||||
// BiDirectional Support Mode (BDSM) is a mode that determines whether the
|
||||
@@ -260,12 +260,12 @@ const (
|
||||
//
|
||||
// See ECMA-48 7.2.1.
|
||||
const (
|
||||
BiDirectionalSupportMode = ANSIMode(8)
|
||||
BDSM = BiDirectionalSupportMode
|
||||
ModeBiDirectionalSupport = ANSIMode(8)
|
||||
BDSM = ModeBiDirectionalSupport
|
||||
|
||||
SetBiDirectionalSupportMode = "\x1b[8h"
|
||||
ResetBiDirectionalSupportMode = "\x1b[8l"
|
||||
RequestBiDirectionalSupportMode = "\x1b[8$p"
|
||||
SetModeBiDirectionalSupport = "\x1b[8h"
|
||||
ResetModeBiDirectionalSupport = "\x1b[8l"
|
||||
RequestModeBiDirectionalSupport = "\x1b[8$p"
|
||||
)
|
||||
|
||||
// Send Receive Mode (SRM) or Local Echo Mode is a mode that determines whether
|
||||
@@ -274,17 +274,17 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/SRM.html
|
||||
const (
|
||||
SendReceiveMode = ANSIMode(12)
|
||||
LocalEchoMode = SendReceiveMode
|
||||
SRM = SendReceiveMode
|
||||
ModeSendReceive = ANSIMode(12)
|
||||
ModeLocalEcho = ModeSendReceive
|
||||
SRM = ModeSendReceive
|
||||
|
||||
SetSendReceiveMode = "\x1b[12h"
|
||||
ResetSendReceiveMode = "\x1b[12l"
|
||||
RequestSendReceiveMode = "\x1b[12$p"
|
||||
SetModeSendReceive = "\x1b[12h"
|
||||
ResetModeSendReceive = "\x1b[12l"
|
||||
RequestModeSendReceive = "\x1b[12$p"
|
||||
|
||||
SetLocalEchoMode = "\x1b[12h"
|
||||
ResetLocalEchoMode = "\x1b[12l"
|
||||
RequestLocalEchoMode = "\x1b[12$p"
|
||||
SetModeLocalEcho = "\x1b[12h"
|
||||
ResetModeLocalEcho = "\x1b[12l"
|
||||
RequestModeLocalEcho = "\x1b[12$p"
|
||||
)
|
||||
|
||||
// Line Feed/New Line Mode (LNM) is a mode that determines whether the terminal
|
||||
@@ -299,12 +299,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/LNM.html
|
||||
const (
|
||||
LineFeedNewLineMode = ANSIMode(20)
|
||||
LNM = LineFeedNewLineMode
|
||||
ModeLineFeedNewLine = ANSIMode(20)
|
||||
LNM = ModeLineFeedNewLine
|
||||
|
||||
SetLineFeedNewLineMode = "\x1b[20h"
|
||||
ResetLineFeedNewLineMode = "\x1b[20l"
|
||||
RequestLineFeedNewLineMode = "\x1b[20$p"
|
||||
SetModeLineFeedNewLine = "\x1b[20h"
|
||||
ResetModeLineFeedNewLine = "\x1b[20l"
|
||||
RequestModeLineFeedNewLine = "\x1b[20$p"
|
||||
)
|
||||
|
||||
// Cursor Keys Mode (DECCKM) is a mode that determines whether the cursor keys
|
||||
@@ -312,18 +312,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECCKM.html
|
||||
const (
|
||||
CursorKeysMode = DECMode(1)
|
||||
DECCKM = CursorKeysMode
|
||||
ModeCursorKeys = DECMode(1)
|
||||
DECCKM = ModeCursorKeys
|
||||
|
||||
SetCursorKeysMode = "\x1b[?1h"
|
||||
ResetCursorKeysMode = "\x1b[?1l"
|
||||
RequestCursorKeysMode = "\x1b[?1$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetCursorKeysMode] and [ResetCursorKeysMode] instead.
|
||||
const (
|
||||
EnableCursorKeys = "\x1b[?1h" //nolint:revive // grouped constants
|
||||
DisableCursorKeys = "\x1b[?1l"
|
||||
SetModeCursorKeys = "\x1b[?1h"
|
||||
ResetModeCursorKeys = "\x1b[?1l"
|
||||
RequestModeCursorKeys = "\x1b[?1$p"
|
||||
)
|
||||
|
||||
// Origin Mode (DECOM) is a mode that determines whether the cursor moves to the
|
||||
@@ -331,12 +325,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECOM.html
|
||||
const (
|
||||
OriginMode = DECMode(6)
|
||||
DECOM = OriginMode
|
||||
ModeOrigin = DECMode(6)
|
||||
DECOM = ModeOrigin
|
||||
|
||||
SetOriginMode = "\x1b[?6h"
|
||||
ResetOriginMode = "\x1b[?6l"
|
||||
RequestOriginMode = "\x1b[?6$p"
|
||||
SetModeOrigin = "\x1b[?6h"
|
||||
ResetModeOrigin = "\x1b[?6l"
|
||||
RequestModeOrigin = "\x1b[?6$p"
|
||||
)
|
||||
|
||||
// Auto Wrap Mode (DECAWM) is a mode that determines whether the cursor wraps
|
||||
@@ -344,12 +338,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECAWM.html
|
||||
const (
|
||||
AutoWrapMode = DECMode(7)
|
||||
DECAWM = AutoWrapMode
|
||||
ModeAutoWrap = DECMode(7)
|
||||
DECAWM = ModeAutoWrap
|
||||
|
||||
SetAutoWrapMode = "\x1b[?7h"
|
||||
ResetAutoWrapMode = "\x1b[?7l"
|
||||
RequestAutoWrapMode = "\x1b[?7$p"
|
||||
SetModeAutoWrap = "\x1b[?7h"
|
||||
ResetModeAutoWrap = "\x1b[?7l"
|
||||
RequestModeAutoWrap = "\x1b[?7$p"
|
||||
)
|
||||
|
||||
// X10 Mouse Mode is a mode that determines whether the mouse reports on button
|
||||
@@ -364,39 +358,29 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
X10MouseMode = DECMode(9)
|
||||
ModeMouseX10 = DECMode(9)
|
||||
|
||||
SetX10MouseMode = "\x1b[?9h"
|
||||
ResetX10MouseMode = "\x1b[?9l"
|
||||
RequestX10MouseMode = "\x1b[?9$p"
|
||||
SetModeMouseX10 = "\x1b[?9h"
|
||||
ResetModeMouseX10 = "\x1b[?9l"
|
||||
RequestModeMouseX10 = "\x1b[?9$p"
|
||||
)
|
||||
|
||||
// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
|
||||
const (
|
||||
TextCursorEnableMode = DECMode(25)
|
||||
DECTCEM = TextCursorEnableMode
|
||||
ModeTextCursorEnable = DECMode(25)
|
||||
DECTCEM = ModeTextCursorEnable
|
||||
|
||||
SetTextCursorEnableMode = "\x1b[?25h"
|
||||
ResetTextCursorEnableMode = "\x1b[?25l"
|
||||
RequestTextCursorEnableMode = "\x1b[?25$p"
|
||||
SetModeTextCursorEnable = "\x1b[?25h"
|
||||
ResetModeTextCursorEnable = "\x1b[?25l"
|
||||
RequestModeTextCursorEnable = "\x1b[?25$p"
|
||||
)
|
||||
|
||||
// These are aliases for [SetTextCursorEnableMode] and [ResetTextCursorEnableMode].
|
||||
// These are aliases for [SetModeTextCursorEnable] and [ResetModeTextCursorEnable].
|
||||
const (
|
||||
ShowCursor = SetTextCursorEnableMode
|
||||
HideCursor = ResetTextCursorEnableMode
|
||||
)
|
||||
|
||||
// Text Cursor Enable Mode (DECTCEM) is a mode that shows/hides the cursor.
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECTCEM.html
|
||||
//
|
||||
// Deprecated: use [SetTextCursorEnableMode] and [ResetTextCursorEnableMode] instead.
|
||||
const (
|
||||
CursorEnableMode = DECMode(25)
|
||||
RequestCursorVisibility = "\x1b[?25$p"
|
||||
ShowCursor = SetModeTextCursorEnable
|
||||
HideCursor = ResetModeTextCursorEnable
|
||||
)
|
||||
|
||||
// Numeric Keypad Mode (DECNKM) is a mode that determines whether the keypad
|
||||
@@ -406,12 +390,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECNKM.html
|
||||
const (
|
||||
NumericKeypadMode = DECMode(66)
|
||||
DECNKM = NumericKeypadMode
|
||||
ModeNumericKeypad = DECMode(66)
|
||||
DECNKM = ModeNumericKeypad
|
||||
|
||||
SetNumericKeypadMode = "\x1b[?66h"
|
||||
ResetNumericKeypadMode = "\x1b[?66l"
|
||||
RequestNumericKeypadMode = "\x1b[?66$p"
|
||||
SetModeNumericKeypad = "\x1b[?66h"
|
||||
ResetModeNumericKeypad = "\x1b[?66l"
|
||||
RequestModeNumericKeypad = "\x1b[?66$p"
|
||||
)
|
||||
|
||||
// Backarrow Key Mode (DECBKM) is a mode that determines whether the backspace
|
||||
@@ -419,12 +403,12 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECBKM.html
|
||||
const (
|
||||
BackarrowKeyMode = DECMode(67)
|
||||
DECBKM = BackarrowKeyMode
|
||||
ModeBackarrowKey = DECMode(67)
|
||||
DECBKM = ModeBackarrowKey
|
||||
|
||||
SetBackarrowKeyMode = "\x1b[?67h"
|
||||
ResetBackarrowKeyMode = "\x1b[?67l"
|
||||
RequestBackarrowKeyMode = "\x1b[?67$p"
|
||||
SetModeBackarrowKey = "\x1b[?67h"
|
||||
ResetModeBackarrowKey = "\x1b[?67l"
|
||||
RequestModeBackarrowKey = "\x1b[?67$p"
|
||||
)
|
||||
|
||||
// Left Right Margin Mode (DECLRMM) is a mode that determines whether the left
|
||||
@@ -432,47 +416,33 @@ const (
|
||||
//
|
||||
// See: https://vt100.net/docs/vt510-rm/DECLRMM.html
|
||||
const (
|
||||
LeftRightMarginMode = DECMode(69)
|
||||
DECLRMM = LeftRightMarginMode
|
||||
ModeLeftRightMargin = DECMode(69)
|
||||
DECLRMM = ModeLeftRightMargin
|
||||
|
||||
SetLeftRightMarginMode = "\x1b[?69h"
|
||||
ResetLeftRightMarginMode = "\x1b[?69l"
|
||||
RequestLeftRightMarginMode = "\x1b[?69$p"
|
||||
SetModeLeftRightMargin = "\x1b[?69h"
|
||||
ResetModeLeftRightMargin = "\x1b[?69l"
|
||||
RequestModeLeftRightMargin = "\x1b[?69$p"
|
||||
)
|
||||
|
||||
// Normal Mouse Mode is a mode that determines whether the mouse reports on
|
||||
// button presses and releases. It will also report modifier keys, wheel
|
||||
// events, and extra buttons.
|
||||
//
|
||||
// It uses the same encoding as [X10MouseMode] with a few differences:
|
||||
// It uses the same encoding as [ModeMouseX10] with a few differences:
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
NormalMouseMode = DECMode(1000)
|
||||
ModeMouseNormal = DECMode(1000)
|
||||
|
||||
SetNormalMouseMode = "\x1b[?1000h"
|
||||
ResetNormalMouseMode = "\x1b[?1000l"
|
||||
RequestNormalMouseMode = "\x1b[?1000$p"
|
||||
)
|
||||
|
||||
// VT Mouse Tracking is a mode that determines whether the mouse reports on
|
||||
// button press and release.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
//
|
||||
// Deprecated: use [NormalMouseMode] instead.
|
||||
const (
|
||||
MouseMode = DECMode(1000)
|
||||
|
||||
EnableMouse = "\x1b[?1000h"
|
||||
DisableMouse = "\x1b[?1000l"
|
||||
RequestMouse = "\x1b[?1000$p"
|
||||
SetModeMouseNormal = "\x1b[?1000h"
|
||||
ResetModeMouseNormal = "\x1b[?1000l"
|
||||
RequestModeMouseNormal = "\x1b[?1000$p"
|
||||
)
|
||||
|
||||
// Highlight Mouse Tracking is a mode that determines whether the mouse reports
|
||||
// on button presses, releases, and highlighted cells.
|
||||
//
|
||||
// It uses the same encoding as [NormalMouseMode] with a few differences:
|
||||
// It uses the same encoding as [ModeMouseNormal] with a few differences:
|
||||
//
|
||||
// On highlight events, the terminal responds with the following encoding:
|
||||
//
|
||||
@@ -481,11 +451,11 @@ const (
|
||||
//
|
||||
// Where the parameters are startx, starty, endx, endy, mousex, and mousey.
|
||||
const (
|
||||
HighlightMouseMode = DECMode(1001)
|
||||
ModeMouseHighlight = DECMode(1001)
|
||||
|
||||
SetHighlightMouseMode = "\x1b[?1001h"
|
||||
ResetHighlightMouseMode = "\x1b[?1001l"
|
||||
RequestHighlightMouseMode = "\x1b[?1001$p"
|
||||
SetModeMouseHighlight = "\x1b[?1001h"
|
||||
ResetModeMouseHighlight = "\x1b[?1001l"
|
||||
RequestModeMouseHighlight = "\x1b[?1001$p"
|
||||
)
|
||||
|
||||
// VT Hilite Mouse Tracking is a mode that determines whether the mouse reports on
|
||||
@@ -493,65 +463,29 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
//
|
||||
// Deprecated: use [HighlightMouseMode] instead.
|
||||
const (
|
||||
MouseHiliteMode = DECMode(1001)
|
||||
|
||||
EnableMouseHilite = "\x1b[?1001h"
|
||||
DisableMouseHilite = "\x1b[?1001l"
|
||||
RequestMouseHilite = "\x1b[?1001$p"
|
||||
)
|
||||
|
||||
// Button Event Mouse Tracking is essentially the same as [NormalMouseMode],
|
||||
// Button Event Mouse Tracking is essentially the same as [ModeMouseNormal],
|
||||
// but it also reports button-motion events when a button is pressed.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
ButtonEventMouseMode = DECMode(1002)
|
||||
ModeMouseButtonEvent = DECMode(1002)
|
||||
|
||||
SetButtonEventMouseMode = "\x1b[?1002h"
|
||||
ResetButtonEventMouseMode = "\x1b[?1002l"
|
||||
RequestButtonEventMouseMode = "\x1b[?1002$p"
|
||||
SetModeMouseButtonEvent = "\x1b[?1002h"
|
||||
ResetModeMouseButtonEvent = "\x1b[?1002l"
|
||||
RequestModeMouseButtonEvent = "\x1b[?1002$p"
|
||||
)
|
||||
|
||||
// Cell Motion Mouse Tracking is a mode that determines whether the mouse
|
||||
// reports on button press, release, and motion events.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
//
|
||||
// Deprecated: use [ButtonEventMouseMode] instead.
|
||||
const (
|
||||
MouseCellMotionMode = DECMode(1002)
|
||||
|
||||
EnableMouseCellMotion = "\x1b[?1002h"
|
||||
DisableMouseCellMotion = "\x1b[?1002l"
|
||||
RequestMouseCellMotion = "\x1b[?1002$p"
|
||||
)
|
||||
|
||||
// Any Event Mouse Tracking is the same as [ButtonEventMouseMode], except that
|
||||
// Any Event Mouse Tracking is the same as [ModeMouseButtonEvent], except that
|
||||
// all motion events are reported even if no mouse buttons are pressed.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
AnyEventMouseMode = DECMode(1003)
|
||||
ModeMouseAnyEvent = DECMode(1003)
|
||||
|
||||
SetAnyEventMouseMode = "\x1b[?1003h"
|
||||
ResetAnyEventMouseMode = "\x1b[?1003l"
|
||||
RequestAnyEventMouseMode = "\x1b[?1003$p"
|
||||
)
|
||||
|
||||
// All Mouse Tracking is a mode that determines whether the mouse reports on
|
||||
// button press, release, motion, and highlight events.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
//
|
||||
// Deprecated: use [AnyEventMouseMode] instead.
|
||||
const (
|
||||
MouseAllMotionMode = DECMode(1003)
|
||||
|
||||
EnableMouseAllMotion = "\x1b[?1003h"
|
||||
DisableMouseAllMotion = "\x1b[?1003l"
|
||||
RequestMouseAllMotion = "\x1b[?1003$p"
|
||||
SetModeMouseAnyEvent = "\x1b[?1003h"
|
||||
ResetModeMouseAnyEvent = "\x1b[?1003l"
|
||||
RequestModeMouseAnyEvent = "\x1b[?1003$p"
|
||||
)
|
||||
|
||||
// Focus Event Mode is a mode that determines whether the terminal reports focus
|
||||
@@ -564,22 +498,11 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Focus-Tracking
|
||||
const (
|
||||
FocusEventMode = DECMode(1004)
|
||||
ModeFocusEvent = DECMode(1004)
|
||||
|
||||
SetFocusEventMode = "\x1b[?1004h"
|
||||
ResetFocusEventMode = "\x1b[?1004l"
|
||||
RequestFocusEventMode = "\x1b[?1004$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetFocusEventMode], [ResetFocusEventMode], and
|
||||
// [RequestFocusEventMode] instead.
|
||||
// Focus reporting mode constants.
|
||||
const (
|
||||
ReportFocusMode = DECMode(1004) //nolint:revive // grouped constants
|
||||
|
||||
EnableReportFocus = "\x1b[?1004h"
|
||||
DisableReportFocus = "\x1b[?1004l"
|
||||
RequestReportFocus = "\x1b[?1004$p"
|
||||
SetModeFocusEvent = "\x1b[?1004h"
|
||||
ResetModeFocusEvent = "\x1b[?1004l"
|
||||
RequestModeFocusEvent = "\x1b[?1004$p"
|
||||
)
|
||||
|
||||
// SGR Extended Mouse Mode is a mode that changes the mouse tracking encoding
|
||||
@@ -589,24 +512,15 @@ const (
|
||||
//
|
||||
// CSI < Cb ; Cx ; Cy M
|
||||
//
|
||||
// Where Cb is the same as [NormalMouseMode], and Cx and Cy are the x and y.
|
||||
// Where Cb is the same as [ModeMouseNormal], and Cx and Cy are the x and y.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
SgrExtMouseMode = DECMode(1006)
|
||||
ModeMouseExtSgr = DECMode(1006)
|
||||
|
||||
SetSgrExtMouseMode = "\x1b[?1006h"
|
||||
ResetSgrExtMouseMode = "\x1b[?1006l"
|
||||
RequestSgrExtMouseMode = "\x1b[?1006$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SgrExtMouseMode] [SetSgrExtMouseMode],
|
||||
// [ResetSgrExtMouseMode], and [RequestSgrExtMouseMode] instead.
|
||||
const (
|
||||
MouseSgrExtMode = DECMode(1006) //nolint:revive // grouped constants
|
||||
EnableMouseSgrExt = "\x1b[?1006h"
|
||||
DisableMouseSgrExt = "\x1b[?1006l"
|
||||
RequestMouseSgrExt = "\x1b[?1006$p"
|
||||
SetModeMouseExtSgr = "\x1b[?1006h"
|
||||
ResetModeMouseExtSgr = "\x1b[?1006l"
|
||||
RequestModeMouseExtSgr = "\x1b[?1006$p"
|
||||
)
|
||||
|
||||
// UTF-8 Extended Mouse Mode is a mode that changes the mouse tracking encoding
|
||||
@@ -614,11 +528,11 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
Utf8ExtMouseMode = DECMode(1005)
|
||||
ModeMouseExtUtf8 = DECMode(1005)
|
||||
|
||||
SetUtf8ExtMouseMode = "\x1b[?1005h"
|
||||
ResetUtf8ExtMouseMode = "\x1b[?1005l"
|
||||
RequestUtf8ExtMouseMode = "\x1b[?1005$p"
|
||||
SetModeMouseExtUtf8 = "\x1b[?1005h"
|
||||
ResetModeMouseExtUtf8 = "\x1b[?1005l"
|
||||
RequestModeMouseExtUtf8 = "\x1b[?1005$p"
|
||||
)
|
||||
|
||||
// URXVT Extended Mouse Mode is a mode that changes the mouse tracking encoding
|
||||
@@ -626,25 +540,25 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
UrxvtExtMouseMode = DECMode(1015)
|
||||
ModeMouseExtUrxvt = DECMode(1015)
|
||||
|
||||
SetUrxvtExtMouseMode = "\x1b[?1015h"
|
||||
ResetUrxvtExtMouseMode = "\x1b[?1015l"
|
||||
RequestUrxvtExtMouseMode = "\x1b[?1015$p"
|
||||
SetModeMouseExtUrxvt = "\x1b[?1015h"
|
||||
ResetModeMouseExtUrxvt = "\x1b[?1015l"
|
||||
RequestModeMouseExtUrxvt = "\x1b[?1015$p"
|
||||
)
|
||||
|
||||
// SGR Pixel Extended Mouse Mode is a mode that changes the mouse tracking
|
||||
// encoding to use SGR parameters with pixel coordinates.
|
||||
//
|
||||
// This is similar to [SgrExtMouseMode], but also reports pixel coordinates.
|
||||
// This is similar to [ModeMouseExtSgr], but also reports pixel coordinates.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Mouse-Tracking
|
||||
const (
|
||||
SgrPixelExtMouseMode = DECMode(1016)
|
||||
ModeMouseExtSgrPixel = DECMode(1016)
|
||||
|
||||
SetSgrPixelExtMouseMode = "\x1b[?1016h"
|
||||
ResetSgrPixelExtMouseMode = "\x1b[?1016l"
|
||||
RequestSgrPixelExtMouseMode = "\x1b[?1016$p"
|
||||
SetModeMouseExtSgrPixel = "\x1b[?1016h"
|
||||
ResetModeMouseExtSgrPixel = "\x1b[?1016l"
|
||||
RequestModeMouseExtSgrPixel = "\x1b[?1016$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Mode is a mode that determines whether the alternate screen
|
||||
@@ -653,11 +567,11 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
const (
|
||||
AltScreenMode = DECMode(1047)
|
||||
ModeAltScreen = DECMode(1047)
|
||||
|
||||
SetAltScreenMode = "\x1b[?1047h"
|
||||
ResetAltScreenMode = "\x1b[?1047l"
|
||||
RequestAltScreenMode = "\x1b[?1047$p"
|
||||
SetModeAltScreen = "\x1b[?1047h"
|
||||
ResetModeAltScreen = "\x1b[?1047l"
|
||||
RequestModeAltScreen = "\x1b[?1047$p"
|
||||
)
|
||||
|
||||
// Save Cursor Mode is a mode that saves the cursor position.
|
||||
@@ -665,42 +579,24 @@ const (
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
const (
|
||||
SaveCursorMode = DECMode(1048)
|
||||
ModeSaveCursor = DECMode(1048)
|
||||
|
||||
SetSaveCursorMode = "\x1b[?1048h"
|
||||
ResetSaveCursorMode = "\x1b[?1048l"
|
||||
RequestSaveCursorMode = "\x1b[?1048$p"
|
||||
SetModeSaveCursor = "\x1b[?1048h"
|
||||
ResetModeSaveCursor = "\x1b[?1048l"
|
||||
RequestModeSaveCursor = "\x1b[?1048$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Save Cursor Mode is a mode that saves the cursor position as in
|
||||
// [SaveCursorMode], switches to the alternate screen buffer as in [AltScreenMode],
|
||||
// [ModeSaveCursor], switches to the alternate screen buffer as in [ModeAltScreen],
|
||||
// and clears the screen on switch.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
const (
|
||||
AltScreenSaveCursorMode = DECMode(1049)
|
||||
ModeAltScreenSaveCursor = DECMode(1049)
|
||||
|
||||
SetAltScreenSaveCursorMode = "\x1b[?1049h"
|
||||
ResetAltScreenSaveCursorMode = "\x1b[?1049l"
|
||||
RequestAltScreenSaveCursorMode = "\x1b[?1049$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Buffer is a mode that determines whether the alternate screen
|
||||
// buffer is active.
|
||||
//
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-The-Alternate-Screen-Buffer
|
||||
//
|
||||
// Deprecated: use [AltScreenSaveCursorMode] instead.
|
||||
const (
|
||||
AltScreenBufferMode = DECMode(1049)
|
||||
|
||||
SetAltScreenBufferMode = "\x1b[?1049h"
|
||||
ResetAltScreenBufferMode = "\x1b[?1049l"
|
||||
RequestAltScreenBufferMode = "\x1b[?1049$p"
|
||||
|
||||
EnableAltScreenBuffer = "\x1b[?1049h"
|
||||
DisableAltScreenBuffer = "\x1b[?1049l"
|
||||
RequestAltScreenBuffer = "\x1b[?1049$p"
|
||||
SetModeAltScreenSaveCursor = "\x1b[?1049h"
|
||||
ResetModeAltScreenSaveCursor = "\x1b[?1049l"
|
||||
RequestModeAltScreenSaveCursor = "\x1b[?1049$p"
|
||||
)
|
||||
|
||||
// Bracketed Paste Mode is a mode that determines whether pasted text is
|
||||
@@ -709,19 +605,11 @@ const (
|
||||
// See: https://cirw.in/blog/bracketed-paste
|
||||
// See: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h2-Bracketed-Paste-Mode
|
||||
const (
|
||||
BracketedPasteMode = DECMode(2004)
|
||||
ModeBracketedPaste = DECMode(2004)
|
||||
|
||||
SetBracketedPasteMode = "\x1b[?2004h"
|
||||
ResetBracketedPasteMode = "\x1b[?2004l"
|
||||
RequestBracketedPasteMode = "\x1b[?2004$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetBracketedPasteMode], [ResetBracketedPasteMode], and
|
||||
// [RequestBracketedPasteMode] instead.
|
||||
const (
|
||||
EnableBracketedPaste = "\x1b[?2004h" //nolint:revive // grouped constants
|
||||
DisableBracketedPaste = "\x1b[?2004l"
|
||||
RequestBracketedPaste = "\x1b[?2004$p"
|
||||
SetModeBracketedPaste = "\x1b[?2004h"
|
||||
ResetModeBracketedPaste = "\x1b[?2004l"
|
||||
RequestModeBracketedPaste = "\x1b[?2004$p"
|
||||
)
|
||||
|
||||
// Synchronized Output Mode is a mode that determines whether output is
|
||||
@@ -729,23 +617,11 @@ const (
|
||||
//
|
||||
// See: https://gist.github.com/christianparpart/d8a62cc1ab659194337d73e399004036
|
||||
const (
|
||||
SynchronizedOutputMode = DECMode(2026)
|
||||
ModeSynchronizedOutput = DECMode(2026)
|
||||
|
||||
SetSynchronizedOutputMode = "\x1b[?2026h"
|
||||
ResetSynchronizedOutputMode = "\x1b[?2026l"
|
||||
RequestSynchronizedOutputMode = "\x1b[?2026$p"
|
||||
)
|
||||
|
||||
// Synchronized Output Mode. See [SynchronizedOutputMode].
|
||||
//
|
||||
// Deprecated: use [SynchronizedOutputMode], [SetSynchronizedOutputMode], and
|
||||
// [ResetSynchronizedOutputMode], and [RequestSynchronizedOutputMode] instead.
|
||||
const (
|
||||
SyncdOutputMode = DECMode(2026)
|
||||
|
||||
EnableSyncdOutput = "\x1b[?2026h"
|
||||
DisableSyncdOutput = "\x1b[?2026l"
|
||||
RequestSyncdOutput = "\x1b[?2026$p"
|
||||
SetModeSynchronizedOutput = "\x1b[?2026h"
|
||||
ResetModeSynchronizedOutput = "\x1b[?2026l"
|
||||
RequestModeSynchronizedOutput = "\x1b[?2026$p"
|
||||
)
|
||||
|
||||
// Unicode Core Mode is a mode that determines whether the terminal should use
|
||||
@@ -754,41 +630,16 @@ const (
|
||||
//
|
||||
// See: https://github.com/contour-terminal/terminal-unicode-core
|
||||
const (
|
||||
UnicodeCoreMode = DECMode(2027)
|
||||
ModeUnicodeCore = DECMode(2027)
|
||||
|
||||
SetUnicodeCoreMode = "\x1b[?2027h"
|
||||
ResetUnicodeCoreMode = "\x1b[?2027l"
|
||||
RequestUnicodeCoreMode = "\x1b[?2027$p"
|
||||
SetModeUnicodeCore = "\x1b[?2027h"
|
||||
ResetModeUnicodeCore = "\x1b[?2027l"
|
||||
RequestModeUnicodeCore = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Grapheme Clustering Mode is a mode that determines whether the terminal
|
||||
// should look for grapheme clusters instead of single runes in the rendered
|
||||
// text. This makes the terminal properly render combining characters such as
|
||||
// emojis.
|
||||
//
|
||||
// See: https://github.com/contour-terminal/terminal-unicode-core
|
||||
//
|
||||
// Deprecated: use [GraphemeClusteringMode], [SetUnicodeCoreMode],
|
||||
// [ResetUnicodeCoreMode], and [RequestUnicodeCoreMode] instead.
|
||||
const (
|
||||
GraphemeClusteringMode = DECMode(2027)
|
||||
|
||||
SetGraphemeClusteringMode = "\x1b[?2027h"
|
||||
ResetGraphemeClusteringMode = "\x1b[?2027l"
|
||||
RequestGraphemeClusteringMode = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Grapheme Clustering Mode. See [GraphemeClusteringMode].
|
||||
//
|
||||
// Deprecated: use [SetUnicodeCoreMode], [ResetUnicodeCoreMode], and
|
||||
// [RequestUnicodeCoreMode] instead.
|
||||
const (
|
||||
EnableGraphemeClustering = "\x1b[?2027h"
|
||||
DisableGraphemeClustering = "\x1b[?2027l"
|
||||
RequestGraphemeClustering = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// LightDarkMode is a mode that enables reporting the operating system's color
|
||||
// ModeLightDark is a mode that enables reporting the operating system's color
|
||||
// scheme (light or dark) preference. It reports the color scheme as a [DSR]
|
||||
// and [LightDarkReport] escape sequences encoded as follows:
|
||||
//
|
||||
@@ -802,14 +653,14 @@ const (
|
||||
//
|
||||
// See: https://contour-terminal.org/vt-extensions/color-palette-update-notifications/
|
||||
const (
|
||||
LightDarkMode = DECMode(2031)
|
||||
ModeLightDark = DECMode(2031)
|
||||
|
||||
SetLightDarkMode = "\x1b[?2031h"
|
||||
ResetLightDarkMode = "\x1b[?2031l"
|
||||
RequestLightDarkMode = "\x1b[?2031$p"
|
||||
SetModeLightDark = "\x1b[?2031h"
|
||||
ResetModeLightDark = "\x1b[?2031l"
|
||||
RequestModeLightDark = "\x1b[?2031$p"
|
||||
)
|
||||
|
||||
// InBandResizeMode is a mode that reports terminal resize events as escape
|
||||
// ModeInBandResize is a mode that reports terminal resize events as escape
|
||||
// sequences. This is useful for systems that do not support [SIGWINCH] like
|
||||
// Windows.
|
||||
//
|
||||
@@ -819,11 +670,11 @@ const (
|
||||
//
|
||||
// See: https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3704bd83
|
||||
const (
|
||||
InBandResizeMode = DECMode(2048)
|
||||
ModeInBandResize = DECMode(2048)
|
||||
|
||||
SetInBandResizeMode = "\x1b[?2048h"
|
||||
ResetInBandResizeMode = "\x1b[?2048l"
|
||||
RequestInBandResizeMode = "\x1b[?2048$p"
|
||||
SetModeInBandResize = "\x1b[?2048h"
|
||||
ResetModeInBandResize = "\x1b[?2048l"
|
||||
RequestModeInBandResize = "\x1b[?2048$p"
|
||||
)
|
||||
|
||||
// Win32Input is a mode that determines whether input is processed by the
|
||||
@@ -831,17 +682,9 @@ const (
|
||||
//
|
||||
// See: https://github.com/microsoft/terminal/blob/main/doc/specs/%234999%20-%20Improved%20keyboard%20handling%20in%20Conpty.md
|
||||
const (
|
||||
Win32InputMode = DECMode(9001)
|
||||
ModeWin32Input = DECMode(9001)
|
||||
|
||||
SetWin32InputMode = "\x1b[?9001h"
|
||||
ResetWin32InputMode = "\x1b[?9001l"
|
||||
RequestWin32InputMode = "\x1b[?9001$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetWin32InputMode], [ResetWin32InputMode], and
|
||||
// [RequestWin32InputMode] instead.
|
||||
const (
|
||||
EnableWin32Input = "\x1b[?9001h" //nolint:revive // grouped constants
|
||||
DisableWin32Input = "\x1b[?9001l"
|
||||
RequestWin32Input = "\x1b[?9001$p"
|
||||
SetModeWin32Input = "\x1b[?9001h"
|
||||
ResetModeWin32Input = "\x1b[?9001l"
|
||||
RequestModeWin32Input = "\x1b[?9001$p"
|
||||
)
|
||||
|
||||
+495
@@ -0,0 +1,495 @@
|
||||
package ansi
|
||||
|
||||
// Keyboard Action Mode (KAM) controls locking of the keyboard.
|
||||
//
|
||||
// Deprecated: use [ModeKeyboardAction] instead.
|
||||
const (
|
||||
KeyboardActionMode = ANSIMode(2)
|
||||
|
||||
SetKeyboardActionMode = "\x1b[2h"
|
||||
ResetKeyboardActionMode = "\x1b[2l"
|
||||
RequestKeyboardActionMode = "\x1b[2$p"
|
||||
)
|
||||
|
||||
// Insert/Replace Mode (IRM) determines whether characters are inserted or replaced.
|
||||
//
|
||||
// Deprecated: use [ModeInsertReplace] instead.
|
||||
const (
|
||||
InsertReplaceMode = ANSIMode(4)
|
||||
|
||||
SetInsertReplaceMode = "\x1b[4h"
|
||||
ResetInsertReplaceMode = "\x1b[4l"
|
||||
RequestInsertReplaceMode = "\x1b[4$p"
|
||||
)
|
||||
|
||||
// BiDirectional Support Mode (BDSM) determines whether the terminal supports bidirectional text.
|
||||
//
|
||||
// Deprecated: use [ModeBiDirectionalSupport] instead.
|
||||
const (
|
||||
BiDirectionalSupportMode = ANSIMode(8)
|
||||
|
||||
SetBiDirectionalSupportMode = "\x1b[8h"
|
||||
ResetBiDirectionalSupportMode = "\x1b[8l"
|
||||
RequestBiDirectionalSupportMode = "\x1b[8$p"
|
||||
)
|
||||
|
||||
// Send Receive Mode (SRM) or Local Echo Mode determines whether the terminal echoes characters.
|
||||
//
|
||||
// Deprecated: use [ModeSendReceive] instead.
|
||||
const (
|
||||
SendReceiveMode = ANSIMode(12)
|
||||
LocalEchoMode = SendReceiveMode
|
||||
|
||||
SetSendReceiveMode = "\x1b[12h"
|
||||
ResetSendReceiveMode = "\x1b[12l"
|
||||
RequestSendReceiveMode = "\x1b[12$p"
|
||||
|
||||
SetLocalEchoMode = "\x1b[12h"
|
||||
ResetLocalEchoMode = "\x1b[12l"
|
||||
RequestLocalEchoMode = "\x1b[12$p"
|
||||
)
|
||||
|
||||
// Line Feed/New Line Mode (LNM) determines whether the terminal interprets line feed as new line.
|
||||
//
|
||||
// Deprecated: use [ModeLineFeedNewLine] instead.
|
||||
const (
|
||||
LineFeedNewLineMode = ANSIMode(20)
|
||||
|
||||
SetLineFeedNewLineMode = "\x1b[20h"
|
||||
ResetLineFeedNewLineMode = "\x1b[20l"
|
||||
RequestLineFeedNewLineMode = "\x1b[20$p"
|
||||
)
|
||||
|
||||
// Cursor Keys Mode (DECCKM) determines whether cursor keys send ANSI or application sequences.
|
||||
//
|
||||
// Deprecated: use [ModeCursorKeys] instead.
|
||||
const (
|
||||
CursorKeysMode = DECMode(1)
|
||||
|
||||
SetCursorKeysMode = "\x1b[?1h"
|
||||
ResetCursorKeysMode = "\x1b[?1l"
|
||||
RequestCursorKeysMode = "\x1b[?1$p"
|
||||
)
|
||||
|
||||
// Cursor Keys mode.
|
||||
//
|
||||
// Deprecated: use [SetModeCursorKeys] and [ResetModeCursorKeys] instead.
|
||||
const (
|
||||
EnableCursorKeys = "\x1b[?1h"
|
||||
DisableCursorKeys = "\x1b[?1l"
|
||||
)
|
||||
|
||||
// Origin Mode (DECOM) determines whether the cursor moves to home or margin position.
|
||||
//
|
||||
// Deprecated: use [ModeOrigin] instead.
|
||||
const (
|
||||
OriginMode = DECMode(6)
|
||||
|
||||
SetOriginMode = "\x1b[?6h"
|
||||
ResetOriginMode = "\x1b[?6l"
|
||||
RequestOriginMode = "\x1b[?6$p"
|
||||
)
|
||||
|
||||
// Auto Wrap Mode (DECAWM) determines whether the cursor wraps to the next line.
|
||||
//
|
||||
// Deprecated: use [ModeAutoWrap] instead.
|
||||
const (
|
||||
AutoWrapMode = DECMode(7)
|
||||
|
||||
SetAutoWrapMode = "\x1b[?7h"
|
||||
ResetAutoWrapMode = "\x1b[?7l"
|
||||
RequestAutoWrapMode = "\x1b[?7$p"
|
||||
)
|
||||
|
||||
// X10 Mouse Mode determines whether the mouse reports on button presses.
|
||||
//
|
||||
// Deprecated: use [ModeMouseX10] instead.
|
||||
const (
|
||||
X10MouseMode = DECMode(9)
|
||||
|
||||
SetX10MouseMode = "\x1b[?9h"
|
||||
ResetX10MouseMode = "\x1b[?9l"
|
||||
RequestX10MouseMode = "\x1b[?9$p"
|
||||
)
|
||||
|
||||
// Text Cursor Enable Mode (DECTCEM) shows/hides the cursor.
|
||||
//
|
||||
// Deprecated: use [ModeTextCursorEnable] instead.
|
||||
const (
|
||||
TextCursorEnableMode = DECMode(25)
|
||||
|
||||
SetTextCursorEnableMode = "\x1b[?25h"
|
||||
ResetTextCursorEnableMode = "\x1b[?25l"
|
||||
RequestTextCursorEnableMode = "\x1b[?25$p"
|
||||
)
|
||||
|
||||
// Text Cursor Enable mode.
|
||||
//
|
||||
// Deprecated: use [SetModeTextCursorEnable] and [ResetModeTextCursorEnable] instead.
|
||||
const (
|
||||
CursorEnableMode = DECMode(25)
|
||||
RequestCursorVisibility = "\x1b[?25$p"
|
||||
)
|
||||
|
||||
// Numeric Keypad Mode (DECNKM) determines whether the keypad sends application or numeric sequences.
|
||||
//
|
||||
// Deprecated: use [ModeNumericKeypad] instead.
|
||||
const (
|
||||
NumericKeypadMode = DECMode(66)
|
||||
|
||||
SetNumericKeypadMode = "\x1b[?66h"
|
||||
ResetNumericKeypadMode = "\x1b[?66l"
|
||||
RequestNumericKeypadMode = "\x1b[?66$p"
|
||||
)
|
||||
|
||||
// Backarrow Key Mode (DECBKM) determines whether the backspace key sends backspace or delete.
|
||||
//
|
||||
// Deprecated: use [ModeBackarrowKey] instead.
|
||||
const (
|
||||
BackarrowKeyMode = DECMode(67)
|
||||
|
||||
SetBackarrowKeyMode = "\x1b[?67h"
|
||||
ResetBackarrowKeyMode = "\x1b[?67l"
|
||||
RequestBackarrowKeyMode = "\x1b[?67$p"
|
||||
)
|
||||
|
||||
// Left Right Margin Mode (DECLRMM) determines whether left and right margins can be set.
|
||||
//
|
||||
// Deprecated: use [ModeLeftRightMargin] instead.
|
||||
const (
|
||||
LeftRightMarginMode = DECMode(69)
|
||||
|
||||
SetLeftRightMarginMode = "\x1b[?69h"
|
||||
ResetLeftRightMarginMode = "\x1b[?69l"
|
||||
RequestLeftRightMarginMode = "\x1b[?69$p"
|
||||
)
|
||||
|
||||
// Normal Mouse Mode determines whether the mouse reports on button presses and releases.
|
||||
//
|
||||
// Deprecated: use [ModeMouseNormal] instead.
|
||||
const (
|
||||
NormalMouseMode = DECMode(1000)
|
||||
|
||||
SetNormalMouseMode = "\x1b[?1000h"
|
||||
ResetNormalMouseMode = "\x1b[?1000l"
|
||||
RequestNormalMouseMode = "\x1b[?1000$p"
|
||||
)
|
||||
|
||||
// VT Mouse Tracking mode.
|
||||
//
|
||||
// Deprecated: use [ModeMouseNormal] instead.
|
||||
const (
|
||||
MouseMode = DECMode(1000)
|
||||
|
||||
EnableMouse = "\x1b[?1000h"
|
||||
DisableMouse = "\x1b[?1000l"
|
||||
RequestMouse = "\x1b[?1000$p"
|
||||
)
|
||||
|
||||
// Highlight Mouse Tracking determines whether the mouse reports on button presses and highlighted cells.
|
||||
//
|
||||
// Deprecated: use [ModeMouseHighlight] instead.
|
||||
const (
|
||||
HighlightMouseMode = DECMode(1001)
|
||||
|
||||
SetHighlightMouseMode = "\x1b[?1001h"
|
||||
ResetHighlightMouseMode = "\x1b[?1001l"
|
||||
RequestHighlightMouseMode = "\x1b[?1001$p"
|
||||
)
|
||||
|
||||
// VT Hilite Mouse Tracking mode.
|
||||
//
|
||||
// Deprecated: use [ModeMouseHighlight] instead.
|
||||
const (
|
||||
MouseHiliteMode = DECMode(1001)
|
||||
|
||||
EnableMouseHilite = "\x1b[?1001h"
|
||||
DisableMouseHilite = "\x1b[?1001l"
|
||||
RequestMouseHilite = "\x1b[?1001$p"
|
||||
)
|
||||
|
||||
// Button Event Mouse Tracking reports button-motion events when a button is pressed.
|
||||
//
|
||||
// Deprecated: use [ModeMouseButtonEvent] instead.
|
||||
const (
|
||||
ButtonEventMouseMode = DECMode(1002)
|
||||
|
||||
SetButtonEventMouseMode = "\x1b[?1002h"
|
||||
ResetButtonEventMouseMode = "\x1b[?1002l"
|
||||
RequestButtonEventMouseMode = "\x1b[?1002$p"
|
||||
)
|
||||
|
||||
// Cell Motion Mouse Tracking mode.
|
||||
//
|
||||
// Deprecated: use [ModeMouseButtonEvent] instead.
|
||||
const (
|
||||
MouseCellMotionMode = DECMode(1002)
|
||||
|
||||
EnableMouseCellMotion = "\x1b[?1002h"
|
||||
DisableMouseCellMotion = "\x1b[?1002l"
|
||||
RequestMouseCellMotion = "\x1b[?1002$p"
|
||||
)
|
||||
|
||||
// Any Event Mouse Tracking reports all motion events.
|
||||
//
|
||||
// Deprecated: use [ModeMouseAnyEvent] instead.
|
||||
const (
|
||||
AnyEventMouseMode = DECMode(1003)
|
||||
|
||||
SetAnyEventMouseMode = "\x1b[?1003h"
|
||||
ResetAnyEventMouseMode = "\x1b[?1003l"
|
||||
RequestAnyEventMouseMode = "\x1b[?1003$p"
|
||||
)
|
||||
|
||||
// All Mouse Tracking mode.
|
||||
//
|
||||
// Deprecated: use [ModeMouseAnyEvent] instead.
|
||||
const (
|
||||
MouseAllMotionMode = DECMode(1003)
|
||||
|
||||
EnableMouseAllMotion = "\x1b[?1003h"
|
||||
DisableMouseAllMotion = "\x1b[?1003l"
|
||||
RequestMouseAllMotion = "\x1b[?1003$p"
|
||||
)
|
||||
|
||||
// Focus Event Mode determines whether the terminal reports focus and blur events.
|
||||
//
|
||||
// Deprecated: use [ModeFocusEvent] instead.
|
||||
const (
|
||||
FocusEventMode = DECMode(1004)
|
||||
|
||||
SetFocusEventMode = "\x1b[?1004h"
|
||||
ResetFocusEventMode = "\x1b[?1004l"
|
||||
RequestFocusEventMode = "\x1b[?1004$p"
|
||||
)
|
||||
|
||||
// Focus reporting mode.
|
||||
//
|
||||
// Deprecated: use [SetModeFocusEvent], [ResetModeFocusEvent], and
|
||||
// [RequestModeFocusEvent] instead.
|
||||
const (
|
||||
ReportFocusMode = DECMode(1004)
|
||||
|
||||
EnableReportFocus = "\x1b[?1004h"
|
||||
DisableReportFocus = "\x1b[?1004l"
|
||||
RequestReportFocus = "\x1b[?1004$p"
|
||||
)
|
||||
|
||||
// UTF-8 Extended Mouse Mode changes the mouse tracking encoding to use UTF-8 parameters.
|
||||
//
|
||||
// Deprecated: use [ModeMouseExtUtf8] instead.
|
||||
const (
|
||||
Utf8ExtMouseMode = DECMode(1005)
|
||||
|
||||
SetUtf8ExtMouseMode = "\x1b[?1005h"
|
||||
ResetUtf8ExtMouseMode = "\x1b[?1005l"
|
||||
RequestUtf8ExtMouseMode = "\x1b[?1005$p"
|
||||
)
|
||||
|
||||
// SGR Extended Mouse Mode changes the mouse tracking encoding to use SGR parameters.
|
||||
//
|
||||
// Deprecated: use [ModeMouseExtSgr] instead.
|
||||
const (
|
||||
SgrExtMouseMode = DECMode(1006)
|
||||
|
||||
SetSgrExtMouseMode = "\x1b[?1006h"
|
||||
ResetSgrExtMouseMode = "\x1b[?1006l"
|
||||
RequestSgrExtMouseMode = "\x1b[?1006$p"
|
||||
)
|
||||
|
||||
// Mouse SGR Extended mode.
|
||||
//
|
||||
// Deprecated: use [ModeMouseExtSgr], [SetModeMouseExtSgr],
|
||||
// [ResetModeMouseExtSgr], and [RequestModeMouseExtSgr] instead.
|
||||
const (
|
||||
MouseSgrExtMode = DECMode(1006)
|
||||
EnableMouseSgrExt = "\x1b[?1006h"
|
||||
DisableMouseSgrExt = "\x1b[?1006l"
|
||||
RequestMouseSgrExt = "\x1b[?1006$p"
|
||||
)
|
||||
|
||||
// URXVT Extended Mouse Mode changes the mouse tracking encoding to use an alternate encoding.
|
||||
//
|
||||
// Deprecated: use [ModeMouseUrxvtExt] instead.
|
||||
const (
|
||||
UrxvtExtMouseMode = DECMode(1015)
|
||||
|
||||
SetUrxvtExtMouseMode = "\x1b[?1015h"
|
||||
ResetUrxvtExtMouseMode = "\x1b[?1015l"
|
||||
RequestUrxvtExtMouseMode = "\x1b[?1015$p"
|
||||
)
|
||||
|
||||
// SGR Pixel Extended Mouse Mode changes the mouse tracking encoding to use SGR parameters with pixel coordinates.
|
||||
//
|
||||
// Deprecated: use [ModeMouseExtSgrPixel] instead.
|
||||
const (
|
||||
SgrPixelExtMouseMode = DECMode(1016)
|
||||
|
||||
SetSgrPixelExtMouseMode = "\x1b[?1016h"
|
||||
ResetSgrPixelExtMouseMode = "\x1b[?1016l"
|
||||
RequestSgrPixelExtMouseMode = "\x1b[?1016$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Mode determines whether the alternate screen buffer is active.
|
||||
//
|
||||
// Deprecated: use [ModeAltScreen] instead.
|
||||
const (
|
||||
AltScreenMode = DECMode(1047)
|
||||
|
||||
SetAltScreenMode = "\x1b[?1047h"
|
||||
ResetAltScreenMode = "\x1b[?1047l"
|
||||
RequestAltScreenMode = "\x1b[?1047$p"
|
||||
)
|
||||
|
||||
// Save Cursor Mode saves the cursor position.
|
||||
//
|
||||
// Deprecated: use [ModeSaveCursor] instead.
|
||||
const (
|
||||
SaveCursorMode = DECMode(1048)
|
||||
|
||||
SetSaveCursorMode = "\x1b[?1048h"
|
||||
ResetSaveCursorMode = "\x1b[?1048l"
|
||||
RequestSaveCursorMode = "\x1b[?1048$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Save Cursor Mode saves the cursor position and switches to alternate screen.
|
||||
//
|
||||
// Deprecated: use [ModeAltScreenSaveCursor] instead.
|
||||
const (
|
||||
AltScreenSaveCursorMode = DECMode(1049)
|
||||
|
||||
SetAltScreenSaveCursorMode = "\x1b[?1049h"
|
||||
ResetAltScreenSaveCursorMode = "\x1b[?1049l"
|
||||
RequestAltScreenSaveCursorMode = "\x1b[?1049$p"
|
||||
)
|
||||
|
||||
// Alternate Screen Buffer mode.
|
||||
//
|
||||
// Deprecated: use [ModeAltScreenSaveCursor] instead.
|
||||
const (
|
||||
AltScreenBufferMode = DECMode(1049)
|
||||
|
||||
SetAltScreenBufferMode = "\x1b[?1049h"
|
||||
ResetAltScreenBufferMode = "\x1b[?1049l"
|
||||
RequestAltScreenBufferMode = "\x1b[?1049$p"
|
||||
|
||||
EnableAltScreenBuffer = "\x1b[?1049h"
|
||||
DisableAltScreenBuffer = "\x1b[?1049l"
|
||||
RequestAltScreenBuffer = "\x1b[?1049$p"
|
||||
)
|
||||
|
||||
// Bracketed Paste Mode determines whether pasted text is bracketed with escape sequences.
|
||||
//
|
||||
// Deprecated: use [ModeBracketedPaste] instead.
|
||||
const (
|
||||
BracketedPasteMode = DECMode(2004)
|
||||
|
||||
SetBracketedPasteMode = "\x1b[?2004h"
|
||||
ResetBracketedPasteMode = "\x1b[?2004l"
|
||||
RequestBracketedPasteMode = "\x1b[?2004$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetModeBracketedPaste], [ResetModeBracketedPaste], and
|
||||
// [RequestModeBracketedPaste] instead.
|
||||
const (
|
||||
EnableBracketedPaste = "\x1b[?2004h" //nolint:revive
|
||||
DisableBracketedPaste = "\x1b[?2004l"
|
||||
RequestBracketedPaste = "\x1b[?2004$p"
|
||||
)
|
||||
|
||||
// Synchronized Output Mode determines whether output is synchronized with the terminal.
|
||||
//
|
||||
// Deprecated: use [ModeSynchronizedOutput] instead.
|
||||
const (
|
||||
SynchronizedOutputMode = DECMode(2026)
|
||||
|
||||
SetSynchronizedOutputMode = "\x1b[?2026h"
|
||||
ResetSynchronizedOutputMode = "\x1b[?2026l"
|
||||
RequestSynchronizedOutputMode = "\x1b[?2026$p"
|
||||
)
|
||||
|
||||
// Synchronized output mode.
|
||||
//
|
||||
// Deprecated: use [ModeSynchronizedOutput], [SetModeSynchronizedOutput],
|
||||
// [ResetModeSynchronizedOutput], and [RequestModeSynchronizedOutput] instead.
|
||||
const (
|
||||
SyncdOutputMode = DECMode(2026)
|
||||
|
||||
EnableSyncdOutput = "\x1b[?2026h"
|
||||
DisableSyncdOutput = "\x1b[?2026l"
|
||||
RequestSyncdOutput = "\x1b[?2026$p"
|
||||
)
|
||||
|
||||
// Unicode Core Mode determines whether the terminal uses Unicode grapheme clustering.
|
||||
//
|
||||
// Deprecated: use [ModeUnicodeCore] instead.
|
||||
const (
|
||||
UnicodeCoreMode = DECMode(2027)
|
||||
|
||||
SetUnicodeCoreMode = "\x1b[?2027h"
|
||||
ResetUnicodeCoreMode = "\x1b[?2027l"
|
||||
RequestUnicodeCoreMode = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Grapheme Clustering Mode determines whether the terminal looks for grapheme clusters.
|
||||
//
|
||||
// Deprecated: use [ModeUnicodeCore], [SetModeUnicodeCore],
|
||||
// [ResetModeUnicodeCore], and [RequestModeUnicodeCore] instead.
|
||||
const (
|
||||
GraphemeClusteringMode = DECMode(2027)
|
||||
|
||||
SetGraphemeClusteringMode = "\x1b[?2027h"
|
||||
ResetGraphemeClusteringMode = "\x1b[?2027l"
|
||||
RequestGraphemeClusteringMode = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Unicode Core mode.
|
||||
//
|
||||
// Deprecated: use [SetModeUnicodeCore], [ResetModeUnicodeCore], and
|
||||
// [RequestModeUnicodeCore] instead.
|
||||
const (
|
||||
EnableGraphemeClustering = "\x1b[?2027h"
|
||||
DisableGraphemeClustering = "\x1b[?2027l"
|
||||
RequestGraphemeClustering = "\x1b[?2027$p"
|
||||
)
|
||||
|
||||
// Light Dark Mode enables reporting the operating system's color scheme preference.
|
||||
//
|
||||
// Deprecated: use [ModeLightDark] instead.
|
||||
const (
|
||||
LightDarkMode = DECMode(2031)
|
||||
|
||||
SetLightDarkMode = "\x1b[?2031h"
|
||||
ResetLightDarkMode = "\x1b[?2031l"
|
||||
RequestLightDarkMode = "\x1b[?2031$p"
|
||||
)
|
||||
|
||||
// In Band Resize Mode reports terminal resize events as escape sequences.
|
||||
//
|
||||
// Deprecated: use [ModeInBandResize] instead.
|
||||
const (
|
||||
InBandResizeMode = DECMode(2048)
|
||||
|
||||
SetInBandResizeMode = "\x1b[?2048h"
|
||||
ResetInBandResizeMode = "\x1b[?2048l"
|
||||
RequestInBandResizeMode = "\x1b[?2048$p"
|
||||
)
|
||||
|
||||
// Win32Input determines whether input is processed by the Win32 console and Conpty.
|
||||
//
|
||||
// Deprecated: use [ModeWin32Input] instead.
|
||||
const (
|
||||
Win32InputMode = DECMode(9001)
|
||||
|
||||
SetWin32InputMode = "\x1b[?9001h"
|
||||
ResetWin32InputMode = "\x1b[?9001l"
|
||||
RequestWin32InputMode = "\x1b[?9001$p"
|
||||
)
|
||||
|
||||
// Deprecated: use [SetModeWin32Input], [ResetModeWin32Input], and
|
||||
// [RequestModeWin32Input] instead.
|
||||
const (
|
||||
EnableWin32Input = "\x1b[?9001h" //nolint:revive
|
||||
DisableWin32Input = "\x1b[?9001l"
|
||||
RequestWin32Input = "\x1b[?9001$p"
|
||||
)
|
||||
+1
-1
@@ -134,7 +134,7 @@ func EncodeMouseButton(b MouseButton, motion, shift, alt, ctrl bool) (m byte) {
|
||||
m |= bitMotion
|
||||
}
|
||||
|
||||
return //nolint:nakedret
|
||||
return m
|
||||
}
|
||||
|
||||
// x10Offset is the offset for X10 mouse events.
|
||||
|
||||
+19
@@ -1,5 +1,10 @@
|
||||
package ansi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Notify sends a desktop notification using iTerm's OSC 9.
|
||||
//
|
||||
// OSC 9 ; Mc ST
|
||||
@@ -11,3 +16,17 @@ package ansi
|
||||
func Notify(s string) string {
|
||||
return "\x1b]9;" + s + "\x07"
|
||||
}
|
||||
|
||||
// DesktopNotification sends a desktop notification based on the extensible OSC
|
||||
// 99 escape code.
|
||||
//
|
||||
// OSC 99 ; <metadata> ; <payload> ST
|
||||
// OSC 99 ; <metadata> ; <payload> BEL
|
||||
//
|
||||
// Where <metadata> is a colon-separated list of key-value pairs, and
|
||||
// <payload> is the notification body.
|
||||
//
|
||||
// See: https://sw.kovidgoyal.net/kitty/desktop-notifications/
|
||||
func DesktopNotification(payload string, metadata ...string) string {
|
||||
return fmt.Sprintf("\x1b]99;%s;%s\x07", strings.Join(metadata, ":"), payload)
|
||||
}
|
||||
|
||||
+17
-16
@@ -4,8 +4,7 @@ import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
)
|
||||
|
||||
// State represents the state of the ANSI escape sequence parser used by
|
||||
@@ -176,10 +175,7 @@ func decodeSequence[T string | []byte](m Method, b T, state State, p *Parser) (s
|
||||
}
|
||||
|
||||
if utf8.RuneStart(c) {
|
||||
seq, _, width, _ = FirstGraphemeCluster(b, -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(seq))
|
||||
}
|
||||
seq, width = FirstGraphemeCluster(b, m)
|
||||
i += len(seq)
|
||||
return b[:i], width, i, NormalState
|
||||
}
|
||||
@@ -434,17 +430,22 @@ func HasEscPrefix[T string | []byte](b T) bool {
|
||||
return len(b) > 0 && b[0] == ESC
|
||||
}
|
||||
|
||||
// FirstGraphemeCluster returns the first grapheme cluster in the given string or byte slice.
|
||||
// This is a syntactic sugar function that wraps
|
||||
// uniseg.FirstGraphemeClusterInString and uniseg.FirstGraphemeCluster.
|
||||
func FirstGraphemeCluster[T string | []byte](b T, state int) (T, T, int, int) {
|
||||
// FirstGraphemeCluster returns the first grapheme cluster in the given string
|
||||
// or byte slice, and its monospace display width.
|
||||
func FirstGraphemeCluster[T string | []byte](b T, m Method) (T, int) {
|
||||
switch b := any(b).(type) {
|
||||
case string:
|
||||
cluster, rest, width, newState := uniseg.FirstGraphemeClusterInString(b, state)
|
||||
return T(cluster), T(rest), width, newState
|
||||
cluster := graphemes.FromString(b).First()
|
||||
if m == WcWidth {
|
||||
return T(cluster), wcOptions.StringWidth(cluster)
|
||||
}
|
||||
return T(cluster), dwOptions.String(cluster)
|
||||
case []byte:
|
||||
cluster, rest, width, newState := uniseg.FirstGraphemeCluster(b, state)
|
||||
return T(cluster), T(rest), width, newState
|
||||
cluster := graphemes.FromBytes(b).First()
|
||||
if m == WcWidth {
|
||||
return T(cluster), wcOptions.StringWidth(string(cluster))
|
||||
}
|
||||
return T(cluster), dwOptions.Bytes(cluster)
|
||||
}
|
||||
panic("unreachable")
|
||||
}
|
||||
@@ -490,7 +491,7 @@ func Command(prefix, inter, final byte) (c int) {
|
||||
c = int(final)
|
||||
c |= int(prefix) << parser.PrefixShift
|
||||
c |= int(inter) << parser.IntermedShift
|
||||
return
|
||||
return c
|
||||
}
|
||||
|
||||
// Param represents a sequence parameter. Sequence parameters with
|
||||
@@ -520,5 +521,5 @@ func Parameter(p int, hasMore bool) (s int) {
|
||||
if hasMore {
|
||||
s |= parser.HasMoreFlag
|
||||
}
|
||||
return
|
||||
return s
|
||||
}
|
||||
|
||||
+1
-1
@@ -10,7 +10,7 @@ var parserPool = sync.Pool{
|
||||
New: func() any {
|
||||
p := NewParser()
|
||||
p.SetParamsSize(parser.MaxParamsSize)
|
||||
p.SetDataSize(1024 * 1024 * 4) // 4MB of data buffer
|
||||
p.SetDataSize(1024 * 4) // 4KB of data buffer
|
||||
return p
|
||||
},
|
||||
}
|
||||
|
||||
+55
-55
@@ -21,59 +21,59 @@ func SGR(ps ...Attr) string {
|
||||
}
|
||||
|
||||
var attrStrings = map[int]string{
|
||||
ResetAttr: resetAttr,
|
||||
BoldAttr: boldAttr,
|
||||
FaintAttr: faintAttr,
|
||||
ItalicAttr: italicAttr,
|
||||
UnderlineAttr: underlineAttr,
|
||||
SlowBlinkAttr: slowBlinkAttr,
|
||||
RapidBlinkAttr: rapidBlinkAttr,
|
||||
ReverseAttr: reverseAttr,
|
||||
ConcealAttr: concealAttr,
|
||||
StrikethroughAttr: strikethroughAttr,
|
||||
NormalIntensityAttr: normalIntensityAttr,
|
||||
NoItalicAttr: noItalicAttr,
|
||||
NoUnderlineAttr: noUnderlineAttr,
|
||||
NoBlinkAttr: noBlinkAttr,
|
||||
NoReverseAttr: noReverseAttr,
|
||||
NoConcealAttr: noConcealAttr,
|
||||
NoStrikethroughAttr: noStrikethroughAttr,
|
||||
BlackForegroundColorAttr: blackForegroundColorAttr,
|
||||
RedForegroundColorAttr: redForegroundColorAttr,
|
||||
GreenForegroundColorAttr: greenForegroundColorAttr,
|
||||
YellowForegroundColorAttr: yellowForegroundColorAttr,
|
||||
BlueForegroundColorAttr: blueForegroundColorAttr,
|
||||
MagentaForegroundColorAttr: magentaForegroundColorAttr,
|
||||
CyanForegroundColorAttr: cyanForegroundColorAttr,
|
||||
WhiteForegroundColorAttr: whiteForegroundColorAttr,
|
||||
ExtendedForegroundColorAttr: extendedForegroundColorAttr,
|
||||
DefaultForegroundColorAttr: defaultForegroundColorAttr,
|
||||
BlackBackgroundColorAttr: blackBackgroundColorAttr,
|
||||
RedBackgroundColorAttr: redBackgroundColorAttr,
|
||||
GreenBackgroundColorAttr: greenBackgroundColorAttr,
|
||||
YellowBackgroundColorAttr: yellowBackgroundColorAttr,
|
||||
BlueBackgroundColorAttr: blueBackgroundColorAttr,
|
||||
MagentaBackgroundColorAttr: magentaBackgroundColorAttr,
|
||||
CyanBackgroundColorAttr: cyanBackgroundColorAttr,
|
||||
WhiteBackgroundColorAttr: whiteBackgroundColorAttr,
|
||||
ExtendedBackgroundColorAttr: extendedBackgroundColorAttr,
|
||||
DefaultBackgroundColorAttr: defaultBackgroundColorAttr,
|
||||
ExtendedUnderlineColorAttr: extendedUnderlineColorAttr,
|
||||
DefaultUnderlineColorAttr: defaultUnderlineColorAttr,
|
||||
BrightBlackForegroundColorAttr: brightBlackForegroundColorAttr,
|
||||
BrightRedForegroundColorAttr: brightRedForegroundColorAttr,
|
||||
BrightGreenForegroundColorAttr: brightGreenForegroundColorAttr,
|
||||
BrightYellowForegroundColorAttr: brightYellowForegroundColorAttr,
|
||||
BrightBlueForegroundColorAttr: brightBlueForegroundColorAttr,
|
||||
BrightMagentaForegroundColorAttr: brightMagentaForegroundColorAttr,
|
||||
BrightCyanForegroundColorAttr: brightCyanForegroundColorAttr,
|
||||
BrightWhiteForegroundColorAttr: brightWhiteForegroundColorAttr,
|
||||
BrightBlackBackgroundColorAttr: brightBlackBackgroundColorAttr,
|
||||
BrightRedBackgroundColorAttr: brightRedBackgroundColorAttr,
|
||||
BrightGreenBackgroundColorAttr: brightGreenBackgroundColorAttr,
|
||||
BrightYellowBackgroundColorAttr: brightYellowBackgroundColorAttr,
|
||||
BrightBlueBackgroundColorAttr: brightBlueBackgroundColorAttr,
|
||||
BrightMagentaBackgroundColorAttr: brightMagentaBackgroundColorAttr,
|
||||
BrightCyanBackgroundColorAttr: brightCyanBackgroundColorAttr,
|
||||
BrightWhiteBackgroundColorAttr: brightWhiteBackgroundColorAttr,
|
||||
AttrReset: attrReset,
|
||||
AttrBold: attrBold,
|
||||
AttrFaint: attrFaint,
|
||||
AttrItalic: attrItalic,
|
||||
AttrUnderline: attrUnderline,
|
||||
AttrBlink: attrBlink,
|
||||
AttrRapidBlink: attrRapidBlink,
|
||||
AttrReverse: attrReverse,
|
||||
AttrConceal: attrConceal,
|
||||
AttrStrikethrough: attrStrikethrough,
|
||||
AttrNormalIntensity: attrNormalIntensity,
|
||||
AttrNoItalic: attrNoItalic,
|
||||
AttrNoUnderline: attrNoUnderline,
|
||||
AttrNoBlink: attrNoBlink,
|
||||
AttrNoReverse: attrNoReverse,
|
||||
AttrNoConceal: attrNoConceal,
|
||||
AttrNoStrikethrough: attrNoStrikethrough,
|
||||
AttrBlackForegroundColor: attrBlackForegroundColor,
|
||||
AttrRedForegroundColor: attrRedForegroundColor,
|
||||
AttrGreenForegroundColor: attrGreenForegroundColor,
|
||||
AttrYellowForegroundColor: attrYellowForegroundColor,
|
||||
AttrBlueForegroundColor: attrBlueForegroundColor,
|
||||
AttrMagentaForegroundColor: attrMagentaForegroundColor,
|
||||
AttrCyanForegroundColor: attrCyanForegroundColor,
|
||||
AttrWhiteForegroundColor: attrWhiteForegroundColor,
|
||||
AttrExtendedForegroundColor: attrExtendedForegroundColor,
|
||||
AttrDefaultForegroundColor: attrDefaultForegroundColor,
|
||||
AttrBlackBackgroundColor: attrBlackBackgroundColor,
|
||||
AttrRedBackgroundColor: attrRedBackgroundColor,
|
||||
AttrGreenBackgroundColor: attrGreenBackgroundColor,
|
||||
AttrYellowBackgroundColor: attrYellowBackgroundColor,
|
||||
AttrBlueBackgroundColor: attrBlueBackgroundColor,
|
||||
AttrMagentaBackgroundColor: attrMagentaBackgroundColor,
|
||||
AttrCyanBackgroundColor: attrCyanBackgroundColor,
|
||||
AttrWhiteBackgroundColor: attrWhiteBackgroundColor,
|
||||
AttrExtendedBackgroundColor: attrExtendedBackgroundColor,
|
||||
AttrDefaultBackgroundColor: attrDefaultBackgroundColor,
|
||||
AttrExtendedUnderlineColor: attrExtendedUnderlineColor,
|
||||
AttrDefaultUnderlineColor: attrDefaultUnderlineColor,
|
||||
AttrBrightBlackForegroundColor: attrBrightBlackForegroundColor,
|
||||
AttrBrightRedForegroundColor: attrBrightRedForegroundColor,
|
||||
AttrBrightGreenForegroundColor: attrBrightGreenForegroundColor,
|
||||
AttrBrightYellowForegroundColor: attrBrightYellowForegroundColor,
|
||||
AttrBrightBlueForegroundColor: attrBrightBlueForegroundColor,
|
||||
AttrBrightMagentaForegroundColor: attrBrightMagentaForegroundColor,
|
||||
AttrBrightCyanForegroundColor: attrBrightCyanForegroundColor,
|
||||
AttrBrightWhiteForegroundColor: attrBrightWhiteForegroundColor,
|
||||
AttrBrightBlackBackgroundColor: attrBrightBlackBackgroundColor,
|
||||
AttrBrightRedBackgroundColor: attrBrightRedBackgroundColor,
|
||||
AttrBrightGreenBackgroundColor: attrBrightGreenBackgroundColor,
|
||||
AttrBrightYellowBackgroundColor: attrBrightYellowBackgroundColor,
|
||||
AttrBrightBlueBackgroundColor: attrBrightBlueBackgroundColor,
|
||||
AttrBrightMagentaBackgroundColor: attrBrightMagentaBackgroundColor,
|
||||
AttrBrightCyanBackgroundColor: attrBrightCyanBackgroundColor,
|
||||
AttrBrightWhiteBackgroundColor: attrBrightWhiteBackgroundColor,
|
||||
}
|
||||
|
||||
+388
-242
@@ -17,7 +17,9 @@ type Attr = int
|
||||
// Style represents an ANSI SGR (Select Graphic Rendition) style.
|
||||
type Style []string
|
||||
|
||||
// NewStyle returns a new style with the given attributes.
|
||||
// NewStyle returns a new style with the given attributes. Attributes are SGR
|
||||
// (Select Graphic Rendition) codes that control text formatting like bold,
|
||||
// italic, colors, etc.
|
||||
func NewStyle(attrs ...Attr) Style {
|
||||
if len(attrs) == 0 {
|
||||
return Style{}
|
||||
@@ -46,7 +48,8 @@ func (s Style) String() string {
|
||||
return "\x1b[" + strings.Join(s, ";") + "m"
|
||||
}
|
||||
|
||||
// Styled returns a styled string with the given style applied.
|
||||
// Styled returns a styled string with the given style applied. The style is
|
||||
// applied at the beginning and reset at the end of the string.
|
||||
func (s Style) Styled(str string) string {
|
||||
if len(s) == 0 {
|
||||
return str
|
||||
@@ -54,309 +57,446 @@ func (s Style) Styled(str string) string {
|
||||
return s.String() + str + ResetStyle
|
||||
}
|
||||
|
||||
// Reset appends the reset style attribute to the style.
|
||||
// Reset appends the reset style attribute to the style. This resets all
|
||||
// formatting attributes to their defaults.
|
||||
func (s Style) Reset() Style {
|
||||
return append(s, resetAttr)
|
||||
return append(s, attrReset)
|
||||
}
|
||||
|
||||
// Bold appends the bold style attribute to the style.
|
||||
// Bold appends the bold or normal intensity style attribute to the style.
|
||||
// You can use [Style.Normal] to reset to normal intensity.
|
||||
func (s Style) Bold() Style {
|
||||
return append(s, boldAttr)
|
||||
return append(s, attrBold)
|
||||
}
|
||||
|
||||
// Faint appends the faint style attribute to the style.
|
||||
// Faint appends the faint or normal intensity style attribute to the style.
|
||||
// You can use [Style.Normal] to reset to normal intensity.
|
||||
func (s Style) Faint() Style {
|
||||
return append(s, faintAttr)
|
||||
return append(s, attrFaint)
|
||||
}
|
||||
|
||||
// Italic appends the italic style attribute to the style.
|
||||
func (s Style) Italic() Style {
|
||||
return append(s, italicAttr)
|
||||
// Italic appends the italic or no italic style attribute to the style.
|
||||
// When v is true, text is rendered in italic. When false, italic is disabled.
|
||||
func (s Style) Italic(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrItalic)
|
||||
}
|
||||
return append(s, attrNoItalic)
|
||||
}
|
||||
|
||||
// Underline appends the underline style attribute to the style.
|
||||
func (s Style) Underline() Style {
|
||||
return append(s, underlineAttr)
|
||||
// Underline appends the underline or no underline style attribute to the style.
|
||||
// When v is true, text is underlined. When false, underline is disabled.
|
||||
func (s Style) Underline(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrUnderline)
|
||||
}
|
||||
return append(s, attrNoUnderline)
|
||||
}
|
||||
|
||||
// UnderlineStyle appends the underline style attribute to the style.
|
||||
func (s Style) UnderlineStyle(u UnderlineStyle) Style {
|
||||
// Supports various underline styles including single, double, curly, dotted,
|
||||
// and dashed.
|
||||
func (s Style) UnderlineStyle(u Underline) Style {
|
||||
switch u {
|
||||
case NoUnderlineStyle:
|
||||
return s.NoUnderline()
|
||||
case SingleUnderlineStyle:
|
||||
return s.Underline()
|
||||
case DoubleUnderlineStyle:
|
||||
return append(s, doubleUnderlineStyle)
|
||||
case CurlyUnderlineStyle:
|
||||
return append(s, curlyUnderlineStyle)
|
||||
case DottedUnderlineStyle:
|
||||
return append(s, dottedUnderlineStyle)
|
||||
case DashedUnderlineStyle:
|
||||
return append(s, dashedUnderlineStyle)
|
||||
case UnderlineNone:
|
||||
return s.Underline(false)
|
||||
case UnderlineSingle:
|
||||
return s.Underline(true)
|
||||
case UnderlineDouble:
|
||||
return append(s, underlineDouble)
|
||||
case UnderlineCurly:
|
||||
return append(s, underlineCurly)
|
||||
case UnderlineDotted:
|
||||
return append(s, underlineDotted)
|
||||
case UnderlineDashed:
|
||||
return append(s, underlineDashed)
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// DoubleUnderline appends the double underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(DoubleUnderlineStyle).
|
||||
func (s Style) DoubleUnderline() Style {
|
||||
return s.UnderlineStyle(DoubleUnderlineStyle)
|
||||
// Blink appends the slow blink or no blink style attribute to the style.
|
||||
// When v is true, text blinks slowly (less than 150 per minute). When false,
|
||||
// blinking is disabled.
|
||||
func (s Style) Blink(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrBlink)
|
||||
}
|
||||
return append(s, attrNoBlink)
|
||||
}
|
||||
|
||||
// CurlyUnderline appends the curly underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(CurlyUnderlineStyle).
|
||||
func (s Style) CurlyUnderline() Style {
|
||||
return s.UnderlineStyle(CurlyUnderlineStyle)
|
||||
// RapidBlink appends the rapid blink or no blink style attribute to the style.
|
||||
// When v is true, text blinks rapidly (150+ per minute). When false, blinking
|
||||
// is disabled.
|
||||
//
|
||||
// Note that this is not widely supported in terminal emulators.
|
||||
func (s Style) RapidBlink(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrRapidBlink)
|
||||
}
|
||||
return append(s, attrNoBlink)
|
||||
}
|
||||
|
||||
// DottedUnderline appends the dotted underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(DottedUnderlineStyle).
|
||||
func (s Style) DottedUnderline() Style {
|
||||
return s.UnderlineStyle(DottedUnderlineStyle)
|
||||
// Reverse appends the reverse or no reverse style attribute to the style.
|
||||
// When v is true, foreground and background colors are swapped. When false,
|
||||
// reverse video is disabled.
|
||||
func (s Style) Reverse(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrReverse)
|
||||
}
|
||||
return append(s, attrNoReverse)
|
||||
}
|
||||
|
||||
// DashedUnderline appends the dashed underline style attribute to the style.
|
||||
// This is a convenience method for UnderlineStyle(DashedUnderlineStyle).
|
||||
func (s Style) DashedUnderline() Style {
|
||||
return s.UnderlineStyle(DashedUnderlineStyle)
|
||||
// Conceal appends the conceal or no conceal style attribute to the style.
|
||||
// When v is true, text is hidden/concealed. When false, concealment is
|
||||
// disabled.
|
||||
func (s Style) Conceal(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrConceal)
|
||||
}
|
||||
return append(s, attrNoConceal)
|
||||
}
|
||||
|
||||
// SlowBlink appends the slow blink style attribute to the style.
|
||||
func (s Style) SlowBlink() Style {
|
||||
return append(s, slowBlinkAttr)
|
||||
// Strikethrough appends the strikethrough or no strikethrough style attribute
|
||||
// to the style. When v is true, text is rendered with a horizontal line through
|
||||
// it. When false, strikethrough is disabled.
|
||||
func (s Style) Strikethrough(v bool) Style {
|
||||
if v {
|
||||
return append(s, attrStrikethrough)
|
||||
}
|
||||
return append(s, attrNoStrikethrough)
|
||||
}
|
||||
|
||||
// RapidBlink appends the rapid blink style attribute to the style.
|
||||
func (s Style) RapidBlink() Style {
|
||||
return append(s, rapidBlinkAttr)
|
||||
}
|
||||
|
||||
// Reverse appends the reverse style attribute to the style.
|
||||
func (s Style) Reverse() Style {
|
||||
return append(s, reverseAttr)
|
||||
}
|
||||
|
||||
// Conceal appends the conceal style attribute to the style.
|
||||
func (s Style) Conceal() Style {
|
||||
return append(s, concealAttr)
|
||||
}
|
||||
|
||||
// Strikethrough appends the strikethrough style attribute to the style.
|
||||
func (s Style) Strikethrough() Style {
|
||||
return append(s, strikethroughAttr)
|
||||
}
|
||||
|
||||
// NormalIntensity appends the normal intensity style attribute to the style.
|
||||
func (s Style) NormalIntensity() Style {
|
||||
return append(s, normalIntensityAttr)
|
||||
// Normal appends the normal intensity style attribute to the style. This
|
||||
// resets [Style.Bold] and [Style.Faint] attributes.
|
||||
func (s Style) Normal() Style {
|
||||
return append(s, attrNormalIntensity)
|
||||
}
|
||||
|
||||
// NoItalic appends the no italic style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Italic](false) instead.
|
||||
func (s Style) NoItalic() Style {
|
||||
return append(s, noItalicAttr)
|
||||
return append(s, attrNoItalic)
|
||||
}
|
||||
|
||||
// NoUnderline appends the no underline style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Underline](false) instead.
|
||||
func (s Style) NoUnderline() Style {
|
||||
return append(s, noUnderlineAttr)
|
||||
return append(s, attrNoUnderline)
|
||||
}
|
||||
|
||||
// NoBlink appends the no blink style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Blink](false) or [Style.RapidBlink](false) instead.
|
||||
func (s Style) NoBlink() Style {
|
||||
return append(s, noBlinkAttr)
|
||||
return append(s, attrNoBlink)
|
||||
}
|
||||
|
||||
// NoReverse appends the no reverse style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Reverse](false) instead.
|
||||
func (s Style) NoReverse() Style {
|
||||
return append(s, noReverseAttr)
|
||||
return append(s, attrNoReverse)
|
||||
}
|
||||
|
||||
// NoConceal appends the no conceal style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Conceal](false) instead.
|
||||
func (s Style) NoConceal() Style {
|
||||
return append(s, noConcealAttr)
|
||||
return append(s, attrNoConceal)
|
||||
}
|
||||
|
||||
// NoStrikethrough appends the no strikethrough style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.Strikethrough](false) instead.
|
||||
func (s Style) NoStrikethrough() Style {
|
||||
return append(s, noStrikethroughAttr)
|
||||
return append(s, attrNoStrikethrough)
|
||||
}
|
||||
|
||||
// DefaultForegroundColor appends the default foreground color style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.ForegroundColor](nil) instead.
|
||||
func (s Style) DefaultForegroundColor() Style {
|
||||
return append(s, defaultForegroundColorAttr)
|
||||
return append(s, attrDefaultForegroundColor)
|
||||
}
|
||||
|
||||
// DefaultBackgroundColor appends the default background color style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.BackgroundColor](nil) instead.
|
||||
func (s Style) DefaultBackgroundColor() Style {
|
||||
return append(s, defaultBackgroundColorAttr)
|
||||
return append(s, attrDefaultBackgroundColor)
|
||||
}
|
||||
|
||||
// DefaultUnderlineColor appends the default underline color style attribute to the style.
|
||||
//
|
||||
// Deprecated: use [Style.UnderlineColor](nil) instead.
|
||||
func (s Style) DefaultUnderlineColor() Style {
|
||||
return append(s, defaultUnderlineColorAttr)
|
||||
return append(s, attrDefaultUnderlineColor)
|
||||
}
|
||||
|
||||
// ForegroundColor appends the foreground color style attribute to the style.
|
||||
// If c is nil, the default foreground color is used. Supports [BasicColor],
|
||||
// [IndexedColor] (256-color), and [color.Color] (24-bit RGB).
|
||||
func (s Style) ForegroundColor(c Color) Style {
|
||||
if c == nil {
|
||||
return append(s, attrDefaultForegroundColor)
|
||||
}
|
||||
return append(s, foregroundColorString(c))
|
||||
}
|
||||
|
||||
// BackgroundColor appends the background color style attribute to the style.
|
||||
// If c is nil, the default background color is used. Supports [BasicColor],
|
||||
// [IndexedColor] (256-color), and [color.Color] (24-bit RGB).
|
||||
func (s Style) BackgroundColor(c Color) Style {
|
||||
if c == nil {
|
||||
return append(s, attrDefaultBackgroundColor)
|
||||
}
|
||||
return append(s, backgroundColorString(c))
|
||||
}
|
||||
|
||||
// UnderlineColor appends the underline color style attribute to the style.
|
||||
// If c is nil, the default underline color is used. Supports [BasicColor],
|
||||
// [IndexedColor] (256-color), and [color.Color] (24-bit RGB).
|
||||
func (s Style) UnderlineColor(c Color) Style {
|
||||
if c == nil {
|
||||
return append(s, attrDefaultUnderlineColor)
|
||||
}
|
||||
return append(s, underlineColorString(c))
|
||||
}
|
||||
|
||||
// Underline represents an ANSI SGR (Select Graphic Rendition) underline style.
|
||||
type Underline = byte
|
||||
|
||||
// UnderlineStyle represents an ANSI SGR (Select Graphic Rendition) underline
|
||||
// style.
|
||||
//
|
||||
// Deprecated: use [Underline] instead.
|
||||
type UnderlineStyle = byte
|
||||
|
||||
const (
|
||||
doubleUnderlineStyle = "4:2"
|
||||
curlyUnderlineStyle = "4:3"
|
||||
dottedUnderlineStyle = "4:4"
|
||||
dashedUnderlineStyle = "4:5"
|
||||
underlineDouble = "4:2"
|
||||
underlineCurly = "4:3"
|
||||
underlineDotted = "4:4"
|
||||
underlineDashed = "4:5"
|
||||
)
|
||||
|
||||
// Underline styles constants.
|
||||
const (
|
||||
// NoUnderlineStyle is the default underline style.
|
||||
NoUnderlineStyle UnderlineStyle = iota
|
||||
// SingleUnderlineStyle is a single underline style.
|
||||
UnderlineNone Underline = iota
|
||||
UnderlineSingle
|
||||
UnderlineDouble
|
||||
UnderlineCurly
|
||||
UnderlineDotted
|
||||
UnderlineDashed
|
||||
)
|
||||
|
||||
// Underline styles constants.
|
||||
//
|
||||
// Deprecated: use [UnderlineNone], [UnderlineSingle], etc. instead.
|
||||
const (
|
||||
NoUnderlineStyle Underline = iota
|
||||
SingleUnderlineStyle
|
||||
// DoubleUnderlineStyle is a double underline style.
|
||||
DoubleUnderlineStyle
|
||||
// CurlyUnderlineStyle is a curly underline style.
|
||||
CurlyUnderlineStyle
|
||||
// DottedUnderlineStyle is a dotted underline style.
|
||||
DottedUnderlineStyle
|
||||
// DashedUnderlineStyle is a dashed underline style.
|
||||
DashedUnderlineStyle
|
||||
)
|
||||
|
||||
// Underline styles constants.
|
||||
//
|
||||
// Deprecated: use [UnderlineNone], [UnderlineSingle], etc. instead.
|
||||
const (
|
||||
UnderlineStyleNone Underline = iota
|
||||
UnderlineStyleSingle
|
||||
UnderlineStyleDouble
|
||||
UnderlineStyleCurly
|
||||
UnderlineStyleDotted
|
||||
UnderlineStyleDashed
|
||||
)
|
||||
|
||||
// SGR (Select Graphic Rendition) style attributes.
|
||||
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
|
||||
const (
|
||||
ResetAttr Attr = 0
|
||||
BoldAttr Attr = 1
|
||||
FaintAttr Attr = 2
|
||||
ItalicAttr Attr = 3
|
||||
UnderlineAttr Attr = 4
|
||||
SlowBlinkAttr Attr = 5
|
||||
RapidBlinkAttr Attr = 6
|
||||
ReverseAttr Attr = 7
|
||||
ConcealAttr Attr = 8
|
||||
StrikethroughAttr Attr = 9
|
||||
NormalIntensityAttr Attr = 22
|
||||
NoItalicAttr Attr = 23
|
||||
NoUnderlineAttr Attr = 24
|
||||
NoBlinkAttr Attr = 25
|
||||
NoReverseAttr Attr = 27
|
||||
NoConcealAttr Attr = 28
|
||||
NoStrikethroughAttr Attr = 29
|
||||
BlackForegroundColorAttr Attr = 30
|
||||
RedForegroundColorAttr Attr = 31
|
||||
GreenForegroundColorAttr Attr = 32
|
||||
YellowForegroundColorAttr Attr = 33
|
||||
BlueForegroundColorAttr Attr = 34
|
||||
MagentaForegroundColorAttr Attr = 35
|
||||
CyanForegroundColorAttr Attr = 36
|
||||
WhiteForegroundColorAttr Attr = 37
|
||||
ExtendedForegroundColorAttr Attr = 38
|
||||
DefaultForegroundColorAttr Attr = 39
|
||||
BlackBackgroundColorAttr Attr = 40
|
||||
RedBackgroundColorAttr Attr = 41
|
||||
GreenBackgroundColorAttr Attr = 42
|
||||
YellowBackgroundColorAttr Attr = 43
|
||||
BlueBackgroundColorAttr Attr = 44
|
||||
MagentaBackgroundColorAttr Attr = 45
|
||||
CyanBackgroundColorAttr Attr = 46
|
||||
WhiteBackgroundColorAttr Attr = 47
|
||||
ExtendedBackgroundColorAttr Attr = 48
|
||||
DefaultBackgroundColorAttr Attr = 49
|
||||
ExtendedUnderlineColorAttr Attr = 58
|
||||
DefaultUnderlineColorAttr Attr = 59
|
||||
BrightBlackForegroundColorAttr Attr = 90
|
||||
BrightRedForegroundColorAttr Attr = 91
|
||||
BrightGreenForegroundColorAttr Attr = 92
|
||||
BrightYellowForegroundColorAttr Attr = 93
|
||||
BrightBlueForegroundColorAttr Attr = 94
|
||||
BrightMagentaForegroundColorAttr Attr = 95
|
||||
BrightCyanForegroundColorAttr Attr = 96
|
||||
BrightWhiteForegroundColorAttr Attr = 97
|
||||
BrightBlackBackgroundColorAttr Attr = 100
|
||||
BrightRedBackgroundColorAttr Attr = 101
|
||||
BrightGreenBackgroundColorAttr Attr = 102
|
||||
BrightYellowBackgroundColorAttr Attr = 103
|
||||
BrightBlueBackgroundColorAttr Attr = 104
|
||||
BrightMagentaBackgroundColorAttr Attr = 105
|
||||
BrightCyanBackgroundColorAttr Attr = 106
|
||||
BrightWhiteBackgroundColorAttr Attr = 107
|
||||
AttrReset Attr = 0
|
||||
AttrBold Attr = 1
|
||||
AttrFaint Attr = 2
|
||||
AttrItalic Attr = 3
|
||||
AttrUnderline Attr = 4
|
||||
AttrBlink Attr = 5
|
||||
AttrRapidBlink Attr = 6
|
||||
AttrReverse Attr = 7
|
||||
AttrConceal Attr = 8
|
||||
AttrStrikethrough Attr = 9
|
||||
AttrNormalIntensity Attr = 22
|
||||
AttrNoItalic Attr = 23
|
||||
AttrNoUnderline Attr = 24
|
||||
AttrNoBlink Attr = 25
|
||||
AttrNoReverse Attr = 27
|
||||
AttrNoConceal Attr = 28
|
||||
AttrNoStrikethrough Attr = 29
|
||||
AttrBlackForegroundColor Attr = 30
|
||||
AttrRedForegroundColor Attr = 31
|
||||
AttrGreenForegroundColor Attr = 32
|
||||
AttrYellowForegroundColor Attr = 33
|
||||
AttrBlueForegroundColor Attr = 34
|
||||
AttrMagentaForegroundColor Attr = 35
|
||||
AttrCyanForegroundColor Attr = 36
|
||||
AttrWhiteForegroundColor Attr = 37
|
||||
AttrExtendedForegroundColor Attr = 38
|
||||
AttrDefaultForegroundColor Attr = 39
|
||||
AttrBlackBackgroundColor Attr = 40
|
||||
AttrRedBackgroundColor Attr = 41
|
||||
AttrGreenBackgroundColor Attr = 42
|
||||
AttrYellowBackgroundColor Attr = 43
|
||||
AttrBlueBackgroundColor Attr = 44
|
||||
AttrMagentaBackgroundColor Attr = 45
|
||||
AttrCyanBackgroundColor Attr = 46
|
||||
AttrWhiteBackgroundColor Attr = 47
|
||||
AttrExtendedBackgroundColor Attr = 48
|
||||
AttrDefaultBackgroundColor Attr = 49
|
||||
AttrExtendedUnderlineColor Attr = 58
|
||||
AttrDefaultUnderlineColor Attr = 59
|
||||
AttrBrightBlackForegroundColor Attr = 90
|
||||
AttrBrightRedForegroundColor Attr = 91
|
||||
AttrBrightGreenForegroundColor Attr = 92
|
||||
AttrBrightYellowForegroundColor Attr = 93
|
||||
AttrBrightBlueForegroundColor Attr = 94
|
||||
AttrBrightMagentaForegroundColor Attr = 95
|
||||
AttrBrightCyanForegroundColor Attr = 96
|
||||
AttrBrightWhiteForegroundColor Attr = 97
|
||||
AttrBrightBlackBackgroundColor Attr = 100
|
||||
AttrBrightRedBackgroundColor Attr = 101
|
||||
AttrBrightGreenBackgroundColor Attr = 102
|
||||
AttrBrightYellowBackgroundColor Attr = 103
|
||||
AttrBrightBlueBackgroundColor Attr = 104
|
||||
AttrBrightMagentaBackgroundColor Attr = 105
|
||||
AttrBrightCyanBackgroundColor Attr = 106
|
||||
AttrBrightWhiteBackgroundColor Attr = 107
|
||||
|
||||
RGBColorIntroducerAttr Attr = 2
|
||||
ExtendedColorIntroducerAttr Attr = 5
|
||||
AttrRGBColorIntroducer Attr = 2
|
||||
AttrExtendedColorIntroducer Attr = 5
|
||||
)
|
||||
|
||||
// SGR (Select Graphic Rendition) style attributes.
|
||||
//
|
||||
// Deprecated: use Attr* constants instead.
|
||||
const (
|
||||
ResetAttr = AttrReset
|
||||
BoldAttr = AttrBold
|
||||
FaintAttr = AttrFaint
|
||||
ItalicAttr = AttrItalic
|
||||
UnderlineAttr = AttrUnderline
|
||||
SlowBlinkAttr = AttrBlink
|
||||
RapidBlinkAttr = AttrRapidBlink
|
||||
ReverseAttr = AttrReverse
|
||||
ConcealAttr = AttrConceal
|
||||
StrikethroughAttr = AttrStrikethrough
|
||||
NormalIntensityAttr = AttrNormalIntensity
|
||||
NoItalicAttr = AttrNoItalic
|
||||
NoUnderlineAttr = AttrNoUnderline
|
||||
NoBlinkAttr = AttrNoBlink
|
||||
NoReverseAttr = AttrNoReverse
|
||||
NoConcealAttr = AttrNoConceal
|
||||
NoStrikethroughAttr = AttrNoStrikethrough
|
||||
BlackForegroundColorAttr = AttrBlackForegroundColor
|
||||
RedForegroundColorAttr = AttrRedForegroundColor
|
||||
GreenForegroundColorAttr = AttrGreenForegroundColor
|
||||
YellowForegroundColorAttr = AttrYellowForegroundColor
|
||||
BlueForegroundColorAttr = AttrBlueForegroundColor
|
||||
MagentaForegroundColorAttr = AttrMagentaForegroundColor
|
||||
CyanForegroundColorAttr = AttrCyanForegroundColor
|
||||
WhiteForegroundColorAttr = AttrWhiteForegroundColor
|
||||
ExtendedForegroundColorAttr = AttrExtendedForegroundColor
|
||||
DefaultForegroundColorAttr = AttrDefaultForegroundColor
|
||||
BlackBackgroundColorAttr = AttrBlackBackgroundColor
|
||||
RedBackgroundColorAttr = AttrRedBackgroundColor
|
||||
GreenBackgroundColorAttr = AttrGreenBackgroundColor
|
||||
YellowBackgroundColorAttr = AttrYellowBackgroundColor
|
||||
BlueBackgroundColorAttr = AttrBlueBackgroundColor
|
||||
MagentaBackgroundColorAttr = AttrMagentaBackgroundColor
|
||||
CyanBackgroundColorAttr = AttrCyanBackgroundColor
|
||||
WhiteBackgroundColorAttr = AttrWhiteBackgroundColor
|
||||
ExtendedBackgroundColorAttr = AttrExtendedBackgroundColor
|
||||
DefaultBackgroundColorAttr = AttrDefaultBackgroundColor
|
||||
ExtendedUnderlineColorAttr = AttrExtendedUnderlineColor
|
||||
DefaultUnderlineColorAttr = AttrDefaultUnderlineColor
|
||||
BrightBlackForegroundColorAttr = AttrBrightBlackForegroundColor
|
||||
BrightRedForegroundColorAttr = AttrBrightRedForegroundColor
|
||||
BrightGreenForegroundColorAttr = AttrBrightGreenForegroundColor
|
||||
BrightYellowForegroundColorAttr = AttrBrightYellowForegroundColor
|
||||
BrightBlueForegroundColorAttr = AttrBrightBlueForegroundColor
|
||||
BrightMagentaForegroundColorAttr = AttrBrightMagentaForegroundColor
|
||||
BrightCyanForegroundColorAttr = AttrBrightCyanForegroundColor
|
||||
BrightWhiteForegroundColorAttr = AttrBrightWhiteForegroundColor
|
||||
BrightBlackBackgroundColorAttr = AttrBrightBlackBackgroundColor
|
||||
BrightRedBackgroundColorAttr = AttrBrightRedBackgroundColor
|
||||
BrightGreenBackgroundColorAttr = AttrBrightGreenBackgroundColor
|
||||
BrightYellowBackgroundColorAttr = AttrBrightYellowBackgroundColor
|
||||
BrightBlueBackgroundColorAttr = AttrBrightBlueBackgroundColor
|
||||
BrightMagentaBackgroundColorAttr = AttrBrightMagentaBackgroundColor
|
||||
BrightCyanBackgroundColorAttr = AttrBrightCyanBackgroundColor
|
||||
BrightWhiteBackgroundColorAttr = AttrBrightWhiteBackgroundColor
|
||||
RGBColorIntroducerAttr = AttrRGBColorIntroducer
|
||||
ExtendedColorIntroducerAttr = AttrExtendedColorIntroducer
|
||||
)
|
||||
|
||||
const (
|
||||
resetAttr = "0"
|
||||
boldAttr = "1"
|
||||
faintAttr = "2"
|
||||
italicAttr = "3"
|
||||
underlineAttr = "4"
|
||||
slowBlinkAttr = "5"
|
||||
rapidBlinkAttr = "6"
|
||||
reverseAttr = "7"
|
||||
concealAttr = "8"
|
||||
strikethroughAttr = "9"
|
||||
normalIntensityAttr = "22"
|
||||
noItalicAttr = "23"
|
||||
noUnderlineAttr = "24"
|
||||
noBlinkAttr = "25"
|
||||
noReverseAttr = "27"
|
||||
noConcealAttr = "28"
|
||||
noStrikethroughAttr = "29"
|
||||
blackForegroundColorAttr = "30"
|
||||
redForegroundColorAttr = "31"
|
||||
greenForegroundColorAttr = "32"
|
||||
yellowForegroundColorAttr = "33"
|
||||
blueForegroundColorAttr = "34"
|
||||
magentaForegroundColorAttr = "35"
|
||||
cyanForegroundColorAttr = "36"
|
||||
whiteForegroundColorAttr = "37"
|
||||
extendedForegroundColorAttr = "38"
|
||||
defaultForegroundColorAttr = "39"
|
||||
blackBackgroundColorAttr = "40"
|
||||
redBackgroundColorAttr = "41"
|
||||
greenBackgroundColorAttr = "42"
|
||||
yellowBackgroundColorAttr = "43"
|
||||
blueBackgroundColorAttr = "44"
|
||||
magentaBackgroundColorAttr = "45"
|
||||
cyanBackgroundColorAttr = "46"
|
||||
whiteBackgroundColorAttr = "47"
|
||||
extendedBackgroundColorAttr = "48"
|
||||
defaultBackgroundColorAttr = "49"
|
||||
extendedUnderlineColorAttr = "58"
|
||||
defaultUnderlineColorAttr = "59"
|
||||
brightBlackForegroundColorAttr = "90"
|
||||
brightRedForegroundColorAttr = "91"
|
||||
brightGreenForegroundColorAttr = "92"
|
||||
brightYellowForegroundColorAttr = "93"
|
||||
brightBlueForegroundColorAttr = "94"
|
||||
brightMagentaForegroundColorAttr = "95"
|
||||
brightCyanForegroundColorAttr = "96"
|
||||
brightWhiteForegroundColorAttr = "97"
|
||||
brightBlackBackgroundColorAttr = "100"
|
||||
brightRedBackgroundColorAttr = "101"
|
||||
brightGreenBackgroundColorAttr = "102"
|
||||
brightYellowBackgroundColorAttr = "103"
|
||||
brightBlueBackgroundColorAttr = "104"
|
||||
brightMagentaBackgroundColorAttr = "105"
|
||||
brightCyanBackgroundColorAttr = "106"
|
||||
brightWhiteBackgroundColorAttr = "107"
|
||||
attrReset = "0"
|
||||
attrBold = "1"
|
||||
attrFaint = "2"
|
||||
attrItalic = "3"
|
||||
attrUnderline = "4"
|
||||
attrBlink = "5"
|
||||
attrRapidBlink = "6"
|
||||
attrReverse = "7"
|
||||
attrConceal = "8"
|
||||
attrStrikethrough = "9"
|
||||
attrNormalIntensity = "22"
|
||||
attrNoItalic = "23"
|
||||
attrNoUnderline = "24"
|
||||
attrNoBlink = "25"
|
||||
attrNoReverse = "27"
|
||||
attrNoConceal = "28"
|
||||
attrNoStrikethrough = "29"
|
||||
attrBlackForegroundColor = "30"
|
||||
attrRedForegroundColor = "31"
|
||||
attrGreenForegroundColor = "32"
|
||||
attrYellowForegroundColor = "33"
|
||||
attrBlueForegroundColor = "34"
|
||||
attrMagentaForegroundColor = "35"
|
||||
attrCyanForegroundColor = "36"
|
||||
attrWhiteForegroundColor = "37"
|
||||
attrExtendedForegroundColor = "38"
|
||||
attrDefaultForegroundColor = "39"
|
||||
attrBlackBackgroundColor = "40"
|
||||
attrRedBackgroundColor = "41"
|
||||
attrGreenBackgroundColor = "42"
|
||||
attrYellowBackgroundColor = "43"
|
||||
attrBlueBackgroundColor = "44"
|
||||
attrMagentaBackgroundColor = "45"
|
||||
attrCyanBackgroundColor = "46"
|
||||
attrWhiteBackgroundColor = "47"
|
||||
attrExtendedBackgroundColor = "48"
|
||||
attrDefaultBackgroundColor = "49"
|
||||
attrExtendedUnderlineColor = "58"
|
||||
attrDefaultUnderlineColor = "59"
|
||||
attrBrightBlackForegroundColor = "90"
|
||||
attrBrightRedForegroundColor = "91"
|
||||
attrBrightGreenForegroundColor = "92"
|
||||
attrBrightYellowForegroundColor = "93"
|
||||
attrBrightBlueForegroundColor = "94"
|
||||
attrBrightMagentaForegroundColor = "95"
|
||||
attrBrightCyanForegroundColor = "96"
|
||||
attrBrightWhiteForegroundColor = "97"
|
||||
attrBrightBlackBackgroundColor = "100"
|
||||
attrBrightRedBackgroundColor = "101"
|
||||
attrBrightGreenBackgroundColor = "102"
|
||||
attrBrightYellowBackgroundColor = "103"
|
||||
attrBrightBlueBackgroundColor = "104"
|
||||
attrBrightMagentaBackgroundColor = "105"
|
||||
attrBrightCyanBackgroundColor = "106"
|
||||
attrBrightWhiteBackgroundColor = "107"
|
||||
)
|
||||
|
||||
// foregroundColorString returns the style SGR attribute for the given
|
||||
@@ -364,42 +504,44 @@ const (
|
||||
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
|
||||
func foregroundColorString(c Color) string {
|
||||
switch c := c.(type) {
|
||||
case nil:
|
||||
return attrDefaultForegroundColor
|
||||
case BasicColor:
|
||||
// 3-bit or 4-bit ANSI foreground
|
||||
// "3<n>" or "9<n>" where n is the color number from 0 to 7
|
||||
switch c {
|
||||
case Black:
|
||||
return blackForegroundColorAttr
|
||||
return attrBlackForegroundColor
|
||||
case Red:
|
||||
return redForegroundColorAttr
|
||||
return attrRedForegroundColor
|
||||
case Green:
|
||||
return greenForegroundColorAttr
|
||||
return attrGreenForegroundColor
|
||||
case Yellow:
|
||||
return yellowForegroundColorAttr
|
||||
return attrYellowForegroundColor
|
||||
case Blue:
|
||||
return blueForegroundColorAttr
|
||||
return attrBlueForegroundColor
|
||||
case Magenta:
|
||||
return magentaForegroundColorAttr
|
||||
return attrMagentaForegroundColor
|
||||
case Cyan:
|
||||
return cyanForegroundColorAttr
|
||||
return attrCyanForegroundColor
|
||||
case White:
|
||||
return whiteForegroundColorAttr
|
||||
return attrWhiteForegroundColor
|
||||
case BrightBlack:
|
||||
return brightBlackForegroundColorAttr
|
||||
return attrBrightBlackForegroundColor
|
||||
case BrightRed:
|
||||
return brightRedForegroundColorAttr
|
||||
return attrBrightRedForegroundColor
|
||||
case BrightGreen:
|
||||
return brightGreenForegroundColorAttr
|
||||
return attrBrightGreenForegroundColor
|
||||
case BrightYellow:
|
||||
return brightYellowForegroundColorAttr
|
||||
return attrBrightYellowForegroundColor
|
||||
case BrightBlue:
|
||||
return brightBlueForegroundColorAttr
|
||||
return attrBrightBlueForegroundColor
|
||||
case BrightMagenta:
|
||||
return brightMagentaForegroundColorAttr
|
||||
return attrBrightMagentaForegroundColor
|
||||
case BrightCyan:
|
||||
return brightCyanForegroundColorAttr
|
||||
return attrBrightCyanForegroundColor
|
||||
case BrightWhite:
|
||||
return brightWhiteForegroundColorAttr
|
||||
return attrBrightWhiteForegroundColor
|
||||
}
|
||||
case ExtendedColor:
|
||||
// 256-color ANSI foreground
|
||||
@@ -414,7 +556,7 @@ func foregroundColorString(c Color) string {
|
||||
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
|
||||
strconv.FormatUint(uint64(shift(b)), 10)
|
||||
}
|
||||
return defaultForegroundColorAttr
|
||||
return attrDefaultForegroundColor
|
||||
}
|
||||
|
||||
// backgroundColorString returns the style SGR attribute for the given
|
||||
@@ -422,42 +564,44 @@ func foregroundColorString(c Color) string {
|
||||
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
|
||||
func backgroundColorString(c Color) string {
|
||||
switch c := c.(type) {
|
||||
case nil:
|
||||
return attrDefaultBackgroundColor
|
||||
case BasicColor:
|
||||
// 3-bit or 4-bit ANSI foreground
|
||||
// "4<n>" or "10<n>" where n is the color number from 0 to 7
|
||||
switch c {
|
||||
case Black:
|
||||
return blackBackgroundColorAttr
|
||||
return attrBlackBackgroundColor
|
||||
case Red:
|
||||
return redBackgroundColorAttr
|
||||
return attrRedBackgroundColor
|
||||
case Green:
|
||||
return greenBackgroundColorAttr
|
||||
return attrGreenBackgroundColor
|
||||
case Yellow:
|
||||
return yellowBackgroundColorAttr
|
||||
return attrYellowBackgroundColor
|
||||
case Blue:
|
||||
return blueBackgroundColorAttr
|
||||
return attrBlueBackgroundColor
|
||||
case Magenta:
|
||||
return magentaBackgroundColorAttr
|
||||
return attrMagentaBackgroundColor
|
||||
case Cyan:
|
||||
return cyanBackgroundColorAttr
|
||||
return attrCyanBackgroundColor
|
||||
case White:
|
||||
return whiteBackgroundColorAttr
|
||||
return attrWhiteBackgroundColor
|
||||
case BrightBlack:
|
||||
return brightBlackBackgroundColorAttr
|
||||
return attrBrightBlackBackgroundColor
|
||||
case BrightRed:
|
||||
return brightRedBackgroundColorAttr
|
||||
return attrBrightRedBackgroundColor
|
||||
case BrightGreen:
|
||||
return brightGreenBackgroundColorAttr
|
||||
return attrBrightGreenBackgroundColor
|
||||
case BrightYellow:
|
||||
return brightYellowBackgroundColorAttr
|
||||
return attrBrightYellowBackgroundColor
|
||||
case BrightBlue:
|
||||
return brightBlueBackgroundColorAttr
|
||||
return attrBrightBlueBackgroundColor
|
||||
case BrightMagenta:
|
||||
return brightMagentaBackgroundColorAttr
|
||||
return attrBrightMagentaBackgroundColor
|
||||
case BrightCyan:
|
||||
return brightCyanBackgroundColorAttr
|
||||
return attrBrightCyanBackgroundColor
|
||||
case BrightWhite:
|
||||
return brightWhiteBackgroundColorAttr
|
||||
return attrBrightWhiteBackgroundColor
|
||||
}
|
||||
case ExtendedColor:
|
||||
// 256-color ANSI foreground
|
||||
@@ -472,7 +616,7 @@ func backgroundColorString(c Color) string {
|
||||
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
|
||||
strconv.FormatUint(uint64(shift(b)), 10)
|
||||
}
|
||||
return defaultBackgroundColorAttr
|
||||
return attrDefaultBackgroundColor
|
||||
}
|
||||
|
||||
// underlineColorString returns the style SGR attribute for the given underline
|
||||
@@ -480,6 +624,8 @@ func backgroundColorString(c Color) string {
|
||||
// See: https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_(Select_Graphic_Rendition)_parameters
|
||||
func underlineColorString(c Color) string {
|
||||
switch c := c.(type) {
|
||||
case nil:
|
||||
return attrDefaultUnderlineColor
|
||||
// NOTE: we can't use 3-bit and 4-bit ANSI color codes with underline
|
||||
// color, use 256-color instead.
|
||||
//
|
||||
@@ -498,7 +644,7 @@ func underlineColorString(c Color) string {
|
||||
strconv.FormatUint(uint64(shift(g)), 10) + ";" +
|
||||
strconv.FormatUint(uint64(shift(b)), 10)
|
||||
}
|
||||
return defaultUnderlineColorAttr
|
||||
return attrDefaultUnderlineColor
|
||||
}
|
||||
|
||||
// ReadStyleColor decodes a color from a slice of parameters. It returns the
|
||||
@@ -526,7 +672,7 @@ func underlineColorString(c Color) string {
|
||||
// 2. Support ignoring and omitting the color space id (second parameter) with respect to RGB colors
|
||||
// 3. Support ignoring and omitting the 6th parameter with respect to RGB and CMY colors
|
||||
// 4. Support reading RGBA colors
|
||||
func ReadStyleColor(params Params, co *color.Color) (n int) {
|
||||
func ReadStyleColor(params Params, co *color.Color) int {
|
||||
if len(params) < 2 { // Need at least SGR type and color type
|
||||
return 0
|
||||
}
|
||||
@@ -535,7 +681,7 @@ func ReadStyleColor(params Params, co *color.Color) (n int) {
|
||||
s := params[0]
|
||||
p := params[1]
|
||||
colorType := p.Param(0)
|
||||
n = 2
|
||||
n := 2
|
||||
|
||||
paramsfn := func() (p1, p2, p3, p4 int) {
|
||||
// Where should we start reading the color?
|
||||
@@ -594,7 +740,7 @@ func ReadStyleColor(params Params, co *color.Color) (n int) {
|
||||
B: uint8(b), //nolint:gosec
|
||||
A: 0xff,
|
||||
}
|
||||
return //nolint:nakedret
|
||||
return n
|
||||
|
||||
case 3: // CMY direct color
|
||||
if len(params) < 5 {
|
||||
@@ -612,7 +758,7 @@ func ReadStyleColor(params Params, co *color.Color) (n int) {
|
||||
Y: uint8(y), //nolint:gosec
|
||||
K: 0,
|
||||
}
|
||||
return //nolint:nakedret
|
||||
return n
|
||||
|
||||
case 4: // CMYK direct color
|
||||
if len(params) < 6 {
|
||||
@@ -630,7 +776,7 @@ func ReadStyleColor(params Params, co *color.Color) (n int) {
|
||||
Y: uint8(y), //nolint:gosec
|
||||
K: uint8(k), //nolint:gosec
|
||||
}
|
||||
return //nolint:nakedret
|
||||
return n
|
||||
|
||||
case 5: // indexed color
|
||||
if len(params) < 3 {
|
||||
@@ -665,7 +811,7 @@ func ReadStyleColor(params Params, co *color.Color) (n int) {
|
||||
B: uint8(b), //nolint:gosec
|
||||
A: uint8(a), //nolint:gosec
|
||||
}
|
||||
return //nolint:nakedret
|
||||
return n
|
||||
|
||||
default:
|
||||
return 0
|
||||
|
||||
+24
-33
@@ -1,11 +1,11 @@
|
||||
package ansi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
"github.com/clipperhouse/displaywidth"
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
)
|
||||
|
||||
// Cut the string, without adding any prefix or tail strings. This function is
|
||||
@@ -74,12 +74,11 @@ func truncate(m Method, s string, length int, tail string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
var cluster []byte
|
||||
var buf bytes.Buffer
|
||||
var cluster string
|
||||
var buf strings.Builder
|
||||
curWidth := 0
|
||||
ignoring := false
|
||||
pstate := parser.GroundState // initial state
|
||||
b := []byte(s)
|
||||
i := 0
|
||||
|
||||
// Here we iterate over the bytes of the string and collect printable
|
||||
@@ -88,16 +87,12 @@ func truncate(m Method, s string, length int, tail string) string {
|
||||
//
|
||||
// Once we reach the given length, we start ignoring characters and only
|
||||
// collect ANSI escape codes until we reach the end of string.
|
||||
for i < len(b) {
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
for i < len(s) {
|
||||
state, action := parser.Table.Transition(pstate, s[i])
|
||||
if state == parser.Utf8State {
|
||||
// This action happens when we transition to the Utf8State.
|
||||
var width int
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
|
||||
cluster, width = FirstGraphemeCluster(s[i:], m)
|
||||
// increment the index by the length of the cluster
|
||||
i += len(cluster)
|
||||
curWidth += width
|
||||
@@ -118,7 +113,7 @@ func truncate(m Method, s string, length int, tail string) string {
|
||||
continue
|
||||
}
|
||||
|
||||
buf.Write(cluster)
|
||||
buf.WriteString(cluster)
|
||||
|
||||
// Done collecting, now we're back in the ground state.
|
||||
pstate = parser.GroundState
|
||||
@@ -152,7 +147,7 @@ func truncate(m Method, s string, length int, tail string) string {
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
buf.WriteByte(b[i])
|
||||
buf.WriteByte(s[i])
|
||||
i++
|
||||
}
|
||||
|
||||
@@ -193,27 +188,23 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
var cluster []byte
|
||||
var buf bytes.Buffer
|
||||
var cluster string
|
||||
var buf strings.Builder
|
||||
curWidth := 0
|
||||
ignoring := true
|
||||
pstate := parser.GroundState
|
||||
b := []byte(s)
|
||||
i := 0
|
||||
|
||||
for i < len(b) {
|
||||
for i < len(s) {
|
||||
if !ignoring {
|
||||
buf.Write(b[i:])
|
||||
buf.WriteString(s[i:])
|
||||
break
|
||||
}
|
||||
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
state, action := parser.Table.Transition(pstate, s[i])
|
||||
if state == parser.Utf8State {
|
||||
var width int
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
cluster, width = FirstGraphemeCluster(s[i:], m)
|
||||
|
||||
i += len(cluster)
|
||||
curWidth += width
|
||||
@@ -224,7 +215,7 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
|
||||
}
|
||||
|
||||
if curWidth > n {
|
||||
buf.Write(cluster)
|
||||
buf.WriteString(cluster)
|
||||
}
|
||||
|
||||
if ignoring {
|
||||
@@ -259,7 +250,7 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
|
||||
}
|
||||
fallthrough
|
||||
default:
|
||||
buf.WriteByte(b[i])
|
||||
buf.WriteByte(s[i])
|
||||
i++
|
||||
}
|
||||
|
||||
@@ -278,22 +269,22 @@ func truncateLeft(m Method, s string, n int, prefix string) string {
|
||||
// You can use this with [Truncate], [TruncateLeft], and [Cut].
|
||||
func ByteToGraphemeRange(str string, byteStart, byteStop int) (charStart, charStop int) {
|
||||
bytePos, charPos := 0, 0
|
||||
gr := uniseg.NewGraphemes(str)
|
||||
gr := graphemes.FromString(str)
|
||||
for byteStart > bytePos {
|
||||
if !gr.Next() {
|
||||
break
|
||||
}
|
||||
bytePos += len(gr.Str())
|
||||
charPos += max(1, gr.Width())
|
||||
bytePos += len(gr.Value())
|
||||
charPos += max(1, displaywidth.String(gr.Value()))
|
||||
}
|
||||
charStart = charPos
|
||||
for byteStop > bytePos {
|
||||
if !gr.Next() {
|
||||
break
|
||||
}
|
||||
bytePos += len(gr.Str())
|
||||
charPos += max(1, gr.Width())
|
||||
bytePos += len(gr.Value())
|
||||
charPos += max(1, displaywidth.String(gr.Value()))
|
||||
}
|
||||
charStop = charPos
|
||||
return
|
||||
return charStart, charStop
|
||||
}
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package ansi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// URxvtExt returns an escape sequence for calling a URxvt perl extension with
|
||||
// the given name and parameters.
|
||||
//
|
||||
// OSC 777 ; extension_name ; param1 ; param2 ; ... ST
|
||||
// OSC 777 ; extension_name ; param1 ; param2 ; ... BEL
|
||||
//
|
||||
// See: https://man.archlinux.org/man/extra/rxvt-unicode/urxvt.7.en#XTerm_Operating_System_Commands
|
||||
func URxvtExt(extension string, params ...string) string {
|
||||
return fmt.Sprintf("\x1b]777;%s;%s\x07", extension, strings.Join(params, ";"))
|
||||
}
|
||||
+4
-10
@@ -4,8 +4,6 @@ import (
|
||||
"bytes"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// Strip removes ANSI escape codes from a string.
|
||||
@@ -83,20 +81,16 @@ func stringWidth(m Method, s string) int {
|
||||
}
|
||||
|
||||
var (
|
||||
pstate = parser.GroundState // initial state
|
||||
cluster string
|
||||
width int
|
||||
pstate = parser.GroundState // initial state
|
||||
width int
|
||||
)
|
||||
|
||||
for i := 0; i < len(s); i++ {
|
||||
state, action := parser.Table.Transition(pstate, s[i])
|
||||
if state == parser.Utf8State {
|
||||
var w int
|
||||
cluster, _, w, _ = uniseg.FirstGraphemeClusterInString(s[i:], -1)
|
||||
if m == WcWidth {
|
||||
w = runewidth.StringWidth(cluster)
|
||||
}
|
||||
cluster, w := FirstGraphemeCluster(s[i:], m)
|
||||
width += w
|
||||
|
||||
i += len(cluster) - 1
|
||||
pstate = parser.GroundState
|
||||
continue
|
||||
|
||||
+24
-26
@@ -2,12 +2,11 @@ package ansi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"strings"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/charmbracelet/x/ansi/parser"
|
||||
"github.com/mattn/go-runewidth"
|
||||
"github.com/rivo/uniseg"
|
||||
)
|
||||
|
||||
// nbsp is a non-breaking space.
|
||||
@@ -55,12 +54,9 @@ func hardwrap(m Method, s string, limit int, preserveSpace bool) string {
|
||||
i := 0
|
||||
for i < len(b) {
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
if state == parser.Utf8State { //nolint:nestif
|
||||
if state == parser.Utf8State {
|
||||
var width int
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
cluster, width = FirstGraphemeCluster(b[i:], m)
|
||||
i += len(cluster)
|
||||
|
||||
if curWidth+width > limit {
|
||||
@@ -192,10 +188,7 @@ func wordwrap(m Method, s string, limit int, breakpoints string) string {
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
if state == parser.Utf8State { //nolint:nestif
|
||||
var width int
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
cluster, width = FirstGraphemeCluster(b[i:], m)
|
||||
i += len(cluster)
|
||||
|
||||
r, _ := utf8.DecodeRune(cluster)
|
||||
@@ -303,7 +296,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
}
|
||||
|
||||
var (
|
||||
cluster []byte
|
||||
cluster string
|
||||
buf bytes.Buffer
|
||||
word bytes.Buffer
|
||||
space bytes.Buffer
|
||||
@@ -311,10 +304,12 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
curWidth int // written width of the line
|
||||
wordLen int // word buffer len without ANSI escape codes
|
||||
pstate = parser.GroundState // initial state
|
||||
b = []byte(s)
|
||||
)
|
||||
|
||||
addSpace := func() {
|
||||
if spaceWidth == 0 && space.Len() == 0 {
|
||||
return
|
||||
}
|
||||
curWidth += spaceWidth
|
||||
buf.Write(space.Bytes())
|
||||
space.Reset()
|
||||
@@ -341,30 +336,27 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
}
|
||||
|
||||
i := 0
|
||||
for i < len(b) {
|
||||
state, action := parser.Table.Transition(pstate, b[i])
|
||||
for i < len(s) {
|
||||
state, action := parser.Table.Transition(pstate, s[i])
|
||||
if state == parser.Utf8State { //nolint:nestif
|
||||
var width int
|
||||
cluster, _, width, _ = uniseg.FirstGraphemeCluster(b[i:], -1)
|
||||
if m == WcWidth {
|
||||
width = runewidth.StringWidth(string(cluster))
|
||||
}
|
||||
cluster, width = FirstGraphemeCluster(s[i:], m)
|
||||
i += len(cluster)
|
||||
|
||||
r, _ := utf8.DecodeRune(cluster)
|
||||
r, _ := utf8.DecodeRuneInString(cluster)
|
||||
switch {
|
||||
case r != utf8.RuneError && unicode.IsSpace(r) && r != nbsp: // nbsp is a non-breaking space
|
||||
addWord()
|
||||
space.WriteRune(r)
|
||||
spaceWidth += width
|
||||
case bytes.ContainsAny(cluster, breakpoints):
|
||||
case strings.ContainsAny(cluster, breakpoints):
|
||||
addSpace()
|
||||
if curWidth+wordLen+width > limit {
|
||||
word.Write(cluster)
|
||||
word.WriteString(cluster)
|
||||
wordLen += width
|
||||
} else {
|
||||
addWord()
|
||||
buf.Write(cluster)
|
||||
buf.WriteString(cluster)
|
||||
curWidth += width
|
||||
}
|
||||
default:
|
||||
@@ -373,12 +365,17 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
addWord()
|
||||
}
|
||||
|
||||
word.Write(cluster)
|
||||
word.WriteString(cluster)
|
||||
wordLen += width
|
||||
|
||||
if curWidth+wordLen+spaceWidth > limit {
|
||||
addNewline()
|
||||
}
|
||||
|
||||
if wordLen == limit {
|
||||
// Hardwrap the word if it's too long
|
||||
addWord()
|
||||
}
|
||||
}
|
||||
|
||||
pstate = parser.GroundState
|
||||
@@ -387,7 +384,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
|
||||
switch action {
|
||||
case parser.PrintAction, parser.ExecuteAction:
|
||||
switch r := rune(b[i]); {
|
||||
switch r := rune(s[i]); {
|
||||
case r == '\n':
|
||||
if wordLen == 0 {
|
||||
if curWidth+spaceWidth > limit {
|
||||
@@ -424,6 +421,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
if curWidth == limit {
|
||||
addNewline()
|
||||
}
|
||||
|
||||
word.WriteRune(r)
|
||||
wordLen++
|
||||
|
||||
@@ -438,7 +436,7 @@ func wrap(m Method, s string, limit int, breakpoints string) string {
|
||||
}
|
||||
|
||||
default:
|
||||
word.WriteByte(b[i])
|
||||
word.WriteByte(s[i])
|
||||
}
|
||||
|
||||
// We manage the UTF8 state separately manually above.
|
||||
|
||||
+8
-7
@@ -1,3 +1,4 @@
|
||||
// Package cellbuf provides terminal cell buffer functionality.
|
||||
package cellbuf
|
||||
|
||||
import (
|
||||
@@ -24,7 +25,7 @@ func NewCell(r rune, comb ...rune) (c *Cell) {
|
||||
}
|
||||
c.Comb = comb
|
||||
c.Width = runewidth.StringWidth(string(append([]rune{r}, comb...)))
|
||||
return
|
||||
return c
|
||||
}
|
||||
|
||||
// NewCellString returns a new cell with the given string content. This is a
|
||||
@@ -46,7 +47,7 @@ func NewCellString(s string) (c *Cell) {
|
||||
c.Comb = append(c.Comb, r)
|
||||
}
|
||||
}
|
||||
return
|
||||
return c
|
||||
}
|
||||
|
||||
// NewGraphemeCell returns a new cell. This is a convenience function that
|
||||
@@ -71,7 +72,7 @@ func newGraphemeCell(s string, w int) (c *Cell) {
|
||||
c.Comb = append(c.Comb, r)
|
||||
}
|
||||
}
|
||||
return
|
||||
return c
|
||||
}
|
||||
|
||||
// Line represents a line in the terminal.
|
||||
@@ -104,7 +105,7 @@ func (l Line) String() (s string) {
|
||||
}
|
||||
}
|
||||
s = strings.TrimRight(s, " ")
|
||||
return
|
||||
return s
|
||||
}
|
||||
|
||||
// At returns the cell at the given x position.
|
||||
@@ -150,7 +151,7 @@ func (l Line) set(x int, c *Cell, clone bool) bool {
|
||||
for j := 1; j < maxCellWidth && x-j >= 0; j++ {
|
||||
wide := l.At(x - j)
|
||||
if wide != nil && wide.Width > 1 && j < wide.Width {
|
||||
for k := 0; k < wide.Width; k++ {
|
||||
for k := range wide.Width {
|
||||
l[x-j+k] = wide.Clone().Blank()
|
||||
}
|
||||
break
|
||||
@@ -206,7 +207,7 @@ func (b *Buffer) String() (s string) {
|
||||
s += "\r\n"
|
||||
}
|
||||
}
|
||||
return
|
||||
return s
|
||||
}
|
||||
|
||||
// Line returns a pointer to the line at the given y position.
|
||||
@@ -296,7 +297,7 @@ func (b *Buffer) FillRect(c *Cell, rect Rectangle) {
|
||||
}
|
||||
for y := rect.Min.Y; y < rect.Max.Y; y++ {
|
||||
for x := rect.Min.X; x < rect.Max.X; x += cellWidth {
|
||||
b.setCell(x, y, c, false) //nolint:errcheck
|
||||
b.setCell(x, y, c, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+31
-52
@@ -96,7 +96,7 @@ func (c *Cell) Clear() bool {
|
||||
func (c *Cell) Clone() (n *Cell) {
|
||||
n = new(Cell)
|
||||
*n = *c
|
||||
return
|
||||
return n
|
||||
}
|
||||
|
||||
// Blank makes the cell a blank cell by setting the rune to a space, comb to
|
||||
@@ -164,12 +164,12 @@ type UnderlineStyle = ansi.UnderlineStyle
|
||||
|
||||
// These are the available underline styles.
|
||||
const (
|
||||
NoUnderline = ansi.NoUnderlineStyle
|
||||
SingleUnderline = ansi.SingleUnderlineStyle
|
||||
DoubleUnderline = ansi.DoubleUnderlineStyle
|
||||
CurlyUnderline = ansi.CurlyUnderlineStyle
|
||||
DottedUnderline = ansi.DottedUnderlineStyle
|
||||
DashedUnderline = ansi.DashedUnderlineStyle
|
||||
NoUnderline = ansi.UnderlineStyleNone
|
||||
SingleUnderline = ansi.UnderlineStyleSingle
|
||||
DoubleUnderline = ansi.UnderlineStyleDouble
|
||||
CurlyUnderline = ansi.UnderlineStyleCurly
|
||||
DottedUnderline = ansi.UnderlineStyleDotted
|
||||
DashedUnderline = ansi.UnderlineStyleDashed
|
||||
)
|
||||
|
||||
// Style represents the Style of a cell.
|
||||
@@ -189,7 +189,7 @@ func (s Style) Sequence() string {
|
||||
|
||||
var b ansi.Style
|
||||
|
||||
if s.Attrs != 0 {
|
||||
if s.Attrs != 0 { //nolint:nestif
|
||||
if s.Attrs&BoldAttr != 0 {
|
||||
b = b.Bold()
|
||||
}
|
||||
@@ -197,36 +197,31 @@ func (s Style) Sequence() string {
|
||||
b = b.Faint()
|
||||
}
|
||||
if s.Attrs&ItalicAttr != 0 {
|
||||
b = b.Italic()
|
||||
b = b.Italic(true)
|
||||
}
|
||||
if s.Attrs&SlowBlinkAttr != 0 {
|
||||
b = b.SlowBlink()
|
||||
b = b.Blink(true)
|
||||
}
|
||||
if s.Attrs&RapidBlinkAttr != 0 {
|
||||
b = b.RapidBlink()
|
||||
b = b.RapidBlink(true)
|
||||
}
|
||||
if s.Attrs&ReverseAttr != 0 {
|
||||
b = b.Reverse()
|
||||
b = b.Reverse(true)
|
||||
}
|
||||
if s.Attrs&ConcealAttr != 0 {
|
||||
b = b.Conceal()
|
||||
b = b.Conceal(true)
|
||||
}
|
||||
if s.Attrs&StrikethroughAttr != 0 {
|
||||
b = b.Strikethrough()
|
||||
b = b.Strikethrough(true)
|
||||
}
|
||||
}
|
||||
if s.UlStyle != NoUnderline {
|
||||
switch s.UlStyle {
|
||||
case SingleUnderline:
|
||||
b = b.Underline()
|
||||
case DoubleUnderline:
|
||||
b = b.DoubleUnderline()
|
||||
case CurlyUnderline:
|
||||
b = b.CurlyUnderline()
|
||||
case DottedUnderline:
|
||||
b = b.DottedUnderline()
|
||||
case DashedUnderline:
|
||||
b = b.DashedUnderline()
|
||||
switch u := s.UlStyle; u {
|
||||
case NoUnderline:
|
||||
b = b.Underline(false)
|
||||
default:
|
||||
b = b.Underline(true)
|
||||
b = b.UnderlineStyle(u)
|
||||
}
|
||||
}
|
||||
if s.Fg != nil {
|
||||
@@ -268,64 +263,48 @@ func (s Style) DiffSequence(o Style) string {
|
||||
isNormal bool
|
||||
)
|
||||
|
||||
if s.Attrs != o.Attrs {
|
||||
if s.Attrs != o.Attrs { //nolint:nestif
|
||||
if s.Attrs&BoldAttr != o.Attrs&BoldAttr {
|
||||
if s.Attrs&BoldAttr != 0 {
|
||||
b = b.Bold()
|
||||
} else if !isNormal {
|
||||
isNormal = true
|
||||
b = b.NormalIntensity()
|
||||
b = b.Normal()
|
||||
}
|
||||
}
|
||||
if s.Attrs&FaintAttr != o.Attrs&FaintAttr {
|
||||
if s.Attrs&FaintAttr != 0 {
|
||||
b = b.Faint()
|
||||
} else if !isNormal {
|
||||
b = b.NormalIntensity()
|
||||
b = b.Normal()
|
||||
}
|
||||
}
|
||||
if s.Attrs&ItalicAttr != o.Attrs&ItalicAttr {
|
||||
if s.Attrs&ItalicAttr != 0 {
|
||||
b = b.Italic()
|
||||
} else {
|
||||
b = b.NoItalic()
|
||||
}
|
||||
b = b.Italic(s.Attrs&ItalicAttr != 0)
|
||||
}
|
||||
if s.Attrs&SlowBlinkAttr != o.Attrs&SlowBlinkAttr {
|
||||
if s.Attrs&SlowBlinkAttr != 0 {
|
||||
b = b.SlowBlink()
|
||||
b = b.Blink(true)
|
||||
} else if !noBlink {
|
||||
noBlink = true
|
||||
b = b.NoBlink()
|
||||
b = b.Blink(false)
|
||||
}
|
||||
}
|
||||
if s.Attrs&RapidBlinkAttr != o.Attrs&RapidBlinkAttr {
|
||||
if s.Attrs&RapidBlinkAttr != 0 {
|
||||
b = b.RapidBlink()
|
||||
b = b.RapidBlink(true)
|
||||
} else if !noBlink {
|
||||
b = b.NoBlink()
|
||||
b = b.Blink(false)
|
||||
}
|
||||
}
|
||||
if s.Attrs&ReverseAttr != o.Attrs&ReverseAttr {
|
||||
if s.Attrs&ReverseAttr != 0 {
|
||||
b = b.Reverse()
|
||||
} else {
|
||||
b = b.NoReverse()
|
||||
}
|
||||
b = b.Reverse(s.Attrs&ReverseAttr != 0)
|
||||
}
|
||||
if s.Attrs&ConcealAttr != o.Attrs&ConcealAttr {
|
||||
if s.Attrs&ConcealAttr != 0 {
|
||||
b = b.Conceal()
|
||||
} else {
|
||||
b = b.NoConceal()
|
||||
}
|
||||
b = b.Conceal(s.Attrs&ConcealAttr != 0)
|
||||
}
|
||||
if s.Attrs&StrikethroughAttr != o.Attrs&StrikethroughAttr {
|
||||
if s.Attrs&StrikethroughAttr != 0 {
|
||||
b = b.Strikethrough()
|
||||
} else {
|
||||
b = b.NoStrikethrough()
|
||||
}
|
||||
b = b.Strikethrough(s.Attrs&StrikethroughAttr != 0)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ func Pos(x, y int) Position {
|
||||
return image.Pt(x, y)
|
||||
}
|
||||
|
||||
// Rectange represents a rectangle.
|
||||
// Rectangle represents a rectangle.
|
||||
type Rectangle = image.Rectangle
|
||||
|
||||
// Rect is a shorthand for Rectangle.
|
||||
|
||||
+10
-9
@@ -75,7 +75,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
|
||||
)
|
||||
|
||||
blank := s.clearBlank()
|
||||
if n > 0 {
|
||||
if n > 0 { //nolint:nestif
|
||||
// Scroll up (forward)
|
||||
v = s.scrollUp(n, top, bot, 0, maxY, blank)
|
||||
if !v {
|
||||
@@ -99,7 +99,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
|
||||
s.move(0, bot-n+1)
|
||||
s.clearToBottom(nil)
|
||||
} else {
|
||||
for i := 0; i < n; i++ {
|
||||
for i := range n {
|
||||
s.move(0, bot-i)
|
||||
s.clearToEnd(nil, false)
|
||||
}
|
||||
@@ -124,7 +124,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
|
||||
// Clear newly shifted-in lines.
|
||||
if v &&
|
||||
(nonDestScrollRegion || (memoryBelow && top == 0)) {
|
||||
for i := 0; i < -n; i++ {
|
||||
for i := range -n {
|
||||
s.move(0, top+i)
|
||||
s.clearToEnd(nil, false)
|
||||
}
|
||||
@@ -133,7 +133,7 @@ func (s *Screen) scrolln(n, top, bot, maxY int) (v bool) { //nolint:unparam
|
||||
}
|
||||
|
||||
if !v {
|
||||
return
|
||||
return v
|
||||
}
|
||||
|
||||
s.scrollBuffer(s.curbuf, n, top, bot, blank)
|
||||
@@ -193,7 +193,7 @@ func (s *Screen) touchLine(width, height, y, n int, changed bool) {
|
||||
|
||||
// scrollUp scrolls the screen up by n lines.
|
||||
func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
if n == 1 && top == minY && bot == maxY {
|
||||
if n == 1 && top == minY && bot == maxY { //nolint:nestif
|
||||
s.move(0, bot)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteByte('\n')
|
||||
@@ -202,13 +202,14 @@ func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.DeleteLine(1))
|
||||
} else if top == minY && bot == maxY {
|
||||
if s.xtermLike {
|
||||
supportsSU := s.caps.Contains(capSU)
|
||||
if supportsSU {
|
||||
s.move(0, bot)
|
||||
} else {
|
||||
s.move(0, top)
|
||||
}
|
||||
s.updatePen(blank)
|
||||
if s.xtermLike {
|
||||
if supportsSU {
|
||||
s.buf.WriteString(ansi.ScrollUp(n))
|
||||
} else {
|
||||
s.buf.WriteString(strings.Repeat("\n", n))
|
||||
@@ -225,7 +226,7 @@ func (s *Screen) scrollUp(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
|
||||
// scrollDown scrolls the screen down by n lines.
|
||||
func (s *Screen) scrollDown(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
if n == 1 && top == minY && bot == maxY {
|
||||
if n == 1 && top == minY && bot == maxY { //nolint:nestif
|
||||
s.move(0, top)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.ReverseIndex)
|
||||
@@ -236,7 +237,7 @@ func (s *Screen) scrollDown(n, top, bot, minY, maxY int, blank *Cell) bool {
|
||||
} else if top == minY && bot == maxY {
|
||||
s.move(0, top)
|
||||
s.updatePen(blank)
|
||||
if s.xtermLike {
|
||||
if s.caps.Contains(capSD) {
|
||||
s.buf.WriteString(ansi.ScrollDown(n))
|
||||
} else {
|
||||
s.buf.WriteString(strings.Repeat(ansi.ReverseIndex, n))
|
||||
|
||||
+8
-8
@@ -15,7 +15,7 @@ func hash(l Line) (h uint64) {
|
||||
}
|
||||
h += (h << 5) + uint64(r)
|
||||
}
|
||||
return
|
||||
return h
|
||||
}
|
||||
|
||||
// hashmap represents a single [Line] hash.
|
||||
@@ -33,7 +33,7 @@ func (s *Screen) updateHashmap() {
|
||||
height := s.newbuf.Height()
|
||||
if len(s.oldhash) >= height && len(s.newhash) >= height {
|
||||
// rehash changed lines
|
||||
for i := 0; i < height; i++ {
|
||||
for i := range height {
|
||||
_, ok := s.touch[i]
|
||||
if ok {
|
||||
s.oldhash[i] = hash(s.curbuf.Line(i))
|
||||
@@ -48,14 +48,14 @@ func (s *Screen) updateHashmap() {
|
||||
if len(s.newhash) != height {
|
||||
s.newhash = make([]uint64, height)
|
||||
}
|
||||
for i := 0; i < height; i++ {
|
||||
for i := range height {
|
||||
s.oldhash[i] = hash(s.curbuf.Line(i))
|
||||
s.newhash[i] = hash(s.newbuf.Line(i))
|
||||
}
|
||||
}
|
||||
|
||||
s.hashtab = make([]hashmap, height*2)
|
||||
for i := 0; i < height; i++ {
|
||||
for i := range height {
|
||||
hashval := s.oldhash[i]
|
||||
|
||||
// Find matching hash or empty slot
|
||||
@@ -71,7 +71,7 @@ func (s *Screen) updateHashmap() {
|
||||
s.hashtab[idx].oldcount++
|
||||
s.hashtab[idx].oldindex = i
|
||||
}
|
||||
for i := 0; i < height; i++ {
|
||||
for i := range height {
|
||||
hashval := s.newhash[i]
|
||||
|
||||
// Find matching hash or empty slot
|
||||
@@ -130,7 +130,7 @@ func (s *Screen) updateHashmap() {
|
||||
s.growHunks()
|
||||
}
|
||||
|
||||
// scrollOldhash
|
||||
// scrollOldhash.
|
||||
func (s *Screen) scrollOldhash(n, top, bot int) {
|
||||
if len(s.oldhash) == 0 {
|
||||
return
|
||||
@@ -287,7 +287,7 @@ func (s *Screen) updateCost(from, to Line) (cost int) {
|
||||
cost++
|
||||
}
|
||||
}
|
||||
return
|
||||
return cost
|
||||
}
|
||||
|
||||
func (s *Screen) updateCostBlank(to Line) (cost int) {
|
||||
@@ -297,5 +297,5 @@ func (s *Screen) updateCostBlank(to Line) (cost int) {
|
||||
cost++
|
||||
}
|
||||
}
|
||||
return
|
||||
return cost
|
||||
}
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import (
|
||||
"github.com/charmbracelet/colorprofile"
|
||||
)
|
||||
|
||||
// Convert converts a hyperlink to respect the given color profile.
|
||||
// ConvertLink converts a hyperlink to respect the given color profile.
|
||||
func ConvertLink(h Link, p colorprofile.Profile) Link {
|
||||
if p == colorprofile.NoTTY {
|
||||
return Link{}
|
||||
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
package cellbuf
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
)
|
||||
|
||||
// PenWriter is a writer that writes to a buffer and keeps track of the current
|
||||
// pen style and link state for the purpose of wrapping with newlines.
|
||||
type PenWriter struct {
|
||||
w io.Writer
|
||||
p *ansi.Parser
|
||||
style Style
|
||||
link Link
|
||||
}
|
||||
|
||||
// NewPenWriter returns a new PenWriter.
|
||||
func NewPenWriter(w io.Writer) *PenWriter {
|
||||
pw := &PenWriter{w: w}
|
||||
pw.p = ansi.GetParser()
|
||||
handleCsi := func(cmd ansi.Cmd, params ansi.Params) {
|
||||
if cmd == 'm' {
|
||||
ReadStyle(params, &pw.style)
|
||||
}
|
||||
}
|
||||
handleOsc := func(cmd int, data []byte) {
|
||||
if cmd == 8 {
|
||||
ReadLink(data, &pw.link)
|
||||
}
|
||||
}
|
||||
pw.p.SetHandler(ansi.Handler{
|
||||
HandleCsi: handleCsi,
|
||||
HandleOsc: handleOsc,
|
||||
})
|
||||
return pw
|
||||
}
|
||||
|
||||
// Style returns the current pen style.
|
||||
func (w *PenWriter) Style() Style {
|
||||
return w.style
|
||||
}
|
||||
|
||||
// Link returns the current pen link.
|
||||
func (w *PenWriter) Link() Link {
|
||||
return w.link
|
||||
}
|
||||
|
||||
// Write writes to the buffer.
|
||||
func (w *PenWriter) Write(p []byte) (int, error) {
|
||||
for i := range p {
|
||||
b := p[i]
|
||||
w.p.Advance(b)
|
||||
if b == '\n' {
|
||||
if !w.style.Empty() {
|
||||
_, _ = w.w.Write([]byte(ansi.ResetStyle))
|
||||
}
|
||||
if !w.link.Empty() {
|
||||
_, _ = w.w.Write([]byte(ansi.ResetHyperlink()))
|
||||
}
|
||||
}
|
||||
|
||||
_, _ = w.w.Write([]byte{b})
|
||||
if b == '\n' {
|
||||
if !w.link.Empty() {
|
||||
_, _ = w.w.Write([]byte(ansi.SetHyperlink(w.link.URL, w.link.Params)))
|
||||
}
|
||||
if !w.style.Empty() {
|
||||
_, _ = w.w.Write([]byte(w.style.Sequence()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// Close closes the writer, resets the style and link if necessary, and releases
|
||||
// its parser. Calling it is performance critical, but forgetting it does not
|
||||
// cause safety issues or leaks.
|
||||
func (w *PenWriter) Close() error {
|
||||
if !w.style.Empty() {
|
||||
_, _ = w.w.Write([]byte(ansi.ResetStyle))
|
||||
}
|
||||
if !w.link.Empty() {
|
||||
_, _ = w.w.Write([]byte(ansi.ResetHyperlink()))
|
||||
}
|
||||
if w.p != nil {
|
||||
ansi.PutParser(w.p)
|
||||
w.p = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+139
-82
@@ -39,9 +39,9 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
var seq strings.Builder
|
||||
|
||||
width, height := s.newbuf.Width(), s.newbuf.Height()
|
||||
if ty != fy {
|
||||
if ty != fy { //nolint:nestif
|
||||
var yseq string
|
||||
if s.xtermLike && !s.opts.RelativeCursor {
|
||||
if s.caps.Contains(capVPA) && !s.opts.RelativeCursor {
|
||||
yseq = ansi.VerticalPositionAbsolute(ty + 1)
|
||||
}
|
||||
|
||||
@@ -54,9 +54,13 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
}
|
||||
shouldScroll := !s.opts.AltScreen && fy+n >= s.scrollHeight
|
||||
if lf := strings.Repeat("\n", n); shouldScroll || (fy+n < height && len(lf) < len(yseq)) {
|
||||
//nolint:godox
|
||||
// TODO: Ensure we're not unintentionally scrolling the screen down.
|
||||
yseq = lf
|
||||
s.scrollHeight = max(s.scrollHeight, fy+n)
|
||||
if s.opts.MapNL {
|
||||
fx = 0
|
||||
}
|
||||
}
|
||||
} else if ty < fy {
|
||||
n := fy - ty
|
||||
@@ -64,6 +68,7 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
yseq = cuu
|
||||
}
|
||||
if n == 1 && fy-1 > 0 {
|
||||
//nolint:godox
|
||||
// TODO: Ensure we're not unintentionally scrolling the screen up.
|
||||
yseq = ansi.ReverseIndex
|
||||
}
|
||||
@@ -72,9 +77,9 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
seq.WriteString(yseq)
|
||||
}
|
||||
|
||||
if tx != fx {
|
||||
if tx != fx { //nolint:nestif
|
||||
var xseq string
|
||||
if s.xtermLike && !s.opts.RelativeCursor {
|
||||
if s.caps.Contains(capHPA) && !s.opts.RelativeCursor {
|
||||
xseq = ansi.HorizontalPositionAbsolute(tx + 1)
|
||||
}
|
||||
|
||||
@@ -93,7 +98,8 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
if tabs > 0 {
|
||||
cht := ansi.CursorHorizontalForwardTab(tabs)
|
||||
tab := strings.Repeat("\t", tabs)
|
||||
if false && s.xtermLike && len(cht) < len(tab) {
|
||||
if false && s.caps.Contains(capCHT) && len(cht) < len(tab) {
|
||||
//nolint:godox
|
||||
// TODO: The linux console and some terminals such as
|
||||
// Alacritty don't support [ansi.CHT]. Enable this when
|
||||
// we have a way to detect this, or after 5 years when
|
||||
@@ -144,7 +150,7 @@ func relativeCursorMove(s *Screen, fx, fy, tx, ty int, overwrite, useTabs, useBa
|
||||
}
|
||||
} else if tx < fx {
|
||||
n := fx - tx
|
||||
if useTabs && s.xtermLike {
|
||||
if useTabs && s.caps.Contains(capCBT) {
|
||||
// VT100 does not support backward tabs [ansi.CBT].
|
||||
|
||||
col := fx
|
||||
@@ -190,7 +196,7 @@ func moveCursor(s *Screen, x, y int, overwrite bool) (seq string) {
|
||||
// Method #0: Use [ansi.CUP] if the distance is long.
|
||||
seq = ansi.CursorPosition(x+1, y+1)
|
||||
if fx == -1 || fy == -1 || notLocal(s.newbuf.Width(), fx, fy, x, y) {
|
||||
return
|
||||
return seq
|
||||
}
|
||||
}
|
||||
|
||||
@@ -234,7 +240,7 @@ func moveCursor(s *Screen, x, y int, overwrite bool) (seq string) {
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return seq
|
||||
}
|
||||
|
||||
// moveCursor moves the cursor to the specified position.
|
||||
@@ -242,10 +248,10 @@ func (s *Screen) moveCursor(x, y int, overwrite bool) {
|
||||
if !s.opts.AltScreen && s.cur.X == -1 && s.cur.Y == -1 {
|
||||
// First cursor movement in inline mode, move the cursor to the first
|
||||
// column before moving to the target position.
|
||||
s.buf.WriteByte('\r') //nolint:errcheck
|
||||
s.buf.WriteByte('\r')
|
||||
s.cur.X, s.cur.Y = 0, 0
|
||||
}
|
||||
s.buf.WriteString(moveCursor(s, x, y, overwrite)) //nolint:errcheck
|
||||
s.buf.WriteString(moveCursor(s, x, y, overwrite))
|
||||
s.cur.X, s.cur.Y = x, y
|
||||
}
|
||||
|
||||
@@ -274,10 +280,11 @@ func (s *Screen) move(x, y int) {
|
||||
// Reset wrap around (phantom cursor) state
|
||||
if s.atPhantom {
|
||||
s.cur.X = 0
|
||||
s.buf.WriteByte('\r') //nolint:errcheck
|
||||
s.atPhantom = false // reset phantom cell state
|
||||
s.buf.WriteByte('\r')
|
||||
s.atPhantom = false // reset phantom cell state
|
||||
}
|
||||
|
||||
//nolint:godox
|
||||
// TODO: Investigate if we need to handle this case and/or if we need the
|
||||
// following code.
|
||||
//
|
||||
@@ -291,7 +298,7 @@ func (s *Screen) move(x, y int) {
|
||||
//
|
||||
// if l > 0 {
|
||||
// s.cur.X = 0
|
||||
// s.buf.WriteString("\r" + strings.Repeat("\n", l)) //nolint:errcheck
|
||||
// s.buf.WriteString("\r" + strings.Repeat("\n", l))
|
||||
// }
|
||||
// }
|
||||
|
||||
@@ -339,6 +346,10 @@ type ScreenOptions struct {
|
||||
HardTabs bool
|
||||
// Backspace is whether to use backspace characters to move the cursor.
|
||||
Backspace bool
|
||||
// MapNL whether we have ONLCR mapping enabled. When we set the terminal to
|
||||
// raw mode, the ONLCR mode gets disabled. ONLCR maps any newline/linefeed
|
||||
// (`\n`) character to carriage return + line feed (`\r\n`).
|
||||
MapNL bool
|
||||
}
|
||||
|
||||
// lineData represents the metadata for a line.
|
||||
@@ -365,13 +376,13 @@ type Screen struct {
|
||||
opts ScreenOptions
|
||||
mu sync.Mutex
|
||||
method ansi.Method
|
||||
scrollHeight int // keeps track of how many lines we've scrolled down (inline mode)
|
||||
altScreenMode bool // whether alternate screen mode is enabled
|
||||
cursorHidden bool // whether text cursor mode is enabled
|
||||
clear bool // whether to force clear the screen
|
||||
xtermLike bool // whether to use xterm-like optimizations, otherwise, it uses vt100 only
|
||||
queuedText bool // whether we have queued non-zero width text queued up
|
||||
atPhantom bool // whether the cursor is out of bounds and at a phantom cell
|
||||
scrollHeight int // keeps track of how many lines we've scrolled down (inline mode)
|
||||
altScreenMode bool // whether alternate screen mode is enabled
|
||||
cursorHidden bool // whether text cursor mode is enabled
|
||||
clear bool // whether to force clear the screen
|
||||
caps capabilities // terminal control sequence capabilities
|
||||
queuedText bool // whether we have queued non-zero width text queued up
|
||||
atPhantom bool // whether the cursor is out of bounds and at a phantom cell
|
||||
}
|
||||
|
||||
// SetMethod sets the method used to calculate the width of cells.
|
||||
@@ -491,36 +502,77 @@ func (s *Screen) FillRect(cell *Cell, r Rectangle) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// isXtermLike returns whether the terminal is xterm-like. This means that the
|
||||
// capabilities represents a mask of supported ANSI escape sequences.
|
||||
type capabilities uint
|
||||
|
||||
const (
|
||||
// Vertical Position Absolute [ansi.VPA].
|
||||
capVPA capabilities = 1 << iota
|
||||
// Horizontal Position Absolute [ansi.HPA].
|
||||
capHPA
|
||||
// Cursor Horizontal Tab [ansi.CHT].
|
||||
capCHT
|
||||
// Cursor Backward Tab [ansi.CBT].
|
||||
capCBT
|
||||
// Repeat Previous Character [ansi.REP].
|
||||
capREP
|
||||
// Erase Character [ansi.ECH].
|
||||
capECH
|
||||
// Insert Character [ansi.ICH].
|
||||
capICH
|
||||
// Scroll Down [ansi.SD].
|
||||
capSD
|
||||
// Scroll Up [ansi.SU].
|
||||
capSU
|
||||
|
||||
noCaps capabilities = 0
|
||||
allCaps = capVPA | capHPA | capCHT | capCBT | capREP | capECH | capICH |
|
||||
capSD | capSU
|
||||
)
|
||||
|
||||
// Contains returns whether the capabilities contains the given capability.
|
||||
func (v capabilities) Contains(c capabilities) bool {
|
||||
return v&c == c
|
||||
}
|
||||
|
||||
// xtermCaps returns whether the terminal is xterm-like. This means that the
|
||||
// terminal supports ECMA-48 and ANSI X3.64 escape sequences.
|
||||
// TODO: Should this be a lookup table into each $TERM terminfo database? Like
|
||||
// we could keep a map of ANSI escape sequence to terminfo capability name and
|
||||
// check if the database supports the escape sequence. Instead of keeping a
|
||||
// list of terminal names here.
|
||||
func isXtermLike(termtype string) (v bool) {
|
||||
// xtermCaps returns a list of control sequence capabilities for the given
|
||||
// terminal type. This only supports a subset of sequences that can
|
||||
// be different among terminals.
|
||||
// NOTE: A hybrid approach would be to support Terminfo databases for a full
|
||||
// set of capabilities.
|
||||
func xtermCaps(termtype string) (v capabilities) {
|
||||
parts := strings.Split(termtype, "-")
|
||||
if len(parts) == 0 {
|
||||
return
|
||||
return v
|
||||
}
|
||||
|
||||
switch parts[0] {
|
||||
case
|
||||
"alacritty",
|
||||
"contour",
|
||||
"foot",
|
||||
"ghostty",
|
||||
"kitty",
|
||||
"linux",
|
||||
"rio",
|
||||
"screen",
|
||||
"st",
|
||||
"tmux",
|
||||
"wezterm",
|
||||
"xterm":
|
||||
v = true
|
||||
v = allCaps
|
||||
case "alacritty":
|
||||
v = allCaps
|
||||
v &^= capCHT // NOTE: alacritty added support for [ansi.CHT] in 2024-12-28 #62d5b13.
|
||||
case "screen":
|
||||
// See https://www.gnu.org/software/screen/manual/screen.html#Control-Sequences-1
|
||||
v = allCaps
|
||||
v &^= capREP
|
||||
case "linux":
|
||||
// See https://man7.org/linux/man-pages/man4/console_codes.4.html
|
||||
v = capVPA | capHPA | capECH | capICH
|
||||
}
|
||||
|
||||
return
|
||||
return v
|
||||
}
|
||||
|
||||
// NewScreen creates a new Screen.
|
||||
@@ -548,14 +600,14 @@ func NewScreen(w io.Writer, width, height int, opts *ScreenOptions) (s *Screen)
|
||||
}
|
||||
|
||||
s.buf = new(bytes.Buffer)
|
||||
s.xtermLike = isXtermLike(s.opts.Term)
|
||||
s.caps = xtermCaps(s.opts.Term)
|
||||
s.curbuf = NewBuffer(width, height)
|
||||
s.newbuf = NewBuffer(width, height)
|
||||
s.cur = Cursor{Position: Pos(-1, -1)} // start at -1 to force a move
|
||||
s.saved = s.cur
|
||||
s.reset()
|
||||
|
||||
return
|
||||
return s
|
||||
}
|
||||
|
||||
// Width returns the width of the screen.
|
||||
@@ -595,7 +647,7 @@ func (s *Screen) putCell(cell *Cell) {
|
||||
|
||||
// wrapCursor wraps the cursor to the next line.
|
||||
//
|
||||
//nolint:unused
|
||||
|
||||
func (s *Screen) wrapCursor() {
|
||||
const autoRightMargin = true
|
||||
if autoRightMargin {
|
||||
@@ -628,9 +680,9 @@ func (s *Screen) putAttrCell(cell *Cell) {
|
||||
}
|
||||
|
||||
s.updatePen(cell)
|
||||
s.buf.WriteRune(cell.Rune) //nolint:errcheck
|
||||
s.buf.WriteRune(cell.Rune)
|
||||
for _, c := range cell.Comb {
|
||||
s.buf.WriteRune(c) //nolint:errcheck
|
||||
s.buf.WriteRune(c)
|
||||
}
|
||||
|
||||
s.cur.X += cell.Width
|
||||
@@ -649,12 +701,12 @@ func (s *Screen) putCellLR(cell *Cell) {
|
||||
// Optimize for the lower right corner cell.
|
||||
curX := s.cur.X
|
||||
if cell == nil || !cell.Empty() {
|
||||
s.buf.WriteString(ansi.ResetAutoWrapMode) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.ResetModeAutoWrap)
|
||||
s.putAttrCell(cell)
|
||||
// Writing to lower-right corner cell should not wrap.
|
||||
s.atPhantom = false
|
||||
s.cur.X = curX
|
||||
s.buf.WriteString(ansi.SetAutoWrapMode) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.SetModeAutoWrap)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -675,11 +727,11 @@ func (s *Screen) updatePen(cell *Cell) {
|
||||
if cell.Style.Empty() && len(seq) > len(ansi.ResetStyle) {
|
||||
seq = ansi.ResetStyle
|
||||
}
|
||||
s.buf.WriteString(seq) //nolint:errcheck
|
||||
s.buf.WriteString(seq)
|
||||
s.cur.Style = cell.Style
|
||||
}
|
||||
if !cell.Link.Equal(&s.cur.Link) {
|
||||
s.buf.WriteString(ansi.SetHyperlink(cell.Link.URL, cell.Link.Params)) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.SetHyperlink(cell.Link.URL, cell.Link.Params))
|
||||
s.cur.Link = cell.Link
|
||||
}
|
||||
}
|
||||
@@ -712,9 +764,9 @@ func (s *Screen) emitRange(line Line, n int) (eoi bool) {
|
||||
ech := ansi.EraseCharacter(count)
|
||||
cup := ansi.CursorPosition(s.cur.X+count, s.cur.Y)
|
||||
rep := ansi.RepeatPreviousCharacter(count)
|
||||
if s.xtermLike && count > len(ech)+len(cup) && cell0 != nil && cell0.Clear() {
|
||||
if s.caps.Contains(capECH) && count > len(ech)+len(cup) && cell0 != nil && cell0.Clear() { //nolint:nestif
|
||||
s.updatePen(cell0)
|
||||
s.buf.WriteString(ech) //nolint:errcheck
|
||||
s.buf.WriteString(ech)
|
||||
|
||||
// If this is the last cell, we don't need to move the cursor.
|
||||
if count < n {
|
||||
@@ -722,7 +774,7 @@ func (s *Screen) emitRange(line Line, n int) (eoi bool) {
|
||||
} else {
|
||||
return true // cursor in the middle
|
||||
}
|
||||
} else if s.xtermLike && count > len(rep) &&
|
||||
} else if s.caps.Contains(capREP) && count > len(rep) &&
|
||||
(cell0 == nil || (len(cell0.Comb) == 0 && cell0.Rune < 256)) {
|
||||
// We only support ASCII characters. Most terminals will handle
|
||||
// non-ASCII characters correctly, but some might not, ahem xterm.
|
||||
@@ -740,13 +792,13 @@ func (s *Screen) emitRange(line Line, n int) (eoi bool) {
|
||||
s.putCell(cell0)
|
||||
repCount-- // cell0 is a single width cell ASCII character
|
||||
|
||||
s.buf.WriteString(ansi.RepeatPreviousCharacter(repCount)) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.RepeatPreviousCharacter(repCount))
|
||||
s.cur.X += repCount
|
||||
if wrapPossible {
|
||||
s.putCell(cell0)
|
||||
}
|
||||
} else {
|
||||
for i := 0; i < count; i++ {
|
||||
for i := range count {
|
||||
s.putCell(line.At(i))
|
||||
}
|
||||
}
|
||||
@@ -755,7 +807,7 @@ func (s *Screen) emitRange(line Line, n int) (eoi bool) {
|
||||
n -= count
|
||||
}
|
||||
|
||||
return
|
||||
return eoi
|
||||
}
|
||||
|
||||
// putRange puts a range of cells from the old line to the new line.
|
||||
@@ -765,7 +817,7 @@ func (s *Screen) putRange(oldLine, newLine Line, y, start, end int) (eoi bool) {
|
||||
inline := min(len(ansi.CursorPosition(start+1, y+1)),
|
||||
min(len(ansi.HorizontalPositionAbsolute(start+1)),
|
||||
len(ansi.CursorForward(start+1))))
|
||||
if (end - start + 1) > inline {
|
||||
if (end - start + 1) > inline { //nolint:nestif
|
||||
var j, same int
|
||||
for j, same = start, 0; j <= end; j++ {
|
||||
oldCell, newCell := oldLine.At(j), newLine.At(j)
|
||||
@@ -817,9 +869,9 @@ func (s *Screen) clearToEnd(blank *Cell, force bool) { //nolint:unparam
|
||||
s.updatePen(blank)
|
||||
count := s.newbuf.Width() - s.cur.X
|
||||
if s.el0Cost() <= count {
|
||||
s.buf.WriteString(ansi.EraseLineRight) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.EraseLineRight)
|
||||
} else {
|
||||
for i := 0; i < count; i++ {
|
||||
for range count {
|
||||
s.putCell(blank)
|
||||
}
|
||||
}
|
||||
@@ -839,12 +891,13 @@ func (s *Screen) clearBlank() *Cell {
|
||||
// insertCells inserts the count cells pointed by the given line at the current
|
||||
// cursor position.
|
||||
func (s *Screen) insertCells(line Line, count int) {
|
||||
if s.xtermLike {
|
||||
supportsICH := s.caps.Contains(capICH)
|
||||
if supportsICH {
|
||||
// Use [ansi.ICH] as an optimization.
|
||||
s.buf.WriteString(ansi.InsertCharacter(count)) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.InsertCharacter(count))
|
||||
} else {
|
||||
// Otherwise, use [ansi.IRM] mode.
|
||||
s.buf.WriteString(ansi.SetInsertReplaceMode) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.SetModeInsertReplace)
|
||||
}
|
||||
|
||||
for i := 0; count > 0; i++ {
|
||||
@@ -852,8 +905,8 @@ func (s *Screen) insertCells(line Line, count int) {
|
||||
count--
|
||||
}
|
||||
|
||||
if !s.xtermLike {
|
||||
s.buf.WriteString(ansi.ResetInsertReplaceMode) //nolint:errcheck
|
||||
if !supportsICH {
|
||||
s.buf.WriteString(ansi.ResetModeInsertReplace)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,7 +915,7 @@ func (s *Screen) insertCells(line Line, count int) {
|
||||
// [ansi.EL] 0 i.e. [ansi.EraseLineRight] to clear
|
||||
// trailing spaces.
|
||||
func (s *Screen) el0Cost() int {
|
||||
if s.xtermLike {
|
||||
if s.caps != noCaps {
|
||||
return 0
|
||||
}
|
||||
return len(ansi.EraseLineRight)
|
||||
@@ -878,7 +931,7 @@ func (s *Screen) transformLine(y int) {
|
||||
|
||||
// Find the first changed cell in the line
|
||||
var lineChanged bool
|
||||
for i := 0; i < s.newbuf.Width(); i++ {
|
||||
for i := range s.newbuf.Width() {
|
||||
if !cellEqual(newLine.At(i), oldLine.At(i)) {
|
||||
lineChanged = true
|
||||
break
|
||||
@@ -886,7 +939,7 @@ func (s *Screen) transformLine(y int) {
|
||||
}
|
||||
|
||||
const ceolStandoutGlitch = false
|
||||
if ceolStandoutGlitch && lineChanged {
|
||||
if ceolStandoutGlitch && lineChanged { //nolint:nestif
|
||||
s.move(0, y)
|
||||
s.clearToEnd(nil, false)
|
||||
s.putRange(oldLine, newLine, y, 0, s.newbuf.Width()-1)
|
||||
@@ -897,12 +950,12 @@ func (s *Screen) transformLine(y int) {
|
||||
// [ansi.EraseLineLeft].
|
||||
if blank == nil || blank.Clear() {
|
||||
var oFirstCell, nFirstCell int
|
||||
for oFirstCell = 0; oFirstCell < s.curbuf.Width(); oFirstCell++ {
|
||||
for oFirstCell = range s.curbuf.Width() {
|
||||
if !cellEqual(oldLine.At(oFirstCell), blank) {
|
||||
break
|
||||
}
|
||||
}
|
||||
for nFirstCell = 0; nFirstCell < s.newbuf.Width(); nFirstCell++ {
|
||||
for nFirstCell = range s.newbuf.Width() {
|
||||
if !cellEqual(newLine.At(nFirstCell), blank) {
|
||||
break
|
||||
}
|
||||
@@ -925,11 +978,11 @@ func (s *Screen) transformLine(y int) {
|
||||
if nFirstCell >= s.newbuf.Width() {
|
||||
s.move(0, y)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.EraseLineRight) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.EraseLineRight)
|
||||
} else {
|
||||
s.move(nFirstCell-1, y)
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.EraseLineLeft) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.EraseLineLeft)
|
||||
}
|
||||
|
||||
for firstCell < nFirstCell {
|
||||
@@ -1045,7 +1098,7 @@ func (s *Screen) transformLine(y int) {
|
||||
|
||||
s.move(n+1, y)
|
||||
ichCost := 3 + nLastCell - oLastCell
|
||||
if s.xtermLike && (nLastCell < nLastNonBlank || ichCost > (m-n)) {
|
||||
if s.caps.Contains(capICH) && (nLastCell < nLastNonBlank || ichCost > (m-n)) {
|
||||
s.putRange(oldLine, newLine, y, n+1, m)
|
||||
} else {
|
||||
s.insertCells(newLine[n+1:], nLastCell-oLastCell)
|
||||
@@ -1079,7 +1132,7 @@ func (s *Screen) transformLine(y int) {
|
||||
func (s *Screen) deleteCells(count int) {
|
||||
// [ansi.DCH] will shift in cells from the right margin so we need to
|
||||
// ensure that they are the right style.
|
||||
s.buf.WriteString(ansi.DeleteCharacter(count)) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.DeleteCharacter(count))
|
||||
}
|
||||
|
||||
// clearToBottom clears the screen from the current cursor position to the end
|
||||
@@ -1091,7 +1144,7 @@ func (s *Screen) clearToBottom(blank *Cell) {
|
||||
}
|
||||
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.EraseScreenBelow) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.EraseScreenBelow)
|
||||
// Clear the rest of the current line
|
||||
s.curbuf.ClearRect(Rect(col, row, s.curbuf.Width()-col, 1))
|
||||
// Clear everything below the current line
|
||||
@@ -1104,7 +1157,7 @@ func (s *Screen) clearToBottom(blank *Cell) {
|
||||
// It returns the top line.
|
||||
func (s *Screen) clearBottom(total int) (top int) {
|
||||
if total <= 0 {
|
||||
return
|
||||
return top
|
||||
}
|
||||
|
||||
top = total
|
||||
@@ -1112,7 +1165,7 @@ func (s *Screen) clearBottom(total int) (top int) {
|
||||
blank := s.clearBlank()
|
||||
canClearWithBlank := blank == nil || blank.Clear()
|
||||
|
||||
if canClearWithBlank {
|
||||
if canClearWithBlank { //nolint:nestif
|
||||
var row int
|
||||
for row = total - 1; row >= 0; row-- {
|
||||
oldLine := s.curbuf.Line(row)
|
||||
@@ -1147,14 +1200,14 @@ func (s *Screen) clearBottom(total int) (top int) {
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return top
|
||||
}
|
||||
|
||||
// clearScreen clears the screen and put cursor at home.
|
||||
func (s *Screen) clearScreen(blank *Cell) {
|
||||
s.updatePen(blank)
|
||||
s.buf.WriteString(ansi.CursorHomePosition) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.EraseEntireScreen) //nolint:errcheck
|
||||
s.buf.WriteString(ansi.CursorHomePosition)
|
||||
s.buf.WriteString(ansi.EraseEntireScreen)
|
||||
s.cur.X, s.cur.Y = 0, 0
|
||||
s.curbuf.Fill(blank)
|
||||
}
|
||||
@@ -1179,7 +1232,7 @@ func (s *Screen) clearUpdate() {
|
||||
s.clearBelow(blank, 0)
|
||||
}
|
||||
nonEmpty = s.clearBottom(nonEmpty)
|
||||
for i := 0; i < nonEmpty; i++ {
|
||||
for i := range nonEmpty {
|
||||
s.transformLine(i)
|
||||
}
|
||||
}
|
||||
@@ -1194,13 +1247,13 @@ func (s *Screen) Flush() (err error) {
|
||||
func (s *Screen) flush() (err error) {
|
||||
// Write the buffer
|
||||
if s.buf.Len() > 0 {
|
||||
_, err = s.w.Write(s.buf.Bytes()) //nolint:errcheck
|
||||
_, err = s.w.Write(s.buf.Bytes())
|
||||
if err == nil {
|
||||
s.buf.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
return err //nolint:wrapcheck
|
||||
}
|
||||
|
||||
// Render renders changes of the screen to the internal buffer. Call
|
||||
@@ -1221,6 +1274,7 @@ func (s *Screen) render() {
|
||||
return
|
||||
}
|
||||
|
||||
//nolint:godox
|
||||
// TODO: Investigate whether this is necessary. Theoretically, terminals
|
||||
// can add/remove tab stops and we should be able to handle that. We could
|
||||
// use [ansi.DECTABSR] to read the tab stops, but that's not implemented in
|
||||
@@ -1235,9 +1289,9 @@ func (s *Screen) render() {
|
||||
// Do we need alt-screen mode?
|
||||
if s.opts.AltScreen != s.altScreenMode {
|
||||
if s.opts.AltScreen {
|
||||
s.buf.WriteString(ansi.SetAltScreenSaveCursorMode)
|
||||
s.buf.WriteString(ansi.SetModeAltScreenSaveCursor)
|
||||
} else {
|
||||
s.buf.WriteString(ansi.ResetAltScreenSaveCursorMode)
|
||||
s.buf.WriteString(ansi.ResetModeAltScreenSaveCursor)
|
||||
}
|
||||
s.altScreenMode = s.opts.AltScreen
|
||||
}
|
||||
@@ -1252,7 +1306,9 @@ func (s *Screen) render() {
|
||||
|
||||
// Do we have queued strings to write above the screen?
|
||||
if len(s.queueAbove) > 0 {
|
||||
//nolint:godox
|
||||
// TODO: Use scrolling region if available.
|
||||
//nolint:godox
|
||||
// TODO: Use [Screen.Write] [io.Writer] interface.
|
||||
|
||||
// We need to scroll the screen up by the number of lines in the queue.
|
||||
@@ -1290,12 +1346,13 @@ func (s *Screen) render() {
|
||||
s.clearBelow(nil, s.newbuf.Height()-1)
|
||||
}
|
||||
|
||||
if s.clear {
|
||||
if s.clear { //nolint:nestif
|
||||
s.clearUpdate()
|
||||
s.clear = false
|
||||
} else if len(s.touch) > 0 {
|
||||
if s.opts.AltScreen {
|
||||
// Optimize scrolling for the alternate screen buffer.
|
||||
//nolint:godox
|
||||
// TODO: Should we optimize for inline mode as well? If so, we need
|
||||
// to know the actual cursor position to use [ansi.DECSTBM].
|
||||
s.scrollOptimize()
|
||||
@@ -1311,7 +1368,7 @@ func (s *Screen) render() {
|
||||
}
|
||||
|
||||
nonEmpty = s.clearBottom(nonEmpty)
|
||||
for i = 0; i < nonEmpty; i++ {
|
||||
for i = range nonEmpty {
|
||||
_, ok := s.touch[i]
|
||||
if ok {
|
||||
s.transformLine(i)
|
||||
@@ -1359,7 +1416,7 @@ func (s *Screen) Close() (err error) {
|
||||
s.move(0, s.newbuf.Height()-1)
|
||||
|
||||
if s.altScreenMode {
|
||||
s.buf.WriteString(ansi.ResetAltScreenSaveCursorMode)
|
||||
s.buf.WriteString(ansi.ResetModeAltScreenSaveCursor)
|
||||
s.altScreenMode = false
|
||||
}
|
||||
|
||||
@@ -1371,11 +1428,11 @@ func (s *Screen) Close() (err error) {
|
||||
// Write the buffer
|
||||
err = s.flush()
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
s.reset()
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// reset resets the screen to its initial state.
|
||||
@@ -1420,9 +1477,9 @@ func (s *Screen) Resize(width, height int) bool {
|
||||
}
|
||||
|
||||
if height > oldh {
|
||||
s.ClearRect(Rect(0, max(oldh-1, 0), width, height-oldh))
|
||||
s.ClearRect(Rect(0, max(oldh, 0), width, height-oldh))
|
||||
} else if height < oldh {
|
||||
s.ClearRect(Rect(0, max(height-1, 0), width, oldh-height))
|
||||
s.ClearRect(Rect(0, max(height, 0), width, oldh-height))
|
||||
}
|
||||
|
||||
s.mu.Lock()
|
||||
|
||||
+2
-2
@@ -4,9 +4,9 @@ import (
|
||||
"github.com/charmbracelet/colorprofile"
|
||||
)
|
||||
|
||||
// Convert converts a style to respect the given color profile.
|
||||
// ConvertStyle converts a style to respect the given color profile.
|
||||
func ConvertStyle(s Style, p colorprofile.Profile) Style {
|
||||
switch p {
|
||||
switch p { //nolint:exhaustive
|
||||
case colorprofile.TrueColor:
|
||||
return s
|
||||
case colorprofile.Ascii:
|
||||
|
||||
-14
@@ -9,20 +9,6 @@ func Height(s string) int {
|
||||
return strings.Count(s, "\n") + 1
|
||||
}
|
||||
|
||||
func min(a, b int) int { //nolint:predeclared
|
||||
if a > b {
|
||||
return b
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func max(a, b int) int { //nolint:predeclared
|
||||
if a > b {
|
||||
return a
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func clamp(v, low, high int) int {
|
||||
if high < low {
|
||||
low, high = high, low
|
||||
|
||||
+13
-7
@@ -2,6 +2,7 @@ package cellbuf
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"slices"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
@@ -20,6 +21,16 @@ const nbsp = '\u00a0'
|
||||
//
|
||||
// Note: breakpoints must be a string of 1-cell wide rune characters.
|
||||
func Wrap(s string, limit int, breakpoints string) string {
|
||||
//nolint:godox
|
||||
// TODO: Use [PenWriter] once we get
|
||||
// https://github.com/charmbracelet/lipgloss/pull/489 out the door and
|
||||
// released.
|
||||
// The problem is that [ansi.Wrap] doesn't keep track of style and link
|
||||
// state, so combining both breaks styled space cells. To fix this, we use
|
||||
// non-breaking space cells for padding and styled blank cells. And since
|
||||
// both wrapping methods respect non-breaking spaces, we can use them to
|
||||
// preserve styled spaces in the output.
|
||||
|
||||
if len(s) == 0 {
|
||||
return ""
|
||||
}
|
||||
@@ -90,7 +101,7 @@ func Wrap(s string, limit int, breakpoints string) string {
|
||||
seq, width, n, newState := ansi.DecodeSequence(s, state, p)
|
||||
switch width {
|
||||
case 0:
|
||||
if ansi.Equal(seq, "\t") {
|
||||
if ansi.Equal(seq, "\t") { //nolint:nestif
|
||||
addWord()
|
||||
space.WriteString(seq)
|
||||
break
|
||||
@@ -176,10 +187,5 @@ func Wrap(s string, limit int, breakpoints string) string {
|
||||
}
|
||||
|
||||
func runeContainsAny[T string | []rune](r rune, s T) bool {
|
||||
for _, c := range []rune(s) {
|
||||
if c == r {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
return slices.Contains([]rune(s), r)
|
||||
}
|
||||
|
||||
+15
-14
@@ -25,7 +25,7 @@ type CellBuffer interface {
|
||||
func FillRect(s CellBuffer, c *Cell, rect Rectangle) {
|
||||
for y := rect.Min.Y; y < rect.Max.Y; y++ {
|
||||
for x := rect.Min.X; x < rect.Max.X; x++ {
|
||||
s.SetCell(x, y, c) //nolint:errcheck
|
||||
s.SetCell(x, y, c)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,7 +68,7 @@ func SetContent(s CellBuffer, str string) {
|
||||
func Render(d CellBuffer) string {
|
||||
var buf bytes.Buffer
|
||||
height := d.Bounds().Dy()
|
||||
for y := 0; y < height; y++ {
|
||||
for y := range height {
|
||||
_, line := RenderLine(d, y)
|
||||
buf.WriteString(line)
|
||||
if y < height-1 {
|
||||
@@ -98,32 +98,32 @@ func RenderLine(d CellBuffer, n int) (w int, line string) {
|
||||
pendingLine = ""
|
||||
}
|
||||
|
||||
for x := 0; x < d.Bounds().Dx(); x++ {
|
||||
if cell := d.Cell(x, n); cell != nil && cell.Width > 0 {
|
||||
for x := range d.Bounds().Dx() {
|
||||
if cell := d.Cell(x, n); cell != nil && cell.Width > 0 { //nolint:nestif
|
||||
// Convert the cell's style and link to the given color profile.
|
||||
cellStyle := cell.Style
|
||||
cellLink := cell.Link
|
||||
if cellStyle.Empty() && !pen.Empty() {
|
||||
writePending()
|
||||
buf.WriteString(ansi.ResetStyle) //nolint:errcheck
|
||||
buf.WriteString(ansi.ResetStyle)
|
||||
pen.Reset()
|
||||
}
|
||||
if !cellStyle.Equal(&pen) {
|
||||
writePending()
|
||||
seq := cellStyle.DiffSequence(pen)
|
||||
buf.WriteString(seq) // nolint:errcheck
|
||||
buf.WriteString(seq)
|
||||
pen = cellStyle
|
||||
}
|
||||
|
||||
// Write the URL escape sequence
|
||||
if cellLink != link && link.URL != "" {
|
||||
writePending()
|
||||
buf.WriteString(ansi.ResetHyperlink()) //nolint:errcheck
|
||||
buf.WriteString(ansi.ResetHyperlink())
|
||||
link.Reset()
|
||||
}
|
||||
if cellLink != link {
|
||||
writePending()
|
||||
buf.WriteString(ansi.SetHyperlink(cellLink.URL, cellLink.Params)) //nolint:errcheck
|
||||
buf.WriteString(ansi.SetHyperlink(cellLink.URL, cellLink.Params))
|
||||
link = cellLink
|
||||
}
|
||||
|
||||
@@ -140,10 +140,10 @@ func RenderLine(d CellBuffer, n int) (w int, line string) {
|
||||
}
|
||||
}
|
||||
if link.URL != "" {
|
||||
buf.WriteString(ansi.ResetHyperlink()) //nolint:errcheck
|
||||
buf.WriteString(ansi.ResetHyperlink())
|
||||
}
|
||||
if !pen.Empty() {
|
||||
buf.WriteString(ansi.ResetStyle) //nolint:errcheck
|
||||
buf.WriteString(ansi.ResetStyle)
|
||||
}
|
||||
return w, strings.TrimRight(buf.String(), " ") // Trim trailing spaces
|
||||
}
|
||||
@@ -201,7 +201,7 @@ func (s *ScreenWriter) SetContentRect(str string, rect Rectangle) {
|
||||
// string to the width of the screen if it exceeds the width of the screen.
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
|
||||
// sequences.
|
||||
func (s *ScreenWriter) Print(str string, v ...interface{}) {
|
||||
func (s *ScreenWriter) Print(str string, v ...any) {
|
||||
if len(v) > 0 {
|
||||
str = fmt.Sprintf(str, v...)
|
||||
}
|
||||
@@ -214,7 +214,7 @@ func (s *ScreenWriter) Print(str string, v ...interface{}) {
|
||||
// the width of the screen if it exceeds the width of the screen.
|
||||
// This will recognize ANSI [ansi.SGR] style and [ansi.SetHyperlink] escape
|
||||
// sequences.
|
||||
func (s *ScreenWriter) PrintAt(x, y int, str string, v ...interface{}) {
|
||||
func (s *ScreenWriter) PrintAt(x, y int, str string, v ...any) {
|
||||
if len(v) > 0 {
|
||||
str = fmt.Sprintf(str, v...)
|
||||
}
|
||||
@@ -299,7 +299,7 @@ func printString[T []byte | string](
|
||||
// Print the cell to the screen
|
||||
cell.Style = style
|
||||
cell.Link = link
|
||||
s.SetCell(x, y, &cell) //nolint:errcheck
|
||||
s.SetCell(x, y, &cell)
|
||||
x += width
|
||||
}
|
||||
}
|
||||
@@ -309,6 +309,7 @@ func printString[T []byte | string](
|
||||
cell.Reset()
|
||||
default:
|
||||
// Valid sequences always have a non-zero Cmd.
|
||||
//nolint:godox
|
||||
// TODO: Handle cursor movement and other sequences
|
||||
switch {
|
||||
case ansi.HasCsiPrefix(seq) && p.Command() == 'm':
|
||||
@@ -333,7 +334,7 @@ func printString[T []byte | string](
|
||||
|
||||
// Make sure to set the last cell if it's not empty.
|
||||
if !cell.Empty() {
|
||||
s.SetCell(x, y, &cell) //nolint:errcheck
|
||||
s.SetCell(x, y, &cell)
|
||||
cell.Reset()
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
// Package term provides a platform-independent interfaces for interacting with
|
||||
// Terminal and TTY devices.
|
||||
package term
|
||||
|
||||
// State contains platform-specific state of a terminal.
|
||||
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
package term
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type state struct {
|
||||
termName string
|
||||
raw bool
|
||||
ctl *os.File
|
||||
}
|
||||
|
||||
// termName returns the name of the terminal or os.ErrNotExist if there is no terminal.
|
||||
func termName(fd uintptr) (string, error) {
|
||||
ctl, err := os.ReadFile(filepath.Join("/fd", fmt.Sprintf("%dctl", fd)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
f := strings.Fields(string(ctl))
|
||||
if len(f) == 0 {
|
||||
return "", os.ErrNotExist
|
||||
}
|
||||
return f[len(f)-1], nil
|
||||
}
|
||||
|
||||
func isTerminal(fd uintptr) bool {
|
||||
ctl, err := os.ReadFile(filepath.Join("/fd", fmt.Sprintf("%dctl", fd)))
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
if strings.Contains(string(ctl), "/dev/cons") {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func makeRaw(fd uintptr) (*State, error) {
|
||||
t, err := termName(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctl, err := os.OpenFile(t, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := ctl.Write([]byte("rawon")); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{state: state{termName: t, raw: true, ctl: ctl}}, nil
|
||||
}
|
||||
|
||||
func getState(fd uintptr) (*State, error) {
|
||||
t, err := termName(fd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctl, err := os.OpenFile(t, os.O_RDWR, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &State{state: state{termName: t, raw: false, ctl: ctl}}, nil
|
||||
|
||||
}
|
||||
|
||||
func restore(_ uintptr, state *State) error {
|
||||
if _, err := state.ctl.Write([]byte("rawoff")); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// getSize returns the size. This will only work if you are running
|
||||
// under a window manager in Plan 9. Else, the only option
|
||||
// is to return a reasonable default.
|
||||
func getSize(fd uintptr) (int, int, error) {
|
||||
w, h := 80, 40
|
||||
b, err := os.ReadFile("/dev/wctl")
|
||||
if err != nil {
|
||||
return w, h, err
|
||||
}
|
||||
f := strings.Fields(string(b))
|
||||
if len(f) != 4 {
|
||||
return w, h, fmt.Errorf("%q only has %d of 4 needed fields:%w", f, len(f), os.ErrInvalid)
|
||||
}
|
||||
// The contents of wctl, as defined in the driver, are
|
||||
// 4 12-char fields: upper left x, y; and lower-right x, y
|
||||
var ulx, uly, lrx, lry int
|
||||
if n, err := fmt.Sscanf(string(b[:48]), "%d%d%d%d", &ulx, &uly, &lrx, &lry); n != 4 || err != nil {
|
||||
return w, h, fmt.Errorf("scanning %q:%d of 4 items scanned:%w", string(b[:48]), n, err)
|
||||
}
|
||||
|
||||
w, h = lrx-lrx, lry-uly
|
||||
return w, h, nil
|
||||
}
|
||||
|
||||
func setState(_ uintptr, state *State) error {
|
||||
raw := "rawoff"
|
||||
if state.raw {
|
||||
raw = "rawon"
|
||||
}
|
||||
if _, err := state.ctl.Write([]byte(raw)); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func readPassword(fd uintptr) ([]byte, error) {
|
||||
f := os.NewFile(fd, "cons")
|
||||
var b [128]byte
|
||||
n, err := f.Read(b[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return b[:n], nil
|
||||
}
|
||||
+10
-10
@@ -19,7 +19,7 @@ func isTerminal(fd uintptr) bool {
|
||||
func makeRaw(fd uintptr) (*State, error) {
|
||||
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, err //nolint:wrapcheck
|
||||
}
|
||||
|
||||
oldState := State{state{Termios: *termios}}
|
||||
@@ -34,7 +34,7 @@ func makeRaw(fd uintptr) (*State, error) {
|
||||
termios.Cc[unix.VMIN] = 1
|
||||
termios.Cc[unix.VTIME] = 0
|
||||
if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios); err != nil {
|
||||
return nil, err
|
||||
return nil, err //nolint:wrapcheck
|
||||
}
|
||||
|
||||
return &oldState, nil
|
||||
@@ -45,26 +45,26 @@ func setState(fd uintptr, state *State) error {
|
||||
if state != nil {
|
||||
termios = &state.Termios
|
||||
}
|
||||
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios)
|
||||
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
func getState(fd uintptr) (*State, error) {
|
||||
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, err //nolint:wrapcheck
|
||||
}
|
||||
|
||||
return &State{state{Termios: *termios}}, nil
|
||||
}
|
||||
|
||||
func restore(fd uintptr, state *State) error {
|
||||
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.Termios)
|
||||
return unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &state.Termios) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
func getSize(fd uintptr) (width, height int, err error) {
|
||||
ws, err := unix.IoctlGetWinsize(int(fd), unix.TIOCGWINSZ)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
return 0, 0, err //nolint:wrapcheck
|
||||
}
|
||||
return int(ws.Col), int(ws.Row), nil
|
||||
}
|
||||
@@ -73,13 +73,13 @@ func getSize(fd uintptr) (width, height int, err error) {
|
||||
type passwordReader int
|
||||
|
||||
func (r passwordReader) Read(buf []byte) (int, error) {
|
||||
return unix.Read(int(r), buf)
|
||||
return unix.Read(int(r), buf) //nolint:wrapcheck
|
||||
}
|
||||
|
||||
func readPassword(fd uintptr) ([]byte, error) {
|
||||
termios, err := unix.IoctlGetTermios(int(fd), ioctlReadTermios)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, err //nolint:wrapcheck
|
||||
}
|
||||
|
||||
newState := *termios
|
||||
@@ -87,10 +87,10 @@ func readPassword(fd uintptr) ([]byte, error) {
|
||||
newState.Lflag |= unix.ICANON | unix.ISIG
|
||||
newState.Iflag |= unix.ICRNL
|
||||
if err := unix.IoctlSetTermios(int(fd), ioctlWriteTermios, &newState); err != nil {
|
||||
return nil, err
|
||||
return nil, err //nolint:wrapcheck
|
||||
}
|
||||
|
||||
defer unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios)
|
||||
defer unix.IoctlSetTermios(int(fd), ioctlWriteTermios, termios) //nolint:errcheck
|
||||
|
||||
return readPasswordLine(passwordReader(fd))
|
||||
}
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ func makeRaw(fd uintptr) (*State, error) {
|
||||
if err := windows.GetConsoleMode(windows.Handle(fd), &st); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT | windows.ENABLE_PROCESSED_OUTPUT)
|
||||
raw := st &^ (windows.ENABLE_ECHO_INPUT | windows.ENABLE_PROCESSED_INPUT | windows.ENABLE_LINE_INPUT)
|
||||
raw |= windows.ENABLE_VIRTUAL_TERMINAL_INPUT
|
||||
if err := windows.SetConsoleMode(windows.Handle(fd), raw); err != nil {
|
||||
return nil, err
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ func readPasswordLine(reader io.Reader) ([]byte, error) {
|
||||
if err == io.EOF && len(ret) > 0 {
|
||||
return ret, nil
|
||||
}
|
||||
return ret, err
|
||||
return ret, err //nolint:wrapcheck
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
.DS_Store
|
||||
*.out
|
||||
*.test
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
The goals and overview of this package can be found in the README.md file,
|
||||
start by reading that.
|
||||
|
||||
The goal of this package is to determine the display (column) width of a
|
||||
string, UTF-8 bytes, or runes, as would happen in a monospace font, especially
|
||||
in a terminal.
|
||||
|
||||
When troubleshooting, write Go unit tests instead of executing debug scripts.
|
||||
The tests can return whatever logs or output you need. If those tests are
|
||||
only for temporary troubleshooting, clean up the tests after the debugging is
|
||||
done.
|
||||
|
||||
(Separate executable debugging scripts are messy, tend to have conflicting
|
||||
dependencies and are hard to cleanup.)
|
||||
|
||||
If you make changes to the trie generation in internal/gen, it can be invoked
|
||||
by running `go generate` from the top package directory.
|
||||
|
||||
## Pull Requests and branches
|
||||
|
||||
For PRs (pull requests), you can use the gh CLI tool. Compare the current branch with main. Reviewing a PR and reviewing a branch are about the same, but the PR may add context.
|
||||
|
||||
Understand the goals of the PR. Note any API changes, especially breaking changes.
|
||||
|
||||
Look for thoroughness of tests, as well as GoDoc comments.
|
||||
|
||||
Retrieve and consider the comments on the PR, which may have come from GitHub Copilot or Cursor BugBot. Think like GitHub Copilot or Cursor BugBot.
|
||||
|
||||
Offer to optionally post a brief summary of the review to the PR, via the gh CLI tool.
|
||||
|
||||
## Tagged Go releases
|
||||
|
||||
If I ask you whether we are ready to release, this means a tagged Go release on the main branch. Go releases are git tagged with a version number.
|
||||
|
||||
Review the changes since the last release, i.e. the previous git tag. Ensure that the changes are complete and correct. Identify new features, bug fixes, and performance improvements.
|
||||
|
||||
Identify breaking changes, especially API changes.
|
||||
|
||||
Ensure good test coverage. Look for performance changes, especially performance regressions, by running benchmarks against the previous release.
|
||||
|
||||
Ensure that the documentation in READMEs and GoDocs are complete, correct and consistent.
|
||||
|
||||
## Comparisons to go-runewidth
|
||||
|
||||
We originally attempted to make this package compatible with go-runewidth.
|
||||
However, we found that there were too many differences in the handling of
|
||||
certain characters and properties.
|
||||
|
||||
We believe, preliminarily, that our choices are more correct and complete,
|
||||
by using more complete categories such as Unicode Cf (format) for zero-width
|
||||
and Mn (Nonspacing_Mark) for combining marks.
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
# Changelog
|
||||
|
||||
## [0.11.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.10.0...v0.11.0)
|
||||
|
||||
### Added
|
||||
- New `ControlSequences8Bit` option to treat 8-bit ECMA-48 (C1) escape sequences as zero-width. (#22)
|
||||
|
||||
### Changed
|
||||
- Upgraded uax29 dependency to v2.7.0 for 8-bit escape sequence support in the grapheme iterator.
|
||||
- Truncation now validates that preserved trailing escape sequences are zero-width, preventing edge cases where non-zero-width sequences could leak into output.
|
||||
|
||||
### Note
|
||||
- `ControlSequences8Bit` is deliberately ignored by `TruncateString` and `TruncateBytes`, because C1 byte values (0x80–0x9F) overlap with UTF-8 multi-byte encoding.
|
||||
|
||||
## [0.10.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.9.0...v0.10.0)
|
||||
|
||||
### Added
|
||||
- New `ControlSequences` option to treat ECMA-48/ANSI escape sequences as zero-width. (#20)
|
||||
- `TruncateString` and `TruncateBytes` now preserve trailing ANSI escape sequences (such as SGR resets) when `ControlSequences` is true, preventing color bleed in terminal output.
|
||||
|
||||
### Changed
|
||||
- Removed `stringish` dependency; generic type constraints are now inline `~string | []byte`.
|
||||
- Upgraded uax29 dependency to v2.6.0 for ANSI escape sequence support in the grapheme iterator.
|
||||
|
||||
## [0.9.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.8.0...v0.9.0)
|
||||
|
||||
### Changed
|
||||
- Unicode 17 support: East Asian Width and emoji data updated to Unicode 17.0.0. (#18)
|
||||
- Upgraded uax29 dependency to v2.5.0 (Unicode 17 grapheme segmentation).
|
||||
|
||||
## [0.8.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.7.0...v0.8.0)
|
||||
|
||||
### Changed
|
||||
- Performance: ASCII fast path that applies to any run of printable
|
||||
ASCII. 2x-10x faster for ASCII text vs v0.7.0. (#16)
|
||||
- Upgraded uax29 dependency to v2.4.0 for Unicode 16 support. Text that includes
|
||||
Indic_Conjunct_Break may segment differently (and more correctly). (#15)
|
||||
|
||||
## [0.7.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.2...v0.7.0)
|
||||
|
||||
### Added
|
||||
- New `TruncateString` and `TruncateBytes` methods to truncate strings to a
|
||||
maximum display width, with optional tail (like an ellipsis). (#13)
|
||||
|
||||
## [0.6.2]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.1...v0.6.2)
|
||||
|
||||
### Changed
|
||||
- Internal: reduced property categories for simpler trie.
|
||||
|
||||
## [0.6.1]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.6.0...v0.6.1)
|
||||
|
||||
### Changed
|
||||
- Perf improvements: replaced the ASCII lookup table with a simple
|
||||
function. A bit more cache-friendly. More inlining.
|
||||
- Bug fix: single regional indicators are now treated as width 2, since that
|
||||
is what actual terminals do.
|
||||
|
||||
## [0.6.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.5.0...v0.6.0)
|
||||
|
||||
### Added
|
||||
- New `StringGraphemes` and `BytesGraphemes` methods, for iterating over the
|
||||
widths of grapheme clusters.
|
||||
|
||||
### Changed
|
||||
- Fast ASCII lookups
|
||||
|
||||
## [0.5.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.4.1...v0.5.0)
|
||||
|
||||
### Added
|
||||
- Unicode 16 support
|
||||
- Improved emoji presentation handling per Unicode TR51
|
||||
|
||||
### Changed
|
||||
- Corrected VS15 (U+FE0E) handling: now preserves base character width (no-op) per Unicode TR51
|
||||
- Performance optimizations: reduced property lookups
|
||||
|
||||
### Fixed
|
||||
- VS15 variation selector now correctly preserves base character width instead of forcing width 1
|
||||
|
||||
## [0.4.1]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.4.0...v0.4.1)
|
||||
|
||||
### Changed
|
||||
- Updated uax29 dependency
|
||||
- Improved flag handling
|
||||
|
||||
## [0.4.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.3.1...v0.4.0)
|
||||
|
||||
### Added
|
||||
- Support for variation selectors (VS15, VS16) and regional indicator pairs (flags)
|
||||
|
||||
## [0.3.1]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.3.0...v0.3.1)
|
||||
|
||||
### Added
|
||||
- Fuzz testing support
|
||||
|
||||
### Changed
|
||||
- Updated stringish dependency
|
||||
|
||||
## [0.3.0]
|
||||
|
||||
[Compare](https://github.com/clipperhouse/displaywidth/compare/v0.2.0...v0.3.0)
|
||||
|
||||
### Changed
|
||||
- Dropped compatibility with go-runewidth
|
||||
- Trie implementation cleanup
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 Matt Sherman
|
||||
|
||||
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.
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
# displaywidth
|
||||
|
||||
A high-performance Go package for measuring the monospace display width of strings, UTF-8 bytes, and runes.
|
||||
|
||||
[](https://pkg.go.dev/github.com/clipperhouse/displaywidth)
|
||||
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gotest.yml)
|
||||
[](https://github.com/clipperhouse/displaywidth/actions/workflows/gofuzz.yml)
|
||||
|
||||
## Install
|
||||
```bash
|
||||
go get github.com/clipperhouse/displaywidth
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/clipperhouse/displaywidth"
|
||||
)
|
||||
|
||||
func main() {
|
||||
width := displaywidth.String("Hello, 世界!")
|
||||
fmt.Println(width)
|
||||
|
||||
width = displaywidth.Bytes([]byte("🌍"))
|
||||
fmt.Println(width)
|
||||
|
||||
width = displaywidth.Rune('🌍')
|
||||
fmt.Println(width)
|
||||
}
|
||||
```
|
||||
|
||||
For most purposes, you should use the `String` or `Bytes` methods. They sum
|
||||
the widths of grapheme clusters in the string or byte slice.
|
||||
|
||||
> Note: in your application, iterating over runes to measure width is likely incorrect;
|
||||
the smallest unit of display is a grapheme, not a rune.
|
||||
|
||||
### Iterating over graphemes
|
||||
|
||||
If you need the individual graphemes:
|
||||
|
||||
```go
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/clipperhouse/displaywidth"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g := displaywidth.StringGraphemes("Hello, 世界!")
|
||||
for g.Next() {
|
||||
width := g.Width()
|
||||
value := g.Value()
|
||||
// do something with the width or value
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
Create the options you need, and then use methods on the options struct.
|
||||
|
||||
```go
|
||||
var myOptions = displaywidth.Options{
|
||||
EastAsianWidth: true,
|
||||
ControlSequences: true,
|
||||
}
|
||||
|
||||
width := myOptions.String("Hello, 世界!")
|
||||
```
|
||||
|
||||
#### ControlSequences
|
||||
|
||||
`ControlSequences` specifies whether to ignore ECMA-48 escape sequences
|
||||
when calculating the display width. When `false` (default), ANSI escape
|
||||
sequences are treated as just a series of characters. When `true`, they are
|
||||
treated as a single zero-width unit.
|
||||
|
||||
#### ControlSequences8Bit
|
||||
|
||||
`ControlSequences8Bit` specifies whether to ignore 8-bit ECMA-48 escape sequences
|
||||
when calculating the display width. When `false` (default), these are treated
|
||||
as just a series of characters. When `true`, they are treated as a single
|
||||
zero-width unit.
|
||||
|
||||
Note: this option is ignored by the `Truncate` methods, as the concatenation
|
||||
can lead to unintended UTF-8 semantics.
|
||||
|
||||
#### EastAsianWidth
|
||||
|
||||
`EastAsianWidth` defines how
|
||||
[East Asian Ambiguous characters](https://www.unicode.org/reports/tr11/#Ambiguous)
|
||||
are treated.
|
||||
|
||||
When `false` (default), East Asian Ambiguous characters are treated as width 1.
|
||||
When `true`, they are treated as width 2.
|
||||
|
||||
You may wish to configure this based on environment variables or locale.
|
||||
`go-runewidth`, for example, does so
|
||||
[during package initialization](https://github.com/mattn/go-runewidth/blob/master/runewidth.go#L26C1-L45C2). `displaywidth` does not do this automatically, we prefer to leave it to you.
|
||||
|
||||
|
||||
## Technical standards and compatibility
|
||||
|
||||
This package implements the Unicode East Asian Width standard
|
||||
([UAX #11](https://www.unicode.org/reports/tr11/tr11-43.html)), and handles
|
||||
[version selectors](https://en.wikipedia.org/wiki/Variation_Selectors_(Unicode_block)),
|
||||
and [regional indicator pairs](https://en.wikipedia.org/wiki/Regional_indicator_symbol)
|
||||
(flags). We implement [Unicode TR51](https://www.unicode.org/reports/tr51/tr51-27.html)
|
||||
for emojis. We are keeping an eye on
|
||||
[emerging standards](https://www.jeffquast.com/post/state-of-terminal-emulation-2025/).
|
||||
|
||||
For control sequences, we implement the [ECMA-48](https://ecma-international.org/publications-and-standards/standards/ecma-48/) standard for 7-bit and 8-bit control sequences.
|
||||
|
||||
`clipperhouse/displaywidth`, `mattn/go-runewidth`, and `rivo/uniseg` will
|
||||
give the same outputs for most real-world text. Extensive details are in the
|
||||
[compatibility analysis](comparison/COMPATIBILITY_ANALYSIS.md).
|
||||
|
||||
## Invalid UTF-8
|
||||
|
||||
This package does not validate UTF-8. If you pass invalid UTF-8, the results
|
||||
are undefined. We fuzz against invalid UTF-8 to ensure we don't panic or
|
||||
loop indefinitely.
|
||||
|
||||
The `ControlSequences8Bit` option means that we will segment valid 8-bit
|
||||
control sequences, which are typically _not_ valid UTF-8. 8-bit control bytes
|
||||
happen to also be UTF-8 continuation bytes. Use with caution.
|
||||
|
||||
## Prior Art
|
||||
|
||||
[mattn/go-runewidth](https://github.com/mattn/go-runewidth)
|
||||
|
||||
[rivo/uniseg](https://github.com/rivo/uniseg)
|
||||
|
||||
[x/text/width](https://pkg.go.dev/golang.org/x/text/width)
|
||||
|
||||
[x/text/internal/triegen](https://pkg.go.dev/golang.org/x/text/internal/triegen)
|
||||
|
||||
## Benchmarks
|
||||
|
||||
```bash
|
||||
cd comparison
|
||||
go test -bench=. -benchmem
|
||||
```
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/clipperhouse/displaywidth/comparison
|
||||
cpu: Apple M2
|
||||
|
||||
BenchmarkString_Mixed/clipperhouse/displaywidth-8 5784 ns/op 291.69 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Mixed/mattn/go-runewidth-8 14751 ns/op 114.36 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Mixed/rivo/uniseg-8 19360 ns/op 87.14 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkString_ASCII/clipperhouse/displaywidth-8 54.60 ns/op 2344.32 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_ASCII/mattn/go-runewidth-8 1195 ns/op 107.08 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_ASCII/rivo/uniseg-8 1578 ns/op 81.13 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkString_EastAsian/clipperhouse/displaywidth-8 5837 ns/op 289.01 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_EastAsian/mattn/go-runewidth-8 24418 ns/op 69.09 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_EastAsian/rivo/uniseg-8 19339 ns/op 87.23 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkString_Emoji/clipperhouse/displaywidth-8 3225 ns/op 224.51 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Emoji/mattn/go-runewidth-8 4851 ns/op 149.25 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkString_Emoji/rivo/uniseg-8 6591 ns/op 109.85 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_Mixed/clipperhouse/displaywidth-8 3385 ns/op 498.34 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_Mixed/mattn/go-runewidth-8 5354 ns/op 315.07 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_EastAsian/clipperhouse/displaywidth-8 3397 ns/op 496.56 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_EastAsian/mattn/go-runewidth-8 15673 ns/op 107.64 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_ASCII/clipperhouse/displaywidth-8 255.7 ns/op 500.53 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_ASCII/mattn/go-runewidth-8 261.5 ns/op 489.55 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkRune_Emoji/clipperhouse/displaywidth-8 1371 ns/op 528.22 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkRune_Emoji/mattn/go-runewidth-8 2267 ns/op 319.43 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkTruncateWithTail/clipperhouse/displaywidth-8 3229 ns/op 54.82 MB/s 192 B/op 14 allocs/op
|
||||
BenchmarkTruncateWithTail/mattn/go-runewidth-8 8408 ns/op 21.05 MB/s 192 B/op 14 allocs/op
|
||||
|
||||
BenchmarkTruncateWithoutTail/clipperhouse/displaywidth-8 3554 ns/op 64.43 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkTruncateWithoutTail/mattn/go-runewidth-8 11189 ns/op 20.47 MB/s 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
Here are some notes on [how to make Unicode things fast](https://clipperhouse.com/go-unicode/).
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
package displaywidth
|
||||
|
||||
//go:generate go run -C internal/gen .
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
package displaywidth
|
||||
|
||||
import (
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
)
|
||||
|
||||
// Graphemes is an iterator over grapheme clusters.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
type Graphemes[T ~string | []byte] struct {
|
||||
iter *graphemes.Iterator[T]
|
||||
options Options
|
||||
}
|
||||
|
||||
// Next advances the iterator to the next grapheme cluster.
|
||||
func (g *Graphemes[T]) Next() bool {
|
||||
return g.iter.Next()
|
||||
}
|
||||
|
||||
// Value returns the current grapheme cluster.
|
||||
func (g *Graphemes[T]) Value() T {
|
||||
return g.iter.Value()
|
||||
}
|
||||
|
||||
// Width returns the display width of the current grapheme cluster.
|
||||
func (g *Graphemes[T]) Width() int {
|
||||
return graphemeWidth(g.Value(), g.options)
|
||||
}
|
||||
|
||||
// StringGraphemes returns an iterator over grapheme clusters for the given
|
||||
// string.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func StringGraphemes(s string) Graphemes[string] {
|
||||
return DefaultOptions.StringGraphemes(s)
|
||||
}
|
||||
|
||||
// StringGraphemes returns an iterator over grapheme clusters for the given
|
||||
// string, with the given options.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func (options Options) StringGraphemes(s string) Graphemes[string] {
|
||||
g := graphemes.FromString(s)
|
||||
g.AnsiEscapeSequences = options.ControlSequences
|
||||
g.AnsiEscapeSequences8Bit = options.ControlSequences8Bit
|
||||
|
||||
return Graphemes[string]{iter: g, options: options}
|
||||
}
|
||||
|
||||
// BytesGraphemes returns an iterator over grapheme clusters for the given
|
||||
// []byte.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func BytesGraphemes(s []byte) Graphemes[[]byte] {
|
||||
return DefaultOptions.BytesGraphemes(s)
|
||||
}
|
||||
|
||||
// BytesGraphemes returns an iterator over grapheme clusters for the given
|
||||
// []byte, with the given options.
|
||||
//
|
||||
// Iterate using the Next method, and get the width of the current grapheme
|
||||
// using the Width method.
|
||||
func (options Options) BytesGraphemes(s []byte) Graphemes[[]byte] {
|
||||
g := graphemes.FromBytes(s)
|
||||
g.AnsiEscapeSequences = options.ControlSequences
|
||||
g.AnsiEscapeSequences8Bit = options.ControlSequences8Bit
|
||||
|
||||
return Graphemes[[]byte]{iter: g, options: options}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package displaywidth
|
||||
|
||||
// Options allows you to specify the treatment of ambiguous East Asian
|
||||
// characters and ANSI escape sequences.
|
||||
type Options struct {
|
||||
// EastAsianWidth specifies whether to treat ambiguous East Asian characters
|
||||
// as width 1 or 2. When false (default), ambiguous East Asian characters
|
||||
// are treated as width 1. When true, they are width 2.
|
||||
EastAsianWidth bool
|
||||
|
||||
// ControlSequences specifies whether to ignore 7-bit ECMA-48 escape sequences
|
||||
// when calculating the display width. When false (default), ANSI escape
|
||||
// sequences are treated as just a series of characters. When true, they are
|
||||
// treated as a single zero-width unit.
|
||||
ControlSequences bool
|
||||
// ControlSequences8Bit specifies whether to ignore 8-bit ECMA-48 escape sequences
|
||||
// when calculating the display width. When false (default), these are treated
|
||||
// as just a series of characters. When true, they are treated as a single
|
||||
// zero-width unit.
|
||||
ControlSequences8Bit bool
|
||||
}
|
||||
|
||||
// DefaultOptions is the default options for the display width
|
||||
// calculation, which is EastAsianWidth false, ControlSequences false, and
|
||||
// ControlSequences8Bit false.
|
||||
var DefaultOptions = Options{
|
||||
EastAsianWidth: false,
|
||||
ControlSequences: false,
|
||||
ControlSequences8Bit: false,
|
||||
}
|
||||
+1699
File diff suppressed because it is too large
Load Diff
+149
@@ -0,0 +1,149 @@
|
||||
package displaywidth
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
)
|
||||
|
||||
// TruncateString truncates a string to the given maxWidth, and appends the
|
||||
// given tail if the string is truncated.
|
||||
//
|
||||
// It ensures the visible width, including the width of the tail, is less than or
|
||||
// equal to maxWidth.
|
||||
//
|
||||
// When [Options.ControlSequences] is true, 7-bit ANSI escape sequences that
|
||||
// appear after the truncation point are preserved in the output. This ensures
|
||||
// that escape sequences such as SGR resets are not lost, preventing color
|
||||
// bleed in terminal output.
|
||||
//
|
||||
// [Options.ControlSequences8Bit] is ignored by truncation. 8-bit C1 byte values
|
||||
// (0x80-0x9F) overlap with UTF-8 multi-byte encoding, so manipulating them
|
||||
// during truncation can shift byte boundaries and form unintended visible
|
||||
// characters. Use [Options.String] or [Options.Bytes] for 8-bit-aware width
|
||||
// measurement.
|
||||
func (options Options) TruncateString(s string, maxWidth int, tail string) string {
|
||||
// We deliberately ignore ControlSequences8Bit for truncation, see above.
|
||||
options.ControlSequences8Bit = false
|
||||
|
||||
maxWidthWithoutTail := maxWidth - options.String(tail)
|
||||
|
||||
var pos, total int
|
||||
g := graphemes.FromString(s)
|
||||
g.AnsiEscapeSequences = options.ControlSequences
|
||||
|
||||
for g.Next() {
|
||||
gw := graphemeWidth(g.Value(), options)
|
||||
if total+gw <= maxWidthWithoutTail {
|
||||
pos = g.End()
|
||||
}
|
||||
total += gw
|
||||
if total > maxWidth {
|
||||
if options.ControlSequences {
|
||||
// Build result with trailing 7-bit ANSI escape sequences preserved
|
||||
var b strings.Builder
|
||||
b.Grow(len(s) + len(tail)) // at most original + tail
|
||||
b.WriteString(s[:pos])
|
||||
b.WriteString(tail)
|
||||
|
||||
rem := graphemes.FromString(s[pos:])
|
||||
rem.AnsiEscapeSequences = options.ControlSequences
|
||||
|
||||
for rem.Next() {
|
||||
v := rem.Value()
|
||||
// Only preserve 7-bit escapes (ESC = 0x1B) that measure
|
||||
// as zero-width on their own; some sequences (e.g. SOS)
|
||||
// are only valid in their original context.
|
||||
if len(v) > 0 && v[0] == 0x1B && options.String(v) == 0 {
|
||||
b.WriteString(v)
|
||||
}
|
||||
}
|
||||
return b.String()
|
||||
}
|
||||
return s[:pos] + tail
|
||||
}
|
||||
}
|
||||
// No truncation
|
||||
return s
|
||||
}
|
||||
|
||||
// TruncateString truncates a string to the given maxWidth, and appends the
|
||||
// given tail if the string is truncated.
|
||||
//
|
||||
// It ensures the total width, including the width of the tail, is less than or
|
||||
// equal to maxWidth.
|
||||
func TruncateString(s string, maxWidth int, tail string) string {
|
||||
return DefaultOptions.TruncateString(s, maxWidth, tail)
|
||||
}
|
||||
|
||||
// TruncateBytes truncates a []byte to the given maxWidth, and appends the
|
||||
// given tail if the []byte is truncated.
|
||||
//
|
||||
// It ensures the visible width, including the width of the tail, is less than or
|
||||
// equal to maxWidth.
|
||||
//
|
||||
// When [Options.ControlSequences] is true, 7-bit ANSI escape sequences that
|
||||
// appear after the truncation point are preserved in the output. This ensures
|
||||
// that escape sequences such as SGR resets are not lost, preventing color
|
||||
// bleed in terminal output.
|
||||
//
|
||||
// [Options.ControlSequences8Bit] is ignored by truncation. 8-bit C1 byte values
|
||||
// (0x80-0x9F) overlap with UTF-8 multi-byte encoding, so manipulating them
|
||||
// during truncation can shift byte boundaries and form unintended visible
|
||||
// characters. Use [Options.String] or [Options.Bytes] for 8-bit-aware width
|
||||
// measurement.
|
||||
func (options Options) TruncateBytes(s []byte, maxWidth int, tail []byte) []byte {
|
||||
// We deliberately ignore ControlSequences8Bit for truncation, see above.
|
||||
options.ControlSequences8Bit = false
|
||||
|
||||
maxWidthWithoutTail := maxWidth - options.Bytes(tail)
|
||||
|
||||
var pos, total int
|
||||
g := graphemes.FromBytes(s)
|
||||
g.AnsiEscapeSequences = options.ControlSequences
|
||||
|
||||
for g.Next() {
|
||||
gw := graphemeWidth(g.Value(), options)
|
||||
if total+gw <= maxWidthWithoutTail {
|
||||
pos = g.End()
|
||||
}
|
||||
total += gw
|
||||
if total > maxWidth {
|
||||
if options.ControlSequences {
|
||||
// Build result with trailing 7-bit ANSI escape sequences preserved
|
||||
result := make([]byte, 0, len(s)+len(tail)) // at most original + tail
|
||||
result = append(result, s[:pos]...)
|
||||
result = append(result, tail...)
|
||||
|
||||
rem := graphemes.FromBytes(s[pos:])
|
||||
rem.AnsiEscapeSequences = options.ControlSequences
|
||||
|
||||
for rem.Next() {
|
||||
v := rem.Value()
|
||||
// Only preserve 7-bit escapes (ESC = 0x1B) that measure
|
||||
// as zero-width on their own; some sequences (e.g. SOS)
|
||||
// are only valid in their original context.
|
||||
if len(v) > 0 && v[0] == 0x1B && options.Bytes(v) == 0 {
|
||||
result = append(result, v...)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
result := make([]byte, 0, pos+len(tail))
|
||||
result = append(result, s[:pos]...)
|
||||
result = append(result, tail...)
|
||||
return result
|
||||
}
|
||||
}
|
||||
// No truncation
|
||||
return s
|
||||
}
|
||||
|
||||
// TruncateBytes truncates a []byte to the given maxWidth, and appends the
|
||||
// given tail if the []byte is truncated.
|
||||
//
|
||||
// It ensures the total width, including the width of the tail, is less than or
|
||||
// equal to maxWidth.
|
||||
func TruncateBytes(s []byte, maxWidth int, tail []byte) []byte {
|
||||
return DefaultOptions.TruncateBytes(s, maxWidth, tail)
|
||||
}
|
||||
+239
@@ -0,0 +1,239 @@
|
||||
package displaywidth
|
||||
|
||||
import (
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/clipperhouse/uax29/v2/graphemes"
|
||||
)
|
||||
|
||||
// String calculates the display width of a string,
|
||||
// by iterating over grapheme clusters in the string
|
||||
// and summing their widths.
|
||||
func String(s string) int {
|
||||
return DefaultOptions.String(s)
|
||||
}
|
||||
|
||||
// String calculates the display width of a string, for the given options, by
|
||||
// iterating over grapheme clusters in the string and summing their widths.
|
||||
func (options Options) String(s string) int {
|
||||
width := 0
|
||||
pos := 0
|
||||
|
||||
for pos < len(s) {
|
||||
// Try ASCII optimization
|
||||
asciiLen := printableASCIILength(s[pos:])
|
||||
if asciiLen > 0 {
|
||||
width += asciiLen
|
||||
pos += asciiLen
|
||||
continue
|
||||
}
|
||||
|
||||
// Not ASCII, use grapheme parsing
|
||||
g := graphemes.FromString(s[pos:])
|
||||
g.AnsiEscapeSequences = options.ControlSequences
|
||||
g.AnsiEscapeSequences8Bit = options.ControlSequences8Bit
|
||||
|
||||
start := pos
|
||||
|
||||
for g.Next() {
|
||||
v := g.Value()
|
||||
width += graphemeWidth(v, options)
|
||||
pos += len(v)
|
||||
|
||||
// Quick check: if remaining might have printable ASCII, break to outer loop
|
||||
if pos < len(s) && s[pos] >= 0x20 && s[pos] <= 0x7E {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Defensive, should not happen: if no progress was made,
|
||||
// skip a byte to prevent infinite loop. Only applies if
|
||||
// the grapheme parser misbehaves.
|
||||
if pos == start {
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
|
||||
// Bytes calculates the display width of a []byte,
|
||||
// by iterating over grapheme clusters in the byte slice
|
||||
// and summing their widths.
|
||||
func Bytes(s []byte) int {
|
||||
return DefaultOptions.Bytes(s)
|
||||
}
|
||||
|
||||
// Bytes calculates the display width of a []byte, for the given options, by
|
||||
// iterating over grapheme clusters in the slice and summing their widths.
|
||||
func (options Options) Bytes(s []byte) int {
|
||||
width := 0
|
||||
pos := 0
|
||||
|
||||
for pos < len(s) {
|
||||
// Try ASCII optimization
|
||||
asciiLen := printableASCIILength(s[pos:])
|
||||
if asciiLen > 0 {
|
||||
width += asciiLen
|
||||
pos += asciiLen
|
||||
continue
|
||||
}
|
||||
|
||||
// Not ASCII, use grapheme parsing
|
||||
g := graphemes.FromBytes(s[pos:])
|
||||
g.AnsiEscapeSequences = options.ControlSequences
|
||||
g.AnsiEscapeSequences8Bit = options.ControlSequences8Bit
|
||||
|
||||
start := pos
|
||||
|
||||
for g.Next() {
|
||||
v := g.Value()
|
||||
width += graphemeWidth(v, options)
|
||||
pos += len(v)
|
||||
|
||||
// Quick check: if remaining might have printable ASCII, break to outer loop
|
||||
if pos < len(s) && s[pos] >= 0x20 && s[pos] <= 0x7E {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Defensive, should not happen: if no progress was made,
|
||||
// skip a byte to prevent infinite loop. Only applies if
|
||||
// the grapheme parser misbehaves.
|
||||
if pos == start {
|
||||
pos++
|
||||
}
|
||||
}
|
||||
|
||||
return width
|
||||
}
|
||||
|
||||
// Rune calculates the display width of a rune. You
|
||||
// should almost certainly use [String] or [Bytes] for
|
||||
// most purposes.
|
||||
//
|
||||
// The smallest unit of display width is a grapheme
|
||||
// cluster, not a rune. Iterating over runes to measure
|
||||
// width is incorrect in many cases.
|
||||
func Rune(r rune) int {
|
||||
return DefaultOptions.Rune(r)
|
||||
}
|
||||
|
||||
// Rune calculates the display width of a rune, for the given options.
|
||||
//
|
||||
// You should almost certainly use [String] or [Bytes] for most purposes.
|
||||
//
|
||||
// The smallest unit of display width is a grapheme cluster, not a rune.
|
||||
// Iterating over runes to measure width is incorrect in many cases.
|
||||
func (options Options) Rune(r rune) int {
|
||||
if r < utf8.RuneSelf {
|
||||
return asciiWidth(byte(r))
|
||||
}
|
||||
|
||||
// Surrogates (U+D800-U+DFFF) are invalid UTF-8.
|
||||
if r >= 0xD800 && r <= 0xDFFF {
|
||||
return 0
|
||||
}
|
||||
|
||||
var buf [4]byte
|
||||
n := utf8.EncodeRune(buf[:], r)
|
||||
|
||||
// Skip the grapheme iterator
|
||||
return graphemeWidth(buf[:n], options)
|
||||
}
|
||||
|
||||
const _Default property = 0
|
||||
|
||||
// graphemeWidth returns the display width of a grapheme cluster.
|
||||
// The passed string must be a single grapheme cluster.
|
||||
func graphemeWidth[T ~string | []byte](s T, options Options) int {
|
||||
if len(s) == 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
// C1 controls (0x80-0x9F) are zero-width when 8-bit control sequences
|
||||
// are enabled. This must be checked before the single-byte optimization
|
||||
// below, which would otherwise return width 1 for these bytes.
|
||||
if options.ControlSequences8Bit && s[0] >= 0x80 && s[0] <= 0x9F {
|
||||
return 0
|
||||
}
|
||||
|
||||
// Optimization: single-byte graphemes need no property lookup
|
||||
if len(s) == 1 {
|
||||
return asciiWidth(s[0])
|
||||
}
|
||||
|
||||
// Multi-byte grapheme clusters led by a C0 control (0x00-0x1F)
|
||||
if s[0] <= 0x1F {
|
||||
return 0
|
||||
}
|
||||
|
||||
p, sz := lookup(s)
|
||||
prop := property(p)
|
||||
|
||||
// Variation Selector 16 (VS16) requests emoji presentation
|
||||
if prop != _Wide && sz > 0 && len(s) >= sz+3 {
|
||||
vs := s[sz : sz+3]
|
||||
if isVS16(vs) {
|
||||
prop = _Wide
|
||||
}
|
||||
// VS15 (0x8E) requests text presentation but does not affect width,
|
||||
// in my reading of Unicode TR51. Falls through to return the base
|
||||
// character's property.
|
||||
}
|
||||
|
||||
if options.EastAsianWidth && prop == _East_Asian_Ambiguous {
|
||||
prop = _Wide
|
||||
}
|
||||
|
||||
if prop > upperBound {
|
||||
prop = _Default
|
||||
}
|
||||
|
||||
return propertyWidths[prop]
|
||||
}
|
||||
|
||||
func asciiWidth(b byte) int {
|
||||
if b <= 0x1F || b == 0x7F {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
// printableASCIILength returns the length of consecutive printable ASCII bytes
|
||||
// starting at the beginning of s.
|
||||
func printableASCIILength[T string | []byte](s T) int {
|
||||
i := 0
|
||||
for ; i < len(s); i++ {
|
||||
b := s[i]
|
||||
// Printable ASCII is 0x20-0x7E (space through tilde)
|
||||
if b < 0x20 || b > 0x7E {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// If the next byte is non-ASCII (>= 0x80), back off by 1. The grapheme
|
||||
// parser may group the last ASCII byte with subsequent non-ASCII bytes,
|
||||
// such as combining marks.
|
||||
if i > 0 && i < len(s) && s[i] >= 0x80 {
|
||||
i--
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
// isVS16 checks if the slice matches VS16 (U+FE0F) UTF-8 encoding
|
||||
// (EF B8 8F). It assumes len(s) >= 3.
|
||||
func isVS16[T ~string | []byte](s T) bool {
|
||||
return s[0] == 0xEF && s[1] == 0xB8 && s[2] == 0x8F
|
||||
}
|
||||
|
||||
// propertyWidths is a jump table of sorts, instead of a switch
|
||||
var propertyWidths = [4]int{
|
||||
_Default: 1,
|
||||
_Zero_Width: 0,
|
||||
_Wide: 2,
|
||||
_East_Asian_Ambiguous: 1,
|
||||
}
|
||||
|
||||
const upperBound = property(len(propertyWidths) - 1)
|
||||
+63
-25
@@ -1,32 +1,34 @@
|
||||
An implementation of grapheme cluster boundaries from [Unicode text segmentation](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (UAX 29), for Unicode version 15.0.0.
|
||||
An implementation of grapheme cluster boundaries from [Unicode text segmentation](https://unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries) (UAX 29), for Unicode 17.
|
||||
|
||||
[](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes)
|
||||

|
||||

|
||||
|
||||
## Quick start
|
||||
|
||||
```
|
||||
go get "github.com/clipperhouse/uax29/v2/graphemes"
|
||||
go get github.com/clipperhouse/uax29/v2/graphemes
|
||||
```
|
||||
|
||||
```go
|
||||
import "github.com/clipperhouse/uax29/v2/graphemes"
|
||||
|
||||
text := "Hello, 世界. Nice dog! 👍🐶"
|
||||
g := graphemes.FromString(text)
|
||||
|
||||
tokens := graphemes.FromString(text)
|
||||
|
||||
for tokens.Next() { // Next() returns true until end of data
|
||||
fmt.Println(tokens.Value()) // Do something with the current grapheme
|
||||
for g.Next() { // Next() returns true until end of data
|
||||
fmt.Println(g.Value()) // Do something with the current grapheme
|
||||
}
|
||||
```
|
||||
|
||||
[](https://pkg.go.dev/github.com/clipperhouse/uax29/v2/graphemes)
|
||||
|
||||
_A grapheme is a “single visible character”, which might be a simple as a single letter, or a complex emoji that consists of several Unicode code points._
|
||||
|
||||
## Conformance
|
||||
|
||||
We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Tests29). Status:
|
||||
We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-36.html#Tests29).
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
## APIs
|
||||
|
||||
@@ -34,11 +36,10 @@ We use the Unicode [test suite](https://unicode.org/reports/tr41/tr41-26.html#Te
|
||||
|
||||
```go
|
||||
text := "Hello, 世界. Nice dog! 👍🐶"
|
||||
g := graphemes.FromString(text)
|
||||
|
||||
tokens := graphemes.FromString(text)
|
||||
|
||||
for tokens.Next() { // Next() returns true until end of data
|
||||
fmt.Println(tokens.Value()) // Do something with the current grapheme
|
||||
for g.Next() { // Next() returns true until end of data
|
||||
fmt.Println(g.Value()) // Do something with the current grapheme
|
||||
}
|
||||
```
|
||||
|
||||
@@ -47,15 +48,15 @@ for tokens.Next() { // Next() returns true until end of data
|
||||
`FromReader` embeds a [`bufio.Scanner`](https://pkg.go.dev/bufio#Scanner), so just use those methods.
|
||||
|
||||
```go
|
||||
r := getYourReader() // from a file or network maybe
|
||||
tokens := graphemes.FromReader(r)
|
||||
r := getYourReader() // from a file or network maybe
|
||||
g := graphemes.FromReader(r)
|
||||
|
||||
for tokens.Scan() { // Scan() returns true until error or EOF
|
||||
fmt.Println(tokens.Text()) // Do something with the current grapheme
|
||||
for g.Scan() { // Scan() returns true until error or EOF
|
||||
fmt.Println(g.Text()) // Do something with the current grapheme
|
||||
}
|
||||
|
||||
if tokens.Err() != nil { // Check the error
|
||||
log.Fatal(tokens.Err())
|
||||
if g.Err() != nil { // Check the error
|
||||
log.Fatal(g.Err())
|
||||
}
|
||||
```
|
||||
|
||||
@@ -64,16 +65,53 @@ if tokens.Err() != nil { // Check the error
|
||||
```go
|
||||
b := []byte("Hello, 世界. Nice dog! 👍🐶")
|
||||
|
||||
tokens := graphemes.FromBytes(b)
|
||||
g := graphemes.FromBytes(b)
|
||||
|
||||
for tokens.Next() { // Next() returns true until end of data
|
||||
fmt.Println(tokens.Value()) // Do something with the current grapheme
|
||||
for g.Next() { // Next() returns true until end of data
|
||||
fmt.Println(g.Value()) // Do something with the current grapheme
|
||||
}
|
||||
```
|
||||
|
||||
### Performance
|
||||
### ANSI escape sequences
|
||||
|
||||
On a Mac M2 laptop, we see around 200MB/s, or around 100 million graphemes per second. You should see ~constant memory, and no allocations.
|
||||
By the UAX 29 specification, ANSI escape sequences are not grapheme clusters. To treat 7-bit ANSI escape sequences as a single cluster, set `AnsiEscapeSequences` to true.
|
||||
|
||||
```go
|
||||
text := "Hello, \x1b[31mworld\x1b[0m!"
|
||||
g := graphemes.FromString(text)
|
||||
g.AnsiEscapeSequences = true
|
||||
|
||||
for g.Next() {
|
||||
fmt.Println(g.Value())
|
||||
}
|
||||
```
|
||||
|
||||
To also parse 8-bit C1 controls (non-UTF-8 bytes), set `AnsiEscapeSequences8Bit` to true.
|
||||
|
||||
```go
|
||||
g.AnsiEscapeSequences = true // 7-bit forms (ESC ...)
|
||||
g.AnsiEscapeSequences8Bit = true // 8-bit C1 forms (0x80-0x9F), not valid UTF-8
|
||||
```
|
||||
|
||||
For ESC-initiated (7-bit) control strings, only 7-bit terminators are recognized.
|
||||
For C1-initiated (8-bit) control strings, only C1 ST (`0x9C`) is recognized as ST.
|
||||
|
||||
We implement [ECMA-48](https://ecma-international.org/publications-and-standards/standards/ecma-48/) control codes in both 7-bit and 8-bit representations. 8-bit control codes are not UTF-8 encoded and are not valid UTF-8, caveat emptor.
|
||||
|
||||
### Benchmarks
|
||||
|
||||
```
|
||||
goos: darwin
|
||||
goarch: arm64
|
||||
pkg: github.com/clipperhouse/uax29/graphemes/comparative
|
||||
cpu: Apple M2
|
||||
|
||||
BenchmarkGraphemesMixed/clipperhouse/uax29-8 142635 ns/op 245.12 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkGraphemesMixed/rivo/uniseg-8 2018284 ns/op 17.32 MB/s 0 B/op 0 allocs/op
|
||||
|
||||
BenchmarkGraphemesASCII/clipperhouse/uax29-8 8846 ns/op 508.73 MB/s 0 B/op 0 allocs/op
|
||||
BenchmarkGraphemesASCII/rivo/uniseg-8 366760 ns/op 12.27 MB/s 0 B/op 0 allocs/op
|
||||
```
|
||||
|
||||
### Invalid inputs
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user