mirror of
https://github.com/antonmedv/fx.git
synced 2024-10-03 21:27:12 +03:00
Add bash and zsh complete
This commit is contained in:
parent
478a3125cc
commit
38e9fea494
69
complete.go
69
complete.go
@ -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()
|
||||
}
|
4
go.mod
4
go.mod
@ -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
|
||||
|
60
go.sum
60
go.sum
@ -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=
|
||||
|
3
help.go
3
help.go
@ -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
|
||||
|
6
internal/complete/bash.go
Normal file
6
internal/complete/bash.go
Normal file
@ -0,0 +1,6 @@
|
||||
package complete
|
||||
|
||||
func Bash() string {
|
||||
return `complete -o filenames -C fx fx
|
||||
`
|
||||
}
|
239
internal/complete/complete.go
Normal file
239
internal/complete/complete.go
Normal file
@ -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)
|
||||
}
|
107
internal/complete/prelude.go
Normal file
107
internal/complete/prelude.go
Normal file
@ -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()
|
||||
}
|
||||
`
|
70
internal/complete/transpile.go
Normal file
70
internal/complete/transpile.go
Normal file
@ -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))
|
||||
}
|
79
internal/complete/utils.go
Normal file
79
internal/complete/utils.go
Normal file
@ -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()
|
||||
}
|
14
internal/complete/zsh.go
Normal file
14
internal/complete/zsh.go
Normal file
@ -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)
|
||||
}
|
||||
}
|
||||
|
45
main.go
45
main.go
@ -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…
Reference in New Issue
Block a user