From 91593cf816dbaee9b84ff8ad6434e3169a369ef8 Mon Sep 17 00:00:00 2001 From: linhx Date: Sat, 30 Nov 2024 00:10:18 +0700 Subject: [PATCH 01/15] feat: Command parser --- common/command_parser.go | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 common/command_parser.go diff --git a/common/command_parser.go b/common/command_parser.go new file mode 100644 index 0000000..9b78362 --- /dev/null +++ b/common/command_parser.go @@ -0,0 +1,50 @@ +package common + +import ( + "regexp" +) + +type Token struct { + IsVariable bool `json:"isVariable"` + Name string `json:"name"` + Value string `json:"value"` + Raw string `json:"raw"` +} + +func TokensParser(tmplStr string) []Token { + re := regexp.MustCompile("{{(`?)\\.([a-z][a-zA-Z0-9]*)(\\|(.+?))?(`?)}}") + + matches := re.FindAllStringSubmatchIndex(tmplStr, -1) + + var tokens []Token + previousIndex := 0 + for _, match := range matches { + if (match[3] - match[2]) != (match[11] - match[10]) { + panic("Missing open/close `") + } + + if previousIndex < match[0] { + var token = Token{IsVariable: false, Value: tmplStr[previousIndex:match[0]]} + tokens = append(tokens, token) + } + var rawMatched = tmplStr[match[0]:match[1]] + if match[3]-match[2] == 1 { // escaped + var token = Token{IsVariable: false, Value: tmplStr[match[2]:match[3]], Raw: rawMatched} + tokens = append(tokens, token) + } else { + var token = Token{IsVariable: true, Name: tmplStr[match[4]:match[5]], Raw: rawMatched} + if match[8] != -1 { + token.Value = tmplStr[match[8]:match[9]] + } + tokens = append(tokens, token) + } + + previousIndex = match[1] + } + if previousIndex < len(tmplStr) { + var token = Token{IsVariable: false, Value: tmplStr[previousIndex:len(tmplStr)], Raw: tmplStr[previousIndex:len(tmplStr)]} + tokens = append(tokens, token) + } + + return tokens +} From 27806bdf3b6da22570e8fbd01098559cff4d6503 Mon Sep 17 00:00:00 2001 From: linhx Date: Sun, 1 Dec 2024 00:30:18 +0700 Subject: [PATCH 02/15] feat: input variables view --- bookmark/go.mod | 28 +++- bookmark/go.sum | 56 +++++++ common/command_parser.go | 2 +- go.mod | 44 +++--- go.sum | 120 +++++++-------- main.go | 3 +- views/inputvariables/go.mod | 33 ++++ views/inputvariables/go.sum | 53 +++++++ views/inputvariables/inputvariable.go | 103 +++++++++++++ views/inputvariables/inputvariables_view.go | 160 ++++++++++++++++++++ views/save/go.mod | 25 ++- views/save/go.sum | 47 ++++++ 12 files changed, 585 insertions(+), 89 deletions(-) create mode 100644 views/inputvariables/go.mod create mode 100644 views/inputvariables/go.sum create mode 100644 views/inputvariables/inputvariable.go create mode 100644 views/inputvariables/inputvariables_view.go diff --git a/bookmark/go.mod b/bookmark/go.mod index ed83938..c50f21c 100644 --- a/bookmark/go.mod +++ b/bookmark/go.mod @@ -10,4 +10,30 @@ require ( linhx.com/tbmk/common v0.0.0-00010101000000-000000000000 ) -require github.com/kylelemons/godebug v1.1.0 // indirect +require ( + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/subosito/gotenv v1.6.0 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) + +require ( + github.com/google/uuid v1.6.0 + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/spf13/viper v1.19.0 +) diff --git a/bookmark/go.sum b/bookmark/go.sum index 339ede6..1de3d49 100644 --- a/bookmark/go.sum +++ b/bookmark/go.sum @@ -1,6 +1,62 @@ +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +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/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/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +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/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sonyarouje/simdb v0.1.0 h1:oCxF05HdVmG3vdZZf5oLINIS0UYLoZBVx/IOJzpMA2A= github.com/sonyarouje/simdb v0.1.0/go.mod h1:sBxWOZxv78yOmCzIyXbUWzHua9+QpXwwnFdlLK/UiUU= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +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.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/common/command_parser.go b/common/command_parser.go index 9b78362..91ad031 100644 --- a/common/command_parser.go +++ b/common/command_parser.go @@ -42,7 +42,7 @@ func TokensParser(tmplStr string) []Token { previousIndex = match[1] } if previousIndex < len(tmplStr) { - var token = Token{IsVariable: false, Value: tmplStr[previousIndex:len(tmplStr)], Raw: tmplStr[previousIndex:len(tmplStr)]} + var token = Token{IsVariable: false, Value: tmplStr[previousIndex:], Raw: tmplStr[previousIndex:]} tokens = append(tokens, token) } diff --git a/go.mod b/go.mod index 84d49c9..8b7dac8 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module linhx.com/tbmk -go 1.19 +go 1.23.1 replace linhx.com/tbmk/common => ./common @@ -10,53 +10,57 @@ replace linhx.com/tbmk/views/save => ./views/save replace linhx.com/tbmk/views/search => ./views/search +replace linhx.com/tbmk/views/inputvariables => ./views/inputvariables + require ( - github.com/charmbracelet/bubbletea v0.23.0 + github.com/charmbracelet/bubbletea v1.2.4 + github.com/spf13/viper v1.19.0 linhx.com/tbmk/bookmark v0.0.0-00010101000000-000000000000 + linhx.com/tbmk/common v0.0.0-00010101000000-000000000000 + linhx.com/tbmk/views/inputvariables v0.0.0-00010101000000-000000000000 linhx.com/tbmk/views/save v0.0.0-00010101000000-000000000000 linhx.com/tbmk/views/search v0.0.0-00010101000000-000000000000 ) require ( github.com/atotto/clipboard v0.1.4 // indirect - github.com/aymanbagabas/go-osc52 v1.0.3 // indirect - github.com/charmbracelet/bubbles v0.14.0 // indirect - github.com/charmbracelet/lipgloss v0.5.0 // indirect - github.com/containerd/console v1.0.3 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/bubbles v0.20.0 // indirect + github.com/charmbracelet/lipgloss v1.0.0 // indirect + github.com/charmbracelet/x/ansi v0.4.5 // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect - github.com/google/uuid v1.5.0 // indirect - github.com/gookit/color v1.5.2 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gookit/color v1.5.4 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect github.com/magiconair/properties v1.8.7 // indirect - github.com/mattn/go-isatty v0.0.17 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-localereader v0.0.1 // indirect - github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mattn/go-runewidth v0.0.16 // 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/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.13.0 // indirect - github.com/pelletier/go-toml/v2 v2.1.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect github.com/sagikazarmark/locafero v0.4.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect - github.com/sahilm/fuzzy v0.1.0 // indirect + github.com/sahilm/fuzzy v0.1.1 // indirect github.com/sonyarouje/simdb v0.1.0 // indirect github.com/sourcegraph/conc v0.3.0 // indirect github.com/spf13/afero v1.11.0 // indirect github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/spf13/viper v1.18.2 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect - golang.org/x/sys v0.15.0 // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/sys v0.27.0 // indirect golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - linhx.com/tbmk/common v0.0.0-00010101000000-000000000000 // indirect ) diff --git a/go.sum b/go.sum index 5f49e25..5a68118 100644 --- a/go.sum +++ b/go.sum @@ -1,75 +1,75 @@ 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 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= -github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= -github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og= -github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= -github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= -github.com/charmbracelet/bubbletea v0.23.0 h1:oGChhsNcm7kltiTdjxJbVlyh93N5fycluO7MsA2JEeg= -github.com/charmbracelet/bubbletea v0.23.0/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= -github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= +github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= +github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= +github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= +github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM= +github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= -github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= -github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= +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/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/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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +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.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.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= -github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +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.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 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 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/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/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +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/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/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= -github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= -github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= -github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +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.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +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.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= -github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= -github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= +github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sonyarouje/simdb v0.1.0 h1:oCxF05HdVmG3vdZZf5oLINIS0UYLoZBVx/IOJzpMA2A= github.com/sonyarouje/simdb v0.1.0/go.mod h1:sBxWOZxv78yOmCzIyXbUWzHua9+QpXwwnFdlLK/UiUU= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= @@ -80,17 +80,18 @@ github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= 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.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ= -github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk= +github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= @@ -101,24 +102,17 @@ go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20210630005230-0f9fa26af87c/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-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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= 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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/main.go b/main.go index f4faa23..74e1e9f 100644 --- a/main.go +++ b/main.go @@ -10,6 +10,8 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/spf13/viper" "linhx.com/tbmk/bookmark" + "linhx.com/tbmk/common" + inputVariableView "linhx.com/tbmk/views/inputvariables" saveView "linhx.com/tbmk/views/save" searchView "linhx.com/tbmk/views/search" ) @@ -29,7 +31,6 @@ func NewCancellationSignal() (func(), func()) { return cancel, exit } - func getAppDir() string { if os.Getenv("APP_ENV") == "dev" { return "." diff --git a/views/inputvariables/go.mod b/views/inputvariables/go.mod new file mode 100644 index 0000000..d33fafb --- /dev/null +++ b/views/inputvariables/go.mod @@ -0,0 +1,33 @@ +module linhx.com/tbmk/views/inputvariables + +replace linhx.com/tbmk/common => ../../common + +go 1.23.1 + +require ( + github.com/charmbracelet/bubbles v0.20.0 + github.com/charmbracelet/bubbletea v1.2.4 + github.com/gookit/color v1.5.4 + linhx.com/tbmk/common v0.0.0-00010101000000-000000000000 +) + +require ( + github.com/atotto/clipboard v0.1.4 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/lipgloss v1.0.0 // indirect + github.com/charmbracelet/x/ansi v0.4.5 // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/lucasb-eyer/go-colorful v1.2.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-localereader v0.0.1 // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect + github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.3.8 // indirect +) diff --git a/views/inputvariables/go.sum b/views/inputvariables/go.sum new file mode 100644 index 0000000..2430f5f --- /dev/null +++ b/views/inputvariables/go.sum @@ -0,0 +1,53 @@ +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/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= +github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= +github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= +github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= +github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM= +github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= +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/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/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0= +github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w= +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/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.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +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/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo= +github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8= +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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= +github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/views/inputvariables/inputvariable.go b/views/inputvariables/inputvariable.go new file mode 100644 index 0000000..40fc8e4 --- /dev/null +++ b/views/inputvariables/inputvariable.go @@ -0,0 +1,103 @@ +package inputvariables + +import ( + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + common "linhx.com/tbmk/common" +) + +type InputVariableModel struct { + token common.Token + input textinput.Model + hasFocus bool + err error + edited bool + selectedAll bool + defaultSet bool +} + +var ( + defaultStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("148")).Background(lipgloss.Color("236")) + placeholderStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("23")).Background(lipgloss.Color("236")) + hasFocusStyle = defaultStyle.Underline(true) + selectedAllStyle = hasFocusStyle.Underline(true).Background(lipgloss.Color("21")).Foreground(lipgloss.Color("255")) +) + +func InputVariable(token common.Token) InputVariableModel { + t := textinput.New() + t.CharLimit = 0 + t.SetValue(token.Value) + t.Placeholder = "{{." + token.Name + "}}" // TODO avoid duplicate code + t.TextStyle = selectedAllStyle + t.Cursor.TextStyle = selectedAllStyle + t.PlaceholderStyle = placeholderStyle + t.Prompt = "" + return InputVariableModel{ + hasFocus: false, + err: nil, + token: token, + input: t, + edited: false, + defaultSet: true, + } +} + +func (m *InputVariableModel) SetFocus(focus bool) { + if focus { + m.Focus() + } else { + m.LoseFocus() + } +} + +func (m *InputVariableModel) Focus() { + m.hasFocus = true + m.input.Focus() + m.input.TextStyle = selectedAllStyle + m.input.Cursor.TextStyle = selectedAllStyle + m.input.CursorEnd() + m.defaultSet = true +} + +func (m *InputVariableModel) LoseFocus() { + m.hasFocus = false + m.selectedAll = false + m.input.CursorStart() + m.input.Blur() + m.input.TextStyle = defaultStyle + m.input.Cursor.TextStyle = defaultStyle +} + +func (m InputVariableModel) Update(msg tea.Msg) (InputVariableModel, tea.Cmd) { + // TODO duplicate code + switch msg := msg.(type) { + case tea.KeyMsg: + if m.defaultSet && (msg.Type == tea.KeyRunes || msg.Type == tea.KeyBackspace || msg.Type == tea.KeyDelete) { + m.input.SetValue("") // Clear the default value + m.defaultSet = false // Mark as edited + m.input.TextStyle = defaultStyle + m.input.Cursor.TextStyle = defaultStyle + m.input.CursorStart() + } else if msg.Type == tea.KeyRight || msg.Type == tea.KeyLeft || msg.Type == tea.KeyHome || msg.Type == tea.KeyEnd || msg.Type == tea.KeyCtrlE { + m.defaultSet = false + m.input.TextStyle = defaultStyle + m.input.Cursor.TextStyle = defaultStyle + } else if msg.Type == tea.KeyCtrlA { // select all + m.defaultSet = true + m.input.TextStyle = selectedAllStyle + m.input.Cursor.TextStyle = selectedAllStyle + m.input.CursorEnd() + } + } + + newInput, cmd := m.input.Update(msg) + m.input = newInput + m.token.Value = newInput.Value() + m.edited = true + return m, cmd +} + +func (m InputVariableModel) View() string { + return m.input.View() +} diff --git a/views/inputvariables/inputvariables_view.go b/views/inputvariables/inputvariables_view.go new file mode 100644 index 0000000..8f6b98d --- /dev/null +++ b/views/inputvariables/inputvariables_view.go @@ -0,0 +1,160 @@ +package inputvariables + +import ( + "fmt" + + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + common "linhx.com/tbmk/common" +) + +const MAX_DISPLAY_ITEMS int = 6 +const ITEM_HEIGHT = 2 +const HEADER_HEIGHT = 3 + +/* + * Sample content of 1 item: + * Search: + * N item(s) + * --------------- + * 1. item 1 + * > command 1 + */ +const MIN_WINDOW_HEIGHT = HEADER_HEIGHT + ITEM_HEIGHT // 1 search input, 1 number of item(s), 1 hr, 2 for one item + +type ( + errMsg error +) + +type state int + +type Model struct { + tokens []common.Token + inputVariableInputs map[string]InputVariableModel // TODO dont use variable name, because it can be duplicated + focusTokenIndex int + focusInputVariable *InputVariableModel + err error + quit bool + windowWidth int + windowHeight int +} + +type Tokens struct { + Items []common.Token +} + +func InitialModel(tokens Tokens) Model { + _inputVariableInputs := make(map[string]InputVariableModel) + focusTokenIndex := -1 + var focusInputVariable InputVariableModel + for i := 0; i < len(tokens.Items); i++ { + token := tokens.Items[i] + if token.IsVariable { + inputVariable := InputVariable(token) + if focusTokenIndex == -1 { + focusTokenIndex = i + inputVariable.Focus() + focusInputVariable = inputVariable + } + _inputVariableInputs[token.Name] = inputVariable + } + } + return Model{ + err: nil, + tokens: tokens.Items, + inputVariableInputs: _inputVariableInputs, + windowWidth: 0, + windowHeight: 0, + focusTokenIndex: focusTokenIndex, + focusInputVariable: &focusInputVariable, + } +} + +func (m Model) Init() tea.Cmd { + return textinput.Blink +} + +func (m *Model) changeFocusInput(msg tea.Msg, newIndex int) tea.Cmd { + if newIndex < 0 { + return nil + } + + cmds := make([]tea.Cmd, 2) + if m.focusTokenIndex > -1 { + variableName := m.tokens[m.focusTokenIndex].Name + currentVariableInput := m.inputVariableInputs[variableName] + currentVariableInput.LoseFocus() + currentVariableInput, cmds[0] = currentVariableInput.Update(msg) + m.inputVariableInputs[variableName] = currentVariableInput + } + + m.focusTokenIndex = newIndex + token := m.tokens[m.focusTokenIndex] + theInput := m.inputVariableInputs[token.Name] + theInput.Focus() + theInput, cmds[1] = theInput.Update(msg) + m.inputVariableInputs[token.Name] = theInput + m.focusInputVariable = &theInput + + return tea.Batch(cmds...) +} + +func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.windowWidth, m.windowHeight = msg.Width, msg.Height + return m, tea.ClearScreen + case tea.KeyMsg: + switch msg.Type { + case tea.KeyEnter: + m.quit = true + return m, tea.Quit + case tea.KeyCtrlC, tea.KeyEsc: + m.quit = true + return m, tea.Quit + case tea.KeyTab: + for i := m.focusTokenIndex + 1; i < len(m.tokens); i++ { + if m.tokens[i].IsVariable { + return m, m.changeFocusInput(msg, i) + } + } + case tea.KeyShiftTab: + if m.focusTokenIndex < 1 { + return m, nil + } + for i := m.focusTokenIndex - 1; i > -1; i-- { + if m.tokens[i].IsVariable { + return m, m.changeFocusInput(msg, i) + } + } + } + // We handle errors just like any other message + case errMsg: + m.err = msg + return m, nil + } + + if m.focusInputVariable != nil { + focusTokenInput := *m.focusInputVariable + focusTokenInput, cmd2 := focusTokenInput.Update(msg) + m.inputVariableInputs[m.tokens[m.focusTokenIndex].Name] = focusTokenInput + m.focusInputVariable = &focusTokenInput + return m, cmd2 + } + + return m, nil +} + +func (m Model) View() string { + var str string + for _, token := range m.tokens { + if token.IsVariable { + str += m.inputVariableInputs[token.Name].View() + } else { + str += token.Value + } + } + style := lipgloss.NewStyle().Width(m.windowWidth - 2) // wrap text + return fmt.Sprintf("%s", style.Render(str)) +} diff --git a/views/save/go.mod b/views/save/go.mod index 1d3f697..971276e 100644 --- a/views/save/go.mod +++ b/views/save/go.mod @@ -19,19 +19,38 @@ require ( github.com/aymanbagabas/go-osc52 v1.0.3 // indirect github.com/charmbracelet/lipgloss v0.5.0 // indirect github.com/containerd/console v1.0.3 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.16 // indirect + github.com/magiconair/properties v1.8.7 // indirect + github.com/mattn/go-isatty v0.0.17 // indirect github.com/mattn/go-localereader v0.0.1 // indirect github.com/mattn/go-runewidth v0.0.14 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect github.com/muesli/cancelreader v0.2.2 // indirect github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.13.0 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/rivo/uniseg v0.2.0 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/sahilm/fuzzy v0.1.0 // indirect github.com/sonyarouje/simdb v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/sys v0.18.0 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/views/save/go.sum b/views/save/go.sum index 15571c5..31dac71 100644 --- a/views/save/go.sum +++ b/views/save/go.sum @@ -15,15 +15,24 @@ github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkX 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/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +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.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= +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/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.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.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= 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-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.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= @@ -31,6 +40,8 @@ github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRC github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 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/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= @@ -43,22 +54,52 @@ github.com/muesli/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 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/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/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sonyarouje/simdb v0.1.0 h1:oCxF05HdVmG3vdZZf5oLINIS0UYLoZBVx/IOJzpMA2A= github.com/sonyarouje/simdb v0.1.0/go.mod h1:sBxWOZxv78yOmCzIyXbUWzHua9+QpXwwnFdlLK/UiUU= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +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.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -67,12 +108,18 @@ golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 47e8476fe2ac8097316ee57b87ccb298065f6934 Mon Sep 17 00:00:00 2001 From: linhx Date: Sun, 1 Dec 2024 00:30:33 +0700 Subject: [PATCH 03/15] chore: remove unused code --- views/save/save_view.go | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/views/save/save_view.go b/views/save/save_view.go index 4033c23..e144132 100644 --- a/views/save/save_view.go +++ b/views/save/save_view.go @@ -121,18 +121,6 @@ func (m Model) updateInputView(msg tea.Msg) (tea.Model, tea.Cmd) { m.quit = true return m, tea.Quit - // Change cursor mode - case "ctrl+r": - m.cursorMode++ - if m.cursorMode > textinput.CursorHide { - m.cursorMode = textinput.CursorBlink - } - cmds := make([]tea.Cmd, len(m.inputs)) - for i := range m.inputs { - cmds[i] = m.inputs[i].SetCursorMode(m.cursorMode) - } - return m, tea.Batch(cmds...) - case "enter": shouldStay, err := m.save(false) if err != nil { From 12051b1bc5cbe33110411dff15a60de90a5f9b5e Mon Sep 17 00:00:00 2001 From: linhx Date: Sun, 1 Dec 2024 00:30:54 +0700 Subject: [PATCH 04/15] chore: add VSCode debugging configuration --- .vscode/launch.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 .vscode/launch.json diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..be8d836 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,19 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch with APP_ENV", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "${workspaceFolder}", // Path to your Go application + "output": "externalConsole", // Open in an external terminal + "console": "externalTerminal", // Explicitly use external terminal + "showLog": true, + "env": { + "APP_ENV": "dev" // Custom environment variable + }, + "args": ["search"] // Add any additional command-line arguments if needed + } + ] +} From 527f08c775b4ed5fb6f888fdf0f6ab70020e47d2 Mon Sep 17 00:00:00 2001 From: linhx Date: Sun, 1 Dec 2024 14:54:49 +0700 Subject: [PATCH 05/15] feat: integrate Variables input view with Search view --- common/command_parser.go | 26 +-- go.mod | 6 +- main.go | 8 +- views/inputvariables/inputvariables_view.go | 160 --------------- views/search/go.mod | 60 ++++-- views/search/go.sum | 146 ++++++++----- views/search/search_view.go | 57 +++++- .../{inputvariables => variableinputs}/go.mod | 2 +- .../{inputvariables => variableinputs}/go.sum | 0 .../variableinput.go} | 24 +-- views/variableinputs/variableinputs_view.go | 192 ++++++++++++++++++ 11 files changed, 409 insertions(+), 272 deletions(-) delete mode 100644 views/inputvariables/inputvariables_view.go rename views/{inputvariables => variableinputs}/go.mod (96%) rename views/{inputvariables => variableinputs}/go.sum (100%) rename views/{inputvariables/inputvariable.go => variableinputs/variableinput.go} (81%) create mode 100644 views/variableinputs/variableinputs_view.go diff --git a/common/command_parser.go b/common/command_parser.go index 91ad031..4479b04 100644 --- a/common/command_parser.go +++ b/common/command_parser.go @@ -5,6 +5,7 @@ import ( ) type Token struct { + Id int `json:"id"` IsVariable bool `json:"isVariable"` Name string `json:"name"` Value string `json:"value"` @@ -12,29 +13,29 @@ type Token struct { } func TokensParser(tmplStr string) []Token { - re := regexp.MustCompile("{{(`?)\\.([a-z][a-zA-Z0-9]*)(\\|(.+?))?(`?)}}") + re := regexp.MustCompile("(\\\\)?({{([a-z][a-zA-Z0-9]*)(\\|(.+?))?}})") matches := re.FindAllStringSubmatchIndex(tmplStr, -1) var tokens []Token previousIndex := 0 + id := 0 for _, match := range matches { - if (match[3] - match[2]) != (match[11] - match[10]) { - panic("Missing open/close `") - } - - if previousIndex < match[0] { - var token = Token{IsVariable: false, Value: tmplStr[previousIndex:match[0]]} + if previousIndex < match[0] { // add literal string + id++ + var token = Token{Id: id, IsVariable: false, Value: tmplStr[previousIndex:match[0]]} tokens = append(tokens, token) } var rawMatched = tmplStr[match[0]:match[1]] if match[3]-match[2] == 1 { // escaped - var token = Token{IsVariable: false, Value: tmplStr[match[2]:match[3]], Raw: rawMatched} + id++ + var token = Token{Id: id, IsVariable: false, Value: tmplStr[match[4]:match[5]], Raw: rawMatched} tokens = append(tokens, token) } else { - var token = Token{IsVariable: true, Name: tmplStr[match[4]:match[5]], Raw: rawMatched} - if match[8] != -1 { - token.Value = tmplStr[match[8]:match[9]] + id++ + var token = Token{Id: id, IsVariable: true, Name: tmplStr[match[6]:match[7]], Raw: rawMatched} + if match[10] != -1 { + token.Value = tmplStr[match[10]:match[11]] } tokens = append(tokens, token) } @@ -42,7 +43,8 @@ func TokensParser(tmplStr string) []Token { previousIndex = match[1] } if previousIndex < len(tmplStr) { - var token = Token{IsVariable: false, Value: tmplStr[previousIndex:], Raw: tmplStr[previousIndex:]} + id++ + var token = Token{Id: id, IsVariable: false, Value: tmplStr[previousIndex:], Raw: tmplStr[previousIndex:]} tokens = append(tokens, token) } diff --git a/go.mod b/go.mod index 8b7dac8..7a51e2b 100644 --- a/go.mod +++ b/go.mod @@ -10,14 +10,12 @@ replace linhx.com/tbmk/views/save => ./views/save replace linhx.com/tbmk/views/search => ./views/search -replace linhx.com/tbmk/views/inputvariables => ./views/inputvariables +replace linhx.com/tbmk/views/variableinputs => ./views/variableinputs require ( github.com/charmbracelet/bubbletea v1.2.4 github.com/spf13/viper v1.19.0 linhx.com/tbmk/bookmark v0.0.0-00010101000000-000000000000 - linhx.com/tbmk/common v0.0.0-00010101000000-000000000000 - linhx.com/tbmk/views/inputvariables v0.0.0-00010101000000-000000000000 linhx.com/tbmk/views/save v0.0.0-00010101000000-000000000000 linhx.com/tbmk/views/search v0.0.0-00010101000000-000000000000 ) @@ -63,4 +61,6 @@ require ( golang.org/x/text v0.14.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + linhx.com/tbmk/common v0.0.0-00010101000000-000000000000 // indirect + linhx.com/tbmk/views/variableinputs v0.0.0-00010101000000-000000000000 // indirect ) diff --git a/main.go b/main.go index 74e1e9f..49db34a 100644 --- a/main.go +++ b/main.go @@ -10,8 +10,6 @@ import ( tea "github.com/charmbracelet/bubbletea" "github.com/spf13/viper" "linhx.com/tbmk/bookmark" - "linhx.com/tbmk/common" - inputVariableView "linhx.com/tbmk/views/inputvariables" saveView "linhx.com/tbmk/views/save" searchView "linhx.com/tbmk/views/search" ) @@ -43,14 +41,14 @@ func getAppDir() string { if err != nil { panic(fmt.Errorf("Cannot get real path from symlink: %w", err)) } - return filepath.Dir(realPath); + return filepath.Dir(realPath) } func main() { _, exit := NewCancellationSignal() defer exit() viper.AddConfigPath(getAppDir()) - viper.SetConfigName("config"); + viper.SetConfigName("config") viper.SetDefault("tbmk.dataDir", "./data") err := viper.ReadInConfig() if err != nil { @@ -84,7 +82,7 @@ func main() { if err != nil { log.Fatal(err) } - selectedCommand := m.(searchView.Model).SelectedItem.Command + selectedCommand := m.(searchView.Model).OutputCommand if len(selectedCommand) > 0 { fmt.Print(selectedCommand) } else { diff --git a/views/inputvariables/inputvariables_view.go b/views/inputvariables/inputvariables_view.go deleted file mode 100644 index 8f6b98d..0000000 --- a/views/inputvariables/inputvariables_view.go +++ /dev/null @@ -1,160 +0,0 @@ -package inputvariables - -import ( - "fmt" - - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - common "linhx.com/tbmk/common" -) - -const MAX_DISPLAY_ITEMS int = 6 -const ITEM_HEIGHT = 2 -const HEADER_HEIGHT = 3 - -/* - * Sample content of 1 item: - * Search: - * N item(s) - * --------------- - * 1. item 1 - * > command 1 - */ -const MIN_WINDOW_HEIGHT = HEADER_HEIGHT + ITEM_HEIGHT // 1 search input, 1 number of item(s), 1 hr, 2 for one item - -type ( - errMsg error -) - -type state int - -type Model struct { - tokens []common.Token - inputVariableInputs map[string]InputVariableModel // TODO dont use variable name, because it can be duplicated - focusTokenIndex int - focusInputVariable *InputVariableModel - err error - quit bool - windowWidth int - windowHeight int -} - -type Tokens struct { - Items []common.Token -} - -func InitialModel(tokens Tokens) Model { - _inputVariableInputs := make(map[string]InputVariableModel) - focusTokenIndex := -1 - var focusInputVariable InputVariableModel - for i := 0; i < len(tokens.Items); i++ { - token := tokens.Items[i] - if token.IsVariable { - inputVariable := InputVariable(token) - if focusTokenIndex == -1 { - focusTokenIndex = i - inputVariable.Focus() - focusInputVariable = inputVariable - } - _inputVariableInputs[token.Name] = inputVariable - } - } - return Model{ - err: nil, - tokens: tokens.Items, - inputVariableInputs: _inputVariableInputs, - windowWidth: 0, - windowHeight: 0, - focusTokenIndex: focusTokenIndex, - focusInputVariable: &focusInputVariable, - } -} - -func (m Model) Init() tea.Cmd { - return textinput.Blink -} - -func (m *Model) changeFocusInput(msg tea.Msg, newIndex int) tea.Cmd { - if newIndex < 0 { - return nil - } - - cmds := make([]tea.Cmd, 2) - if m.focusTokenIndex > -1 { - variableName := m.tokens[m.focusTokenIndex].Name - currentVariableInput := m.inputVariableInputs[variableName] - currentVariableInput.LoseFocus() - currentVariableInput, cmds[0] = currentVariableInput.Update(msg) - m.inputVariableInputs[variableName] = currentVariableInput - } - - m.focusTokenIndex = newIndex - token := m.tokens[m.focusTokenIndex] - theInput := m.inputVariableInputs[token.Name] - theInput.Focus() - theInput, cmds[1] = theInput.Update(msg) - m.inputVariableInputs[token.Name] = theInput - m.focusInputVariable = &theInput - - return tea.Batch(cmds...) -} - -func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - switch msg := msg.(type) { - case tea.WindowSizeMsg: - m.windowWidth, m.windowHeight = msg.Width, msg.Height - return m, tea.ClearScreen - case tea.KeyMsg: - switch msg.Type { - case tea.KeyEnter: - m.quit = true - return m, tea.Quit - case tea.KeyCtrlC, tea.KeyEsc: - m.quit = true - return m, tea.Quit - case tea.KeyTab: - for i := m.focusTokenIndex + 1; i < len(m.tokens); i++ { - if m.tokens[i].IsVariable { - return m, m.changeFocusInput(msg, i) - } - } - case tea.KeyShiftTab: - if m.focusTokenIndex < 1 { - return m, nil - } - for i := m.focusTokenIndex - 1; i > -1; i-- { - if m.tokens[i].IsVariable { - return m, m.changeFocusInput(msg, i) - } - } - } - // We handle errors just like any other message - case errMsg: - m.err = msg - return m, nil - } - - if m.focusInputVariable != nil { - focusTokenInput := *m.focusInputVariable - focusTokenInput, cmd2 := focusTokenInput.Update(msg) - m.inputVariableInputs[m.tokens[m.focusTokenIndex].Name] = focusTokenInput - m.focusInputVariable = &focusTokenInput - return m, cmd2 - } - - return m, nil -} - -func (m Model) View() string { - var str string - for _, token := range m.tokens { - if token.IsVariable { - str += m.inputVariableInputs[token.Name].View() - } else { - str += token.Value - } - } - style := lipgloss.NewStyle().Width(m.windowWidth - 2) // wrap text - return fmt.Sprintf("%s", style.Render(str)) -} diff --git a/views/search/go.mod b/views/search/go.mod index c4352fa..a156317 100644 --- a/views/search/go.mod +++ b/views/search/go.mod @@ -1,34 +1,60 @@ module linhx.com/tbmk/views/search -go 1.19 +go 1.23.1 replace linhx.com/tbmk/bookmark => ../../bookmark +replace linhx.com/tbmk/common => ../../common + +replace linhx.com/tbmk/views/variableinputs => ../variableinputs + require ( - github.com/charmbracelet/bubbles v0.14.0 - github.com/charmbracelet/bubbletea v0.23.0 - github.com/gookit/color v1.5.2 + github.com/charmbracelet/bubbles v0.20.0 + github.com/charmbracelet/bubbletea v1.2.4 + github.com/gookit/color v1.5.4 linhx.com/tbmk/bookmark v0.0.0-00010101000000-000000000000 + linhx.com/tbmk/views/variableinputs v0.0.0-00010101000000-000000000000 ) require ( github.com/atotto/clipboard v0.1.4 // indirect - github.com/aymanbagabas/go-osc52 v1.0.3 // indirect - github.com/charmbracelet/lipgloss v0.5.0 // indirect - github.com/containerd/console v1.0.3 // indirect + github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect + github.com/charmbracelet/lipgloss v1.0.0 // indirect + github.com/charmbracelet/x/ansi v0.4.5 // indirect + github.com/charmbracelet/x/term v0.2.1 // indirect + github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect github.com/lucasb-eyer/go-colorful v1.2.0 // indirect - github.com/mattn/go-isatty v0.0.16 // 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/mattn/go-runewidth v0.0.14 // indirect - github.com/muesli/ansi v0.0.0-20211018074035-2e021307bc4b // indirect + github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect - github.com/muesli/reflow v0.3.0 // indirect - github.com/muesli/termenv v0.13.0 // indirect - github.com/rivo/uniseg v0.2.0 // indirect - github.com/sahilm/fuzzy v0.1.0 // indirect + github.com/muesli/termenv v0.15.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/rivo/uniseg v0.4.7 // indirect + github.com/sagikazarmark/locafero v0.4.0 // indirect + github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sahilm/fuzzy v0.1.1 // indirect github.com/sonyarouje/simdb v0.1.0 // indirect + github.com/sourcegraph/conc v0.3.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cast v1.6.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/spf13/viper v1.19.0 // indirect + github.com/subosito/gotenv v1.6.0 // indirect github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect - golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect - golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect - golang.org/x/text v0.3.7 // indirect + go.uber.org/atomic v1.9.0 // indirect + go.uber.org/multierr v1.9.0 // indirect + golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect + golang.org/x/sync v0.9.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.14.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + linhx.com/tbmk/common v0.0.0-00010101000000-000000000000 // indirect ) diff --git a/views/search/go.sum b/views/search/go.sum index 15571c5..5a68118 100644 --- a/views/search/go.sum +++ b/views/search/go.sum @@ -1,78 +1,120 @@ 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 h1:DTwqENW7X9arYimJrPeGZcV0ln14sGMt3pHZspWD+Mg= -github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4= -github.com/charmbracelet/bubbles v0.14.0 h1:DJfCwnARfWjZLvMglhSQzo76UZ2gucuHPy9jLWX45Og= -github.com/charmbracelet/bubbles v0.14.0/go.mod h1:bbeTiXwPww4M031aGi8UK2HT9RDWoiNibae+1yCMtcc= -github.com/charmbracelet/bubbletea v0.21.0/go.mod h1:GgmJMec61d08zXsOhqRC/AiOx4K4pmz+VIcRIm1FKr4= -github.com/charmbracelet/bubbletea v0.23.0 h1:oGChhsNcm7kltiTdjxJbVlyh93N5fycluO7MsA2JEeg= -github.com/charmbracelet/bubbletea v0.23.0/go.mod h1:JAfGK/3/pPKHTnAS8JIE2u9f61BjWTQY57RbT25aMXU= -github.com/charmbracelet/harmonica v0.2.0/go.mod h1:KSri/1RMQOZLbw7AHqgcBycp8pgJnQMYYT8QZRqZ1Ao= -github.com/charmbracelet/lipgloss v0.5.0 h1:lulQHuVeodSgDez+3rGiuxlPVXSnhth442DATR2/8t8= -github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0xFhg5SaqxScmgs= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= +github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= +github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8= +github.com/charmbracelet/bubbles v0.20.0 h1:jSZu6qD8cRQ6k9OMfR1WlM+ruM8fkPWkHvQWD9LIutE= +github.com/charmbracelet/bubbles v0.20.0/go.mod h1:39slydyswPy+uVOHZ5x/GjwVAFkCsV8IIVy+4MhzwwU= +github.com/charmbracelet/bubbletea v1.2.4 h1:KN8aCViA0eps9SCOThb2/XPIlea3ANJLUkv3KnQRNCE= +github.com/charmbracelet/bubbletea v1.2.4/go.mod h1:Qr6fVQw+wX7JkWWkVyXYk/ZUQ92a6XNekLXa3rR18MM= +github.com/charmbracelet/lipgloss v1.0.0 h1:O7VkGDvqEdGi93X+DeqsQ7PKHDgtQfF8j8/O2qFMQNg= +github.com/charmbracelet/lipgloss v1.0.0/go.mod h1:U5fy9Z+C38obMs+T+tJqst9VGzlOYGj4ri9reL3qUlo= +github.com/charmbracelet/x/ansi v0.4.5 h1:LqK4vwBNaXw2AyGIICa5/29Sbdq58GbGdFngSexTdRM= +github.com/charmbracelet/x/ansi v0.4.5/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw= +github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ= +github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg= 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/gookit/color v1.5.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= -github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +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.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +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/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/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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +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.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0= -github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +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.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 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= -github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -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/cancelreader v0.2.0/go.mod h1:3XuTXfFS2VjM+HTLZY9Ak0l6eUKfijIfMUZ4EgX0QYo= +github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= +github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +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/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/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/termenv v0.11.1-0.20220204035834-5ac8409525e0/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.11.1-0.20220212125758-44cd13922739/go.mod h1:Bd5NYQ7pd+SrtBSrSNoBBmXlcY8+Xj4BMJgh8qcZrvs= -github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0= -github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +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.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/sahilm/fuzzy v0.1.0 h1:FzWGaw2Opqyu+794ZQ9SYifWv2EIXpwP4q8dY1kDAwI= -github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= +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.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= +github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= +github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= +github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA= +github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= github.com/sonyarouje/simdb v0.1.0 h1:oCxF05HdVmG3vdZZf5oLINIS0UYLoZBVx/IOJzpMA2A= github.com/sonyarouje/simdb v0.1.0/go.mod h1:sBxWOZxv78yOmCzIyXbUWzHua9+QpXwwnFdlLK/UiUU= +github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= +github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0= +github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= +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.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= +github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= +github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= -golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20210630005230-0f9fa26af87c/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-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= -golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= +go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= +golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 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= +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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/views/search/search_view.go b/views/search/search_view.go index 3f5cede..7eae164 100644 --- a/views/search/search_view.go +++ b/views/search/search_view.go @@ -8,6 +8,7 @@ import ( tea "github.com/charmbracelet/bubbletea" color "github.com/gookit/color" bookmark "linhx.com/tbmk/bookmark" + variableinputs "linhx.com/tbmk/views/variableinputs" ) const MAX_DISPLAY_ITEMS int = 6 @@ -31,8 +32,11 @@ type ( type state int const ( - initializing state = iota - ready + initializing state = 0 + ready state = 1 + SEARCH_MODE string = "SEARCH" + DELETE_MODE string = "DELETE" + INPUT_VARIABLES_MODE string = "VARIABLES" ) type Model struct { @@ -47,10 +51,12 @@ type Model struct { firstIndex int // first item index of current displayed items lastIndex int // last item index of current displayed items SelectedItem bookmark.MatchedItem + OutputCommand string quit bool - deleteMode bool windowWidth int windowHeight int + activateMode string + variablesInputView *variableinputs.Model } func InitialModel(bmk bookmark.Bookmark, query string) Model { @@ -66,6 +72,7 @@ func InitialModel(bmk bookmark.Bookmark, query string) Model { return Model{ state: initializing, + activateMode: SEARCH_MODE, query: query, queryInput: ti, err: nil, @@ -75,7 +82,6 @@ func InitialModel(bmk bookmark.Bookmark, query string) Model { firstIndex: 0, lastIndex: 0, matches: matches, - deleteMode: false, windowWidth: 0, windowHeight: 0, } @@ -182,9 +188,9 @@ func (m Model) updateDeleteMode(msg tea.Msg) (tea.Model, tea.Cmd) { m.bmk.Remove(m.matches[m.cursor].Id) m.matches, err = m.bmk.Search(m.query) m.reCalcCursor() - m.deleteMode = false + m.activateMode = SEARCH_MODE case "n", "ctrl+c": - m.deleteMode = false + m.activateMode = SEARCH_MODE } case errMsg: m.err = msg @@ -217,6 +223,12 @@ func (m Model) updateSearchMode(msg tea.Msg) (tea.Model, tea.Cmd) { case tea.KeyEnter: if len(m.matches) > 0 { m.SelectedItem = m.matches[m.cursor] + // open variables input view + variablesInput := variableinputs.InitialModel(m.SelectedItem.Command, m.windowWidth, m.windowHeight) + m.variablesInputView = &variablesInput + m.activateMode = INPUT_VARIABLES_MODE + + return m.updateInputVariablesMode(nil) // avoid escalate KeyEnter to variableinputs_view } m.quit = true return m, tea.Quit @@ -224,7 +236,7 @@ func (m Model) updateSearchMode(msg tea.Msg) (tea.Model, tea.Cmd) { m.quit = true return m, tea.Quit case tea.KeyCtrlD: - m.deleteMode = true + m.activateMode = DELETE_MODE return m, cmd } @@ -247,6 +259,24 @@ func (m Model) updateSearchMode(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd } +func (m Model) updateInputVariablesMode(msg tea.Msg) (tea.Model, tea.Cmd) { + variablesInputView := *m.variablesInputView + variablesInputView, _ = variablesInputView.Update(msg) + if variablesInputView.Quit { + m.variablesInputView = nil + m.activateMode = SEARCH_MODE + m.quit = true + m.OutputCommand = variablesInputView.GetValue() + return m, tea.Quit + } + if variablesInputView.Cancel { + m.activateMode = SEARCH_MODE + m.variablesInputView = nil + } + m.variablesInputView = &variablesInputView + return m, nil +} + func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.WindowSizeMsg: @@ -256,9 +286,12 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.resetCursor() } - if m.deleteMode { + switch m.activateMode { + case INPUT_VARIABLES_MODE: + return m.updateInputVariablesMode(msg) + case DELETE_MODE: return m.updateDeleteMode(msg) - } else { + default: return m.updateSearchMode(msg) } } @@ -274,7 +307,11 @@ func (m Model) View() string { if m.windowHeight < MIN_WINDOW_HEIGHT { return "Window height is not enough to display" } - if m.deleteMode { + + switch m.activateMode { + case INPUT_VARIABLES_MODE: + return (*m.variablesInputView).View() + case DELETE_MODE: return m.DeleteView() } if m.quit { diff --git a/views/inputvariables/go.mod b/views/variableinputs/go.mod similarity index 96% rename from views/inputvariables/go.mod rename to views/variableinputs/go.mod index d33fafb..07d25e5 100644 --- a/views/inputvariables/go.mod +++ b/views/variableinputs/go.mod @@ -1,4 +1,4 @@ -module linhx.com/tbmk/views/inputvariables +module linhx.com/tbmk/views/variableinputs replace linhx.com/tbmk/common => ../../common diff --git a/views/inputvariables/go.sum b/views/variableinputs/go.sum similarity index 100% rename from views/inputvariables/go.sum rename to views/variableinputs/go.sum diff --git a/views/inputvariables/inputvariable.go b/views/variableinputs/variableinput.go similarity index 81% rename from views/inputvariables/inputvariable.go rename to views/variableinputs/variableinput.go index 40fc8e4..14ccfc0 100644 --- a/views/inputvariables/inputvariable.go +++ b/views/variableinputs/variableinput.go @@ -1,4 +1,4 @@ -package inputvariables +package variableinputs import ( "github.com/charmbracelet/bubbles/textinput" @@ -7,7 +7,7 @@ import ( common "linhx.com/tbmk/common" ) -type InputVariableModel struct { +type VariableInputModel struct { token common.Token input textinput.Model hasFocus bool @@ -24,16 +24,16 @@ var ( selectedAllStyle = hasFocusStyle.Underline(true).Background(lipgloss.Color("21")).Foreground(lipgloss.Color("255")) ) -func InputVariable(token common.Token) InputVariableModel { +func VariableInput(token common.Token) VariableInputModel { t := textinput.New() t.CharLimit = 0 t.SetValue(token.Value) - t.Placeholder = "{{." + token.Name + "}}" // TODO avoid duplicate code - t.TextStyle = selectedAllStyle - t.Cursor.TextStyle = selectedAllStyle + t.Placeholder = "{{" + token.Name + "}}" // TODO avoid duplicate code + t.TextStyle = defaultStyle + t.Cursor.TextStyle = defaultStyle t.PlaceholderStyle = placeholderStyle t.Prompt = "" - return InputVariableModel{ + return VariableInputModel{ hasFocus: false, err: nil, token: token, @@ -43,7 +43,7 @@ func InputVariable(token common.Token) InputVariableModel { } } -func (m *InputVariableModel) SetFocus(focus bool) { +func (m *VariableInputModel) SetFocus(focus bool) { if focus { m.Focus() } else { @@ -51,7 +51,7 @@ func (m *InputVariableModel) SetFocus(focus bool) { } } -func (m *InputVariableModel) Focus() { +func (m *VariableInputModel) Focus() { m.hasFocus = true m.input.Focus() m.input.TextStyle = selectedAllStyle @@ -60,7 +60,7 @@ func (m *InputVariableModel) Focus() { m.defaultSet = true } -func (m *InputVariableModel) LoseFocus() { +func (m *VariableInputModel) LoseFocus() { m.hasFocus = false m.selectedAll = false m.input.CursorStart() @@ -69,7 +69,7 @@ func (m *InputVariableModel) LoseFocus() { m.input.Cursor.TextStyle = defaultStyle } -func (m InputVariableModel) Update(msg tea.Msg) (InputVariableModel, tea.Cmd) { +func (m VariableInputModel) Update(msg tea.Msg) (VariableInputModel, tea.Cmd) { // TODO duplicate code switch msg := msg.(type) { case tea.KeyMsg: @@ -98,6 +98,6 @@ func (m InputVariableModel) Update(msg tea.Msg) (InputVariableModel, tea.Cmd) { return m, cmd } -func (m InputVariableModel) View() string { +func (m VariableInputModel) View() string { return m.input.View() } diff --git a/views/variableinputs/variableinputs_view.go b/views/variableinputs/variableinputs_view.go new file mode 100644 index 0000000..3ae45c7 --- /dev/null +++ b/views/variableinputs/variableinputs_view.go @@ -0,0 +1,192 @@ +package variableinputs + +import ( + "fmt" + + "github.com/charmbracelet/bubbles/textinput" + tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + common "linhx.com/tbmk/common" +) + +const MAX_DISPLAY_ITEMS int = 6 +const ITEM_HEIGHT = 2 +const HEADER_HEIGHT = 3 + +/* + * Sample content of 1 item: + * Search: + * N item(s) + * --------------- + * 1. item 1 + * > command 1 + */ +const MIN_WINDOW_HEIGHT = HEADER_HEIGHT + ITEM_HEIGHT // 1 search input, 1 number of item(s), 1 hr, 2 for one item + +type ( + errMsg error +) + +type Model struct { + tokens []common.Token + focusTokenIndex int + variableInputs map[int]VariableInputModel + focusVariableInput *VariableInputModel + err error + Quit bool + Cancel bool + windowWidth int + windowHeight int +} + +func InitialModel(command string, windowWidth int, windowHeight int) Model { + tokens := common.TokensParser(command) + variableInputs := make(map[int]VariableInputModel) + focusTokenIndex := -1 + var focusVariableInput VariableInputModel + for i := 0; i < len(tokens); i++ { + token := tokens[i] + if token.IsVariable { + variableInput := VariableInput(token) + if focusTokenIndex == -1 { + focusTokenIndex = i + variableInput.Focus() + focusVariableInput = variableInput + } + variableInputs[token.Id] = variableInput + } + } + return Model{ + err: nil, + tokens: tokens, + variableInputs: variableInputs, + focusTokenIndex: focusTokenIndex, + focusVariableInput: &focusVariableInput, + windowWidth: windowWidth, + windowHeight: windowHeight, + } +} + +func (m Model) Init() tea.Cmd { + return textinput.Blink +} + +func (m Model) GetValue() string { + str := "" + for _, token := range m.tokens { + str += token.Value + } + fmt.Print(str) + return str +} + +func (m *Model) updateTokenValueById(id int, value string) { + for i := 0; i < len(m.tokens); i++ { + if m.tokens[i].Id == id { + m.tokens[i].Value = value + } + } +} + +func (m *Model) changeFocusInput(msg tea.Msg, newIndex int) tea.Cmd { + if newIndex < 0 { + return nil + } + + cmds := make([]tea.Cmd, 2) + // blur current variable input + if m.focusVariableInput != nil { + tokenId := m.tokens[m.focusTokenIndex].Id + currentVariableInput := *m.focusVariableInput + currentVariableInput.LoseFocus() + currentVariableInput, cmds[0] = currentVariableInput.Update(msg) + m.variableInputs[tokenId] = currentVariableInput // override + } + + // focus to the new one + m.focusTokenIndex = newIndex + tokenId := m.tokens[m.focusTokenIndex].Id + newVariableInput := m.variableInputs[tokenId] + newVariableInput.Focus() + newVariableInput, cmds[1] = newVariableInput.Update(msg) + m.variableInputs[tokenId] = newVariableInput // override + m.focusVariableInput = &newVariableInput + + return tea.Batch(cmds...) +} + +func (m Model) hasVariable() bool { + for _, e := range m.tokens { + if e.IsVariable { + return true + } + } + return false +} + +func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { + if !m.hasVariable() { + m.Quit = true + return m, tea.Quit + } + m.Quit = false + m.Cancel = false + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.windowWidth, m.windowHeight = msg.Width, msg.Height + return m, tea.ClearScreen + case tea.KeyMsg: + switch msg.Type { + case tea.KeyEnter: + m.Quit = true + return m, tea.Quit + case tea.KeyCtrlC, tea.KeyEsc: + m.Cancel = true + return m, tea.Quit + case tea.KeyTab: + for i := m.focusTokenIndex + 1; i < len(m.tokens); i++ { + if m.tokens[i].IsVariable { + return m, m.changeFocusInput(msg, i) + } + } + case tea.KeyShiftTab: + if m.focusTokenIndex < 1 { + return m, nil + } + for i := m.focusTokenIndex - 1; i > -1; i-- { + if m.tokens[i].IsVariable { + return m, m.changeFocusInput(msg, i) + } + } + } + + case errMsg: + m.err = msg + return m, nil + } + + // update focused input + if m.focusVariableInput != nil { + focusTokenInput := *m.focusVariableInput + focusTokenInput, cmd2 := focusTokenInput.Update(msg) + m.variableInputs[focusTokenInput.token.Id] = focusTokenInput + m.focusVariableInput = &focusTokenInput + m.updateTokenValueById(focusTokenInput.token.Id, focusTokenInput.input.Value()) + return m, cmd2 + } + + return m, nil +} + +func (m Model) View() string { + var str string + for _, token := range m.tokens { + if token.IsVariable { + str += m.variableInputs[token.Id].View() + } else { + str += token.Value + } + } + style := lipgloss.NewStyle().Width(m.windowWidth - 2) // wrap text + return style.Render(str) +} From d1f184734deefbe34aaa204989178c1a241e2c3b Mon Sep 17 00:00:00 2001 From: linhx Date: Sun, 1 Dec 2024 16:04:39 +0700 Subject: [PATCH 06/15] feat: allow create multiple lines command --- views/save/go.mod | 2 - views/save/go.sum | 20 ++++--- views/save/save_view.go | 115 ++++++++++++++++++++++------------------ 3 files changed, 72 insertions(+), 65 deletions(-) diff --git a/views/save/go.mod b/views/save/go.mod index 971276e..80e6bf4 100644 --- a/views/save/go.mod +++ b/views/save/go.mod @@ -9,7 +9,6 @@ replace linhx.com/tbmk/common => ../../common require ( github.com/charmbracelet/bubbles v0.14.0 github.com/charmbracelet/bubbletea v0.23.0 - github.com/gookit/color v1.5.2 linhx.com/tbmk/bookmark v0.0.0-00010101000000-000000000000 linhx.com/tbmk/common v0.0.0-00010101000000-000000000000 ) @@ -44,7 +43,6 @@ require ( github.com/spf13/pflag v1.0.5 // indirect github.com/spf13/viper v1.19.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect - github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.9.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect diff --git a/views/save/go.sum b/views/save/go.sum index 31dac71..c27aa6d 100644 --- a/views/save/go.sum +++ b/views/save/go.sum @@ -13,16 +13,18 @@ github.com/charmbracelet/lipgloss v0.5.0/go.mod h1:EZLha/HbzEt7cYqdFPovlqy5FZPj0 github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= 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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= 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.2 h1:uLnfXcaFjlrDnQDT+NCBcfhrXqYTx/rcCa6xn01Y8yI= -github.com/gookit/color v1.5.2/go.mod h1:w8h4bGiHeeBpvQVePTutdbERIUf3oJE5lZ8HM0UgXyg= 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/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 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.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY= @@ -30,8 +32,8 @@ github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i 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.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= -github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-localereader v0.0.1 h1:ygSAOl7ZXTx4RdPYinUpg6W99U8jWvWi9Ye2JC/oIi4= github.com/mattn/go-localereader v0.0.1/go.mod h1:8fBrzywKY7BI3czFoHkuzRoWE9C+EiG4R1k4Cjx5p88= @@ -56,11 +58,12 @@ github.com/muesli/termenv v0.13.0 h1:wK20DRpJdDX8b7Ek2QfhvqhRQFZ237RGRO0RQ/Iqdy0 github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc= github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= -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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 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/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= @@ -85,15 +88,12 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 h1:QldyIu/L63oPpyvQmHgvgickp1Yw510KJOqX7H24mg8= -github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778/go.mod h1:2MuV+tbUrU1zIOPMxZ5EncGwgmMJsa+9ucAQZXxsObs= go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= @@ -101,23 +101,21 @@ go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTV golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20210630005230-0f9fa26af87c/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-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 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/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 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/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/views/save/save_view.go b/views/save/save_view.go index e144132..583d669 100644 --- a/views/save/save_view.go +++ b/views/save/save_view.go @@ -5,52 +5,53 @@ import ( common "linhx.com/tbmk/common" + "github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" bookmark "linhx.com/tbmk/bookmark" ) +const TOP_HEIGHT = 3 // 1 for app title, 1 for title input, 1 for command prompt +const MIN_WINDOW_HEIGHT = 4 // 1 for app title, 1 for title input, 2 for command input +const MAX_COMMAND_INPUT_HEIGHT = 5 + type Model struct { focusIndex int - inputs []textinput.Model + titleInput textinput.Model + commandInput textarea.Model cursorMode textinput.CursorMode err error bmk bookmark.Bookmark quit bool confirmOverrideMode bool confirmOverrideMsg string + windowWidth int + windowHeight int } +const ( + NUM_INPUTS = 2 +) + func (m *Model) GetItem() (string, string) { - return m.inputs[0].Value(), m.inputs[1].Value() + return m.titleInput.Value(), m.commandInput.Value() } func InitialModel(bmk bookmark.Bookmark, command string) Model { - m := Model{ - inputs: make([]textinput.Model, 2), - confirmOverrideMode: false, - bmk: bmk, - } + titleInput := textinput.New() + titleInput.CharLimit = 100 + titleInput.Prompt = "Title: " + titleInput.Focus() - var t textinput.Model - for i := range m.inputs { - t = textinput.New() - - switch i { - case 0: - t.CharLimit = 100 - t.Prompt = "Title: " - t.Focus() - case 1: - t.CharLimit = 500 - t.Prompt = "Command: " - t.SetValue(command) - } + commandInput := textarea.New() + commandInput.SetValue(command) - m.inputs[i] = t + return Model{ + confirmOverrideMode: false, + bmk: bmk, + titleInput: titleInput, + commandInput: commandInput, } - - return m } func (m Model) Init() tea.Cmd { @@ -102,13 +103,12 @@ func (m Model) updateOverrideMode(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m *Model) updateInputs(msg tea.Msg) tea.Cmd { - cmds := make([]tea.Cmd, len(m.inputs)) + cmds := make([]tea.Cmd, NUM_INPUTS) // Only text inputs with Focus() set will respond, so it's safe to simply // update all of them here without any further logic. - for i := range m.inputs { - m.inputs[i], cmds[i] = m.inputs[i].Update(msg) - } + m.titleInput, cmds[0] = m.titleInput.Update(msg) + m.commandInput, cmds[1] = m.commandInput.Update(msg) return tea.Batch(cmds...) } @@ -116,12 +116,12 @@ func (m *Model) updateInputs(msg tea.Msg) tea.Cmd { func (m Model) updateInputView(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: - switch msg.String() { - case "ctrl+c", "esc": + switch msg.Type { + case tea.KeyCtrlC, tea.KeyEsc: m.quit = true return m, tea.Quit - case "enter": + case tea.KeyCtrlS: shouldStay, err := m.save(false) if err != nil { m.err = err @@ -133,31 +133,30 @@ func (m Model) updateInputView(msg tea.Msg) (tea.Model, tea.Cmd) { return m, tea.Quit } // Set focus to next input - case "tab", "shift+tab", "up", "down": - s := msg.String() - + case tea.KeyTab, tea.KeyShiftTab: // Cycle indexes - if s == "up" || s == "shift+tab" { + if msg.Type == tea.KeyShiftTab { m.focusIndex-- } else { m.focusIndex++ } - if m.focusIndex > len(m.inputs) { + if m.focusIndex > NUM_INPUTS { m.focusIndex = 0 } else if m.focusIndex < 0 { - m.focusIndex = len(m.inputs) + m.focusIndex = NUM_INPUTS } - cmds := make([]tea.Cmd, len(m.inputs)) - for i := 0; i <= len(m.inputs)-1; i++ { - if i == m.focusIndex { - // Set focused state - cmds[i] = m.inputs[i].Focus() - continue - } - // Remove focused state - m.inputs[i].Blur() + cmds := make([]tea.Cmd, NUM_INPUTS) + switch m.focusIndex { + case 0: + cmds[0] = m.titleInput.Focus() + m.commandInput.Blur() + break + case 1: + m.titleInput.Blur() + cmds[1] = m.commandInput.Focus() + break } return m, tea.Batch(cmds...) @@ -171,6 +170,16 @@ func (m Model) updateInputView(msg tea.Msg) (tea.Model, tea.Cmd) { } func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { + case tea.WindowSizeMsg: + m.windowWidth, m.windowHeight = msg.Width, msg.Height + commandInputHeight := m.windowHeight - TOP_HEIGHT + if commandInputHeight > MAX_COMMAND_INPUT_HEIGHT { + commandInputHeight = MAX_COMMAND_INPUT_HEIGHT + } + m.commandInput.SetHeight(commandInputHeight) + return m, tea.ClearScreen + } if m.confirmOverrideMode { return m.updateOverrideMode(msg) } else { @@ -183,6 +192,9 @@ func (m Model) ConfirmOverrideView() string { } func (m Model) View() string { + if m.windowHeight < MIN_WINDOW_HEIGHT { + return "Window height is not enough to display" + } if m.confirmOverrideMode { return m.ConfirmOverrideView() } @@ -192,12 +204,11 @@ func (m Model) View() string { var b strings.Builder b.WriteString("Commands bookmark\n") - for i := range m.inputs { - b.WriteString(m.inputs[i].View()) - if i < len(m.inputs)-1 { - b.WriteRune('\n') - } - } + + b.WriteString(m.titleInput.View()) + b.WriteRune('\n') + b.WriteString("Command: \n") + b.WriteString(m.commandInput.View()) return b.String() } From d52f66e6e103d1aa66085babb4dab0d6fc18da56 Mon Sep 17 00:00:00 2001 From: linhx Date: Sun, 1 Dec 2024 16:44:47 +0700 Subject: [PATCH 07/15] feat: truncate long command in search view --- common/go.mod | 7 +++++++ common/utils.go | 25 +++++++++++++++++++++++++ go.mod | 1 + go.sum | 6 ++++++ views/search/search_view.go | 13 ++++++++++++- 5 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 common/utils.go diff --git a/common/go.mod b/common/go.mod index 56f0182..28e6cc9 100644 --- a/common/go.mod +++ b/common/go.mod @@ -1,3 +1,10 @@ module linhx.com/tbmk/common go 1.19 + +require github.com/muesli/reflow v0.3.0 + +require ( + github.com/mattn/go-runewidth v0.0.12 // indirect + github.com/rivo/uniseg v0.2.0 // indirect +) diff --git a/common/utils.go b/common/utils.go new file mode 100644 index 0000000..146528b --- /dev/null +++ b/common/utils.go @@ -0,0 +1,25 @@ +package common + +import ( + "strings" + + "github.com/muesli/reflow/truncate" +) + +const ELLIPSIS = ".." + +func GetFirstLine(str string) (string, bool) { + if idx := strings.Index(str, "\n"); idx != -1 { + return str[:idx], true + } + return str, false +} + +func TruncateWithEllipsis(str string, maxWidth int) (string, bool) { + _str, isMultilines := GetFirstLine(str) + truncatedStr := truncate.String(_str, uint(maxWidth-8)) + if isMultilines { + return _str, true + } + return truncatedStr, len(str) > len(truncatedStr) +} diff --git a/go.mod b/go.mod index 7a51e2b..6c79f77 100644 --- a/go.mod +++ b/go.mod @@ -40,6 +40,7 @@ require ( github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 // indirect github.com/muesli/cancelreader v0.2.2 // indirect + github.com/muesli/reflow v0.3.0 // indirect github.com/muesli/termenv v0.15.2 // indirect github.com/pelletier/go-toml/v2 v2.2.2 // indirect github.com/rivo/uniseg v0.4.7 // indirect diff --git a/go.sum b/go.sum index 5a68118..f460e57 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= +github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= 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/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= @@ -44,6 +46,7 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE 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.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= @@ -52,6 +55,8 @@ github.com/muesli/ansi v0.0.0-20230316100256-276c6243b2f6 h1:ZK8zHtRHOkbHy6Mmr5D 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/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.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= @@ -59,6 +64,7 @@ github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= diff --git a/views/search/search_view.go b/views/search/search_view.go index 7eae164..eaadead 100644 --- a/views/search/search_view.go +++ b/views/search/search_view.go @@ -6,8 +6,10 @@ import ( "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" color "github.com/gookit/color" bookmark "linhx.com/tbmk/bookmark" + common "linhx.com/tbmk/common" variableinputs "linhx.com/tbmk/views/variableinputs" ) @@ -177,6 +179,7 @@ var ( highlightStyle = color.Yellow selectedStyle = color.BgGray matchedSelectedStyle = color.New(color.Yellow, color.BgGray) + ellipsisStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) ) func (m Model) updateDeleteMode(msg tea.Msg) (tea.Model, tea.Cmd) { @@ -354,7 +357,8 @@ func (m Model) View() string { } // format command _matchCommand := match.MatchCommand - for j := 0; j < len(match.Command); j++ { + truncatedCommand, isTruncated := common.TruncateWithEllipsis(match.Command, m.windowWidth) + for j := 0; j < len(truncatedCommand); j++ { if isSelected { if contains(j, _matchCommand.MatchedIndexes) { line += matchedSelectedStyle.Render(string(match.Command[j])) @@ -369,6 +373,13 @@ func (m Model) View() string { } } } + if isTruncated { + if isSelected { + line += selectedStyle.Render(ellipsisStyle.Render(common.ELLIPSIS)) + } else { + line += ellipsisStyle.Render(common.ELLIPSIS) + } + } matchesContent += line } } From 13fd7dd4c699e35898aa309ef644948b80d71a44 Mon Sep 17 00:00:00 2001 From: linhx Date: Sun, 1 Dec 2024 21:15:34 +0700 Subject: [PATCH 08/15] feat: preview selected command --- views/search/search_view.go | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/views/search/search_view.go b/views/search/search_view.go index eaadead..cee0437 100644 --- a/views/search/search_view.go +++ b/views/search/search_view.go @@ -102,6 +102,10 @@ func (m *Model) reNumberOfDisplayableItems() { m.numberOfDisplayableItems = min(heightForItems/ITEM_HEIGHT, MAX_DISPLAY_ITEMS) } +func (m Model) getContentHeight() int { + return m.numberOfDisplayableItems * ITEM_HEIGHT +} + func (m *Model) reCalcCursor() { matchesLastIndex := len(m.matches) - 1 if m.cursor >= matchesLastIndex { @@ -180,6 +184,9 @@ var ( selectedStyle = color.BgGray matchedSelectedStyle = color.New(color.Yellow, color.BgGray) ellipsisStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) + listCommandsStyle = lipgloss.NewStyle().Width(20).Border(lipgloss.NormalBorder(), false, true, false, false).BorderForeground(lipgloss.Color("69")) + previewCommandStyle = lipgloss.NewStyle().Width(20) + previewTitleStyle = lipgloss.NewStyle().Background(lipgloss.Color("148")).Foreground(lipgloss.Color("236")) ) func (m Model) updateDeleteMode(msg tea.Msg) (tea.Model, tea.Cmd) { @@ -287,6 +294,9 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.state = ready m.reNumberOfDisplayableItems() m.resetCursor() + contentHeight := m.getContentHeight() + listCommandsStyle = listCommandsStyle.MaxHeight(contentHeight).Width((m.windowWidth / 5) * 2) + previewCommandStyle = previewCommandStyle.MaxHeight(contentHeight).Width(m.windowWidth - listCommandsStyle.GetWidth()) } switch m.activateMode { @@ -321,12 +331,17 @@ func (m Model) View() string { return "" } var matchesContent = strconv.Itoa(len(m.matches)) + " item(s)\n----------" + var content string if len(m.matches) > 0 { // TODO refactor check if empty MatchedIndexes then don't need to format each char + listCommandsContent := "" for i := m.firstIndex; i <= m.lastIndex; i++ { match := m.matches[i] isSelected := m.cursor == i var line = "\n" + if i == m.firstIndex { + line = "" + } if isSelected { line += selectedStyle.Render(strconv.Itoa(i+1) + ". ") } else { @@ -357,7 +372,7 @@ func (m Model) View() string { } // format command _matchCommand := match.MatchCommand - truncatedCommand, isTruncated := common.TruncateWithEllipsis(match.Command, m.windowWidth) + truncatedCommand, isTruncated := common.TruncateWithEllipsis(match.Command, listCommandsStyle.GetWidth()) for j := 0; j < len(truncatedCommand); j++ { if isSelected { if contains(j, _matchCommand.MatchedIndexes) { @@ -380,8 +395,12 @@ func (m Model) View() string { line += ellipsisStyle.Render(common.ELLIPSIS) } } - matchesContent += line + listCommandsContent += line } + + selectedCommandStr := m.matches[m.cursor].Command + + content = lipgloss.JoinHorizontal(lipgloss.Top, listCommandsStyle.Render(listCommandsContent), previewCommandStyle.Render(previewTitleStyle.Render(" Preview ")+"\n"+selectedCommandStr)) } - return fmt.Sprintf("%s\n%s", m.queryInput.View(), matchesContent) + return fmt.Sprintf("%s\n%s\n%s", m.queryInput.View(), matchesContent, content) } From 343d6464adc22916dc077f23c1930d9d8654c050 Mon Sep 17 00:00:00 2001 From: linhx Date: Sun, 1 Dec 2024 23:21:26 +0700 Subject: [PATCH 09/15] feat: scroll to the focus variable when switching --- common/go.sum | 7 ++ views/variableinputs/variableinputs_view.go | 82 ++++++++++++++++----- 2 files changed, 72 insertions(+), 17 deletions(-) create mode 100644 common/go.sum diff --git a/common/go.sum b/common/go.sum new file mode 100644 index 0000000..d7e0b15 --- /dev/null +++ b/common/go.sum @@ -0,0 +1,7 @@ +github.com/mattn/go-runewidth v0.0.12 h1:Y41i/hVW3Pgwr8gV+J23B9YEY0zxjptBuCWEaxmAOow= +github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk= +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/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= diff --git a/views/variableinputs/variableinputs_view.go b/views/variableinputs/variableinputs_view.go index 3ae45c7..1d3dc4f 100644 --- a/views/variableinputs/variableinputs_view.go +++ b/views/variableinputs/variableinputs_view.go @@ -27,22 +27,27 @@ type ( errMsg error ) +var titleStyle = lipgloss.NewStyle().Background(lipgloss.Color("148")).Foreground(lipgloss.Color("236")) + +// TODO refactor Model structure type Model struct { - tokens []common.Token - focusTokenIndex int - variableInputs map[int]VariableInputModel - focusVariableInput *VariableInputModel - err error - Quit bool - Cancel bool - windowWidth int - windowHeight int + tokens []common.Token + focusTokenIndex int + variableInputs map[int]VariableInputModel + focusVariableInputIndex int + focusVariableInput *VariableInputModel + err error + Quit bool + Cancel bool + windowWidth int + windowHeight int } func InitialModel(command string, windowWidth int, windowHeight int) Model { tokens := common.TokensParser(command) variableInputs := make(map[int]VariableInputModel) focusTokenIndex := -1 + focusVariableInputIndex := -1 var focusVariableInput VariableInputModel for i := 0; i < len(tokens); i++ { token := tokens[i] @@ -52,18 +57,20 @@ func InitialModel(command string, windowWidth int, windowHeight int) Model { focusTokenIndex = i variableInput.Focus() focusVariableInput = variableInput + focusVariableInputIndex = 0 } variableInputs[token.Id] = variableInput } } return Model{ - err: nil, - tokens: tokens, - variableInputs: variableInputs, - focusTokenIndex: focusTokenIndex, - focusVariableInput: &focusVariableInput, - windowWidth: windowWidth, - windowHeight: windowHeight, + err: nil, + tokens: tokens, + variableInputs: variableInputs, + focusVariableInputIndex: focusVariableInputIndex, + focusTokenIndex: focusTokenIndex, + focusVariableInput: &focusVariableInput, + windowWidth: windowWidth, + windowHeight: windowHeight, } } @@ -146,6 +153,7 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { case tea.KeyTab: for i := m.focusTokenIndex + 1; i < len(m.tokens); i++ { if m.tokens[i].IsVariable { + m.focusVariableInputIndex++ return m, m.changeFocusInput(msg, i) } } @@ -155,6 +163,7 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { } for i := m.focusTokenIndex - 1; i > -1; i-- { if m.tokens[i].IsVariable { + m.focusVariableInputIndex-- return m, m.changeFocusInput(msg, i) } } @@ -178,6 +187,42 @@ func (m Model) Update(msg tea.Msg) (Model, tea.Cmd) { return m, nil } +func removeFirstNLines(input string, n int) string { + if n < 1 { + return input + } + lineCount := 0 + for i := 0; i < len(input); i++ { + if input[i] == '\n' { + lineCount++ + if lineCount == n { + return input[i+1:] + } + } + } + return "" +} + +func (m Model) getLineNumberOfVariableToken(token common.Token) int { + var content string + for _, _token := range m.tokens { + if _token == token { + content += "." + break + } + content += _token.Value + } + + renderStr := lipgloss.NewStyle().Width(m.windowWidth - 2).Render(content) + count := 0 + for _, char := range renderStr { + if char == '\n' { + count++ + } + } + return count +} + func (m Model) View() string { var str string for _, token := range m.tokens { @@ -188,5 +233,8 @@ func (m Model) View() string { } } style := lipgloss.NewStyle().Width(m.windowWidth - 2) // wrap text - return style.Render(str) + str = style.Render(str) + var totalVariables = len(m.variableInputs) + numOfVariables := titleStyle.Render(fmt.Sprintf("Variables: %d/%d", m.focusVariableInputIndex+1, totalVariables)) + return numOfVariables + "\n" + style.MaxHeight(5).Render(removeFirstNLines(str, m.getLineNumberOfVariableToken(m.tokens[m.focusTokenIndex])-1)) } From e4664a01f8a186966421faaa207b2fb64a6ff024 Mon Sep 17 00:00:00 2001 From: linhx Date: Sun, 1 Dec 2024 23:28:29 +0700 Subject: [PATCH 10/15] chore: update go version --- .github/workflows/go.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index 2e7a406..f63abae 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -19,7 +19,7 @@ jobs: - name: Set up Go uses: actions/setup-go@v4 with: - go-version: '1.20' + go-version: '1.23.1' - name: Build run: ./build.sh From f5bd5b52cd35221822b3b9078493cc1c89c33b9b Mon Sep 17 00:00:00 2001 From: Nguyen Dinh Linh <50461902+linhx@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:01:47 +0700 Subject: [PATCH 11/15] fix: cannot display color for search view --- main.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/main.go b/main.go index 49db34a..ce9040e 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,8 @@ import ( "path/filepath" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" + "github.com/muesli/termenv" "github.com/spf13/viper" "linhx.com/tbmk/bookmark" saveView "linhx.com/tbmk/views/save" @@ -45,6 +47,7 @@ func getAppDir() string { } func main() { + lipgloss.SetColorProfile(termenv.TrueColor) _, exit := NewCancellationSignal() defer exit() viper.AddConfigPath(getAppDir()) From 258ec8e09033fc391fff0cda122c494bf26935ae Mon Sep 17 00:00:00 2001 From: Nguyen Dinh Linh <50461902+linhx@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:10:12 +0700 Subject: [PATCH 12/15] fix: duplicate output command --- views/variableinputs/variableinputs_view.go | 1 - 1 file changed, 1 deletion(-) diff --git a/views/variableinputs/variableinputs_view.go b/views/variableinputs/variableinputs_view.go index 1d3dc4f..c15bf4c 100644 --- a/views/variableinputs/variableinputs_view.go +++ b/views/variableinputs/variableinputs_view.go @@ -83,7 +83,6 @@ func (m Model) GetValue() string { for _, token := range m.tokens { str += token.Value } - fmt.Print(str) return str } From 228fcea39710a6f492cb607d5d761c3999f52c59 Mon Sep 17 00:00:00 2001 From: Nguyen Dinh Linh <50461902+linhx@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:12:55 +0700 Subject: [PATCH 13/15] feat: add some colors to search view --- views/search/search_view.go | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/views/search/search_view.go b/views/search/search_view.go index cee0437..f54fd6c 100644 --- a/views/search/search_view.go +++ b/views/search/search_view.go @@ -41,6 +41,17 @@ const ( INPUT_VARIABLES_MODE string = "VARIABLES" ) +var ( + topLabelStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("148")).Background(lipgloss.Color("236")).MarginRight(1) + highlightStyle = color.Yellow + selectedStyle = color.BgGray + matchedSelectedStyle = color.New(color.Yellow, color.BgGray) + ellipsisStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) + listCommandsStyle = lipgloss.NewStyle().Width(20).Border(lipgloss.NormalBorder(), false, true, false, false).BorderForeground(lipgloss.Color("69")) + previewCommandStyle = lipgloss.NewStyle().Width(20) + previewTitleStyle = lipgloss.NewStyle().Background(lipgloss.Color("148")).Foreground(lipgloss.Color("236")) +) + type Model struct { state state query string @@ -66,7 +77,8 @@ func InitialModel(bmk bookmark.Bookmark, query string) Model { ti.Focus() ti.CharLimit = 156 ti.Width = 20 - ti.Prompt = "Search: " + ti.Prompt = "TBMK - Search:" + ti.PromptStyle = topLabelStyle ti.SetValue(query) var matches []bookmark.MatchedItem @@ -179,16 +191,6 @@ func contains(needle int, haystack []int) bool { return false } -var ( - highlightStyle = color.Yellow - selectedStyle = color.BgGray - matchedSelectedStyle = color.New(color.Yellow, color.BgGray) - ellipsisStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("205")) - listCommandsStyle = lipgloss.NewStyle().Width(20).Border(lipgloss.NormalBorder(), false, true, false, false).BorderForeground(lipgloss.Color("69")) - previewCommandStyle = lipgloss.NewStyle().Width(20) - previewTitleStyle = lipgloss.NewStyle().Background(lipgloss.Color("148")).Foreground(lipgloss.Color("236")) -) - func (m Model) updateDeleteMode(msg tea.Msg) (tea.Model, tea.Cmd) { var err errMsg switch msg := msg.(type) { @@ -349,7 +351,8 @@ func (m Model) View() string { } // format title _matchTitle := match.MatchTitle - for j := 0; j < len(match.Title); j++ { + truncatedTitle, isTruncatedTitle := common.TruncateWithEllipsis(match.Title, listCommandsStyle.GetWidth()) + for j := 0; j < len(truncatedTitle); j++ { if isSelected { if contains(j, _matchTitle.MatchedIndexes) { line += matchedSelectedStyle.Render(string(match.Title[j])) @@ -364,6 +367,13 @@ func (m Model) View() string { } } } + if isTruncatedTitle { + if isSelected { + line += selectedStyle.Render(ellipsisStyle.Render(common.ELLIPSIS)) + } else { + line += ellipsisStyle.Render(common.ELLIPSIS) + } + } // break line between tile and command if isSelected { line += selectedStyle.Render(":") + "\n" + selectedStyle.Render(" > ") @@ -400,7 +410,7 @@ func (m Model) View() string { selectedCommandStr := m.matches[m.cursor].Command - content = lipgloss.JoinHorizontal(lipgloss.Top, listCommandsStyle.Render(listCommandsContent), previewCommandStyle.Render(previewTitleStyle.Render(" Preview ")+"\n"+selectedCommandStr)) + content = lipgloss.JoinHorizontal(lipgloss.Top, listCommandsStyle.MaxHeight(m.getContentHeight()).Render(listCommandsContent), previewCommandStyle.Render(previewTitleStyle.Render(" Preview ")+"\n"+selectedCommandStr)) } return fmt.Sprintf("%s\n%s\n%s", m.queryInput.View(), matchesContent, content) } From e5b7149c149d0968d2dedd071467a62128a3cd60 Mon Sep 17 00:00:00 2001 From: Nguyen Dinh Linh <50461902+linhx@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:13:51 +0700 Subject: [PATCH 14/15] feat: update UI for save screen --- views/save/save_view.go | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/views/save/save_view.go b/views/save/save_view.go index 583d669..c3a585b 100644 --- a/views/save/save_view.go +++ b/views/save/save_view.go @@ -1,25 +1,28 @@ package views_save import ( - "strings" - common "linhx.com/tbmk/common" "github.com/charmbracelet/bubbles/textarea" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" + "github.com/charmbracelet/lipgloss" bookmark "linhx.com/tbmk/bookmark" ) -const TOP_HEIGHT = 3 // 1 for app title, 1 for title input, 1 for command prompt -const MIN_WINDOW_HEIGHT = 4 // 1 for app title, 1 for title input, 2 for command input -const MAX_COMMAND_INPUT_HEIGHT = 5 +const TOP_HEIGHT = 4 // 1 for app title, 1 for title input, 1 for command prompt +const MIN_WINDOW_HEIGHT = 5 // 1 for app title, 1 for title input, 2 for command input +const MAX_COMMAND_INPUT_HEIGHT = 6 + +var ( + topLabelStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("148")).Background(lipgloss.Color("236")).MarginRight(1) + helpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("241")) +) type Model struct { focusIndex int titleInput textinput.Model commandInput textarea.Model - cursorMode textinput.CursorMode err error bmk bookmark.Bookmark quit bool @@ -178,7 +181,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { commandInputHeight = MAX_COMMAND_INPUT_HEIGHT } m.commandInput.SetHeight(commandInputHeight) - return m, tea.ClearScreen + m.commandInput.SetWidth(m.windowWidth) } if m.confirmOverrideMode { return m.updateOverrideMode(msg) @@ -202,13 +205,5 @@ func (m Model) View() string { return "" } - var b strings.Builder - b.WriteString("Commands bookmark\n") - - b.WriteString(m.titleInput.View()) - b.WriteRune('\n') - b.WriteString("Command: \n") - b.WriteString(m.commandInput.View()) - - return b.String() + return topLabelStyle.Render("TBMK - Save") + "\n" + helpStyle.Render("(Ctrl + S to save)") + "\n" + m.titleInput.View() + "\n" + "Command: \n" + m.commandInput.View() } From edaf828f589a405d0fb3cb40853a774bfd9a35d2 Mon Sep 17 00:00:00 2001 From: Nguyen Dinh Linh <50461902+linhx@users.noreply.github.com> Date: Mon, 2 Dec 2024 14:17:30 +0700 Subject: [PATCH 15/15] chore: update README.md --- README.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/README.md b/README.md index 1c50699..360f93b 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,15 @@ A commands bookmark for terminal ![demo](./tbmk.gif) +Support placeholders and multilines command + +![Add command](https://github.com/user-attachments/assets/0f501fbf-2963-484e-8b9d-1510b4c087c9) + +![Preview command](https://github.com/user-attachments/assets/7601267b-430e-4abc-bbd5-d9507051cdda) + +![Edit placeholders](https://github.com/user-attachments/assets/d2c46031-f661-43ae-bfb9-c539d7665b10) + + ## Worked on - Linux: bash, zsh, fish