feat(config): use custom config parser

This commit is contained in:
Jan De Dobbeleer 2024-03-11 09:23:00 +01:00 committed by Jan De Dobbeleer
parent 8e0a4c11de
commit 87d1719bd0
9 changed files with 143 additions and 189 deletions

View file

@ -36,8 +36,8 @@ type style struct {
}
type Colors struct {
Background string `json:"background"`
Foreground string `json:"foreground"`
Background string `json:"background" toml:"background"`
Foreground string `json:"foreground" toml:"foreground"`
}
const (

View file

@ -1,6 +1,6 @@
package ansi
type Palettes struct {
Template string `json:"template,omitempty"`
List map[string]Palette `json:"list,omitempty"`
Template string `json:"template,omitempty" toml:"template,omitempty"`
List map[string]Palette `json:"list,omitempty" toml:"list,omitempty"`
}

View file

@ -38,19 +38,19 @@ const (
// Block defines a part of the prompt with optional segments
type Block struct {
Type BlockType `json:"type,omitempty"`
Alignment BlockAlignment `json:"alignment,omitempty"`
Segments []*Segment `json:"segments,omitempty"`
Newline bool `json:"newline,omitempty"`
Filler string `json:"filler,omitempty"`
Overflow Overflow `json:"overflow,omitempty"`
Type BlockType `json:"type,omitempty" toml:"type,omitempty"`
Alignment BlockAlignment `json:"alignment,omitempty" toml:"alignment,omitempty"`
Segments []*Segment `json:"segments,omitempty" toml:"segments,omitempty"`
Newline bool `json:"newline,omitempty" toml:"newline,omitempty"`
Filler string `json:"filler,omitempty" toml:"filler,omitempty"`
Overflow Overflow `json:"overflow,omitempty" toml:"overflow,omitempty"`
// Deprecated: keep the logic for legacy purposes
HorizontalOffset int `json:"horizontal_offset,omitempty"`
VerticalOffset int `json:"vertical_offset,omitempty"`
HorizontalOffset int `json:"horizontal_offset,omitempty" toml:"horizontal_offset,omitempty"`
VerticalOffset int `json:"vertical_offset,omitempty" toml:"vertical_offset,omitempty"`
MaxWidth int `json:"max_width,omitempty"`
MinWidth int `json:"min_width,omitempty"`
MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"`
MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"`
env platform.Environment
writer *ansi.Writer

View file

@ -2,7 +2,6 @@ package engine
import (
"bytes"
json2 "encoding/json"
"fmt"
"io"
"os"
@ -10,18 +9,16 @@ import (
"strings"
"time"
json "github.com/goccy/go-json"
yaml "github.com/goccy/go-yaml"
"github.com/gookit/goutil/jsonutil"
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
"github.com/jandedobbeleer/oh-my-posh/src/properties"
"github.com/jandedobbeleer/oh-my-posh/src/segments"
"github.com/jandedobbeleer/oh-my-posh/src/shell"
"github.com/jandedobbeleer/oh-my-posh/src/template"
"github.com/gookit/config/v2"
"github.com/gookit/config/v2/json"
"github.com/gookit/config/v2/toml"
yaml "github.com/gookit/config/v2/yamlv3"
"github.com/mitchellh/mapstructure"
toml "github.com/pelletier/go-toml/v2"
)
const (
@ -34,33 +31,33 @@ const (
// Config holds all the theme for rendering the prompt
type Config struct {
Version int `json:"version"`
FinalSpace bool `json:"final_space,omitempty"`
ConsoleTitleTemplate string `json:"console_title_template,omitempty"`
TerminalBackground string `json:"terminal_background,omitempty"`
AccentColor string `json:"accent_color,omitempty"`
Blocks []*Block `json:"blocks,omitempty"`
Tooltips []*Segment `json:"tooltips,omitempty"`
TransientPrompt *Segment `json:"transient_prompt,omitempty"`
ValidLine *Segment `json:"valid_line,omitempty"`
ErrorLine *Segment `json:"error_line,omitempty"`
SecondaryPrompt *Segment `json:"secondary_prompt,omitempty"`
DebugPrompt *Segment `json:"debug_prompt,omitempty"`
Palette ansi.Palette `json:"palette,omitempty"`
Palettes *ansi.Palettes `json:"palettes,omitempty"`
Cycle ansi.Cycle `json:"cycle,omitempty"`
ShellIntegration bool `json:"shell_integration,omitempty"`
PWD string `json:"pwd,omitempty"`
Var map[string]any `json:"var,omitempty"`
DisableCursorPositioning bool `json:"disable_cursor_positioning,omitempty"`
PatchPwshBleed bool `json:"patch_pwsh_bleed,omitempty"`
Version int `json:"version" toml:"version"`
FinalSpace bool `json:"final_space,omitempty" toml:"final_space,omitempty"`
ConsoleTitleTemplate string `json:"console_title_template,omitempty" toml:"console_title_template,omitempty"`
TerminalBackground string `json:"terminal_background,omitempty" toml:"terminal_background,omitempty"`
AccentColor string `json:"accent_color,omitempty" toml:"accent_color,omitempty"`
Blocks []*Block `json:"blocks,omitempty" toml:"blocks,omitempty"`
Tooltips []*Segment `json:"tooltips,omitempty" toml:"tooltips,omitempty"`
TransientPrompt *Segment `json:"transient_prompt,omitempty" toml:"transient_prompt,omitempty"`
ValidLine *Segment `json:"valid_line,omitempty" toml:"valid_line,omitempty"`
ErrorLine *Segment `json:"error_line,omitempty" toml:"error_line,omitempty"`
SecondaryPrompt *Segment `json:"secondary_prompt,omitempty" toml:"secondary_prompt,omitempty"`
DebugPrompt *Segment `json:"debug_prompt,omitempty" toml:"debug_prompt,omitempty"`
Palette ansi.Palette `json:"palette,omitempty" toml:"palette,omitempty"`
Palettes *ansi.Palettes `json:"palettes,omitempty" toml:"palettes,omitempty"`
Cycle ansi.Cycle `json:"cycle,omitempty" toml:"cycle,omitempty"`
ShellIntegration bool `json:"shell_integration,omitempty" toml:"shell_integration,omitempty"`
PWD string `json:"pwd,omitempty" toml:"pwd,omitempty"`
Var map[string]any `json:"var,omitempty" toml:"var,omitempty"`
DisableCursorPositioning bool `json:"disable_cursor_positioning,omitempty" toml:"disable_cursor_positioning,omitempty"`
PatchPwshBleed bool `json:"patch_pwsh_bleed,omitempty" toml:"patch_pwsh_bleed,omitempty"`
// Deprecated
OSC99 bool `json:"osc99,omitempty"`
OSC99 bool `json:"osc99,omitempty" toml:"osc99,omitempty"`
Output string `json:"-"`
MigrateGlyphs bool `json:"-"`
Format string `json:"-"`
Output string `json:"-" toml:"-"`
MigrateGlyphs bool `json:"-" toml:"-"`
Format string `json:"-" toml:"-"`
origin string
// eval bool
@ -135,94 +132,82 @@ func loadConfig(env platform.Environment) *Config {
cfg.Format = strings.TrimPrefix(filepath.Ext(configFile), ".")
cfg.env = env
// support different extensions
switch cfg.Format {
case "yml":
cfg.Format = YAML
case "jsonc":
cfg.Format = JSON
}
config.AddDriver(yaml.Driver.WithAliases("yaml", "yml"))
config.AddDriver(json.Driver.WithAliases("json", "jsonc"))
config.AddDriver(toml.Driver)
if config.Default().IsEmpty() {
config.WithOptions(func(opt *config.Options) {
opt.DecoderConfig = &mapstructure.DecoderConfig{
TagName: "json",
}
})
}
err := config.LoadFiles(configFile)
// read the data
data, err := os.ReadFile(configFile)
if err != nil {
env.Error(err)
env.DebugF("error reading config file: %s", err)
return defaultConfig(env, true)
}
err = config.BindStruct("", &cfg)
switch cfg.Format {
case "yml", "yaml":
cfg.Format = YAML
err = yaml.Unmarshal(data, &cfg)
case "jsonc", "json":
cfg.Format = JSON
if cfg.Format == "jsonc" {
str := jsonutil.StripComments(string(data))
data = []byte(str)
}
decoder := json.NewDecoder(bytes.NewReader(data))
err = decoder.Decode(&cfg)
case "toml", "tml":
cfg.Format = TOML
err = toml.Unmarshal(data, &cfg)
default:
err = fmt.Errorf("unsupported config file format: %s", cfg.Format)
}
if err != nil {
env.Error(err)
env.DebugF("error decoding config file: %s", err)
return defaultConfig(env, true)
}
return &cfg
}
func (cfg *Config) sync() {
if !cfg.updated {
return
}
var structMap map[string]any
inrec, err := json2.Marshal(cfg)
if err != nil {
return
}
err = json2.Unmarshal(inrec, &structMap)
if err != nil {
return
}
// remove empty structs
for k, v := range structMap {
if smap, OK := v.(map[string]any); OK && len(smap) == 0 {
delete(structMap, k)
}
}
config.SetData(structMap)
}
func (cfg *Config) Export(format string) string {
cfg.sync()
if len(format) != 0 {
cfg.Format = format
}
config.AddDriver(yaml.Driver)
config.AddDriver(toml.Driver)
var result bytes.Buffer
if cfg.Format == JSON {
jsonEncoder := json2.NewEncoder(&result)
switch cfg.Format {
case YAML:
prefix := "# yaml-language-server: $schema=https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
yamlEncoder := yaml.NewEncoder(&result)
err := yamlEncoder.Encode(cfg)
if err != nil {
return ""
}
return prefix + escapeGlyphs(result.String(), cfg.MigrateGlyphs)
case JSON:
jsonEncoder := json.NewEncoder(&result)
jsonEncoder.SetEscapeHTML(false)
jsonEncoder.SetIndent("", " ")
_ = jsonEncoder.Encode(cfg)
prefix := "{\n \"$schema\": \"https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\","
data := strings.Replace(result.String(), "{", prefix, 1)
return escapeGlyphs(data, cfg.MigrateGlyphs)
case TOML:
prefix := "#:schema https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
tomlEncoder := toml.NewEncoder(&result)
err := tomlEncoder.Encode(cfg)
if err != nil {
return ""
}
return prefix + escapeGlyphs(result.String(), cfg.MigrateGlyphs)
}
_, _ = config.DumpTo(&result, cfg.Format)
var prefix string
switch cfg.Format {
case YAML:
prefix = "# yaml-language-server: $schema=https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
case TOML:
prefix = "#:schema https://raw.githubusercontent.com/JanDeDobbeleer/oh-my-posh/main/themes/schema.json\n\n"
}
return prefix + escapeGlyphs(result.String(), cfg.MigrateGlyphs)
// unsupported format
return ""
}
func (cfg *Config) BackupAndMigrate() {
@ -233,19 +218,30 @@ func (cfg *Config) BackupAndMigrate() {
func (cfg *Config) Write(format string) {
content := cfg.Export(format)
if len(content) == 0 {
// we are unable to perform the export
os.Exit(1)
return
}
destination := cfg.Output
if len(destination) == 0 {
destination = cfg.origin
}
f, err := os.OpenFile(destination, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755)
if err != nil {
return
}
defer func() {
_ = f.Close()
}()
_, err = f.WriteString(content)
if err != nil {
return
}
_ = f.Close()
}
func (cfg *Config) Backup() {

View file

@ -6,46 +6,12 @@ import (
"github.com/jandedobbeleer/oh-my-posh/src/ansi"
"github.com/jandedobbeleer/oh-my-posh/src/mock"
"github.com/jandedobbeleer/oh-my-posh/src/platform"
"github.com/jandedobbeleer/oh-my-posh/src/segments"
"github.com/gookit/config/v2"
"github.com/mitchellh/mapstructure"
"github.com/stretchr/testify/assert"
mock2 "github.com/stretchr/testify/mock"
)
func testClearDefaultConfig() {
config.Default().ClearAll()
}
func TestParseMappedLocations(t *testing.T) {
defer testClearDefaultConfig()
cases := []struct {
Case string
JSON string
}{
{Case: "new format", JSON: `{ "properties": { "mapped_locations": {"folder1": "one","folder2": "two"} } }`},
{Case: "old format", JSON: `{ "properties": { "mapped_locations": [["folder1", "one"], ["folder2", "two"]] } }`},
}
for _, tc := range cases {
config.ClearAll()
config.WithOptions(func(opt *config.Options) {
opt.DecoderConfig = &mapstructure.DecoderConfig{
TagName: "config",
}
})
err := config.LoadStrings(config.JSON, tc.JSON)
assert.NoError(t, err)
var segment Segment
err = config.BindStruct("", &segment)
assert.NoError(t, err)
mappedLocations := segment.Properties.GetKeyValueMap(segments.MappedLocations, make(map[string]string))
assert.Equal(t, "two", mappedLocations["folder2"])
}
}
func TestEscapeGlyphs(t *testing.T) {
defer testClearDefaultConfig()
cases := []struct {
Input string
Expected string

View file

@ -113,7 +113,6 @@ func engineRender() {
defer env.Close()
cfg := LoadConfig(env)
defer testClearDefaultConfig()
writerColors := cfg.MakeColors()
writer := &ansi.Writer{

View file

@ -20,28 +20,28 @@ import (
// Segment represent a single segment and it's configuration
type Segment struct {
Type SegmentType `json:"type,omitempty"`
Tips []string `json:"tips,omitempty"`
Style SegmentStyle `json:"style,omitempty"`
PowerlineSymbol string `json:"powerline_symbol,omitempty"`
InvertPowerline bool `json:"invert_powerline,omitempty"`
Foreground string `json:"foreground,omitempty"`
ForegroundTemplates template.List `json:"foreground_templates,omitempty"`
Background string `json:"background,omitempty"`
BackgroundTemplates template.List `json:"background_templates,omitempty"`
LeadingDiamond string `json:"leading_diamond,omitempty"`
TrailingDiamond string `json:"trailing_diamond,omitempty"`
Template string `json:"template,omitempty"`
Templates template.List `json:"templates,omitempty"`
TemplatesLogic template.Logic `json:"templates_logic,omitempty"`
Properties properties.Map `json:"properties,omitempty"`
Interactive bool `json:"interactive,omitempty"`
Alias string `json:"alias,omitempty"`
MaxWidth int `json:"max_width,omitempty"`
MinWidth int `json:"min_width,omitempty"`
Filler string `json:"filler,omitempty"`
Type SegmentType `json:"type,omitempty" toml:"type,omitempty"`
Tips []string `json:"tips,omitempty" toml:"tips,omitempty"`
Style SegmentStyle `json:"style,omitempty" toml:"style,omitempty"`
PowerlineSymbol string `json:"powerline_symbol,omitempty" toml:"powerline_symbol,omitempty"`
InvertPowerline bool `json:"invert_powerline,omitempty" toml:"invert_powerline,omitempty"`
Foreground string `json:"foreground,omitempty" toml:"foreground,omitempty"`
ForegroundTemplates template.List `json:"foreground_templates,omitempty" toml:"foreground_templates,omitempty"`
Background string `json:"background,omitempty" toml:"background,omitempty"`
BackgroundTemplates template.List `json:"background_templates,omitempty" toml:"background_templates,omitempty"`
LeadingDiamond string `json:"leading_diamond,omitempty" toml:"leading_diamond,omitempty"`
TrailingDiamond string `json:"trailing_diamond,omitempty" toml:"trailing_diamond,omitempty"`
Template string `json:"template,omitempty" toml:"template,omitempty"`
Templates template.List `json:"templates,omitempty" toml:"templates,omitempty"`
TemplatesLogic template.Logic `json:"templates_logic,omitempty" toml:"templates_logic,omitempty"`
Properties properties.Map `json:"properties,omitempty" toml:"properties,omitempty"`
Interactive bool `json:"interactive,omitempty" toml:"interactive,omitempty"`
Alias string `json:"alias,omitempty" toml:"alias,omitempty"`
MaxWidth int `json:"max_width,omitempty" toml:"max_width,omitempty"`
MinWidth int `json:"min_width,omitempty" toml:"min_width,omitempty"`
Filler string `json:"filler,omitempty" toml:"filler,omitempty"`
Enabled bool `json:"-"`
Enabled bool `json:"-" toml:"-"`
colors *ansi.Colors
env platform.Environment

View file

@ -13,11 +13,8 @@ require (
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0
github.com/google/uuid v1.6.0 // indirect
github.com/gookit/color v1.5.4
github.com/gookit/config/v2 v2.2.5
github.com/gookit/goutil v0.6.15 // indirect
github.com/huandu/xstrings v1.4.0 // indirect
github.com/mitchellh/copystructure v1.2.0 // indirect
github.com/mitchellh/mapstructure v1.5.0
github.com/sergi/go-diff v1.3.1 // indirect
github.com/shirou/gopsutil/v3 v3.24.2
github.com/stretchr/objx v0.5.2 // indirect
@ -35,8 +32,12 @@ require (
github.com/charmbracelet/bubbles v0.18.0
github.com/charmbracelet/bubbletea v0.25.0
github.com/charmbracelet/lipgloss v0.10.0
github.com/goccy/go-json v0.10.2
github.com/goccy/go-yaml v1.11.3
github.com/gookit/goutil v0.6.15
github.com/hashicorp/hcl/v2 v2.20.0
github.com/mattn/go-runewidth v0.0.15
github.com/pelletier/go-toml/v2 v2.1.1
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3
@ -62,7 +63,6 @@ require (
)
require (
dario.cat/mergo v1.0.0 // indirect
dmitri.shuralyov.com/font/woff2 v0.0.0-20180220214647-957792cbbdab // indirect
github.com/agext/levenshtein v1.2.3 // indirect
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
@ -70,13 +70,11 @@ require (
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/dsnet/compress v0.0.1 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/goccy/go-yaml v1.11.3 // indirect
github.com/fatih/color v1.10.0 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect
github.com/mitchellh/go-wordwrap v1.0.1 // indirect
github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect
@ -93,7 +91,7 @@ require (
golang.org/x/sync v0.6.0 // indirect
golang.org/x/term v0.17.0 // indirect
golang.org/x/tools v0.17.0 // indirect
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 // indirect
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
)
replace github.com/atotto/clipboard v0.1.4 => github.com/jandedobbeleer/clipboard v0.1.4-1

View file

@ -1,5 +1,3 @@
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/font/woff2 v0.0.0-20180220214647-957792cbbdab h1:Ew70NL+wL6v9looOiJJthlqA41VzoJS+q9AyjHJe6/g=
dmitri.shuralyov.com/font/woff2 v0.0.0-20180220214647-957792cbbdab/go.mod h1:FvHgTMJanm43G7B3MVSjS/jim5ytVqAJNAOpRhnuHJc=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
@ -44,8 +42,8 @@ github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5Jflh
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/esimov/stackblur-go v1.1.0 h1:fwnZJC/7sHFzu4CDMgdJ1QxMN/q3k5MGILuoU4hH6oQ=
github.com/esimov/stackblur-go v1.1.0/go.mod h1:7PcTPCHHKStxbZvBkUlQJjRclqjnXtQ0NoORZt1AlHE=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@ -76,12 +74,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gookit/config/v2 v2.2.5 h1:RECbYYbtherywmzn3LNeu9NA5ZqhD7MSKEMsJ7l+MpU=
github.com/gookit/config/v2 v2.2.5/go.mod h1:NeX+yiNYn6Ei10eJvCQFXuHEPIE/IPS8bqaFIsszzaM=
github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo=
github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY=
github.com/gookit/ini/v2 v2.2.3 h1:nSbN+x9OfQPcMObTFP+XuHt8ev6ndv/fWWqxFhPMu2E=
github.com/gookit/ini/v2 v2.2.3/go.mod h1:Vu6p7P7xcfmb8KYu3L0ek8bqu/Im63N81q208SCCZY4=
github.com/hashicorp/hcl/v2 v2.20.0 h1:l++cRs/5jQOiKVvqXZm/P1ZEfVXJmvLS9WSVxkaeTb4=
github.com/hashicorp/hcl/v2 v2.20.0/go.mod h1:WmcD/Ym72MDOOx5F62Ly+leloeu6H7m0pG7VBiU6pQk=
github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
@ -112,9 +106,9 @@ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed h1:036IscGBfJsFIgJQzlui7nK1Ncm0tp2ktmPj8xO4N/0=
github.com/lufia/plan9stats v0.0.0-20231016141302-07b5767bb0ed/go.mod h1:ilwx/Dta8jXAgpFYFvSWEMwxmbWXyiUHkd5FwyKhb5k=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
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=
@ -127,8 +121,6 @@ github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa1
github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s=
github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0=
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
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/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
@ -140,6 +132,8 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
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/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
@ -225,13 +219,14 @@ golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@ -259,8 +254,8 @@ golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/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=