Add bash and zsh complete

pull/296/head
Anton Medvedev 2 months ago
parent 478a3125cc
commit 38e9fea494

@ -1,69 +0,0 @@
package main
import (
"fmt"
"os"
"strings"
"github.com/antonmedv/fx/internal/shlex"
)
var flags = []string{
"--help",
"--raw",
"--slurp",
"--themes",
"--version",
"--yaml",
"-h",
"-r",
"-s",
"-v",
}
func complete() {
compLine, ok := os.LookupEnv("COMP_LINE")
if !ok || len(os.Args) < 3 {
return
}
// Get the current partial word to be completed
partial := os.Args[2]
var reply []string
if strings.HasPrefix(partial, "-") {
// Filter the flags that match the partial word
for _, flag := range flags {
if strings.HasPrefix(flag, partial) {
reply = append(reply, flag)
}
}
}
args, err := shlex.Split(compLine)
if err != nil {
return
}
if len(args) <= 2 {
reply = files(partial)
}
for _, word := range reply {
fmt.Println(word)
}
os.Exit(0)
}
// log appends the given arguments to the log file.
func log(args ...interface{}) {
file, err := os.OpenFile("complete.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return
}
fmt.Fprintln(file, args...)
file.Close()
}

@ -8,6 +8,7 @@ require (
github.com/charmbracelet/bubbletea v0.25.0
github.com/charmbracelet/lipgloss v0.10.0
github.com/charmbracelet/x/exp/teatest v0.0.0-20231025135604-4a717d4fb812
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204
github.com/goccy/go-yaml v1.11.3
github.com/mattn/go-isatty v0.0.20
github.com/mattn/go-runewidth v0.0.15
@ -22,7 +23,10 @@ require (
github.com/aymanbagabas/go-udiff v0.1.3 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/fatih/color v1.16.0 // indirect
github.com/go-sourcemap/sourcemap v2.1.3+incompatible // indirect
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-localereader v0.0.1 // indirect

@ -14,18 +14,43 @@ github.com/charmbracelet/lipgloss v0.10.0 h1:KWeXFSexGcfahHX+54URiZGkBFazf70JNMt
github.com/charmbracelet/lipgloss v0.10.0/go.mod h1:Wig9DSfvANsxqkRsqj6x87irdy123SR4dOXlKa91ciE=
github.com/charmbracelet/x/exp/teatest v0.0.0-20231025135604-4a717d4fb812 h1:W/hU7Z+y+QsZo2qg0hwjv56qSMP12Z72DJR8k+ULbA4=
github.com/charmbracelet/x/exp/teatest v0.0.0-20231025135604-4a717d4fb812/go.mod h1:TckAxPtan3aJ5wbTgBkySpc50SZhXJRZ8PtYICnZJEw=
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
github.com/containerd/console v1.0.4/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
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/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0 h1:7lJfhqlPssTb1WQx4yvTHN0uElPEv52sbaECrAQxjAo=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dop251/goja v0.0.0-20211022113120-dc8c55024d06/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204 h1:O7I1iuzEA7SG+dK8ocOBSlYAA9jBUmCYl/Qa7ey7JAM=
github.com/dop251/goja v0.0.0-20240220182346-e401ed450204/go.mod h1:QMWlm50DNe14hD7t24KEqZuUdC9sOTy8W6XbCU1mlw4=
github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/dop251/goja_nodejs v0.0.0-20211022123610-8dd9abb0616d/go.mod h1:DngW8aVqWbuLRMHItjPUyqdj+HWPvnQe8V8y1nDpIbM=
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible h1:W1iEw64niKVGogNgBN3ePyLFfuisuzeidWPMPWmECqU=
github.com/go-sourcemap/sourcemap v2.1.3+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
github.com/goccy/go-yaml v1.11.3 h1:B3W9IdWbvrUu2OYQGwvU1nZtvMQJPBKgBUuweJjLj6I=
github.com/goccy/go-yaml v1.11.3/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
@ -54,25 +79,58 @@ github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
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.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/sahilm/fuzzy v0.1.1 h1:ceu5RHF8DGgoi+/dR5PsECjCDH1BE3Fnmpo7aVXOdRA=
github.com/sahilm/fuzzy v0.1.1/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
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/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/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-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8=
golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.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=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

@ -25,9 +25,10 @@ func usage(keyMap KeyMap) string {
-h, --help print help
-v, --version print version
--themes print themes
--yaml parse input as YAML
--comp <shell> print completion script
-r, --raw treat input as a raw string
-s, --slurp read all inputs into an array
--yaml parse input as YAML
%v
%v

@ -0,0 +1,6 @@
package complete
func Bash() string {
return `complete -o filenames -C fx fx
`
}

@ -0,0 +1,239 @@
package complete
import (
"fmt"
"os"
"path/filepath"
"regexp"
"strings"
"github.com/dop251/goja"
"github.com/goccy/go-yaml"
"github.com/antonmedv/fx/internal/shlex"
)
var flags = []string{
"--help",
"--raw",
"--slurp",
"--themes",
"--version",
"--yaml",
"-h",
"-r",
"-s",
"-v",
}
var globals = []string{
"JSON.stringify",
"JSON.parse",
"YAML.stringify",
"YAML.parse",
"Object.keys",
"Object.values",
"Object.entries",
"Object.fromEntries",
"Array.isArray",
"Array.from",
"len",
"uniq",
"sort",
"map",
"sortBy",
"groupBy",
"chunk",
"zip",
"flatten",
"reverse",
"keys",
"values",
}
func Complete() bool {
compLine, ok := os.LookupEnv("COMP_LINE")
if ok && len(os.Args) == 3 {
doComplete(compLine, os.Args[2])
return true
}
compZsh, ok := os.LookupEnv("COMP_ZSH")
if ok {
doComplete(compZsh, lastWord(compZsh))
return true
}
return false
}
func doComplete(compLine string, compWord string) {
if strings.HasPrefix(compWord, "-") {
compReply(filterReply(flags, compWord))
return
}
args, err := shlex.Split(compLine)
if err != nil {
return
}
compWord = shlex.Parse(compWord)
var flagYaml bool
for _, arg := range args {
if arg == "--yaml" {
flagYaml = true
break
}
}
// Remove flags from args.
args = filterArgs(args)
isSecondArgIsFile := false
if len(args) == 0 {
return
} else if len(args) == 1 {
fileComplete(compWord)
return
} else if len(args) == 2 {
isSecondArgIsFile = isFile(args[1])
if !isSecondArgIsFile {
fileComplete(compWord)
return
}
} else {
isSecondArgIsFile = isFile(args[1])
}
if isSecondArgIsFile {
if globalsComplete(compWord) {
return
}
file := args[1]
hasYamlExt, _ := regexp.MatchString(`(?i)\.ya?ml$`, file)
if !flagYaml && hasYamlExt {
flagYaml = true
}
input, err := os.ReadFile(file)
if err != nil {
return
}
// If input is bigger than 1MB, skip completion.
if len(input) > 1024*1024 {
return
}
if flagYaml {
input, err = yaml.YAMLToJSON(input)
if err != nil {
return
}
}
codeComplete(string(input), args, compWord)
}
}
func globalsComplete(compWord string) bool {
if compWord == "" {
// We will not complete globals if compWord is empty,
// as we want to show only object keys.
return false
}
reply := filterReply(globals, compWord)
if len(reply) > 0 {
compReply(reply)
return true
}
return false
}
func codeComplete(input string, args []string, compWord string) {
args = args[2:] // Drop binary & file from the args.
if compWord == "" {
args = append(args, ".__keys()")
} else {
if len(args) > 0 {
last := args[len(args)-1]
last = dropTail(args[len(args)-1])
last = last + ".__keys()"
last = balanceBrackets(last)
args[len(args)-1] = last
}
}
var code strings.Builder
code.WriteString(prelude)
code.WriteString(fmt.Sprintf("let json = %s\n", input))
for _, arg := range args {
if arg == "" {
continue
}
code.WriteString(Transform(arg))
}
code.WriteString("\n__keys\n")
out, err := goja.New().RunString(code.String())
if err != nil {
return
}
if array, ok := out.Export().([]interface{}); ok {
prefix := dropTail(compWord)
var reply []string
for _, key := range array {
reply = append(reply, prefix+key.(string))
}
compReply(filterReply(reply, compWord))
}
}
func filterArgs(args []string) []string {
filtered := make([]string, 0, len(args))
for _, arg := range args {
found := false
for _, flag := range flags {
if arg == flag {
found = true
break
}
}
if !found {
filtered = append(filtered, arg)
}
}
return filtered
}
func fileComplete(compWord string) {
var matches []string
dir, filePrefix := filepath.Split(compWord)
if dir == "" {
dir = "."
}
pattern := filepath.Join(dir, filePrefix+"*")
files, err := filepath.Glob(pattern)
if err != nil {
fmt.Println("Error reading directory:", err)
return
}
for _, match := range files {
relativePath, err := filepath.Rel(".", match)
if err != nil {
continue
}
matches = append(matches, relativePath)
}
compReply(matches)
}

@ -0,0 +1,107 @@
package complete
const prelude = `
const __keys = new Set()
Object.prototype.__keys = function () {
if (Array.isArray(this)) return
if (typeof this === 'string') return
if (this instanceof String) return
if (typeof this === 'object' && this !== null)
Object.keys(this)
.filter(x => /^\w+$/.test(x))
.forEach(x => __keys.add('.' + x))
}
function apply(fn, ...args) {
if (typeof fn === 'function') return fn(...args)
return fn
}
function len(x) {
if (Array.isArray(x)) return x.length
if (typeof x === 'string') return x.length
if (typeof x === 'object' && x !== null) return Object.keys(x).length
throw new Error()
}
function uniq(x) {
if (Array.isArray(x)) return [...new Set(x)]
throw new Error()
}
function sort(x) {
if (Array.isArray(x)) return x.sort()
throw new Error()
}
function map(fn) {
return function (x) {
if (Array.isArray(x)) return x.map((v, i) => fn(v, i))
throw new Error()
}
}
function sortBy(fn) {
return function (x) {
if (Array.isArray(x)) return x.sort((a, b) => {
const fa = fn(a)
const fb = fn(b)
return fa < fb ? -1 : fa > fb ? 1 : 0
})
throw new Error()
}
}
function groupBy(keyFn) {
return function (x) {
const grouped = {}
for (const item of x) {
const key = typeof keyFn === 'function' ? keyFn(item) : item[keyFn]
if (!grouped.hasOwnProperty(key)) grouped[key] = []
grouped[key].push(item)
}
return grouped
}
}
function chunk(size) {
return function (x) {
const res = []
let i = 0
while (i < x.length) {
res.push(x.slice(i, i += size))
}
return res
}
}
function zip(...x) {
const length = Math.min(...x.map(a => a.length))
const res = []
for (let i = 0; i < length; i++) {
res.push(x.map(a => a[i]))
}
return res
}
function flatten(x) {
if (Array.isArray(x)) return x.flat()
throw new Error()
}
function reverse(x) {
if (Array.isArray(x)) return x.reverse()
throw new Error()
}
function keys(x) {
if (typeof x === 'object' && x !== null) return Object.keys(x)
throw new Error()
}
function values(x) {
if (typeof x === 'object' && x !== null) return Object.values(x)
throw new Error()
}
`

@ -0,0 +1,70 @@
package complete
import (
"fmt"
"regexp"
"strings"
)
func Transform(code string) string {
return fmt.Sprintf(`json = apply((function () {
const x = this
return %s
}).call(json), json)
`, transpile(code))
}
var (
reBracket = regexp.MustCompile(`^(\.\w*)+\[]`)
reBracketStart = regexp.MustCompile(`^\.\[`)
reDotStart = regexp.MustCompile(`^\.`)
reMap = regexp.MustCompile(`^map\(.+?\)$`)
reAt = regexp.MustCompile(`^@`)
)
func transpile(code string) string {
if code == "." {
return "x"
}
if reBracket.MatchString(code) {
return fmt.Sprintf("(%s)(x)", fold(strings.Split(code, "[]")))
}
if reBracketStart.MatchString(code) {
return "x" + code[1:]
}
if reDotStart.MatchString(code) {
return "x" + code
}
if reMap.MatchString(code) {
s := code[4 : len(code)-1]
if s[0] == '.' {
s = "x" + s
}
return fmt.Sprintf(`x.map((x, i) => apply(%s, x, i))`, s)
}
if reAt.MatchString(code) {
jsCode := transpile(code[1:])
return fmt.Sprintf(`x.map((x, i) => apply(%s, x, i))`, jsCode)
}
return code
}
func fold(s []string) string {
if len(s) == 1 {
return "x => x" + s[0]
}
obj := s[0]
s = s[1:]
if obj == "." {
obj = "x"
} else {
obj = "x" + obj
}
return fmt.Sprintf(`x => %s.flatMap(%s)`, obj, fold(s))
}

@ -0,0 +1,79 @@
package complete
import (
"fmt"
"os"
"regexp"
"strings"
)
func compReply(reply []string) {
for _, word := range reply {
fmt.Println(word)
}
}
func filterReply(reply []string, compWord string) []string {
var filtered []string
for _, word := range reply {
if strings.HasPrefix(word, compWord) {
filtered = append(filtered, word)
}
}
return filtered
}
func isFile(path string) bool {
info, err := os.Stat(path)
if err != nil {
return false
}
return !info.IsDir()
}
var tailRe = regexp.MustCompile(`\.?\w*$`)
func dropTail(s string) string {
return tailRe.ReplaceAllString(s, "")
}
func balanceBrackets(code string) string {
var stack []rune
brackets := map[rune]rune{')': '(', '}': '{', ']': '['}
reverseBrackets := map[rune]rune{'(': ')', '{': '}', '[': ']'}
for _, char := range code {
switch char {
case '(', '{', '[':
stack = append(stack, char)
case ')', '}', ']':
if len(stack) > 0 && brackets[char] == stack[len(stack)-1] {
stack = stack[:len(stack)-1] // Pop
}
}
}
for i := len(stack) - 1; i >= 0; i-- {
code += string(reverseBrackets[stack[i]])
}
return code
}
func lastWord(line string) string {
words := strings.Split(line, " ")
var s string
if len(words) > 0 {
s = words[len(words)-1]
}
return s
}
func debug(args ...interface{}) {
file, err := os.OpenFile("complete.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
if err != nil {
return
}
_, _ = fmt.Fprintln(file, args...)
_ = file.Close()
}

@ -0,0 +1,14 @@
package complete
func Zsh() string {
return `_fx_complete() {
if [[ $CURRENT -eq 2 ]]; then
_files
else
compadd $(COMP_ZSH="${LBUFFER}" fx)
fi
}
compdef _fx_complete fx
`
}

@ -408,3 +408,19 @@ func Split(s string) ([]string, error) {
subStrings = append(subStrings, word)
}
}
// Parse removes the shell-style quoting from a string.
func Parse(s string) string {
l := NewLexer(strings.NewReader(s))
var b strings.Builder
for {
word, err := l.Next()
if err != nil {
if err == io.EOF {
return b.String()
}
return ""
}
b.WriteString(word)
}
}

@ -3,6 +3,7 @@ package main
import (
"encoding/json"
"errors"
"flag"
"fmt"
"io"
"io/fs"
@ -24,13 +25,13 @@ import (
"github.com/mattn/go-isatty"
"github.com/sahilm/fuzzy"
"github.com/antonmedv/fx/internal/complete"
jsonpath "github.com/antonmedv/fx/path"
)
var (
flagHelp bool
flagVersion bool
flagYaml bool
flagYaml bool
flagComp bool
)
func main() {
@ -51,36 +52,46 @@ func main() {
defer pprof.WriteHeapProfile(memProf)
}
complete()
if complete.Complete() {
os.Exit(0)
return
}
var args []string
for _, arg := range os.Args[1:] {
if strings.HasPrefix(arg, "--comp") {
flagComp = true
continue
}
switch arg {
case "-h", "--help":
flagHelp = true
fmt.Println(usage(keyMap))
return
case "-v", "-V", "--version":
flagVersion = true
fmt.Println(version)
return
case "--themes":
themeTester()
return
case "--export-themes":
exportThemes()
return
case "--yaml":
flagYaml = true
default:
args = append(args, arg)
}
}
if flagHelp {
fmt.Println(usage(keyMap))
return
}
if flagVersion {
fmt.Println(version)
if flagComp {
shell := flag.String("comp", "", "")
flag.Parse()
switch *shell {
case "bash":
fmt.Print(complete.Bash())
case "zsh":
fmt.Print(complete.Zsh())
default:
fmt.Println("unknown shell type")
}
return
}
@ -112,7 +123,7 @@ func main() {
} else if !stdinIsTty && len(args) == 0 {
src = os.Stdin
} else {
reduce(args)
reduce(os.Args[1:])
return
}

Loading…
Cancel
Save