diff --git a/.golangci.yml b/.golangci.yml index 4cbe16f..2d4d24e 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -24,7 +24,7 @@ linters: - goimports - goprintffuncname - gosec - - ifshort + # - ifshort - misspell - prealloc - revive diff --git a/Makefile b/Makefile index 6d80864..4985f56 100644 --- a/Makefile +++ b/Makefile @@ -2,8 +2,6 @@ .PHONY: default clean glow run log -LOGFILE := debug.log - default: glow clean: @@ -13,8 +11,7 @@ glow: go build run: clean glow - GLOW_LOGFILE=$(LOGFILE) ./glow + ./glow log: - > $(LOGFILE) - tail -f $(LOGFILE) + tail -f ~/.cache/glow/glow.log diff --git a/README.md b/README.md index 4862ad2..3750751 100644 --- a/README.md +++ b/README.md @@ -20,13 +20,9 @@ Glow is a terminal based markdown reader designed from the ground up to bring out the beauty—and power—of the CLI. Use it to discover markdown files, read documentation directly on the command -line and stash markdown files to your own private collection, so you can read -them anywhere. Glow will find local markdown files in subdirectories or a local +line. Glow will find local markdown files in subdirectories or a local Git repository. -By the way, all data stashed is encrypted end-to-end: only you can decrypt it. -More on that below. - ## Installation ### Package Manager @@ -84,6 +80,7 @@ packages. ARM builds are also available for macOS, Linux, FreeBSD and OpenBSD. ### Go Or just install it with `go`: + ```bash go install github.com/charmbracelet/glow@latest ``` @@ -98,11 +95,10 @@ go build [releases]: https://github.com/charmbracelet/glow/releases - ## The TUI Simply run `glow` without arguments to start the textual user interface and -browse local and stashed markdown. Glow will find local markdown files in the +browse local. Glow will find local markdown files in the current directory and below or, if you’re in a Git repository, Glow will search the repo. @@ -110,15 +106,6 @@ Markdown files can be read with Glow's high-performance pager. Most of the keystrokes you know from `less` are the same, but you can press `?` to list the hotkeys. -### Stashing - -Glow works with the Charm Cloud to allow you to store any markdown files in -your own private collection. You can stash a local document from the Glow TUI by -pressing `s`. - -Stashing is private, its contents will not be exposed publicly, and it's -encrypted end-to-end. More on encryption below. - ## The CLI In addition to a TUI, Glow has a CLI for working with Markdown. To format a @@ -138,18 +125,6 @@ glow github.com/charmbracelet/glow glow https://host.tld/file.md ``` -### Stashing - -You can also stash documents from the CLI: - -```bash -glow stash README.md -``` - -Then, when you run `glow` without arguments will you can browse through your -stashed documents. This is a great way to keep track of things that you need to -reference often. - ### Word Wrapping The `-w` flag lets you set a maximum width at which the output will be wrapped: @@ -211,32 +186,19 @@ pager: true width: 80 ``` -## 🔒 Encryption: How It Works - -Encryption works by issuing symmetric keys (basically a generated password) and -encrypting it with the local SSH public key generated by the open-source -[charm][charmlib] library. That encrypted key is then sent up to our server. -We can’t read it since we don’t have your private key. When you want to decrypt -something or view your stash, that key is downloaded from our server and -decrypted locally using the SSH private key. When you link accounts, the -symmetric key is encrypted for each new public key. This happens on your -machine and not our server, so we never see any unencrypted data. - -[charmlib]: https://github.com/charmbracelet/charm - ## Feedback We’d love to hear your thoughts on this project. Feel free to drop us a note! -* [Twitter](https://twitter.com/charmcli) -* [The Fediverse](https://mastodon.social/@charmcli) -* [Discord](https://charm.sh/chat) +- [Twitter](https://twitter.com/charmcli) +- [The Fediverse](https://mastodon.social/@charmcli) +- [Discord](https://charm.sh/chat) ## License [MIT](https://github.com/charmbracelet/glow/raw/master/LICENSE) -*** +--- Part of [Charm](https://charm.sh). diff --git a/config_cmd.go b/config_cmd.go index a7e59e6..902ea82 100644 --- a/config_cmd.go +++ b/config_cmd.go @@ -13,61 +13,35 @@ import ( const defaultConfig = `# style name or JSON path (default "auto") style: "auto" -# show local files only; no network (TUI-mode only) -local: false # mouse support (TUI-mode only) mouse: false # use pager to display markdown pager: false # word-wrap at width -width: 80` +width: 80 +` + +func defaultConfigFile() string { + scope := gap.NewScope(gap.User, "glow") + path, _ := scope.ConfigPath("glow.yml") + return path +} var configCmd = &cobra.Command{ - Use: "config", - Hidden: false, - Short: "Edit the glow config file", - Long: paragraph(fmt.Sprintf("\n%s the glow config file. We’ll use EDITOR to determine which editor to use. If the config file doesn't exist, it will be created.", keyword("Edit"))), - Example: paragraph("glow config\nglow config --config path/to/config.yml"), - Args: cobra.NoArgs, - SilenceUsage: true, - RunE: func(_ *cobra.Command, _ []string) error { - if configFile == "" { - scope := gap.NewScope(gap.User, "glow") - - var err error - configFile, err = scope.ConfigPath("glow.yml") - if err != nil { - return err - } - } - - if ext := path.Ext(configFile); ext != ".yaml" && ext != ".yml" { - return fmt.Errorf("'%s' is not a supported config type: use '%s' or '%s'", ext, ".yaml", ".yml") - } - - if _, err := os.Stat(configFile); os.IsNotExist(err) { - // File doesn't exist yet, create all necessary directories and - // write the default config file - if err := os.MkdirAll(filepath.Dir(configFile), 0o700); err != nil { - return err - } - - f, err := os.Create(configFile) - if err != nil { - return err - } - defer func() { _ = f.Close() }() - - if _, err := f.WriteString(defaultConfig); err != nil { - return err - } - } else if err != nil { // some other error occurred + Use: "config", + Hidden: false, + Short: "Edit the glow config file", + Long: paragraph(fmt.Sprintf("\n%s the glow config file. We’ll use EDITOR to determine which editor to use. If the config file doesn't exist, it will be created.", keyword("Edit"))), + Example: paragraph("glow config\nglow config --config path/to/config.yml"), + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + if err := ensureConfigFile(); err != nil { return err } c, err := editor.Cmd("Glow", configFile) if err != nil { - return fmt.Errorf("could not edit %s: %w", configFile, err) + return err } c.Stdin = os.Stdin c.Stdout = os.Stdout @@ -80,3 +54,37 @@ var configCmd = &cobra.Command{ return nil }, } + +func ensureConfigFile() error { + if configFile == "" { + configFile = defaultConfigFile() + if err := os.MkdirAll(filepath.Dir(configFile), 0o755); err != nil { + return fmt.Errorf("Could not write config file: %w", err) + } + } + + if ext := path.Ext(configFile); ext != ".yaml" && ext != ".yml" { + return fmt.Errorf("'%s' is not a supported config type: use '%s' or '%s'", ext, ".yaml", ".yml") + } + + if _, err := os.Stat(configFile); os.IsNotExist(err) { + // File doesn't exist yet, create all necessary directories and + // write the default config file + if err := os.MkdirAll(filepath.Dir(configFile), 0o700); err != nil { + return err + } + + f, err := os.Create(configFile) + if err != nil { + return err + } + defer func() { _ = f.Close() }() + + if _, err := f.WriteString(defaultConfig); err != nil { + return err + } + } else if err != nil { // some other error occurred + return err + } + return nil +} diff --git a/go.mod b/go.mod index bf98dcb..c92758b 100644 --- a/go.mod +++ b/go.mod @@ -1,72 +1,73 @@ module github.com/charmbracelet/glow -go 1.17 +go 1.21.4 require ( + github.com/adrg/xdg v0.4.0 github.com/atotto/clipboard v0.1.4 - github.com/charmbracelet/bubbles v0.15.0 - github.com/charmbracelet/bubbletea v0.25.0 - github.com/charmbracelet/charm v0.8.7 - github.com/charmbracelet/glamour v0.6.0 - github.com/charmbracelet/lipgloss v0.6.0 - github.com/charmbracelet/x/editor v0.0.0-20231116172829-450eedbca1ab + github.com/caarlos0/env/v11 v11.0.1 + github.com/charmbracelet/bubbles v0.18.0 + github.com/charmbracelet/bubbletea v0.26.4 + github.com/charmbracelet/glamour v0.6.1-0.20230519131405-7528eaad6620 + github.com/charmbracelet/lipgloss v0.11.1-0.20240608174255-33b3263db7dd + github.com/charmbracelet/log v0.4.0 + github.com/charmbracelet/x/editor v0.0.0-20240625164403-2627ec16405d github.com/dustin/go-humanize v1.0.1 github.com/mattn/go-runewidth v0.0.15 - github.com/meowgorithm/babyenv v1.3.1 github.com/mitchellh/go-homedir v1.1.0 github.com/muesli/gitcha v0.2.0 github.com/muesli/go-app-paths v0.2.2 github.com/muesli/reflow v0.3.0 github.com/muesli/termenv v0.15.2 - github.com/sahilm/fuzzy v0.1.0 - github.com/segmentio/ksuid v1.0.4 - github.com/spf13/cobra v1.8.0 - github.com/spf13/viper v1.14.0 - golang.org/x/sys v0.17.0 - golang.org/x/term v0.15.0 - golang.org/x/text v0.14.0 + github.com/sahilm/fuzzy v0.1.1 + github.com/spf13/cobra v1.7.0 + github.com/spf13/viper v1.15.0 + golang.org/x/sys v0.21.0 + golang.org/x/term v0.21.0 + golang.org/x/text v0.16.0 ) require ( - github.com/alecthomas/chroma v0.10.0 // indirect + github.com/alecthomas/chroma/v2 v2.8.0 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/aymerick/douceur v0.2.0 // indirect - github.com/calmh/randomart v1.1.0 // indirect - github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect + github.com/charmbracelet/x/ansi v0.1.2 // indirect + github.com/charmbracelet/x/input v0.1.2 // indirect + github.com/charmbracelet/x/term v0.1.1 // indirect + github.com/charmbracelet/x/windows v0.1.2 // indirect github.com/dlclark/regexp2 v1.4.0 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect github.com/gorilla/css v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/kr/pretty v0.3.1 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/magiconair/properties v1.8.6 // indirect - github.com/mattn/go-isatty v0.0.18 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/microcosm-cc/bluemonday v1.0.21 // indirect - github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a // indirect + github.com/microcosm-cc/bluemonday v1.0.25 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect - github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/sasquatch v0.0.0-20200811221207-66979d92330a // indirect github.com/olekukonko/tablewriter v0.0.5 // indirect - github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.0.5 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/pelletier/go-toml/v2 v2.0.6 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/rogpeppe/go-internal v1.12.0 // indirect github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94 // indirect - github.com/spf13/afero v1.9.2 // indirect + github.com/spf13/afero v1.9.3 // indirect github.com/spf13/cast v1.5.0 // indirect github.com/spf13/jwalterweatherman v1.1.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/subosito/gotenv v1.4.1 // indirect - github.com/yuin/goldmark v1.5.2 // indirect - github.com/yuin/goldmark-emoji v1.0.1 // indirect - golang.org/x/crypto v0.17.0 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sync v0.1.0 // indirect - gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect + github.com/subosito/gotenv v1.4.2 // indirect + github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect + github.com/yuin/goldmark v1.5.4 // indirect + github.com/yuin/goldmark-emoji v1.0.2 // indirect + golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/sync v0.7.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index deeaa1e..cdfa083 100644 --- a/go.sum +++ b/go.sum @@ -17,264 +17,78 @@ cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHOb cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= -cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= -cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= -cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= -cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= -cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= -cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= -cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= -cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= -cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= -cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= -cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= -cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= -cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= -cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= -cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= -cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= -cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= -cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= -cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= -cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= -cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= -cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= -cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= -cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= -cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= -cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= -cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= -cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= -cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= -cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= -cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= -cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= -cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= -cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= -cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= -cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= -cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= -cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= -cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= -cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= -cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= -cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= -cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= -cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= -cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= -cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= -cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= -cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= -cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= -cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= -cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= -cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= -cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= -cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= -cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= -cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= -cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= -cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= -cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= -cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= -cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= -cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= -cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= -cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= -cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= -cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= -cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= -cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= -cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= -cloud.google.com/go/firestore v1.8.0/go.mod h1:r3KB8cAdRIe8znzoPWLw8S6gpDVd9treohhn8b09424= -cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= -cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= -cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= -cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= -cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= -cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= -cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= -cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= -cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= -cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= -cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= -cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= -cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= -cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= -cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= -cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= -cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= -cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= -cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= -cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= -cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= -cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= -cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= -cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= -cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= -cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= -cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= -cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= -cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= -cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= -cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= -cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= -cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= -cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= -cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= -cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= -cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= -cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= -cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= -cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= -cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= -cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= -cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= -cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= -cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= -cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= -cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= -cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= -cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= -cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= -cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= -cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= -cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= -cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= -cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= -cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= -cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= 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= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= -cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= -cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= -cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= -cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= -cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= -cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= -cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= -cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= -cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= -cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= -cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= -cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= -cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek= -github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= -github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-metrics v0.3.10/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4QAOwNTFc= -github.com/armon/go-metrics v0.4.0/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/atotto/clipboard v0.1.2/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= +github.com/adrg/xdg v0.4.0 h1:RzRqFcjH4nE5C6oTAxhBtoE2IRyjBSa62SCbyPidvls= +github.com/adrg/xdg v0.4.0/go.mod h1:N6ag73EX4wyxeaoeHctc1mas01KZgsj5tYiAIwqJE/E= +github.com/alecthomas/assert/v2 v2.2.1 h1:XivOgYcduV98QCahG8T5XTezV5bylXe+lBxLG2K2ink= +github.com/alecthomas/assert/v2 v2.2.1/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ= +github.com/alecthomas/chroma/v2 v2.8.0 h1:w9WJUjFFmHHB2e8mRpL9jjy3alYDlU0QLDezj1xE264= +github.com/alecthomas/chroma/v2 v2.8.0/go.mod h1:yrkMI9807G1ROx13fhe1v6PN2DDeaR73L3d+1nmYQtw= +github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk= +github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4= github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI= -github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= -github.com/aymanbagabas/go-osc52 v1.2.1 h1:q2sWUyDcozPLcLabEMd+a+7Ea2DitxZVN9hTxab9L4E= -github.com/aymanbagabas/go-osc52 v1.2.1/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk= github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/calmh/randomart v1.1.0 h1:evl+iwc10LXtHdMZhzLxmsCQVmWnkXs44SbC6Uk0Il8= -github.com/calmh/randomart v1.1.0/go.mod h1:DQUbPVyP+7PAs21w/AnfMKG5NioxS3TbZ2F9MSK/jFM= +github.com/caarlos0/env/v11 v11.0.1 h1:A8dDt9Ub9ybqRSUF3fQc/TA/gTam2bKT4Pit+cwrsPs= +github.com/caarlos0/env/v11 v11.0.1/go.mod h1:2RC3HQu8BQqtEK3V4iHPxj0jOdWdbPpWJ6pOueeU1xM= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/charmbracelet/bubbles v0.7.5/go.mod h1:IRTORFvhEI6OUH7WhN2Ks8Z8miNGimk1BE6cmHijOkM= -github.com/charmbracelet/bubbles v0.15.0 h1:c5vZ3woHV5W2b8YZI1q7v4ZNQaPetfHuoHzx+56Z6TI= -github.com/charmbracelet/bubbles v0.15.0/go.mod h1:Y7gSFbBzlMpUDR/XM9MhZI374Q+1p1kluf1uLl8iK74= -github.com/charmbracelet/bubbletea v0.12.2/go.mod h1:3gZkYELUOiEUOp0bTInkxguucy/xRbGSOcbMs1geLxg= -github.com/charmbracelet/bubbletea v0.23.1/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= -github.com/charmbracelet/bubbletea v0.23.2/go.mod h1:FaP3WUivcTM0xOKNmhciz60M6I+weYLF76mr1JyI7sM= -github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM= -github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg= -github.com/charmbracelet/charm v0.8.7 h1:FJ9b7IxWUWHOPR72zS/QJLEqtudOB2Mwfc+Sir0eZR8= -github.com/charmbracelet/charm v0.8.7/go.mod h1:ApJYwJljEjODkOYJgFDzbUqztLrCWQct9zyPD+xcVr4= -github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc= -github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.6.0 h1:1StyZB9vBSOyuZxQUcUwGr17JmojPNm87inij9N3wJY= -github.com/charmbracelet/lipgloss v0.6.0/go.mod h1:tHh2wr34xcHjC2HCXIlGSG1jaDF0S0atAUvBMP6Ppuk= -github.com/charmbracelet/x/editor v0.0.0-20231116172829-450eedbca1ab h1:95WbogoQheYFuAUy1olU8OgxrHk2K86zA7mSNELiMfU= -github.com/charmbracelet/x/editor v0.0.0-20231116172829-450eedbca1ab/go.mod h1:lrin7iXW742pX5pePBEWhLPDTp53YW15r/Lp4Rcfg0M= +github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0= +github.com/charmbracelet/bubbles v0.18.0/go.mod h1:08qhZhtIwzgrtBjAcJnij1t1H0ZRjwHyGsy6AL11PSw= +github.com/charmbracelet/bubbletea v0.26.4 h1:2gDkkzLZaTjMl/dQBpNVtnvcCxsh/FCkimep7FC9c40= +github.com/charmbracelet/bubbletea v0.26.4/go.mod h1:P+r+RRA5qtI1DOHNFn0otoNwB4rn+zNAzSj/EXz6xU0= +github.com/charmbracelet/glamour v0.6.1-0.20230519131405-7528eaad6620 h1:kjH+o66+/WLisZBZ+30/5Z6UQk69KVFdqcjr7mlMC8Y= +github.com/charmbracelet/glamour v0.6.1-0.20230519131405-7528eaad6620/go.mod h1:swCB3CXFsh22H1ESDYdY1tirLiNqCziulDyJ1B6Nt7Q= +github.com/charmbracelet/lipgloss v0.11.1-0.20240608174255-33b3263db7dd h1:vK2fQGG+d7VMa/Ly4f6HoCqzTGC8wnwl4dKulWCHaH0= +github.com/charmbracelet/lipgloss v0.11.1-0.20240608174255-33b3263db7dd/go.mod h1:U8h88bAaHc+dNaV2UXjn0U3WChVxskgaD8/52JAIupM= +github.com/charmbracelet/log v0.4.0 h1:G9bQAcx8rWA2T3pWvx7YtPTPwgqpk7D68BX21IRW8ZM= +github.com/charmbracelet/log v0.4.0/go.mod h1:63bXt/djrizTec0l11H20t8FDSvA4CRZJ1KH22MdptM= +github.com/charmbracelet/x/ansi v0.1.2 h1:6+LR39uG8DE6zAmbu023YlqjJHkYXDF1z36ZwzO4xZY= +github.com/charmbracelet/x/ansi v0.1.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/editor v0.0.0-20240625164403-2627ec16405d h1:QCbdJx9CBznXPO5Wk/5FWXft/mSPzeWX5HMdIH9bNAA= +github.com/charmbracelet/x/editor v0.0.0-20240625164403-2627ec16405d/go.mod h1:oivrEbcP/AYt/Hpvk5pwDXXrQ933gQS6UzL6fxqAGSA= +github.com/charmbracelet/x/input v0.1.2 h1:QJAZr33eOhDowkkEQ24rsJy4Llxlm+fRDf/cQrmqJa0= +github.com/charmbracelet/x/input v0.1.2/go.mod h1:LGBim0maUY4Pitjn/4fHnuXb4KirU3DODsyuHuXdOyA= +github.com/charmbracelet/x/term v0.1.1 h1:3cosVAiPOig+EV4X9U+3LDgtwwAoEzJjNdwbXDjF6yI= +github.com/charmbracelet/x/term v0.1.1/go.mod h1:wB1fHt5ECsu3mXYusyzcngVWWlu1KKUmmLhfgr/Flxw= +github.com/charmbracelet/x/windows v0.1.2 h1:Iumiwq2G+BRmgoayww/qfcvof7W/3uLoelhxojXlRWg= +github.com/charmbracelet/x/windows v0.1.2/go.mod h1:GLEO/l+lizvFDBPLIOk+49gdX49L9YWMB5t+DZd0jkQ= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= -github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= -github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= -github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 h1:q2hJAaP1k2wIvVRd/hEHD7lacgqrCPS+k8g1MndzfWY= -github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E= github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= @@ -282,40 +96,22 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= -github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= -github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= -github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4= +github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= 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= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -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.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +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/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -323,8 +119,6 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= -github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -339,10 +133,6 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -353,20 +143,12 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/goterm v0.0.0-20190703233501-fc88cf888a3f/go.mod h1:nOFQdrUlIlx6M6ODdSpBj1NVA+VgLC6kmw60mkw34H4= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -377,342 +159,141 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= -github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= -github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= -github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= -github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= -github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= -github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= -github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY= github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/consul/api v1.15.3/go.mod h1:/g/qgcoBcEXALCNZgRRisyTW0nY86++L0KbeAMXYCeY= -github.com/hashicorp/consul/sdk v0.11.0/go.mod h1:yPkX5Q6CsxTFMjQQDJwzeNmUUF5NUGGbrDsv9wTb8cw= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= -github.com/hashicorp/go-hclog v0.12.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v0.14.1/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-hclog v1.2.0/go.mod h1:whpDNt7SSdeAju8AWKIWsul05p54N/39EeqMAyrmvFQ= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= -github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= -github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= -github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/memberlist v0.3.0/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/memberlist v0.3.1/go.mod h1:MS2lj3INKhZjWNqd3N0m3J+Jxf3DAOnAH9VT3Sh9MUE= -github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= -github.com/hashicorp/serf v0.9.8/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= +github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= +github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lucasb-eyer/go-colorful v1.0.3/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= -github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= -github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= -github.com/mattn/go-isatty v0.0.3/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.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84= -github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.18 h1:DOKFKCQ7FNG2L1rbrmstDN4QVRdS89Nkh85u68Uwp98= -github.com/mattn/go-isatty v0.0.18/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +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-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.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= -github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= -github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/meowgorithm/babyenv v1.3.0/go.mod h1:lwNX+J6AGBFqNrMZ2PTLkM6SO+W4X8DOg9zBDO4j3Ig= -github.com/meowgorithm/babyenv v1.3.1 h1:18ZEYIgbzoFQfRLF9+lxjRfk/ui6w8U0FWl07CgWvvc= -github.com/meowgorithm/babyenv v1.3.1/go.mod h1:lwNX+J6AGBFqNrMZ2PTLkM6SO+W4X8DOg9zBDO4j3Ig= -github.com/microcosm-cc/bluemonday v1.0.21 h1:dNH3e4PSyE4vNX+KlRGHT5KrSvjeUkoNPwEORjffHJg= -github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM= -github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= -github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a h1:eU8j/ClY2Ty3qdHnn0TyW3ivFoPC/0F1gQZz8yTxbbE= -github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a/go.mod h1:v8eSC2SMp9/7FTKUncp7fH9IwPfw+ysMObcEz5FWheQ= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/microcosm-cc/bluemonday v1.0.25 h1:4NEwSfiJ+Wva0VxN5B8OwMicaJvD8r9tlJWm9rtloEg= +github.com/microcosm-cc/bluemonday v1.0.25/go.mod h1:ZIOjCQp1OrzBBPIJmfX4qDYFuhU02nx4bn030ixfHLE= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -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/muesli/ansi v0.0.0-20211018074035-2e021307bc4b h1:1XF24mVaiu7u+CFywTdcDo2ie1pzzhwjt6RHqzpMU34= -github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b/go.mod h1:fQuZ0gauxyBcmsdE3ZT4NasjaRdxmbCS0jRHsrWu3Ho= +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= github.com/muesli/cancelreader v0.2.2 h1:3I4Kt4BQjOR54NavqnDogx/MIoWBFa0StPA8ELUXHmA= github.com/muesli/cancelreader v0.2.2/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= github.com/muesli/gitcha v0.2.0 h1:+wOgT2dI9s2Tznj1t1rb/qkK5e0cb6qD8c4IX2TR/YY= github.com/muesli/gitcha v0.2.0/go.mod h1:Ri8m9TZS4+ORG4JVmVKUQcWZuxDvUW3UKxMdQfzG2zI= -github.com/muesli/go-app-paths v0.2.1/go.mod h1:SxS3Umca63pcFcLtbjVb+J0oD7cl4ixQWoBKhGEtEho= github.com/muesli/go-app-paths v0.2.2 h1:NqG4EEZwNIhBq/pREgfBmgDmt3h1Smr1MjZiXbpZUnI= github.com/muesli/go-app-paths v0.2.2/go.mod h1:SxS3Umca63pcFcLtbjVb+J0oD7cl4ixQWoBKhGEtEho= -github.com/muesli/reflow v0.2.1-0.20210115123740-9e1d0d53df68/go.mod h1:Xk+z4oIWdQqJzsxyjgl3P22oYZnHdZ8FFTHAQQt5BMQ= github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s= github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8= -github.com/muesli/sasquatch v0.0.0-20200811221207-66979d92330a h1:Hw/15RYEOUD6T9UCRkUmNBa33kJkH33Fui6hE4sRLKU= -github.com/muesli/sasquatch v0.0.0-20200811221207-66979d92330a/go.mod h1:+XG0ne5zXWBTSbbe7Z3/RWxaT8PZY6zaZ1dX6KjprYY= -github.com/muesli/termenv v0.7.2/go.mod h1:ct2L5N2lmix82RaY3bMWwVu/jUFc9Ule0KGDCiKYPh8= -github.com/muesli/termenv v0.7.4/go.mod h1:pZ7qY9l3F7e5xsAOS0zCew2tME+p7bWeBkotCEcIIcc= -github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= -github.com/muesli/termenv v0.14.0/go.mod h1:kG/pF1E7fh949Xhe156crRUrHNyK221IuGO7Ez60Uc8= github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg= -github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= +github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +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/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94 h1:G04eS0JkAIVZfaJLjla9dNxkJCPiKIGZlw9AfOhzOD0= github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94/go.mod h1:b18R55ulyQ/h3RaWyloPyER7fWQVZvimKKhnI5OfrJQ= -github.com/sagikazarmark/crypt v0.8.0/go.mod h1:TmKwZAo97S4Fy4sfMH/HX/cQP5D+ijra2NyLpNNmttY= -github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= -github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c= -github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= +github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/spf13/afero v1.9.3 h1:41FoI0fD7OR7mGcKE/aOiLkGreyf8ifIOQmJANWogMk= +github.com/spf13/afero v1.9.3/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= -github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= +github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.14.0 h1:Rg7d3Lo706X9tHsJMUjdiwMpHB7W8WnSVOssIY+JElU= -github.com/spf13/viper v1.14.0/go.mod h1:WT//axPky3FdvXHzGw33dNdXXXfFQqmEalje+egj8As= +github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= +github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 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.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.5/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs= -github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= +github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no= +github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark v1.5.2 h1:ALmeCk/px5FSm1MAcFBAsVKZjDuMVj8Tm7FFIlMJnqU= -github.com/yuin/goldmark v1.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os= -github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/etcd/api/v3 v3.5.5/go.mod h1:KFtNaxGDw4Yx/BA4iPPwevUTAuqcsPxzyX8PHydchN8= -go.etcd.io/etcd/client/pkg/v3 v3.5.5/go.mod h1:ggrwbk069qxpKPq8/FKkQ3Xq9y39kbFR4LnKszpRXeQ= -go.etcd.io/etcd/client/v2 v2.305.5/go.mod h1:zQjKllfqfBVyVStbt4FaosoX2iYd8fV/GRy/PbowgP4= -go.etcd.io/etcd/client/v3 v3.5.5/go.mod h1:aApjR4WGlSumpnJ2kloS75h6aHUmAyaPLjHMxpc7E7c= +github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU= +github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/goldmark-emoji v1.0.2 h1:c/RgTShNgHTtc6xdz2KKI74jJr6rWi7FPgnP9GAsO5s= +github.com/yuin/goldmark-emoji v1.0.2/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= -go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/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-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= 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= @@ -723,6 +304,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-20240604190554-fc45aab8b7f8 h1:LoYXNGAShUG3m/ehNk4iFctuhGX/+R1ZpfJ4/ia80JM= +golang.org/x/exp v0.0.0-20240604190554-fc45aab8b7f8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI= 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= @@ -736,7 +319,6 @@ golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRu golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -747,26 +329,18 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -783,33 +357,11 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= -golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= 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= @@ -819,21 +371,6 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= -golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= -golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -844,37 +381,21 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -886,97 +407,36 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201009025420-dfb3f7c4e634/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201020230747-6e5568b54d1a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= -golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= 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= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/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.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 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-20220609170525-579cf78fd858/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -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-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -990,7 +450,6 @@ golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgw golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -1015,7 +474,6 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -1024,24 +482,12 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= -golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= -golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 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= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -1061,36 +507,6 @@ google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz513 google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= -google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= -google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= -google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= -google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= -google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= -google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= -google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= -google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= -google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= -google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= -google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= -google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= -google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= -google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= -google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= -google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= -google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= -google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= -google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= -google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= -google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= -google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= -google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= -google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1121,7 +537,6 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -1134,76 +549,9 @@ google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= -google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= -google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= -google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= -google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= -google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= -google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= -google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= -google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= -google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= -google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= -google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= -google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= -google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= -google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= -google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= -google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= -google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= -google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= -google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= @@ -1215,30 +563,9 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= -google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= -google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= -google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= -google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= -google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= 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= @@ -1249,32 +576,15 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= -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.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -1287,4 +597,3 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9 rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/log.go b/log.go new file mode 100644 index 0000000..330db7c --- /dev/null +++ b/log.go @@ -0,0 +1,27 @@ +package main + +import ( + "os" + + "github.com/adrg/xdg" + "github.com/charmbracelet/log" +) + +func getLogFilePath() (string, error) { + return xdg.CacheFile("glow/glow.log") +} + +func setupLog() (func() error, error) { + // Log to file, if set + logFile, err := getLogFilePath() + if err != nil { + return nil, err + } + f, err := os.OpenFile(logFile, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0o644) + if err != nil { + return nil, err + } + log.SetOutput(f) + log.SetLevel(log.DebugLevel) + return f.Close, nil +} diff --git a/main.go b/main.go index 86a089c..4aac4e7 100644 --- a/main.go +++ b/main.go @@ -11,11 +11,11 @@ import ( "path/filepath" "strings" - tea "github.com/charmbracelet/bubbletea" + "github.com/caarlos0/env/v11" "github.com/charmbracelet/glamour" "github.com/charmbracelet/glow/ui" "github.com/charmbracelet/glow/utils" - "github.com/meowgorithm/babyenv" + "github.com/charmbracelet/log" gap "github.com/muesli/go-app-paths" "github.com/spf13/cobra" "github.com/spf13/viper" @@ -34,17 +34,21 @@ var ( style string width uint showAllFiles bool - localOnly bool mouse bool rootCmd = &cobra.Command{ - Use: "glow [SOURCE|DIR]", - Short: "Render markdown on the CLI, with pizzazz!", - Long: paragraph(fmt.Sprintf("\nRender markdown on the CLI, %s!", keyword("with pizzazz"))), + Use: "glow [SOURCE|DIR]", + Short: "Render markdown on the CLI, with pizzazz!", + Long: paragraph( + fmt.Sprintf("\nRender markdown on the CLI, %s!", keyword("with pizzazz")), + ), SilenceErrors: false, - SilenceUsage: false, + SilenceUsage: true, TraverseChildren: true, - RunE: execute, + PersistentPreRunE: func(cmd *cobra.Command, _ []string) error { + return validateOptions(cmd) + }, + RunE: execute, } ) @@ -103,7 +107,10 @@ func sourceFromArg(arg string) (*source, error) { st, err := os.Stat(arg) if err == nil && st.IsDir() { var src *source - _ = filepath.Walk(arg, func(path string, info os.FileInfo, err error) error { + _ = filepath.Walk(arg, func(path string, _ os.FileInfo, err error) error { + if err != nil { + return err + } for _, v := range readmeNames { if strings.EqualFold(filepath.Base(path), v) { r, err := os.Open(path) @@ -137,13 +144,12 @@ func sourceFromArg(arg string) (*source, error) { func validateOptions(cmd *cobra.Command) error { // grab config values from Viper width = viper.GetUint("width") - localOnly = viper.GetBool("local") mouse = viper.GetBool("mouse") pager = viper.GetBool("pager") // validate the glamour style style = viper.GetString("style") - if style != "auto" && glamour.DefaultStyles[style] == nil { + if style != glamour.AutoStyle && glamour.DefaultStyles[style] == nil { style = utils.ExpandPath(style) if _, err := os.Stat(style); os.IsNotExist(err) { return fmt.Errorf("Specified style does not exist: %s", style) @@ -188,11 +194,6 @@ func stdinIsPipe() (bool, error) { } func execute(cmd *cobra.Command, args []string) error { - initConfig() - if err := validateOptions(cmd); err != nil { - return err - } - // if stdin is a pipe then use stdin for input. note that you can also // explicitly use a - to read from stdin. if yes, err := stdinIsPipe(); err != nil { @@ -206,7 +207,7 @@ func execute(cmd *cobra.Command, args []string) error { switch len(args) { // TUI running on cwd case 0: - return runTUI("", false) + return runTUI("") // TUI with possible dir argument case 1: @@ -216,7 +217,7 @@ func execute(cmd *cobra.Command, args []string) error { if err == nil && info.IsDir() { p, err := filepath.Abs(args[0]) if err == nil { - return runTUI(p, false) + return runTUI(p) } } fallthrough @@ -259,16 +260,11 @@ func executeCLI(cmd *cobra.Command, src *source, w io.Writer) error { baseURL = u.String() + "/" } - // initialize glamour - var gs glamour.TermRendererOption - if style == "auto" { - gs = glamour.WithEnvironmentConfig() - } else { - gs = glamour.WithStylePath(style) - } + isCode := !utils.IsMarkdownFile(src.URL) + // initialize glamour r, err := glamour.NewTermRenderer( - gs, + utils.GlamourStyle(style, isCode), glamour.WithWordWrap(int(width)), glamour.WithBaseURL(baseURL), glamour.WithPreservedNewLines(), @@ -277,23 +273,28 @@ func executeCLI(cmd *cobra.Command, src *source, w io.Writer) error { return err } - out, err := r.RenderBytes(b) + s := string(b) + ext := filepath.Ext(src.URL) + if isCode { + s = utils.WrapCodeBlock(string(b), ext) + } + + out, err := r.Render(s) if err != nil { return err } // trim lines - lines := strings.Split(string(out), "\n") - var cb strings.Builder + lines := strings.Split(out, "\n") + var content strings.Builder for i, s := range lines { - cb.WriteString(strings.TrimSpace(s)) + content.WriteString(strings.TrimSpace(s)) // don't add an artificial newline after the last split if i+1 < len(lines) { - cb.WriteString("\n") + content.WriteByte('\n') } } - content := cb.String() // display if pager || cmd.Flags().Changed("pager") { @@ -304,44 +305,29 @@ func executeCLI(cmd *cobra.Command, src *source, w io.Writer) error { pa := strings.Split(pagerCmd, " ") c := exec.Command(pa[0], pa[1:]...) // nolint:gosec - c.Stdin = strings.NewReader(content) + c.Stdin = strings.NewReader(content.String()) c.Stdout = os.Stdout return c.Run() } - fmt.Fprint(w, content) + fmt.Fprint(w, content.String()) //nolint: errcheck return nil } -func runTUI(workingDirectory string, stashedOnly bool) error { +func runTUI(workingDirectory string) error { // Read environment to get debugging stuff - var cfg ui.Config - if err := babyenv.Parse(&cfg); err != nil { + cfg, err := env.ParseAs[ui.Config]() + if err != nil { return fmt.Errorf("error parsing config: %v", err) } - // Log to file, if set - if cfg.Logfile != "" { - f, err := tea.LogToFile(cfg.Logfile, "glow") - if err != nil { - return err - } - defer f.Close() //nolint:errcheck - } - cfg.WorkingDirectory = workingDirectory - cfg.DocumentTypes = ui.NewDocTypeSet() + cfg.ShowAllFiles = showAllFiles cfg.GlamourMaxWidth = width cfg.GlamourStyle = style cfg.EnableMouse = mouse - if stashedOnly { - cfg.DocumentTypes.Add(ui.StashedDoc, ui.NewsDoc) - } else if localOnly { - cfg.DocumentTypes.Add(ui.LocalDoc) - } - // Run Bubble Tea program if _, err := ui.NewProgram(cfg).Run(); err != nil { return err @@ -351,12 +337,20 @@ func runTUI(workingDirectory string, stashedOnly bool) error { } func main() { - if err := rootCmd.Execute(); err != nil { + closer, err := setupLog() + if err != nil { + fmt.Println(err) os.Exit(1) } + if err := rootCmd.Execute(); err != nil { + _ = closer() + os.Exit(1) + } + _ = closer() } func init() { + tryLoadConfigFromDefaultPlaces() if len(CommitSHA) >= 7 { vt := rootCmd.VersionTemplate() rootCmd.SetVersionTemplate(vt[:len(vt)-1] + " (" + CommitSHA[0:7] + ")\n") @@ -366,62 +360,56 @@ func init() { } rootCmd.Version = Version - scope := gap.NewScope(gap.User, "glow") - defaultConfigFile, _ := scope.ConfigPath("glow.yml") - // "Glow Classic" cli arguments - rootCmd.PersistentFlags().StringVar(&configFile, "config", "", fmt.Sprintf("config file (default %s)", defaultConfigFile)) + rootCmd.PersistentFlags().StringVar(&configFile, "config", "", fmt.Sprintf("config file (default %s)", defaultConfigFile())) rootCmd.Flags().BoolVarP(&pager, "pager", "p", false, "display with pager") - rootCmd.Flags().StringVarP(&style, "style", "s", "auto", "style name or JSON path") + rootCmd.Flags().StringVarP(&style, "style", "s", glamour.AutoStyle, "style name or JSON path") rootCmd.Flags().UintVarP(&width, "width", "w", 0, "word-wrap at width") rootCmd.Flags().BoolVarP(&showAllFiles, "all", "a", false, "show system files and directories (TUI-mode only)") - rootCmd.Flags().BoolVarP(&localOnly, "local", "l", false, "show local files only; no network (TUI-mode only)") + rootCmd.Flags().BoolVarP(&mouse, "mouse", "m", false, "enable mouse wheel (TUI-mode only)") _ = rootCmd.Flags().MarkHidden("mouse") // Config bindings _ = viper.BindPFlag("style", rootCmd.Flags().Lookup("style")) _ = viper.BindPFlag("width", rootCmd.Flags().Lookup("width")) - _ = viper.BindPFlag("local", rootCmd.Flags().Lookup("local")) + _ = viper.BindPFlag("debug", rootCmd.Flags().Lookup("debug")) _ = viper.BindPFlag("mouse", rootCmd.Flags().Lookup("mouse")) - viper.SetDefault("style", "auto") + viper.SetDefault("style", glamour.AutoStyle) viper.SetDefault("width", 0) - viper.SetDefault("local", "false") - - // Stash - stashCmd.PersistentFlags().StringVarP(&memo, "memo", "m", "", "memo/note for stashing") - rootCmd.AddCommand(stashCmd) rootCmd.AddCommand(configCmd) } -func initConfig() { - if configFile != "" { - viper.SetConfigFile(configFile) - } else { - scope := gap.NewScope(gap.User, "glow") - dirs, err := scope.ConfigDirs() - if err != nil { - fmt.Println("Can't retrieve default config. Please manually pass a config file with '--config'") - os.Exit(1) - } - - for _, v := range dirs { - viper.AddConfigPath(v) - } - viper.SetConfigName("glow") - viper.SetConfigType("yaml") +func tryLoadConfigFromDefaultPlaces() { + scope := gap.NewScope(gap.User, "glow") + dirs, err := scope.ConfigDirs() + if err != nil { + fmt.Println("Could not load find config directory.") + os.Exit(1) } + for _, v := range dirs { + viper.AddConfigPath(v) + } + + viper.SetConfigName("glow") + viper.SetConfigType("yaml") viper.SetEnvPrefix("glow") viper.AutomaticEnv() if err := viper.ReadInConfig(); err != nil { if _, ok := err.(viper.ConfigFileNotFoundError); !ok { - fmt.Println("Error parsing config:", err) - os.Exit(1) + log.Warn("Could not parse configuration file", "err", err) } } - // fmt.Println("Using config file:", viper.ConfigFileUsed()) + if used := viper.ConfigFileUsed(); used != "" { + log.Debug("Using configuration file", "path", viper.ConfigFileUsed()) + } else { + if err := ensureConfigFile(); err != nil { + fmt.Println("Could not create default config.") + os.Exit(1) + } + } } diff --git a/stash_cmd.go b/stash_cmd.go deleted file mode 100644 index efb8bbd..0000000 --- a/stash_cmd.go +++ /dev/null @@ -1,82 +0,0 @@ -package main - -import ( - "fmt" - "io" - "log" - "os" - "path" - "strings" - - "github.com/charmbracelet/charm" - "github.com/charmbracelet/lipgloss" - "github.com/spf13/cobra" -) - -var ( - memo string - dot = lipgloss.NewStyle().Foreground(lipgloss.Color("#04B575")).Render("•") - - stashCmd = &cobra.Command{ - Use: "stash [SOURCE]", - Hidden: false, - Short: "Stash a markdown", - Long: paragraph(fmt.Sprintf("\nDo %s stuff. Run with no arguments to browse your stash or pass a path to a markdown file to stash it.", keyword("stash"))), - Example: paragraph("glow stash\nglow stash README.md\nglow stash -m \"secret notes\" path/to/notes.md"), - Args: cobra.MaximumNArgs(1), - RunE: func(cmd *cobra.Command, args []string) error { - initConfig() - if len(args) == 0 { - return runTUI("", true) - } - - filePath := args[0] - - if memo == "" { - memo = strings.Replace(path.Base(filePath), path.Ext(filePath), "", 1) - } - - cc := initCharmClient() - f, err := os.Open(filePath) - if err != nil { - return fmt.Errorf("bad filename") - } - - defer f.Close() //nolint:errcheck - b, err := io.ReadAll(f) - if err != nil { - return fmt.Errorf("error reading file") - } - - _, err = cc.StashMarkdown(memo, string(b)) - if err != nil { - return fmt.Errorf("error stashing markdown") - } - - fmt.Println(dot + " Stashed!") - return nil - }, - } -) - -func getCharmConfig() *charm.Config { - cfg, err := charm.ConfigFromEnv() - if err != nil { - log.Fatal(err) - } - - return cfg -} - -func initCharmClient() *charm.Client { - cfg := getCharmConfig() - cc, err := charm.NewClient(cfg) - if err == charm.ErrMissingSSHAuth { - fmt.Println(paragraph("We had some trouble authenticating via SSH. If this continues to happen the Charm tool may be able to help you. More info at https://github.com/charmbracelet/charm.")) - os.Exit(1) - } else if err != nil { - fmt.Println(err) - os.Exit(1) - } - return cc -} diff --git a/style.go b/style.go index 81a1c2d..b688a4e 100644 --- a/style.go +++ b/style.go @@ -1,13 +1,13 @@ package main -import . "github.com/charmbracelet/lipgloss" //nolint:revive +import "github.com/charmbracelet/lipgloss" var ( - keyword = NewStyle(). - Foreground(Color("#04B575")). + keyword = lipgloss.NewStyle(). + Foreground(lipgloss.Color("#04B575")). Render - paragraph = NewStyle(). + paragraph = lipgloss.NewStyle(). Width(78). Padding(0, 0, 0, 2). Render diff --git a/ui/config.go b/ui/config.go index 9db2340..2365528 100644 --- a/ui/config.go +++ b/ui/config.go @@ -12,19 +12,7 @@ type Config struct { // Which directory should we start from? WorkingDirectory string - // Which document types shall we show? - DocumentTypes DocTypeSet - // For debugging the UI - Logfile string `env:"GLOW_LOGFILE"` - HighPerformancePager bool `env:"GLOW_HIGH_PERFORMANCE_PAGER" default:"true"` - GlamourEnabled bool `env:"GLOW_ENABLE_GLAMOUR" default:"true"` -} - -func (c Config) localOnly() bool { - return c.DocumentTypes.Equals(NewDocTypeSet(LocalDoc)) -} - -func (c Config) stashedOnly() bool { - return c.DocumentTypes.Contains(StashedDoc) && !c.DocumentTypes.Contains(LocalDoc) + HighPerformancePager bool `env:"GLOW_HIGH_PERFORMANCE_PAGER" envDefault:"true"` + GlamourEnabled bool `env:"GLOW_ENABLE_GLAMOUR" envDefault:"true"` } diff --git a/ui/consts_unix.go b/ui/consts_unix.go deleted file mode 100644 index 57f75dd..0000000 --- a/ui/consts_unix.go +++ /dev/null @@ -1,8 +0,0 @@ -//go:build !windows -// +build !windows - -package ui - -const ( - pagerStashIcon = "🔒" -) diff --git a/ui/consts_windows.go b/ui/consts_windows.go deleted file mode 100644 index 25f9851..0000000 --- a/ui/consts_windows.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build windows - -package ui - -const ( - pagerStashIcon = "•" -) diff --git a/ui/doctypes.go b/ui/doctypes.go deleted file mode 100644 index 8d2ef14..0000000 --- a/ui/doctypes.go +++ /dev/null @@ -1,90 +0,0 @@ -package ui - -// DocType represents a type of markdown document. -type DocType int - -// Available document types. -const ( - NoDocType DocType = iota - LocalDoc - StashedDoc - ConvertedDoc - NewsDoc -) - -func (d DocType) String() string { - return [...]string{ - "none", - "local", - "stashed", - "converted", - "news", - }[d] -} - -// DocTypeSet is a set (in the mathematic sense) of document types. -type DocTypeSet map[DocType]struct{} - -// NewDocTypeSet returns a set of document types. -func NewDocTypeSet(t ...DocType) DocTypeSet { - d := DocTypeSet(make(map[DocType]struct{})) - if len(t) > 0 { - d.Add(t...) - } - return d -} - -// Add adds a document type of the set. -func (d *DocTypeSet) Add(t ...DocType) int { - for _, v := range t { - (*d)[v] = struct{}{} - } - return len(*d) -} - -// Contains returns whether or not the set contains the given DocTypes. -func (d DocTypeSet) Contains(m ...DocType) bool { - matches := 0 - for _, t := range m { - if _, found := d[t]; found { - matches++ - } - } - return matches == len(m) -} - -// Difference return a DocumentType set that does not contain the given types. -func (d DocTypeSet) Difference(t ...DocType) DocTypeSet { - c := copyDocumentTypes(d) - for k := range c { - for _, docType := range t { - if k == docType { - delete(c, k) - break - } - } - } - return c -} - -// Equals returns whether or not the two sets are equal. -func (d DocTypeSet) Equals(other DocTypeSet) bool { - return d.Contains(other.AsSlice()...) && len(d) == len(other) -} - -// AsSlice returns the set as a slice of document types. -func (d DocTypeSet) AsSlice() (agg []DocType) { - for k := range d { - agg = append(agg, k) - } - return -} - -// Return a copy of the given DoctTypes map. -func copyDocumentTypes(d DocTypeSet) DocTypeSet { - c := make(map[DocType]struct{}) - for k, v := range d { - c[k] = v - } - return c -} diff --git a/ui/doctypes_test.go b/ui/doctypes_test.go deleted file mode 100644 index e882b14..0000000 --- a/ui/doctypes_test.go +++ /dev/null @@ -1,48 +0,0 @@ -package ui - -import ( - "reflect" - "testing" -) - -func TestDocTypeContains(t *testing.T) { - d := NewDocTypeSet(LocalDoc) - - if !d.Contains(LocalDoc) { - t.Error("Contains reported it doesn't contain a value which it absolutely does contain") - } - - if d.Contains(NewsDoc) { - t.Error("Contains reported the set contains a value it certainly does not") - } -} - -func TestDocTypeDifference(t *testing.T) { - original := NewDocTypeSet(LocalDoc, StashedDoc, ConvertedDoc, NewsDoc) - difference := original.Difference(LocalDoc, NewsDoc) - expected := NewDocTypeSet(StashedDoc, ConvertedDoc) - - // Make sure the difference operation worked - if !reflect.DeepEqual(difference, expected) { - t.Errorf("difference returned %+v; expected %+v", difference, expected) - } - - // Make sure original set was not mutated - if reflect.DeepEqual(original, difference) { - t.Errorf("original set was mutated when it should not have been") - } -} - -func TestDocTypeEquality(t *testing.T) { - a := NewDocTypeSet(LocalDoc, StashedDoc) - b := NewDocTypeSet(LocalDoc, StashedDoc) - c := NewDocTypeSet(LocalDoc) - - if !a.Equals(b) { - t.Errorf("Equality test failed for %+v and %+v; expected true, got false", a, b) - } - - if a.Equals(c) { - t.Errorf("Equality test failed for %+v and %+v; expected false, got true", a, c) - } -} diff --git a/ui/editor.go b/ui/editor.go index ff094fc..e2d4439 100644 --- a/ui/editor.go +++ b/ui/editor.go @@ -11,12 +11,9 @@ func openEditor(path string) tea.Cmd { cb := func(err error) tea.Msg { return editorFinishedMsg{err} } - - editor, err := editor.Cmd("Glow", path) + cmd, err := editor.Cmd("Glow", path) if err != nil { - return func() tea.Msg { - return errMsg{err} - } + return func() tea.Msg { return cb(err) } } - return tea.ExecProcess(editor, cb) + return tea.ExecProcess(cmd, cb) } diff --git a/ui/ignore_darwin.go b/ui/ignore_darwin.go index cccd305..b69437a 100644 --- a/ui/ignore_darwin.go +++ b/ui/ignore_darwin.go @@ -1,13 +1,14 @@ +//go:build darwin // +build darwin package ui import "path/filepath" -func ignorePatterns(m model) []string { +func ignorePatterns(m commonModel) []string { return []string{ - filepath.Join(m.common.cfg.HomeDir, "Library"), - m.common.cfg.Gopath, + filepath.Join(m.cfg.HomeDir, "Library"), + m.cfg.Gopath, "node_modules", ".*", } diff --git a/ui/ignore_general.go b/ui/ignore_general.go index 43c1dd1..f4dc8be 100644 --- a/ui/ignore_general.go +++ b/ui/ignore_general.go @@ -3,9 +3,9 @@ package ui -func ignorePatterns(m model) []string { +func ignorePatterns(m commonModel) []string { return []string{ - m.common.cfg.Gopath, + m.cfg.Gopath, "node_modules", ".*", } diff --git a/ui/markdown.go b/ui/markdown.go index e1d5086..8bafad1 100644 --- a/ui/markdown.go +++ b/ui/markdown.go @@ -1,42 +1,18 @@ package ui import ( - "log" "math" - "path" - "strings" "time" "unicode" - "github.com/charmbracelet/charm" + "github.com/charmbracelet/log" "github.com/dustin/go-humanize" - "github.com/segmentio/ksuid" "golang.org/x/text/runes" "golang.org/x/text/transform" "golang.org/x/text/unicode/norm" ) -// markdown wraps charm.Markdown. type markdown struct { - docType DocType - - // Stash identifier. This exists so we can keep track of documents stashed - // in-session as they relate to their original, non-stashed counterparts. - // All documents have a stashID, however when a document is stashed that - // document inherits the stashID of the original. - stashID ksuid.KSUID - - // Unique identifier. Unlike the stash identifier, this value should always - // be unique so we can confidently find it an operate on it (versus stashID, - // which could match both an original or stashed document). - uniqueID ksuid.KSUID - - // Some of the document's original values before this document was stashed. - // These are irrelevant if this document was not stashed in this session. - originalDocType DocType - originalTimestamp time.Time - originalNote string - // Full path of a local markdown file. Only relevant to local documents and // those that have been stashed in this session. localPath string @@ -46,102 +22,24 @@ type markdown struct { // field is ephemeral, and should only be referenced during filtering. filterValue string - charm.Markdown -} - -func (m *markdown) generateIDs() { - if m.stashID.IsNil() { - m.stashID = ksuid.New() - } - m.uniqueID = ksuid.New() -} - -// convertToStashed converts this document into its stashed state. -func (m *markdown) convertToStashed() { - if m.docType == ConvertedDoc { - if debug { - log.Println("not converting already converted document:", m) - } - return - } - - m.originalDocType = m.docType - m.originalTimestamp = m.CreatedAt - m.originalNote = m.Note - - if m.docType == LocalDoc { - m.Note = strings.Replace(path.Base(m.localPath), path.Ext(m.localPath), "", 1) - } - m.CreatedAt = time.Now() - m.docType = ConvertedDoc -} - -// revert reverts this document from its stashed state. -func (m *markdown) revertFromStashed() { - if m.docType != ConvertedDoc { - log.Printf("not reverting document of type %s: %v", m.docType, m) - } - - m.docType = m.originalDocType - m.CreatedAt = m.originalTimestamp - m.Note = m.originalNote + Body string + Note string + Modtime time.Time } // Generate the value we're doing to filter against. func (m *markdown) buildFilterValue() { note, err := normalize(m.Note) if err != nil { - if debug { - log.Printf("error normalizing '%s': %v", m.Note, err) - } + log.Error("error normalizing", "note", m.Note, "error", err) m.filterValue = m.Note } m.filterValue = note } -// shouldSortAsLocal returns whether or not this markdown should be sorted as though -// it's a local markdown document. -func (m markdown) shouldSortAsLocal() bool { - return m.docType == LocalDoc || m.docType == ConvertedDoc -} - -// Sort documents with local files first, then by date. -type markdownsByLocalFirst []*markdown - -func (m markdownsByLocalFirst) Len() int { return len(m) } -func (m markdownsByLocalFirst) Swap(i, j int) { m[i], m[j] = m[j], m[i] } -func (m markdownsByLocalFirst) Less(i, j int) bool { - iIsLocal := m[i].shouldSortAsLocal() - jIsLocal := m[j].shouldSortAsLocal() - - // Local files (and files that used to be local) come first - if iIsLocal && !jIsLocal { - return true - } - if !iIsLocal && jIsLocal { - return false - } - - // If both are local files, sort by filename. Note that we should never - // hit equality here since two files can't have the same path. - if iIsLocal && jIsLocal { - return strings.Compare(m[i].localPath, m[j].localPath) == -1 - } - - // Neither are local files so sort by date descending - if !m[i].CreatedAt.Equal(m[j].CreatedAt) { - return m[i].CreatedAt.After(m[j].CreatedAt) - } - - // If the times also match, sort by unique ID. - ids := []ksuid.KSUID{m[i].uniqueID, m[j].uniqueID} - ksuid.Sort(ids) - return ids[0] == m[i].uniqueID -} - func (m markdown) relativeTime() string { - return relativeTime(m.CreatedAt) + return relativeTime(m.Modtime) } // Normalize text to aid in the filtering process. In particular, we remove @@ -153,18 +51,6 @@ func normalize(in string) (string, error) { return out, err } -// wrapMarkdowns wraps a *charm.Markdown with a *markdown in order to add some -// extra metadata. -func wrapMarkdowns(t DocType, md []*charm.Markdown) (m []*markdown) { - for _, v := range md { - m = append(m, &markdown{ - docType: t, - Markdown: *v, - }) - } - return m -} - // Return the time in a human-readable format relative to the current time. func relativeTime(then time.Time) string { now := time.Now() diff --git a/ui/pager.go b/ui/pager.go index 95f6025..3ed307c 100644 --- a/ui/pager.go +++ b/ui/pager.go @@ -2,25 +2,28 @@ package ui import ( "fmt" - "log" "math" + "path/filepath" "strings" "time" "github.com/atotto/clipboard" - "github.com/charmbracelet/bubbles/spinner" - "github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/charm" "github.com/charmbracelet/glamour" + "github.com/charmbracelet/glow/utils" "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/log" runewidth "github.com/mattn/go-runewidth" "github.com/muesli/reflow/ansi" "github.com/muesli/reflow/truncate" + "github.com/muesli/termenv" ) -const statusBarHeight = 1 +const ( + statusBarHeight = 1 + lineNumberWidth = 4 +) var ( pagerHelpHeight int @@ -28,11 +31,7 @@ var ( mintGreen = lipgloss.AdaptiveColor{Light: "#89F0CB", Dark: "#89F0CB"} darkGreen = lipgloss.AdaptiveColor{Light: "#1C8760", Dark: "#1C8760"} - noteHeading = lipgloss.NewStyle(). - Foreground(cream). - Background(green). - Padding(0, 1). - SetString("Set Memo") + lineNumberFg = lipgloss.AdaptiveColor{Light: "#656565", Dark: "#7D7D7D"} statusBarNoteFg = lipgloss.AdaptiveColor{Light: "#656565", Dark: "#7D7D7D"} statusBarBg = lipgloss.AdaptiveColor{Light: "#E6E6E6", Dark: "#242424"} @@ -52,21 +51,11 @@ var ( Background(lipgloss.AdaptiveColor{Light: "#DCDCDC", Dark: "#323232"}). Render - statusBarStashDotStyle = lipgloss.NewStyle(). - Foreground(green). - Background(statusBarBg). - Render - statusBarMessageStyle = lipgloss.NewStyle(). Foreground(mintGreen). Background(darkGreen). Render - statusBarMessageStashIconStyle = lipgloss.NewStyle(). - Foreground(mintGreen). - Background(darkGreen). - Render - statusBarMessageScrollPosStyle = lipgloss.NewStyle(). Foreground(mintGreen). Background(darkGreen). @@ -82,47 +71,27 @@ var ( Background(lipgloss.AdaptiveColor{Light: "#f2f2f2", Dark: "#1B1B1B"}). Render - spinnerStyle = lipgloss.NewStyle(). - Foreground(statusBarNoteFg). - Background(statusBarBg) - - pagerNoteInputPromptStyle = lipgloss.NewStyle(). - Foreground(darkGray). - Background(yellowGreen). - Padding(0, 1) - - pagerNoteInputStyle = lipgloss.NewStyle(). - Foreground(darkGray). - Background(yellowGreen) - - pagerNoteInputCursorStyle = lipgloss.NewStyle(). - Foreground(fuchsia) + lineNumberStyle = lipgloss.NewStyle(). + Foreground(lineNumberFg). + Render ) type ( contentRenderedMsg string - noteSavedMsg *charm.Markdown ) type pagerState int const ( pagerStateBrowse pagerState = iota - pagerStateSetNote - pagerStateStashing - pagerStateStashSuccess pagerStateStatusMessage ) type pagerModel struct { - common *commonModel - viewport viewport.Model - state pagerState - showHelp bool - textInput textinput.Model - - spinner spinner.Model - spinnerStart time.Time + common *commonModel + viewport viewport.Model + state pagerState + showHelp bool statusMessage string statusMessageTimer *time.Timer @@ -130,10 +99,6 @@ type pagerModel struct { // Current document being rendered, sans-glamour rendering. We cache // it here so we can re-render it on resize. currentDocument markdown - - // Newly stashed markdown. We store it here temporarily so we can replace - // currentDocument above after a stash. - stashedDocument *markdown } func newPagerModel(common *commonModel) pagerModel { @@ -142,34 +107,16 @@ func newPagerModel(common *commonModel) pagerModel { vp.YPosition = 0 vp.HighPerformanceRendering = config.HighPerformancePager - // Text input for notes/memos - ti := textinput.New() - ti.Prompt = " > " - ti.PromptStyle = pagerNoteInputPromptStyle - ti.TextStyle = pagerNoteInputStyle - ti.CursorStyle = pagerNoteInputCursorStyle - ti.CharLimit = noteCharacterLimit - ti.Focus() - - // Text input for search - sp := spinner.New() - sp.Style = spinnerStyle - return pagerModel{ - common: common, - state: pagerStateBrowse, - textInput: ti, - viewport: vp, - spinner: sp, + common: common, + state: pagerStateBrowse, + viewport: vp, } } func (m *pagerModel) setSize(w, h int) { m.viewport.Width = w m.viewport.Height = h - statusBarHeight - m.textInput.Width = w - - ansi.PrintableRuneWidth(noteHeading.String()) - - ansi.PrintableRuneWidth(m.textInput.Prompt) - 1 if m.showHelp { if pagerHelpHeight == 0 { @@ -191,13 +138,18 @@ func (m *pagerModel) toggleHelp() { } } +type pagerStatusMessage struct { + message string + isError bool +} + // Perform stuff that needs to happen after a successful markdown stash. Note // that the the returned command should be sent back the through the pager // update function. -func (m *pagerModel) showStatusMessage(statusMessage string) tea.Cmd { +func (m *pagerModel) showStatusMessage(msg pagerStatusMessage) tea.Cmd { // Show a success message to the user m.state = pagerStateStatusMessage - m.statusMessage = statusMessage + m.statusMessage = msg.message if m.statusMessageTimer != nil { m.statusMessageTimer.Stop() } @@ -216,7 +168,6 @@ func (m *pagerModel) unload() { m.state = pagerStateBrowse m.viewport.SetContent("") m.viewport.YOffset = 0 - m.textInput.Reset() } func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) { @@ -227,126 +178,38 @@ func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: - switch m.state { - case pagerStateSetNote: - switch msg.String() { - case keyEsc: + switch msg.String() { + case "q", keyEsc: + if m.state != pagerStateBrowse { m.state = pagerStateBrowse return m, nil - case keyEnter: - var cmd tea.Cmd - if m.textInput.Value() != m.currentDocument.Note { // don't update if the note didn't change - m.currentDocument.Note = m.textInput.Value() // update optimistically - cmd = saveDocumentNote(m.common.cc, m.currentDocument.ID, m.currentDocument.Note) - } - m.state = pagerStateBrowse - m.textInput.Reset() - return m, cmd } - default: - switch msg.String() { - case "q", keyEsc: - if m.state != pagerStateBrowse { - m.state = pagerStateBrowse - return m, nil - } - case "home", "g": - m.viewport.GotoTop() - if m.viewport.HighPerformanceRendering { - cmds = append(cmds, viewport.Sync(m.viewport)) - } - case "end", "G": - m.viewport.GotoBottom() - if m.viewport.HighPerformanceRendering { - cmds = append(cmds, viewport.Sync(m.viewport)) - } - case "m": - isStashed := m.currentDocument.docType == StashedDoc || - m.currentDocument.docType == ConvertedDoc - - // Users can only set the note on user-stashed markdown - if !isStashed { - break - } - - m.state = pagerStateSetNote - - // Stop the timer for hiding a status message since changing - // the state above will have cleared it. - if m.statusMessageTimer != nil { - m.statusMessageTimer.Stop() - } - - // Pre-populate note with existing value - if m.textInput.Value() == "" { - m.textInput.SetValue(m.currentDocument.Note) - m.textInput.CursorEnd() - } - - return m, textinput.Blink - - case "e": - if m.currentDocument.docType == LocalDoc { - return m, openEditor(m.currentDocument.localPath) - } - - case "c": - err := clipboard.WriteAll(m.currentDocument.Body) - if err != nil { - cmds = append(cmds, m.showStatusMessage("Unable to copy contents")) - } else { - cmds = append(cmds, m.showStatusMessage("Copied contents")) - } - - case "s": - if m.common.authStatus != authOK { - break - } - - md := m.currentDocument - - _, alreadyStashed := m.common.filesStashed[md.stashID] - if alreadyStashed { - cmds = append(cmds, m.showStatusMessage("Already stashed")) - break - } - - // Stash a local document - if m.state != pagerStateStashing && stashableDocTypes.Contains(md.docType) { - m.state = pagerStateStashing - m.spinnerStart = time.Now() - cmds = append( - cmds, - stashDocument(m.common.cc, md), - m.spinner.Tick, - ) - } - case "?": - m.toggleHelp() - if m.viewport.HighPerformanceRendering { - cmds = append(cmds, viewport.Sync(m.viewport)) - } + case "home", "g": + m.viewport.GotoTop() + if m.viewport.HighPerformanceRendering { + cmds = append(cmds, viewport.Sync(m.viewport)) + } + case "end", "G": + m.viewport.GotoBottom() + if m.viewport.HighPerformanceRendering { + cmds = append(cmds, viewport.Sync(m.viewport)) } - } - case spinner.TickMsg: - spinnerMinTimeout := m.spinnerStart. - Add(spinnerVisibilityTimeout). - Add(spinnerMinLifetime) + case "e": + return m, openEditor(m.currentDocument.localPath) - if m.state == pagerStateStashing || time.Now().Before(spinnerMinTimeout) { - // We're either still stashing or we haven't reached the spinner's - // full lifetime. In either case we need to spin the spinner - // irrespective of it's more fine-grained visibility rules. - var cmd tea.Cmd - m.spinner, cmd = m.spinner.Update(msg) - cmds = append(cmds, cmd) - } else if m.state == pagerStateStashSuccess { - // Successful stash. Stop spinning and update accordingly. - m.state = pagerStateBrowse - m.currentDocument = *m.stashedDocument - m.stashedDocument = nil - cmds = append(cmds, m.showStatusMessage("Stashed!")) + case "c": + // Copy using OSC 52 + termenv.Copy(m.currentDocument.Body) + // Copy using native system clipboard + _ = clipboard.WriteAll(m.currentDocument.Body) + cmds = append(cmds, m.showStatusMessage(pagerStatusMessage{"Copied contents", false})) + + case "?": + m.toggleHelp() + if m.viewport.HighPerformanceRendering { + cmds = append(cmds, viewport.Sync(m.viewport)) + } } // Glow has rendered the content @@ -356,75 +219,36 @@ func (m pagerModel) update(msg tea.Msg) (pagerModel, tea.Cmd) { cmds = append(cmds, viewport.Sync(m.viewport)) } + case editMardownMsg: + return m, openEditor(msg.md.localPath) + // We've finished editing the document, potentially making changes. Let's // retrieve the latest version of the document so that we display // up-to-date contents. case editorFinishedMsg: return m, loadLocalMarkdown(&m.currentDocument) - // We've reveived terminal dimensions, either for the first time or + // We've received terminal dimensions, either for the first time or // after a resize case tea.WindowSizeMsg: return m, renderWithGlamour(m, m.currentDocument.Body) - case stashSuccessMsg: - // Stashing was successful. Convert the loaded document to a stashed - // one and show a status message. Note that we're also handling this - // message in the main update function where we're adding this stashed - // item to the stash listing. - m.state = pagerStateStashSuccess - - if !m.spinnerVisible() { - // The spinner has finished spinning, so tell the user the stash - // was successful. - m.state = pagerStateBrowse - m.currentDocument = markdown(msg) - cmds = append(cmds, m.showStatusMessage("Stashed!")) - } else { - // The spinner is still spinning, so just take note of the newly - // stashed document for now. - md := markdown(msg) - m.stashedDocument = &md - } - - case stashFailMsg: - delete(m.common.filesStashed, msg.markdown.stashID) - case statusMessageTimeoutMsg: m.state = pagerStateBrowse } - switch m.state { - case pagerStateSetNote: - m.textInput, cmd = m.textInput.Update(msg) - cmds = append(cmds, cmd) - default: - m.viewport, cmd = m.viewport.Update(msg) - cmds = append(cmds, cmd) - } + m.viewport, cmd = m.viewport.Update(msg) + cmds = append(cmds, cmd) return m, tea.Batch(cmds...) } -// spinnerVisible returns whether or not the spinner should be drawn. -func (m pagerModel) spinnerVisible() bool { - windowStart := m.spinnerStart.Add(spinnerVisibilityTimeout) - windowEnd := windowStart.Add(spinnerMinLifetime) - now := time.Now() - return now.After(windowStart) && now.Before(windowEnd) -} - func (m pagerModel) View() string { var b strings.Builder fmt.Fprint(&b, m.viewport.View()+"\n") // Footer - switch m.state { - case pagerStateSetNote: - m.setNoteView(&b) - default: - m.statusBarView(&b) - } + m.statusBarView(&b) if m.showHelp { fmt.Fprint(&b, "\n"+m.helpView()) @@ -440,11 +264,10 @@ func (m pagerModel) statusBarView(b *strings.Builder) { percentToStringMagnitude float64 = 100.0 ) - isStashed := m.currentDocument.docType == StashedDoc || m.currentDocument.docType == ConvertedDoc showStatusMessage := m.state == pagerStateStatusMessage // Logo - logo := glowLogoView(" Glow ") + logo := glowLogoView() // Scroll percent percent := math.Max(minPercent, math.Min(maxPercent, m.viewport.ScrollPercent())) @@ -463,34 +286,16 @@ func (m pagerModel) statusBarView(b *strings.Builder) { helpNote = statusBarHelpStyle(" ? Help ") } - // Status indicator; spinner or stash dot - var statusIndicator string - if m.state == pagerStateStashing || m.state == pagerStateStashSuccess { - var spinner string - if m.spinnerVisible() { - spinner = m.spinner.View() - } - statusIndicator = statusBarNoteStyle(" ") + spinner - } else if isStashed && showStatusMessage { - statusIndicator = statusBarMessageStashIconStyle(" " + pagerStashIcon) - } else if isStashed { - statusIndicator = statusBarStashDotStyle(" " + pagerStashIcon) - } - // Note var note string if showStatusMessage { note = m.statusMessage } else { note = m.currentDocument.Note - if len(note) == 0 { - note = "(No memo)" - } } note = truncate.StringWithTail(" "+note+" ", uint(max(0, m.common.width- ansi.PrintableRuneWidth(logo)- - ansi.PrintableRuneWidth(statusIndicator)- ansi.PrintableRuneWidth(scrollPercent)- ansi.PrintableRuneWidth(helpNote), )), ellipsis) @@ -504,7 +309,6 @@ func (m pagerModel) statusBarView(b *strings.Builder) { padding := max(0, m.common.width- ansi.PrintableRuneWidth(logo)- - ansi.PrintableRuneWidth(statusIndicator)- ansi.PrintableRuneWidth(note)- ansi.PrintableRuneWidth(scrollPercent)- ansi.PrintableRuneWidth(helpNote), @@ -516,9 +320,8 @@ func (m pagerModel) statusBarView(b *strings.Builder) { emptySpace = statusBarNoteStyle(emptySpace) } - fmt.Fprintf(b, "%s%s%s%s%s%s", + fmt.Fprintf(b, "%s%s%s%s%s", logo, - statusIndicator, note, emptySpace, scrollPercent, @@ -526,36 +329,18 @@ func (m pagerModel) statusBarView(b *strings.Builder) { ) } -func (m pagerModel) setNoteView(b *strings.Builder) { - fmt.Fprint(b, noteHeading) - fmt.Fprint(b, m.textInput.View()) -} - func (m pagerModel) helpView() (s string) { - memoOrStash := "m set memo" - if m.common.authStatus == authOK && m.currentDocument.docType == LocalDoc { - memoOrStash = "s stash this document" - } - - editOrBlank := "e edit this document" - if m.currentDocument.docType != LocalDoc || m.currentDocument.localPath == "" { - editOrBlank = "" - } + edit := "e edit this document" col1 := []string{ "g/home go to top", "G/end go to bottom", "c copy contents", - editOrBlank, - memoOrStash, + edit, "esc back to files", "q quit", } - if m.currentDocument.docType == NewsDoc { - deleteFromStringSlice(col1, 3) - } - s += "\n" s += "k/↑ up " + col1[0] + "\n" s += "j/↓ down " + col1[1] + "\n" @@ -591,9 +376,7 @@ func renderWithGlamour(m pagerModel, md string) tea.Cmd { return func() tea.Msg { s, err := glamourRender(m, md) if err != nil { - if debug { - log.Println("error rendering with Glamour:", err) - } + log.Error("error rendering with Glamour", "error", err) return errMsg{err} } return contentRenderedMsg(s) @@ -602,53 +385,58 @@ func renderWithGlamour(m pagerModel, md string) tea.Cmd { // This is where the magic happens. func glamourRender(m pagerModel, markdown string) (string, error) { + trunc := lipgloss.NewStyle().MaxWidth(m.viewport.Width - lineNumberWidth).Render + if !config.GlamourEnabled { return markdown, nil } - // initialize glamour - var gs glamour.TermRendererOption - if m.common.cfg.GlamourStyle == "auto" { - gs = glamour.WithAutoStyle() - } else { - gs = glamour.WithStylePath(m.common.cfg.GlamourStyle) + isCode := !utils.IsMarkdownFile(m.currentDocument.Note) + width := max(0, min(int(m.common.cfg.GlamourMaxWidth), m.viewport.Width)) + if isCode { + width = 0 } - width := max(0, min(int(m.common.cfg.GlamourMaxWidth), m.viewport.Width)) r, err := glamour.NewTermRenderer( - gs, + utils.GlamourStyle(m.common.cfg.GlamourStyle, isCode), glamour.WithWordWrap(width), ) if err != nil { return "", err } + if isCode { + markdown = utils.WrapCodeBlock(markdown, filepath.Ext(m.currentDocument.Note)) + } + out, err := r.Render(markdown) if err != nil { return "", err } + if isCode { + out = strings.TrimSpace(out) + } + // trim lines lines := strings.Split(out, "\n") - var content string + var content strings.Builder for i, s := range lines { - content += strings.TrimSpace(s) + if isCode { + content.WriteString(lineNumberStyle(fmt.Sprintf("%"+fmt.Sprint(lineNumberWidth)+"d", i+1))) + content.WriteString(trunc(s)) + } else { + content.WriteString(strings.TrimSpace(s)) + } // don't add an artificial newline after the last split if i+1 < len(lines) { - content += "\n" + content.WriteRune('\n') } } - return content, nil + return content.String(), nil } -// ETC - -// Note: this runs in linear time; O(n). -func deleteFromStringSlice(a []string, i int) []string { - copy(a[i:], a[i+1:]) - a[len(a)-1] = "" - return a[:len(a)-1] -} +type editMardownMsg struct{ md *markdown } diff --git a/ui/sort.go b/ui/sort.go new file mode 100644 index 0000000..4f389fb --- /dev/null +++ b/ui/sort.go @@ -0,0 +1,12 @@ +package ui + +import ( + "cmp" + "slices" +) + +func sortMarkdowns(mds []*markdown) { + slices.SortStableFunc(mds, func(a, b *markdown) int { + return cmp.Compare(a.Note, b.Note) + }) +} diff --git a/ui/stash.go b/ui/stash.go index c61ac32..ea2b4ce 100644 --- a/ui/stash.go +++ b/ui/stash.go @@ -3,7 +3,6 @@ package ui import ( "errors" "fmt" - "log" "os" "sort" "strings" @@ -13,8 +12,8 @@ import ( "github.com/charmbracelet/bubbles/spinner" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/charm" "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/log" "github.com/muesli/reflow/ansi" "github.com/muesli/reflow/truncate" "github.com/sahilm/fuzzy" @@ -28,15 +27,11 @@ const ( stashViewHorizontalPadding = 6 ) -var ( - stashingStatusMessage = statusMessage{normalStatusMessage, "Stashing..."} - alreadyStashedStatusMessage = statusMessage{subtleStatusMessage, "Already stashed"} -) +var stashingStatusMessage = statusMessage{normalStatusMessage, "Stashing..."} var ( - dividerDot = darkGrayFg.SetString(" • ") - dividerBar = darkGrayFg.SetString(" │ ") - offlineHeaderNote = darkGrayFg.SetString("(Offline)") + dividerDot = darkGrayFg.SetString(" • ") + dividerBar = darkGrayFg.SetString(" │ ") logoStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("#ECFD65")). @@ -56,17 +51,10 @@ var ( // MSG type ( - deletedStashedItemMsg int - filteredMarkdownMsg []*markdown - fetchedMarkdownMsg *markdown + filteredMarkdownMsg []*markdown + fetchedMarkdownMsg *markdown ) -type markdownFetchFailedMsg struct { - err error - id int - note string -} - // MODEL // stashViewState is the high-level state of the file listing. @@ -82,9 +70,7 @@ const ( type sectionKey int const ( - localSection = iota - stashedSection - newsSection + documentsSection = iota filterSection ) @@ -92,7 +78,6 @@ const ( // its contents in the file listing view. type section struct { key sectionKey - docTypes DocTypeSet paginator paginator.Model cursor int } @@ -109,15 +94,6 @@ const ( filterApplied // a filter is applied and user is not editing filter ) -// selectionState is the state of the currently selected document. -type selectionState int - -const ( - selectionIdle = iota - selectionSettingNote - selectionPromptingDelete -) - // statusMessageType adds some context to the status message being sent. type statusMessageType int @@ -136,24 +112,12 @@ type statusMessage struct { func initSections() { sections = map[sectionKey]section{ - localSection: { - key: localSection, - docTypes: NewDocTypeSet(LocalDoc), - paginator: newStashPaginator(), - }, - stashedSection: { - key: stashedSection, - docTypes: NewDocTypeSet(StashedDoc, ConvertedDoc), - paginator: newStashPaginator(), - }, - newsSection: { - key: newsSection, - docTypes: NewDocTypeSet(NewsDoc), + documentsSection: { + key: documentsSection, paginator: newStashPaginator(), }, filterSection: { key: filterSection, - docTypes: DocTypeSet{}, paginator: newStashPaginator(), }, } @@ -176,12 +140,10 @@ type stashModel struct { common *commonModel err error spinner spinner.Model - noteInput textinput.Model filterInput textinput.Model stashFullyLoaded bool // have we loaded all available stashed documents from the server? viewState stashViewState filterState filterState - selectionState selectionState showFullHelp bool showStatusMessage bool statusMessage statusMessage @@ -194,8 +156,8 @@ type stashModel struct { // Index of the section we're currently looking at sectionIndex int - // Tracks what exactly is loaded between the stash, news and local files - loaded DocTypeSet + // Tracks if docs were loaded + loaded bool // The master set of markdown documents we're working with. markdowns []*markdown @@ -209,19 +171,11 @@ type stashModel struct { // from the local pagination. Generally, the server will return more items // than we can display at a time so we can paginate locally without having // to fetch every time. - serverPage int -} - -func (m stashModel) localOnly() bool { - return m.common.cfg.localOnly() -} - -func (m stashModel) stashedOnly() bool { - return m.common.cfg.stashedOnly() + serverPage int64 } func (m stashModel) loadingDone() bool { - return m.loaded.Equals(m.common.cfg.DocumentTypes.Difference(ConvertedDoc)) + return m.loaded } func (m stashModel) currentSection() *section { @@ -244,51 +198,30 @@ func (m *stashModel) setCursor(i int) { m.currentSection().cursor = i } -// Returns whether or not we're online. That is, when "local-only" mode is -// disabled and we've authenticated successfully. -func (m stashModel) online() bool { - return !m.localOnly() && m.common.authStatus == authOK -} - // Whether or not the spinner should be spinning. func (m stashModel) shouldSpin() bool { loading := !m.loadingDone() - stashing := m.common.isStashing() openingDocument := m.viewState == stashStateLoadingDocument - return loading || stashing || openingDocument + return loading || openingDocument } func (m *stashModel) setSize(width, height int) { m.common.width = width m.common.height = height - m.noteInput.Width = width - stashViewHorizontalPadding*2 - ansi.PrintableRuneWidth(m.noteInput.Prompt) - m.filterInput.Width = width - stashViewHorizontalPadding*2 - ansi.PrintableRuneWidth(m.filterInput.Prompt) + m.filterInput.Width = width - stashViewHorizontalPadding*2 - ansi.PrintableRuneWidth( + m.filterInput.Prompt, + ) m.updatePagination() } -// bakeConvertedDocs turns converted documents into stashed ones. Essentially, -// we're discarding the fact that they were ever converted so we can stop -// treating them like converted documents. -func (m *stashModel) bakeConvertedDocs() { - for _, md := range m.markdowns { - if md.docType == ConvertedDoc { - md.docType = StashedDoc - } - } -} - func (m *stashModel) resetFiltering() { m.filterState = unfiltered m.filterInput.Reset() m.filteredMarkdowns = nil - // Turn converted markdowns into stashed ones so that the next time we - // filter we get both local and stashed results. - m.bakeConvertedDocs() - - sort.Stable(markdownsByLocalFirst(m.markdowns)) + sortMarkdowns(m.markdowns) // If the filtered section is present (it's always at the end) slice it out // of the sections slice to remove it from the UI. @@ -315,7 +248,7 @@ func (m stashModel) filterApplied() bool { func (m stashModel) shouldUpdateFilter() bool { // If we're in the middle of setting a note don't update the filter so that // the focus won't jump around. - return m.filterApplied() && m.selectionState != selectionSettingNote + return m.filterApplied() } // Update pagination according to the amount of markdowns for the current @@ -361,58 +294,16 @@ func (m stashModel) selectedMarkdown() *markdown { // Adds markdown documents to the model. func (m *stashModel) addMarkdowns(mds ...*markdown) { - if len(mds) > 0 { - for _, md := range mds { - md.generateIDs() - } - - m.markdowns = append(m.markdowns, mds...) - if !m.filterApplied() { - sort.Stable(markdownsByLocalFirst(m.markdowns)) - } - m.updatePagination() - } -} - -// Return the number of markdown documents of a given type. -func (m stashModel) countMarkdowns(t DocType) (found int) { - if len(m.markdowns) == 0 { + if len(mds) == 0 { return } - var mds []*markdown - if m.filterState == filtering { - mds = m.getVisibleMarkdowns() - } else { - mds = m.markdowns + m.markdowns = append(m.markdowns, mds...) + if !m.filterApplied() { + sortMarkdowns(m.markdowns) } - for i := 0; i < len(mds); i++ { - if mds[i].docType == t { - found++ - } - } - return -} - -// Sift through the master markdown collection for the specified types. -func (m stashModel) getMarkdownByType(types ...DocType) []*markdown { - var agg []*markdown - - if len(m.markdowns) == 0 { - return agg - } - - for _, t := range types { - for _, md := range m.markdowns { - if md.docType == t { - agg = append(agg, md) - } - } - } - - sort.Stable(markdownsByLocalFirst(agg)) - return agg + m.updatePagination() } // Returns the markdowns that should be currently shown. @@ -421,47 +312,17 @@ func (m stashModel) getVisibleMarkdowns() []*markdown { return m.filteredMarkdowns } - return m.getMarkdownByType(m.currentSection().docTypes.AsSlice()...) -} - -// Return the markdowns eligible to be filtered. -func (m stashModel) getFilterableMarkdowns() (agg []*markdown) { - mds := m.getMarkdownByType(LocalDoc, ConvertedDoc, StashedDoc) - - // Copy values - for _, v := range mds { - p := *v - agg = append(agg, &p) - } - - return + return m.markdowns } // Command for opening a markdown document in the pager. Note that this also // alters the model. func (m *stashModel) openMarkdown(md *markdown) tea.Cmd { - var cmd tea.Cmd m.viewState = stashStateLoadingDocument - - if md.docType == LocalDoc { - cmd = loadLocalMarkdown(md) - } else { - cmd = loadRemoteMarkdown(m.common.cc, md) - } - + cmd := loadLocalMarkdown(md) return tea.Batch(cmd, m.spinner.Tick) } -func (m *stashModel) newStatusMessage(sm statusMessage) tea.Cmd { - m.showStatusMessage = true - m.statusMessage = sm - if m.statusMessageTimer != nil { - m.statusMessageTimer.Stop() - } - m.statusMessageTimer = time.NewTimer(statusMessageTimeout) - return waitForStatusMessageTimeout(stashContext, m.statusMessageTimer) -} - func (m *stashModel) hideStatusMessage() { m.showStatusMessage = false m.statusMessage = statusMessage{} @@ -518,45 +379,21 @@ func newStashModel(common *commonModel) stashModel { sp.Spinner = spinner.Line sp.Style = stashSpinnerStyle - ni := textinput.New() - ni.Prompt = "Memo:" - ni.PromptStyle = stashInputPromptStyle - ni.CursorStyle = stashInputCursorStyle - ni.CharLimit = noteCharacterLimit - ni.Focus() - si := textinput.New() si.Prompt = "Find:" si.PromptStyle = stashInputPromptStyle - si.CursorStyle = stashInputCursorStyle - si.CharLimit = noteCharacterLimit + si.Cursor.Style = stashInputCursorStyle si.Focus() - var s []section - if common.cfg.localOnly() { - s = []section{ - sections[localSection], - } - } else if common.cfg.stashedOnly() { - s = []section{ - sections[stashedSection], - sections[newsSection], - } - } else { - s = []section{ - sections[localSection], - sections[stashedSection], - sections[newsSection], - } + s := []section{ + sections[documentsSection], } m := stashModel{ common: common, spinner: sp, - noteInput: ni, filterInput: si, serverPage: 1, - loaded: NewDocTypeSet(), sections: s, } @@ -580,72 +417,13 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) { case errMsg: m.err = msg - case stashLoadErrMsg: - m.err = msg.err - m.loaded.Add(StashedDoc) // still done, albeit unsuccessfully - m.stashFullyLoaded = true - - case newsLoadErrMsg: - m.err = msg.err - m.loaded.Add(NewsDoc) // still done, albeit unsuccessfully - case localFileSearchFinished: // We're finished searching for local files - m.loaded.Add(LocalDoc) - - case gotStashMsg, gotNewsMsg: - // Stash or news results have come in from the server. - // - // With the stash, this doesn't mean the whole stash listing is loaded, - // but now know it can load, at least, so mark the stash as loaded here. - var docs []*markdown - - switch msg := msg.(type) { - case gotStashMsg: - m.loaded.Add(StashedDoc) - docs = wrapMarkdowns(StashedDoc, msg) - - if len(msg) == 0 { - // If the server comes back with nothing then we've got - // everything - m.stashFullyLoaded = true - } else { - // Load the next page - m.serverPage++ - cmds = append(cmds, loadStash(m)) - } - - case gotNewsMsg: - m.loaded.Add(NewsDoc) - docs = wrapMarkdowns(NewsDoc, msg) - } - - // If we're filtering build filter indexes immediately so any - // matching results will show up in the filter. - if m.filterApplied() { - for _, md := range docs { - md.buildFilterValue() - } - } - if m.shouldUpdateFilter() { - cmds = append(cmds, filterMarkdowns(m)) - } - - m.addMarkdowns(docs...) - - case markdownFetchFailedMsg: - s := "Couldn't load markdown" - if msg.note != "" { - s += ": " + msg.note - } - cmd := m.newStatusMessage(statusMessage{ - status: normalStatusMessage, - message: s, - }) - return m, cmd + m.loaded = true case filteredMarkdownMsg: m.filteredMarkdowns = msg + m.setCursor(0) return m, nil case spinner.TickMsg: @@ -655,28 +433,6 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) { cmds = append(cmds, cmd) } - // A note was set on a document. This may have happened in the pager so - // we'll find the corresponding document here and update accordingly. - case noteSavedMsg: - for i := range m.markdowns { - if m.markdowns[i].ID == msg.ID { - m.markdowns[i].Note = msg.Note - } - } - - case stashSuccessMsg: - // No-op: mechanical stuff related to stash success is handled in the - // parent update function. - - // Note: mechanical stuff related to stash failure is handled in the parent - // update function. - case stashFailMsg: - m.err = msg.err - cmds = append(cmds, m.newStatusMessage(statusMessage{ - status: errorStatusMessage, - message: fmt.Sprintf("Couldn’t stash ‘%s’", msg.markdown.Note), - })) - case statusMessageTimeoutMsg: if applicationContext(msg) == stashContext { m.hideStatusMessage() @@ -688,15 +444,6 @@ func (m stashModel) update(msg tea.Msg) (stashModel, tea.Cmd) { return m, tea.Batch(cmds...) } - switch m.selectionState { - case selectionSettingNote: - cmds = append(cmds, m.handleNoteInput(msg)) - return m, tea.Batch(cmds...) - case selectionPromptingDelete: - cmds = append(cmds, m.handleDeleteConfirmation(msg)) - return m, tea.Batch(cmds...) - } - // Updates per the current state switch m.viewState { case stashStateReady: @@ -765,17 +512,14 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { } m.updatePagination() + case "F": + m.loaded = false + return findLocalFiles(*m.common) + // Edit document in EDITOR case "e": md := m.selectedMarkdown() - if md == nil || md.docType != LocalDoc { - break - } - file := m.selectedMarkdown().localPath - if file == "" { - break - } - return openEditor(file) + return openEditor(md.localPath) // Open document case keyEnter: @@ -793,14 +537,13 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { // Filter your notes case "/": m.hideStatusMessage() - m.bakeConvertedDocs() // Build values we'll filter against for _, md := range m.markdowns { md.buildFilterValue() } - m.filteredMarkdowns = m.getFilterableMarkdowns() + m.filteredMarkdowns = m.markdowns m.paginator().Page = 0 m.setCursor(0) @@ -809,99 +552,6 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { m.filterInput.Focus() return textinput.Blink - // Set note - case "m": - m.hideStatusMessage() - - if numDocs == 0 { - break - } - - md := m.selectedMarkdown() - isUserMarkdown := md.docType == StashedDoc || md.docType == ConvertedDoc - isSettingNote := m.selectionState == selectionSettingNote - isPromptingDelete := m.selectionState == selectionPromptingDelete - - if isUserMarkdown && !isSettingNote && !isPromptingDelete { - m.selectionState = selectionSettingNote - m.noteInput.SetValue(md.Note) - m.noteInput.CursorEnd() - return textinput.Blink - } - - // Stash - case "s": - if numDocs == 0 || !m.online() || m.selectedMarkdown() == nil { - break - } - - md := m.selectedMarkdown() - - // Is this a document we're allowed to stash? - if !stashableDocTypes.Contains(md.docType) { - break - } - - // Was this document already stashed? - if _, alreadyStashed := m.common.filesStashed[md.stashID]; alreadyStashed { - cmds = append(cmds, m.newStatusMessage(alreadyStashedStatusMessage)) - break - } - - // Is the document missing a stash ID? - if md.stashID.IsNil() { - if debug && md.stashID.IsNil() { - log.Printf("refusing to stash markdown; local ID path is nil: %#v", md) - } - break - } - - // Checks passed; perform the stash. - m.common.filesStashed[md.stashID] = struct{}{} - m.common.filesStashing[md.stashID] = struct{}{} - m.common.latestFileStashed = md.stashID - cmds = append(cmds, - stashDocument(m.common.cc, *md), - m.newStatusMessage(stashingStatusMessage), - ) - - // If we're stashing a filtered item, optimistically convert the - // filtered item into a stashed item. - if m.filterApplied() { - for _, v := range m.filteredMarkdowns { - if v.uniqueID == md.uniqueID { - v.convertToStashed() - } - } - } - - // The spinner subtly shows the stash state in a non-optimistic - // fashion, namely because it was originally implemented this way. - // Ideally, if this stash succeeds quickly enough, the spinner - // wouldn't run at all. - cmds = append(cmds, m.spinner.Tick) - - // Prompt for deletion - case "x": - m.hideStatusMessage() - - validState := m.viewState == stashStateReady && - m.selectionState == selectionIdle - - if numDocs == 0 && !validState { - break - } - - md := m.selectedMarkdown() - if md == nil { - break - } - - t := md.docType - if t == StashedDoc || t == ConvertedDoc { - m.selectionState = selectionPromptingDelete - } - // Toggle full help case "?": m.showFullHelp = !m.showFullHelp @@ -942,84 +592,6 @@ func (m *stashModel) handleDocumentBrowsing(msg tea.Msg) tea.Cmd { return tea.Batch(cmds...) } -// Updates for when a user is being prompted whether or not to delete a -// markdown item. -func (m *stashModel) handleDeleteConfirmation(msg tea.Msg) tea.Cmd { - if msg, ok := msg.(tea.KeyMsg); ok { - switch msg.String() { - case "y": - if m.selectionState != selectionPromptingDelete { - break - } - - smd := m.selectedMarkdown() - - for _, md := range m.markdowns { - if md.uniqueID != smd.uniqueID { - continue - } - - // Remove from the things-we-stashed-this-session set - delete(m.common.filesStashed, md.stashID) - - // Delete optimistically and remove the stashed item before - // we've received a success response. - mds, err := deleteMarkdown(m.markdowns, md) - if err == nil { - m.markdowns = mds - } - - break - } - - // Also optimistically delete from filtered markdowns - if m.filterApplied() { - for _, md := range m.filteredMarkdowns { - if md.uniqueID != smd.uniqueID { - continue - } - - switch md.docType { - case ConvertedDoc: - // If the document was stashed in this session, convert it - // back to it's original document type - if md.originalDocType == LocalDoc { - md.revertFromStashed() - break - } - - // Other documents fall through and delete as normal - fallthrough - - // Otherwise, remove the document from the listing - default: - mds, err := deleteMarkdown(m.filteredMarkdowns, md) - if err == nil { - m.filteredMarkdowns = mds - } - } - break - } - } - - m.selectionState = selectionIdle - m.updatePagination() - - if len(m.filteredMarkdowns) == 0 { - m.resetFiltering() - } - - return deleteStashedItem(m.common.cc, smd.ID) - - // Any other key cancels deletion - default: - m.selectionState = selectionIdle - } - } - - return nil -} - // Updates for when a user is in the filter editing interface. func (m *stashModel) handleFiltering(msg tea.Msg) tea.Cmd { var cmds []tea.Cmd @@ -1088,53 +660,6 @@ func (m *stashModel) handleFiltering(msg tea.Msg) tea.Cmd { return tea.Batch(cmds...) } -func (m *stashModel) handleNoteInput(msg tea.Msg) tea.Cmd { - var cmds []tea.Cmd - - if msg, ok := msg.(tea.KeyMsg); ok { - switch msg.String() { - case keyEsc: - // Cancel note - m.noteInput.Reset() - m.selectionState = selectionIdle - case keyEnter: - // Set new note - md := m.selectedMarkdown() - - // If the user is issuing a rename on a newly stashed item in a - // filtered listing, there's a small chance the user could try and - // set a note before the stash is complete. - if md.ID == 0 { - if debug { - log.Printf("user attempted to rename, but markdown ID is 0: %v", md) - } - return m.newStatusMessage(statusMessage{ - status: subtleStatusMessage, - message: "Too fast. Try again in a sec.", - }) - } - - newNote := m.noteInput.Value() - cmd := saveDocumentNote(m.common.cc, md.ID, newNote) - md.Note = newNote - m.noteInput.Reset() - m.selectionState = selectionIdle - return cmd - } - } - - if m.shouldUpdateFilter() { - cmds = append(cmds, filterMarkdowns(*m)) - } - - // Update the note text input component - newNoteInputModel, noteInputCmd := m.noteInput.Update(msg) - m.noteInput = newNoteInputModel - cmds = append(cmds, noteInputCmd) - - return tea.Batch(cmds...) -} - // VIEW func (m stashModel) view() string { @@ -1145,25 +670,14 @@ func (m stashModel) view() string { case stashStateLoadingDocument: s += " " + m.spinner.View() + " Loading document..." case stashStateReady: - loadingIndicator := " " if m.shouldSpin() { loadingIndicator = m.spinner.View() } - var header string - switch m.selectionState { - case selectionPromptingDelete: - header = redFg("Delete this item from your stash? ") + faintRedFg("(y/N)") - case selectionSettingNote: - header = yellowFg("Set the memo for this item?") - } - // Only draw the normal header if we're not using the header area for // something else (like a note or delete prompt). - if header == "" { - header = m.headerView() - } + header := m.headerView() // Rules for the logo, filter and status message. logoOrFilter := " " @@ -1172,7 +686,7 @@ func (m stashModel) view() string { } else if m.filterState == filtering { logoOrFilter += m.filterInput.View() } else { - logoOrFilter += glowLogoView(" Glow ") + logoOrFilter += glowLogoView() if m.showStatusMessage { logoOrFilter += " " + m.statusMessage.String() } @@ -1208,7 +722,7 @@ func (m stashModel) view() string { // One could argue, in fact, that using pointers in // a functional framework is an antipattern and our use of // pointers in our model should be refactored away. - var p paginator.Model = *(m.paginator()) + p := *(m.paginator()) p.Type = paginator.Arabic pagination = paginationStyle.Render(p.View()) } @@ -1232,28 +746,23 @@ func (m stashModel) view() string { return "\n" + indent(s, stashIndent) } -func glowLogoView(text string) string { - return logoStyle.Render(text) +func glowLogoView() string { + return logoStyle.Render(" Glow ") } func (m stashModel) headerView() string { - localCount := m.countMarkdowns(LocalDoc) - stashedCount := m.countMarkdowns(StashedDoc) + m.countMarkdowns(ConvertedDoc) - newsCount := m.countMarkdowns(NewsDoc) + localCount := len(m.markdowns) var sections []string //nolint:prealloc // Filter results if m.filterState == filtering { - if localCount+stashedCount+newsCount == 0 { + if localCount == 0 { return grayFg("Nothing found.") } if localCount > 0 { sections = append(sections, fmt.Sprintf("%d local", localCount)) } - if stashedCount > 0 { - sections = append(sections, fmt.Sprintf("%d stashed", stashedCount)) - } for i := range sections { sections[i] = grayFg(sections[i]) @@ -1262,38 +771,14 @@ func (m stashModel) headerView() string { return strings.Join(sections, dividerDot.String()) } - if m.loadingDone() && len(m.markdowns) == 0 { - var maybeOffline string - if m.common.authStatus == authFailed { - maybeOffline = " " + offlineHeaderNote.String() - } - - if m.stashedOnly() { - return subtleStyle.Render("Can’t load stash") + maybeOffline - } - return subtleStyle.Render("No markdown files found") + maybeOffline - } - // Tabs for i, v := range m.sections { var s string switch v.key { - case localSection: - if m.stashedOnly() { - continue - } - s = fmt.Sprintf("%d local", localCount) - case stashedSection: - if m.localOnly() { - continue - } - s = fmt.Sprintf("%d stashed", stashedCount) - case newsSection: - if m.localOnly() { - continue - } - s = fmt.Sprintf("%d news", newsCount) + case documentsSection: + s = fmt.Sprintf("%d documents", localCount) + case filterSection: s = fmt.Sprintf("%d “%s”", len(m.filteredMarkdowns), m.filterInput.Value()) } @@ -1306,12 +791,7 @@ func (m stashModel) headerView() string { sections = append(sections, s) } - s := strings.Join(sections, dividerBar.String()) - if m.common.authStatus == authFailed { - s += dividerDot.String() + offlineHeaderNote.String() - } - - return s + return strings.Join(sections, dividerBar.String()) } func (m stashModel) populatedView() string { @@ -1326,28 +806,12 @@ func (m stashModel) populatedView() string { } switch m.sections[m.sectionIndex].key { - case localSection: + case documentsSection: if m.loadingDone() { - f("No local files found.") + f("No files found.") } else { f("Looking for local files...") } - case stashedSection: - if m.common.authStatus == authFailed { - f("Can't load your stash. Are you offline?") - } else if m.loadingDone() { - f("Nothing stashed yet.") - } else { - f("Loading your stash...") - } - case newsSection: - if m.common.authStatus == authFailed { - f("Can't load news. Are you offline?") - } else if m.loadingDone() { - f("No stashed files found.") - } else { - f("Loading your stash...") - } case filterSection: return "" } @@ -1384,67 +848,31 @@ func (m stashModel) populatedView() string { // COMMANDS -// loadRemoteMarkdown is a command for loading markdown from the server. -func loadRemoteMarkdown(cc *charm.Client, md *markdown) tea.Cmd { - return func() tea.Msg { - newMD, err := fetchMarkdown(cc, md.ID, md.docType) - if err != nil { - if debug { - log.Printf("error loading %s markdown (ID %d, Note: '%s'): %v", md.docType, md.ID, md.Note, err) - } - return markdownFetchFailedMsg{ - err: err, - id: md.ID, - note: md.Note, - } - } - newMD.stashID = md.stashID - return fetchedMarkdownMsg(newMD) - } -} - func loadLocalMarkdown(md *markdown) tea.Cmd { return func() tea.Msg { - if md.docType != LocalDoc { - return errMsg{errors.New("could not load local file: not a local file")} - } if md.localPath == "" { return errMsg{errors.New("could not load file: missing path")} } data, err := os.ReadFile(md.localPath) if err != nil { - if debug { - log.Println("error reading local markdown:", err) - } + log.Debug("error reading local file", "error", err) return errMsg{err} } + md.Note = md.localPath md.Body = string(data) return fetchedMarkdownMsg(md) } } -func deleteStashedItem(cc *charm.Client, id int) tea.Cmd { - return func() tea.Msg { - err := cc.DeleteMarkdown(id) - if err != nil { - if debug { - log.Println("could not delete stashed item:", err) - } - return errMsg{err} - } - return deletedStashedItemMsg(id) - } -} - func filterMarkdowns(m stashModel) tea.Cmd { return func() tea.Msg { if m.filterInput.Value() == "" || !m.filterApplied() { - return filteredMarkdownMsg(m.getFilterableMarkdowns()) // return everything + return filteredMarkdownMsg(m.markdowns) // return everything } targets := []string{} - mds := m.getFilterableMarkdowns() + mds := m.markdowns for _, t := range mds { targets = append(targets, t.filterValue) @@ -1462,54 +890,6 @@ func filterMarkdowns(m stashModel) tea.Cmd { } } -// ETC - -// fetchMarkdown performs the actual I/O for loading markdown from the sever. -func fetchMarkdown(cc *charm.Client, id int, t DocType) (*markdown, error) { - var md *charm.Markdown - var err error - - switch t { - case StashedDoc, ConvertedDoc: - md, err = cc.GetStashMarkdown(id) - case NewsDoc: - md, err = cc.GetNewsMarkdown(id) - default: - err = fmt.Errorf("unknown markdown type: %s", t) - } - - if err != nil { - return nil, err - } - - return &markdown{ - docType: t, - Markdown: *md, - }, nil -} - -// Delete a markdown from a slice of markdowns. -func deleteMarkdown(markdowns []*markdown, target *markdown) ([]*markdown, error) { - index := -1 - - // Operate on a copy to avoid any pointer weirdness - mds := make([]*markdown, len(markdowns)) - copy(mds, markdowns) - - for i, v := range mds { - if v.uniqueID == target.uniqueID { - index = i - break - } - } - - if index == -1 { - err := fmt.Errorf("could not find markdown to delete") - if debug { - log.Println(err) - } - return nil, err - } - - return append(mds[:index], mds[index+1:]...), nil +func (m *stashModel) loadDocs() tea.Cmd { + return findLocalFiles(*m.common) } diff --git a/ui/stashhelp.go b/ui/stashhelp.go index 2b30c2e..af9c16d 100644 --- a/ui/stashhelp.go +++ b/ui/stashhelp.go @@ -99,17 +99,7 @@ func (m stashModel) helpView() (string, int) { return m.renderHelp(h) } - // Help for when we're interacting with a single document - switch m.selectionState { - case selectionSettingNote: - return m.renderHelp([]string{"enter", "confirm", "esc", "cancel"}, []string{"q", "quit"}) - case selectionPromptingDelete: - return m.renderHelp([]string{"y", "delete", "n", "cancel"}, []string{"q", "quit"}) - } - var ( - isStashed bool - isStashable bool isEditable bool navHelp []string filterHelp []string @@ -119,13 +109,6 @@ func (m stashModel) helpView() (string, int) { appHelp []string ) - if numDocs > 0 { - md := m.selectedMarkdown() - isStashed = md != nil && md.docType == StashedDoc - isStashable = md != nil && md.docType == LocalDoc && m.online() - isEditable = md != nil && md.docType == LocalDoc && md.localPath != "" - } - if numDocs > 0 && m.showFullHelp { navHelp = []string{"enter", "open", "j/k ↑/↓", "choose"} } @@ -143,16 +126,13 @@ func (m stashModel) helpView() (string, int) { } // If we're browsing a filtered set - if m.filterState == filterApplied { - filterHelp = []string{"/", "edit search", "esc", "clear search"} + if m.filterApplied() { + filterHelp = []string{"/", "edit search", "esc", "clear filter"} } else { filterHelp = []string{"/", "find"} - } - - if isStashed { - selectionHelp = []string{"x", "delete", "m", "set memo"} - } else if isStashable { - selectionHelp = []string{"s", "stash"} + if m.stashFullyLoaded { + filterHelp = append(filterHelp, "t", "team filter") + } } if isEditable { @@ -181,13 +161,15 @@ func (m stashModel) helpView() (string, int) { return m.renderHelp(navHelp, filterHelp, selectionHelp, editHelp, sectionHelp, appHelp) } +const minHelpViewHeight = 5 + // renderHelp returns the rendered help view and associated line height for // the given groups of help items. func (m stashModel) renderHelp(groups ...[]string) (string, int) { if m.showFullHelp { str := m.fullHelpView(groups...) numLines := strings.Count(str, "\n") + 1 - return str, numLines + return str, max(numLines, minHelpViewHeight) } return m.miniHelpView(concatStringSlices(groups...)...), 1 } @@ -219,14 +201,8 @@ func (m stashModel) miniHelpView(entries ...string) string { k := entries[i] v := entries[i+1] - switch k { - case "s": - k = greenFg(k) - v = semiDimGreenFg(v) - default: - k = grayFg(k) - v = midGrayFg(v) - } + k = grayFg(k) + v = midGrayFg(v) next = fmt.Sprintf("%s %s", k, v) diff --git a/ui/stashitem.go b/ui/stashitem.go index d853fc7..26e8bb6 100644 --- a/ui/stashitem.go +++ b/ui/stashitem.go @@ -2,47 +2,31 @@ package ui import ( "fmt" - "log" "strings" "github.com/charmbracelet/lipgloss" - "github.com/muesli/reflow/ansi" + "github.com/charmbracelet/log" "github.com/muesli/reflow/truncate" "github.com/sahilm/fuzzy" ) const ( verticalLine = "│" - noMemoTitle = "No Memo" fileListingStashIcon = "• " ) func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { var ( - truncateTo = uint(m.common.width - stashViewHorizontalPadding*2) - gutter string - title = md.Note - date = md.relativeTime() - icon = "" + truncateTo = uint(m.common.width - stashViewHorizontalPadding*2) + gutter string + title = truncate.StringWithTail(md.Note, truncateTo, ellipsis) + date = md.relativeTime() + editedBy = "" + hasEditedBy = false + icon = "" + separator = "" ) - switch md.docType { - case NewsDoc: - if title == "" { - title = "News" - } else { - title = truncate.StringWithTail(title, truncateTo, ellipsis) - } - case StashedDoc, ConvertedDoc: - icon = fileListingStashIcon - if title == "" { - title = noMemoTitle - } - title = truncate.StringWithTail(title, truncateTo-uint(ansi.PrintableRuneWidth(icon)), ellipsis) - default: - title = truncate.StringWithTail(title, truncateTo, ellipsis) - } - isSelected := index == m.cursor() isFiltering := m.filterState == filtering singleFilteredItem := isFiltering && len(m.getVisibleMarkdowns()) == 1 @@ -52,87 +36,65 @@ func stashItemView(b *strings.Builder, m stashModel, index int, md *markdown) { // highlight that first item since pressing return will open it. if isSelected && !isFiltering || singleFilteredItem { // Selected item - - switch m.selectionState { - case selectionPromptingDelete: - gutter = faintRedFg(verticalLine) - icon = faintRedFg(icon) - title = redFg(title) - date = faintRedFg(date) - case selectionSettingNote: - gutter = dullYellowFg(verticalLine) - icon = "" - title = m.noteInput.View() - date = dullYellowFg(date) - default: - if m.common.latestFileStashed == md.stashID && - m.statusMessage == stashingStatusMessage { - gutter = greenFg(verticalLine) - icon = dimGreenFg(icon) - title = greenFg(title) - date = semiDimGreenFg(date) - } else { - gutter = dullFuchsiaFg(verticalLine) - icon = dullFuchsiaFg(icon) - if m.currentSection().key == filterSection && - m.filterState == filterApplied || singleFilteredItem { - s := lipgloss.NewStyle().Foreground(fuchsia) - title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true)) - } else { - title = fuchsiaFg(title) - } - date = dullFuchsiaFg(date) - } - } - } else { - // Regular (non-selected) items - - gutter = " " - - if m.common.latestFileStashed == md.stashID && - m.statusMessage == stashingStatusMessage { + if m.statusMessage == stashingStatusMessage { + gutter = greenFg(verticalLine) icon = dimGreenFg(icon) title = greenFg(title) date = semiDimGreenFg(date) - } else if md.docType == NewsDoc { - if isFiltering && m.filterInput.Value() == "" { - title = dimIndigoFg(title) - date = dimSubtleIndigoFg(date) + editedBy = semiDimGreenFg(editedBy) + separator = semiDimGreenFg(separator) + } else { + gutter = dullFuchsiaFg(verticalLine) + if m.currentSection().key == filterSection && + m.filterState == filterApplied || singleFilteredItem { + s := lipgloss.NewStyle().Foreground(fuchsia) + title = styleFilteredText(title, m.filterInput.Value(), s, s.Underline(true)) } else { - s := lipgloss.NewStyle().Foreground(indigo) - title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true)) - date = subtleIndigoFg(date) + title = fuchsiaFg(title) + icon = fuchsiaFg(icon) } + date = dimFuchsiaFg(date) + editedBy = dimDullFuchsiaFg(editedBy) + separator = dullFuchsiaFg(separator) + } + } else { + gutter = " " + if m.statusMessage == stashingStatusMessage { + icon = dimGreenFg(icon) + title = greenFg(title) + date = semiDimGreenFg(date) + editedBy = semiDimGreenFg(editedBy) + separator = semiDimGreenFg(separator) } else if isFiltering && m.filterInput.Value() == "" { icon = dimGreenFg(icon) - if title == noMemoTitle { - title = dimBrightGrayFg(title) - } else { - title = dimNormalFg(title) - } + title = dimNormalFg(title) date = dimBrightGrayFg(date) + editedBy = dimBrightGrayFg(editedBy) + separator = dimBrightGrayFg(separator) } else { icon = greenFg(icon) - if title == noMemoTitle { - title = brightGrayFg(title) - } else { - s := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#1a1a1a", Dark: "#dddddd"}) - title = styleFilteredText(title, m.filterInput.Value(), s, s.Copy().Underline(true)) - } - date = brightGrayFg(date) + + s := lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#1a1a1a", Dark: "#dddddd"}) + title = styleFilteredText(title, m.filterInput.Value(), s, s.Underline(true)) + date = grayFg(date) + editedBy = midGrayFg(editedBy) + separator = brightGrayFg(separator) } } - fmt.Fprintf(b, "%s %s%s\n", gutter, icon, title) + fmt.Fprintf(b, "%s %s%s%s%s\n", gutter, icon, separator, separator, title) fmt.Fprintf(b, "%s %s", gutter, date) + if hasEditedBy { + fmt.Fprintf(b, " %s", editedBy) + } } func styleFilteredText(haystack, needles string, defaultStyle, matchedStyle lipgloss.Style) string { b := strings.Builder{} normalizedHay, err := normalize(haystack) - if err != nil && debug { - log.Printf("error normalizing '%s': %v", haystack, err) + if err != nil { + log.Error("error normalizing", "haystack", haystack, "error", err) } matches := fuzzy.Find(needles, []string{normalizedHay}) diff --git a/ui/styles.go b/ui/styles.go index e5a6c83..fd25882 100644 --- a/ui/styles.go +++ b/ui/styles.go @@ -1,84 +1,46 @@ package ui -import . "github.com/charmbracelet/lipgloss" //nolint: revive +import "github.com/charmbracelet/lipgloss" // Colors. var ( - normal = AdaptiveColor{Light: "#1A1A1A", Dark: "#dddddd"} - normalDim = AdaptiveColor{Light: "#A49FA5", Dark: "#777777"} - gray = AdaptiveColor{Light: "#909090", Dark: "#626262"} - midGray = AdaptiveColor{Light: "#B2B2B2", Dark: "#4A4A4A"} - darkGray = AdaptiveColor{Light: "#DDDADA", Dark: "#3C3C3C"} - brightGray = AdaptiveColor{Light: "#847A85", Dark: "#979797"} - dimBrightGray = AdaptiveColor{Light: "#C2B8C2", Dark: "#4D4D4D"} - indigo = AdaptiveColor{Light: "#5A56E0", Dark: "#7571F9"} - dimIndigo = AdaptiveColor{Light: "#9498FF", Dark: "#494690"} - subtleIndigo = AdaptiveColor{Light: "#7D79F6", Dark: "#514DC1"} - dimSubtleIndigo = AdaptiveColor{Light: "#BBBDFF", Dark: "#383584"} - cream = AdaptiveColor{Light: "#FFFDF5", Dark: "#FFFDF5"} - yellowGreen = AdaptiveColor{Light: "#04B575", Dark: "#ECFD65"} - dullYellowGreen = AdaptiveColor{Light: "#6BCB94", Dark: "#9BA92F"} - fuchsia = AdaptiveColor{Light: "#EE6FF8", Dark: "#EE6FF8"} - dimFuchsia = AdaptiveColor{Light: "#F1A8FF", Dark: "#99519E"} - dullFuchsia = AdaptiveColor{Dark: "#AD58B4", Light: "#F793FF"} - dimDullFuchsia = AdaptiveColor{Light: "#F6C9FF", Dark: "#6B3A6F"} - green = Color("#04B575") - red = AdaptiveColor{Light: "#FF4672", Dark: "#ED567A"} - faintRed = AdaptiveColor{Light: "#FF6F91", Dark: "#C74665"} - - semiDimGreen = AdaptiveColor{Light: "#35D79C", Dark: "#036B46"} - dimGreen = AdaptiveColor{Light: "#72D2B0", Dark: "#0B5137"} + normalDim = lipgloss.AdaptiveColor{Light: "#A49FA5", Dark: "#777777"} + gray = lipgloss.AdaptiveColor{Light: "#909090", Dark: "#626262"} + midGray = lipgloss.AdaptiveColor{Light: "#B2B2B2", Dark: "#4A4A4A"} + darkGray = lipgloss.AdaptiveColor{Light: "#DDDADA", Dark: "#3C3C3C"} + brightGray = lipgloss.AdaptiveColor{Light: "#847A85", Dark: "#979797"} + dimBrightGray = lipgloss.AdaptiveColor{Light: "#C2B8C2", Dark: "#4D4D4D"} + cream = lipgloss.AdaptiveColor{Light: "#FFFDF5", Dark: "#FFFDF5"} + yellowGreen = lipgloss.AdaptiveColor{Light: "#04B575", Dark: "#ECFD65"} + fuchsia = lipgloss.AdaptiveColor{Light: "#EE6FF8", Dark: "#EE6FF8"} + dimFuchsia = lipgloss.AdaptiveColor{Light: "#F1A8FF", Dark: "#99519E"} + dullFuchsia = lipgloss.AdaptiveColor{Dark: "#AD58B4", Light: "#F793FF"} + dimDullFuchsia = lipgloss.AdaptiveColor{Light: "#F6C9FF", Dark: "#7B4380"} + green = lipgloss.Color("#04B575") + red = lipgloss.AdaptiveColor{Light: "#FF4672", Dark: "#ED567A"} + semiDimGreen = lipgloss.AdaptiveColor{Light: "#35D79C", Dark: "#036B46"} + dimGreen = lipgloss.AdaptiveColor{Light: "#72D2B0", Dark: "#0B5137"} ) // Ulimately, we'll transition to named styles. -// nolint:deadcode,unused,varcheck var ( - normalFg = NewStyle().Foreground(normal).Render - dimNormalFg = NewStyle().Foreground(normalDim).Render - - brightGrayFg = NewStyle().Foreground(brightGray).Render - dimBrightGrayFg = NewStyle().Foreground(dimBrightGray).Render - - grayFg = NewStyle().Foreground(gray).Render - midGrayFg = NewStyle().Foreground(midGray).Render - darkGrayFg = NewStyle().Foreground(darkGray) - - greenFg = NewStyle().Foreground(green).Render - semiDimGreenFg = NewStyle().Foreground(semiDimGreen).Render - dimGreenFg = NewStyle().Foreground(dimGreen).Render - - fuchsiaFg = NewStyle().Foreground(fuchsia).Render - dimFuchsiaFg = NewStyle().Foreground(dimFuchsia).Render - - dullFuchsiaFg = NewStyle().Foreground(dullFuchsia).Render - dimDullFuchsiaFg = NewStyle().Foreground(dimDullFuchsia).Render - - indigoFg = NewStyle().Foreground(fuchsia).Render - dimIndigoFg = NewStyle().Foreground(dimIndigo).Render - - subtleIndigoFg = NewStyle().Foreground(subtleIndigo).Render - dimSubtleIndigoFg = NewStyle().Foreground(dimSubtleIndigo).Render - - yellowFg = NewStyle().Foreground(yellowGreen).Render // renders light green on light backgrounds - dullYellowFg = NewStyle().Foreground(dullYellowGreen).Render // renders light green on light backgrounds - redFg = NewStyle().Foreground(red).Render - faintRedFg = NewStyle().Foreground(faintRed).Render -) - -var ( - tabStyle = NewStyle(). - Foreground(AdaptiveColor{Light: "#909090", Dark: "#626262"}) - - selectedTabStyle = NewStyle(). - Foreground(AdaptiveColor{Light: "#333333", Dark: "#979797"}) - - errorTitleStyle = NewStyle(). - Foreground(cream). - Background(red). - Padding(0, 1) - - subtleStyle = NewStyle(). - Foreground(AdaptiveColor{Light: "#9B9B9B", Dark: "#5C5C5C"}) - - paginationStyle = subtleStyle.Copy() + dimNormalFg = lipgloss.NewStyle().Foreground(normalDim).Render + brightGrayFg = lipgloss.NewStyle().Foreground(brightGray).Render + dimBrightGrayFg = lipgloss.NewStyle().Foreground(dimBrightGray).Render + grayFg = lipgloss.NewStyle().Foreground(gray).Render + midGrayFg = lipgloss.NewStyle().Foreground(midGray).Render + darkGrayFg = lipgloss.NewStyle().Foreground(darkGray) + greenFg = lipgloss.NewStyle().Foreground(green).Render + semiDimGreenFg = lipgloss.NewStyle().Foreground(semiDimGreen).Render + dimGreenFg = lipgloss.NewStyle().Foreground(dimGreen).Render + fuchsiaFg = lipgloss.NewStyle().Foreground(fuchsia).Render + dimFuchsiaFg = lipgloss.NewStyle().Foreground(dimFuchsia).Render + dullFuchsiaFg = lipgloss.NewStyle().Foreground(dullFuchsia).Render + dimDullFuchsiaFg = lipgloss.NewStyle().Foreground(dimDullFuchsia).Render + redFg = lipgloss.NewStyle().Foreground(red).Render + tabStyle = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#909090", Dark: "#626262"}) + selectedTabStyle = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#333333", Dark: "#979797"}) + errorTitleStyle = lipgloss.NewStyle().Foreground(cream).Background(red).Padding(0, 1) + subtleStyle = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "#9B9B9B", Dark: "#5C5C5C"}) + paginationStyle = subtleStyle ) diff --git a/ui/ui.go b/ui/ui.go index df7e37f..ac2bd95 100644 --- a/ui/ui.go +++ b/ui/ui.go @@ -1,33 +1,25 @@ package ui import ( - "errors" "fmt" - "log" "os" "path/filepath" "strings" "time" + "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/charm" - "github.com/charmbracelet/charm/keygen" + "github.com/charmbracelet/glamour" "github.com/charmbracelet/glow/utils" + "github.com/charmbracelet/lipgloss" + "github.com/charmbracelet/log" "github.com/muesli/gitcha" te "github.com/muesli/termenv" - "github.com/segmentio/ksuid" ) const ( - noteCharacterLimit = 256 // should match server - statusMessageTimeout = time.Second * 2 // how long to show status messages like "stashed!" + statusMessageTimeout = time.Second * 3 // how long to show status messages like "stashed!" ellipsis = "…" - - // Only show the spinner if it spins for at least this amount of time. - spinnerVisibilityTimeout = time.Millisecond * 140 - - // Minimum amount of time the spinner should be visible once it starts. - spinnerMinLifetime = time.Millisecond * 550 ) var ( @@ -36,29 +28,25 @@ var ( markdownExtensions = []string{ "*.md", "*.mdown", "*.mkdn", "*.mkd", "*.markdown", } - - // True if we're logging to a file, in which case we'll log more stuff. - debug = false - - // Types of documents we allow the user to stash. - stashableDocTypes = NewDocTypeSet(LocalDoc, NewsDoc) ) // NewProgram returns a new Tea program. func NewProgram(cfg Config) *tea.Program { - if cfg.Logfile != "" { - log.Println("-- Starting Glow ----------------") - log.Printf("High performance pager: %v", cfg.HighPerformancePager) - log.Printf("Glamour rendering: %v", cfg.GlamourEnabled) - log.Println("Bubble Tea now initializing...") - debug = true - } + log.Debug( + "Starting glow", + "high_perf_pager", + cfg.HighPerformancePager, + "glamour", + cfg.GlamourEnabled, + ) + config = cfg opts := []tea.ProgramOption{tea.WithAltScreen()} if cfg.EnableMouse { opts = append(opts, tea.WithMouseCellMotion()) } - return tea.NewProgram(newModel(cfg), opts...) + m := newModel(cfg) + return tea.NewProgram(m, opts...) } type errMsg struct{ err error } @@ -66,10 +54,6 @@ type errMsg struct{ err error } func (e errMsg) Error() string { return e.err.Error() } type ( - newCharmClientMsg *charm.Client - sshAuthErrMsg struct{} - keygenFailedMsg struct{ err error } - keygenSuccessMsg struct{} initLocalFileSearchMsg struct { cwd string ch chan gitcha.SearchResult @@ -79,16 +63,7 @@ type ( type ( foundLocalFileMsg gitcha.SearchResult localFileSearchFinished struct{} - gotStashMsg []*charm.Markdown - stashLoadErrMsg struct{ err error } - gotNewsMsg []*charm.Markdown statusMessageTimeoutMsg applicationContext - newsLoadErrMsg struct{ err error } - stashSuccessMsg markdown - stashFailMsg struct { - err error - markdown markdown - } ) // applicationContext indicates the area of the application something applies @@ -115,60 +90,18 @@ func (s state) String() string { }[s] } -type authStatus int - -const ( - authConnecting authStatus = iota - authOK - authFailed -) - -func (s authStatus) String() string { - return map[authStatus]string{ - authConnecting: "connecting", - authOK: "ok", - authFailed: "failed", - }[s] -} - -type keygenState int - -const ( - keygenUnstarted keygenState = iota - keygenRunning - keygenFinished -) - // Common stuff we'll need to access in all models. type commonModel struct { - cfg Config - cc *charm.Client - cwd string - authStatus authStatus - width int - height int - - // Local IDs of files stashed this session. We treat this like a set, - // ignoring the value portion with an empty struct. - filesStashed map[ksuid.KSUID]struct{} - - // ID of the most recently stashed markdown - latestFileStashed ksuid.KSUID - - // Files currently being stashed. We remove files from this set once - // a stash operation has either succeeded or failed. - filesStashing map[ksuid.KSUID]struct{} -} - -func (c commonModel) isStashing() bool { - return len(c.filesStashing) > 0 + cfg Config + cwd string + width int + height int } type model struct { - common *commonModel - state state - keygenState keygenState - fatalErr error + common *commonModel + state state + fatalErr error // Sub-models stash stashModel @@ -201,49 +134,37 @@ func (m *model) unloadDocument() []tea.Cmd { func newModel(cfg Config) tea.Model { initSections() - if cfg.GlamourStyle == "auto" { + if cfg.GlamourStyle == glamour.AutoStyle { if te.HasDarkBackground() { - cfg.GlamourStyle = "dark" + cfg.GlamourStyle = glamour.DarkStyle } else { - cfg.GlamourStyle = "light" + cfg.GlamourStyle = glamour.LightStyle } } - if len(cfg.DocumentTypes) == 0 { - cfg.DocumentTypes.Add(LocalDoc, StashedDoc, ConvertedDoc, NewsDoc) - } + teamList := list.New([]list.Item{}, list.NewDefaultDelegate(), 0, 0) + teamList.Styles.Title = lipgloss.NewStyle().Foreground(yellowGreen) + teamList.SetStatusBarItemName("team", "teams") + teamList.SetShowHelp(true) + + // We use the team list status message as a permanent placeholder. + teamList.StatusMessageLifetime = time.Hour common := commonModel{ - cfg: cfg, - authStatus: authConnecting, - filesStashed: make(map[ksuid.KSUID]struct{}), - filesStashing: make(map[ksuid.KSUID]struct{}), + cfg: cfg, } return model{ - common: &common, - state: stateShowStash, - keygenState: keygenUnstarted, - pager: newPagerModel(&common), - stash: newStashModel(&common), + common: &common, + state: stateShowStash, + pager: newPagerModel(&common), + stash: newStashModel(&common), } } func (m model) Init() tea.Cmd { - var cmds []tea.Cmd - d := m.common.cfg.DocumentTypes - - if d.Contains(StashedDoc) || d.Contains(NewsDoc) { - cmds = append(cmds, - newCharmClient, - m.stash.spinner.Tick, - ) - } - - if d.Contains(LocalDoc) { - cmds = append(cmds, findLocalFiles(m)) - } - + cmds := []tea.Cmd{m.stash.spinner.Tick} + cmds = append(cmds, m.stash.loadDocs()) return tea.Batch(cmds...) } @@ -261,7 +182,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyMsg: switch msg.String() { case "esc": - if m.state == stateShowDocument { + if m.state == stateShowDocument || m.stash.viewState == stashStateLoadingDocument { batch := m.unloadDocument() return m, tea.Batch(batch...) } @@ -272,28 +193,16 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch m.state { case stateShowStash: // pass through all keys if we're editing the filter - if m.stash.filterState == filtering || m.stash.selectionState == selectionSettingNote { + if m.stash.filterState == filtering { m.stash, cmd = m.stash.update(msg) return m, cmd } - - // Special cases for the pager - case stateShowDocument: - switch m.pager.state { - // If setting a note send all keys straight through - case pagerStateSetNote: - var batch []tea.Cmd - newPagerModel, cmd := m.pager.update(msg) - m.pager = newPagerModel - batch = append(batch, cmd) - return m, tea.Batch(batch...) - } } return m, tea.Quit case "left", "h", "delete": - if m.state == stateShowDocument && m.pager.state != pagerStateSetNote { + if m.state == stateShowDocument { cmds = append(cmds, m.unloadDocument()...) return m, tea.Batch(cmds...) } @@ -315,66 +224,16 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.common.cwd = msg.cwd cmds = append(cmds, findNextLocalFile(m)) - case sshAuthErrMsg: - if m.keygenState != keygenFinished { // if we haven't run the keygen yet, do that - m.keygenState = keygenRunning - cmds = append(cmds, generateSSHKeys) - } else { - // The keygen ran but things still didn't work and we can't auth - m.common.authStatus = authFailed - m.stash.err = errors.New("SSH authentication failed; we tried ssh-agent, loading keys from disk, and generating SSH keys") - if debug { - log.Println("entering offline mode;", m.stash.err) - } - - // Even though it failed, news/stash loading is finished - m.stash.loaded.Add(StashedDoc, NewsDoc) - } - - case keygenFailedMsg: - // Keygen failed. That sucks. - m.common.authStatus = authFailed - m.stash.err = errors.New("could not authenticate; could not generate SSH keys") - if debug { - log.Println("entering offline mode;", m.stash.err) - } - - m.keygenState = keygenFinished - - // Even though it failed, news/stash loading is finished - m.stash.loaded.Add(StashedDoc, NewsDoc) - - case keygenSuccessMsg: - // The keygen's done, so let's try initializing the charm client again - m.keygenState = keygenFinished - cmds = append(cmds, newCharmClient) - - case newCharmClientMsg: - m.common.cc = msg - m.common.authStatus = authOK - cmds = append(cmds, loadStash(m.stash), loadNews(m.stash)) - - case stashLoadErrMsg: - m.common.authStatus = authFailed - case fetchedMarkdownMsg: // We've loaded a markdown file's contents for rendering m.pager.currentDocument = *msg - msg.Body = string(utils.RemoveFrontmatter([]byte(msg.Body))) - cmds = append(cmds, renderWithGlamour(m.pager, msg.Body)) + body := string(utils.RemoveFrontmatter([]byte(msg.Body))) + cmds = append(cmds, renderWithGlamour(m.pager, body)) case contentRenderedMsg: m.state = stateShowDocument - case noteSavedMsg: - // A note was saved to a document. This will have been done in the - // pager, so we'll need to find the corresponding note in the stash. - // So, pass the message to the stash for processing. - stashModel, cmd := m.stash.update(msg) - m.stash = stashModel - return m, cmd - - case localFileSearchFinished, gotStashMsg, gotNewsMsg: + case localFileSearchFinished: // Always pass these messages to the stash so we can keep it updated // about network activity, even if the user isn't currently viewing // the stash. @@ -393,35 +252,6 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { } cmds = append(cmds, findNextLocalFile(m)) - case stashSuccessMsg: - // Common handling that should happen regardless of application state - md := markdown(msg) - m.stash.addMarkdowns(&md) - m.common.filesStashed[msg.stashID] = struct{}{} - delete(m.common.filesStashing, md.stashID) - - if m.stash.filterApplied() { - for _, v := range m.stash.filteredMarkdowns { - if v.stashID == msg.stashID && v.docType == ConvertedDoc { - // Add the server-side ID we got back so we can do things - // like rename and stash it. - v.ID = msg.ID - - // Keep the unique ID in sync so we can do things like - // delete. Note that the markdown received a new unique ID - // when it was added to the file listing in - // stash.addMarkdowns. - v.uniqueID = md.uniqueID - break - } - } - } - - case stashFailMsg: - // Common handling that should happen regardless of application state - delete(m.common.filesStashed, msg.markdown.stashID) - delete(m.common.filesStashing, msg.markdown.stashID) - case filteredMarkdownMsg: if m.state == stateShowDocument { newStashModel, cmd := m.stash.update(msg) @@ -476,10 +306,11 @@ func errorView(err error, fatal bool) string { // COMMANDS -func findLocalFiles(m model) tea.Cmd { +func findLocalFiles(m commonModel) tea.Cmd { return func() tea.Msg { + log.Info("findLocalFiles") var ( - cwd = m.common.cfg.WorkingDirectory + cwd = m.cfg.WorkingDirectory err error ) @@ -495,26 +326,19 @@ func findLocalFiles(m model) tea.Cmd { // Note that this is one error check for both cases above if err != nil { - if debug { - log.Println("error finding local files:", err) - } + log.Error("error finding local files", "error", err) return errMsg{err} } - if debug { - log.Println("local directory is:", cwd) - } - + log.Debug("local directory is", "cwd", cwd) var ignore []string - if !m.common.cfg.ShowAllFiles { + if !m.cfg.ShowAllFiles { ignore = ignorePatterns(m) } ch, err := gitcha.FindFilesExcept(cwd, markdownExtensions, ignore) if err != nil { - if debug { - log.Println("error finding local files:", err) - } + log.Error("error finding local files", "error", err) return errMsg{err} } @@ -531,186 +355,11 @@ func findNextLocalFile(m model) tea.Cmd { return foundLocalFileMsg(res) } // We're done - if debug { - log.Println("local file search finished") - } + log.Debug("local file search finished") return localFileSearchFinished{} } } -func newCharmClient() tea.Msg { - cfg, err := charm.ConfigFromEnv() - if err != nil { - return errMsg{err} - } - - cc, err := charm.NewClient(cfg) - if err == charm.ErrMissingSSHAuth { - if debug { - log.Println("missing SSH auth:", err) - } - return sshAuthErrMsg{} - } else if err != nil { - if debug { - log.Println("error creating new charm client:", err) - } - return errMsg{err} - } - - return newCharmClientMsg(cc) -} - -func loadStash(m stashModel) tea.Cmd { - return func() tea.Msg { - if m.common.cc == nil { - err := errors.New("no charm client") - if debug { - log.Println("error loading stash:", err) - } - return stashLoadErrMsg{err} - } - stash, err := m.common.cc.GetStash(m.serverPage) - if err != nil { - if debug { - if _, ok := err.(charm.ErrAuthFailed); ok { - log.Println("auth failure while loading stash:", err) - } else { - log.Println("error loading stash:", err) - } - } - return stashLoadErrMsg{err} - } - if debug { - log.Println("loaded stash page", m.serverPage) - } - return gotStashMsg(stash) - } -} - -func loadNews(m stashModel) tea.Cmd { - return func() tea.Msg { - if m.common.cc == nil { - err := errors.New("no charm client") - if debug { - log.Println("error loading news:", err) - } - return newsLoadErrMsg{err} - } - news, err := m.common.cc.GetNews(1) // just fetch the first page - if err != nil { - if debug { - log.Println("error loading news:", err) - } - return newsLoadErrMsg{err} - } - if debug { - log.Println("fetched news") - } - return gotNewsMsg(news) - } -} - -func generateSSHKeys() tea.Msg { - if debug { - log.Println("running keygen...") - } - _, err := keygen.NewSSHKeyPair(nil) - if err != nil { - if debug { - log.Println("keygen failed:", err) - } - return keygenFailedMsg{err} - } - if debug { - log.Println("keys generated successfully") - } - return keygenSuccessMsg{} -} - -func saveDocumentNote(cc *charm.Client, id int, note string) tea.Cmd { - if cc == nil { - return func() tea.Msg { - err := errors.New("can't set note; no charm client") - if debug { - log.Println("error saving note:", err) - } - return errMsg{err} - } - } - return func() tea.Msg { - if err := cc.SetMarkdownNote(id, note); err != nil { - if debug { - log.Println("error saving note:", err) - } - return errMsg{err} - } - return noteSavedMsg(&charm.Markdown{ID: id, Note: note}) - } -} - -func stashDocument(cc *charm.Client, md markdown) tea.Cmd { - return func() tea.Msg { - if cc == nil { - err := errors.New("can't stash; no charm client") - if debug { - log.Println("error stashing document:", err) - } - return stashFailMsg{err, md} - } - - // Is the document missing a body? If so, it likely means it needs to - // be loaded. But...if it turns out the document body really is empty - // then we'll stash it anyway. - if len(md.Body) == 0 { - switch md.docType { - case LocalDoc: - data, err := os.ReadFile(md.localPath) - if err != nil { - if debug { - log.Println("error loading document body for stashing:", err) - } - return stashFailMsg{err, md} - } - md.Body = string(data) - - case NewsDoc: - newMD, err := fetchMarkdown(cc, md.ID, md.docType) - if err != nil { - if debug { - log.Println(err) - } - return stashFailMsg{err, md} - } - md.Body = newMD.Body - - default: - err := fmt.Errorf("user is attempting to stash an unsupported markdown type: %s", md.docType) - if debug { - log.Println(err) - } - return stashFailMsg{err, md} - } - } - - newMd, err := cc.StashMarkdown(md.Note, md.Body) - if err != nil { - if debug { - log.Println("error stashing document:", err) - } - return stashFailMsg{err, md} - } - - md.convertToStashed() - - // The server sends the whole stashed document back, but we really just - // need to know the ID so we can operate on this newly stashed - // markdown. - md.ID = newMd.ID - - return stashSuccessMsg(md) - } -} - func waitForStatusMessageTimeout(appCtx applicationContext, t *time.Timer) tea.Cmd { return func() tea.Msg { <-t.C @@ -725,19 +374,16 @@ func waitForStatusMessageTimeout(appCtx applicationContext, t *time.Timer) tea.C // a directory, but we trust that gitcha has already done that. func localFileToMarkdown(cwd string, res gitcha.SearchResult) *markdown { md := &markdown{ - docType: LocalDoc, localPath: res.Path, - Markdown: charm.Markdown{ - Note: stripAbsolutePath(res.Path, cwd), - CreatedAt: res.Info.ModTime(), - }, + Note: stripAbsolutePath(res.Path, cwd), + Modtime: res.Info.ModTime(), } return md } func stripAbsolutePath(fullPath, cwd string) string { - return strings.Replace(fullPath, cwd+string(os.PathSeparator), "", -1) + return strings.ReplaceAll(fullPath, cwd+string(os.PathSeparator), "") } // Lightweight version of reflow's indent function. diff --git a/utils/utils.go b/utils/utils.go index fe38c13..693cccc 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -2,8 +2,13 @@ package utils import ( "os" + "path/filepath" "regexp" + "strings" + "github.com/charmbracelet/glamour" + "github.com/charmbracelet/glamour/ansi" + "github.com/charmbracelet/lipgloss" "github.com/mitchellh/go-homedir" ) @@ -32,3 +37,73 @@ func ExpandPath(path string) string { } return os.ExpandEnv(path) } + +// WrapCodeBlock wraps a string in a code block with the given language. +func WrapCodeBlock(s, language string) string { + return "```" + language + "\n" + s + "```" +} + +var markdownExtensions = []string{ + ".md", ".mdown", ".mkdn", ".mkd", ".markdown", +} + +// IsMarkdownFile returns whether the filename has a markdown extension. +func IsMarkdownFile(filename string) bool { + ext := filepath.Ext(filename) + + if ext == "" { + // By default, assume it's a markdown file. + return true + } + + for _, v := range markdownExtensions { + if strings.EqualFold(ext, v) { + return true + } + } + + // Has an extension but not markdown + // so assume this is a code file. + return false +} + +func GlamourStyle(style string, isCode bool) glamour.TermRendererOption { + if !isCode { + if style == glamour.AutoStyle { + return glamour.WithAutoStyle() + } else { + return glamour.WithStylePath(style) + } + } + + // If we are rendering a pure code block, we need to modify the style to + // remove the indentation. + + var styleConfig ansi.StyleConfig + + switch style { + case glamour.AutoStyle: + if lipgloss.HasDarkBackground() { + styleConfig = glamour.DarkStyleConfig + } else { + styleConfig = glamour.LightStyleConfig + } + case glamour.DarkStyle: + styleConfig = glamour.DarkStyleConfig + case glamour.LightStyle: + styleConfig = glamour.LightStyleConfig + case glamour.PinkStyle: + styleConfig = glamour.PinkStyleConfig + case glamour.NoTTYStyle: + styleConfig = glamour.NoTTYStyleConfig + case glamour.DraculaStyle: + styleConfig = glamour.DraculaStyleConfig + default: + return glamour.WithStylesFromJSONFile(style) + } + + var margin uint + styleConfig.CodeBlock.Margin = &margin + + return glamour.WithStyles(styleConfig) +}