From 5c32aa4b791c7b23acdc8aeeb23f917c9b365c63 Mon Sep 17 00:00:00 2001 From: rsteube Date: Sat, 13 Jan 2024 14:29:08 +0100 Subject: [PATCH] bridge: use shlex.Join --- Dockerfile | 34 +++++++++++++++++++ docker-compose.yml | 10 ++++++ go.mod | 3 +- go.sum | 8 ++--- pkg/actions/bridge/bash.go | 30 +++++++++------- pkg/actions/bridge/bash.sh | 7 +++- pkg/actions/bridge/click.go | 3 +- pkg/actions/bridge/fish.go | 11 ++---- pkg/actions/bridge/inshellisense.go | 10 +++++- pkg/actions/bridge/powershell.go | 3 +- .../zsh-capture-completion/capture.zsh | 3 +- 11 files changed, 91 insertions(+), 31 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2ffb048 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM ghcr.io/rsteube/carapace:latest + +RUN apt-get update \ + && apt-get install -y bash-completion \ + npm \ + pip + +# argcomplete +RUN pip install --break-system-packages azure-cli + +# click +RUN pip install --break-system-packages td-watson + +# cobra +RUN curl -Lo /usr/local/bin/minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 \ + && chmod +x /usr/local/bin/minikube + +# complete +RUN curl --proto '=https' --tlsv1.2 -fsSL https://get.opentofu.org/install-opentofu.sh | sh -s -- --install-method deb + +# inshellisense +RUN npm install -g @microsoft/inshellisense +RUN cd /usr/local/lib/node_modules/@microsoft/inshellisense/ \ + && npm i @withfig/autocomplete@2.648.2 + +# kingpin +RUN curl https://goteleport.com/static/install.sh | bash -s 14.3.3 + +# urvavecli +RUN curl -Lo /usr/local/bin/tea https://dl.gitea.com/tea/0.9.2/tea-0.9.2-linux-amd64 \ + && chmod +x /usr/local/bin/tea + +# yargs +RUN npm install -g @angular/cli diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..af08d70 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3' + +services: + bridge: + build: . + command: elvish + environment: + TARGET: /carapace-bridge/cmd/carapace-bridge/carapace-bridge + volumes: + - '.:/carapace-bridge/' diff --git a/go.mod b/go.mod index 03e2be2..bcdae73 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/rsteube/carapace-bridge go 1.16 require ( - github.com/rsteube/carapace v0.49.0 + github.com/rsteube/carapace v0.49.1 + github.com/rsteube/carapace-shlex v0.1.2 github.com/spf13/cobra v1.8.0 ) diff --git a/go.sum b/go.sum index 8b60946..c66f5fb 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/rsteube/carapace v0.49.0 h1:HhUyBCiZHdWusl73D652XuWS+3YsYBoS3QXNfpiyv9c= -github.com/rsteube/carapace v0.49.0/go.mod h1:4ZC5bulItu9t9sZ5yPcHgPREd8rPf274Q732n+wfl/o= -github.com/rsteube/carapace-shlex v0.1.1 h1:fRQEBBKyYKm4TXUabm4tzH904iFWSmXJl3UZhMfQNYU= -github.com/rsteube/carapace-shlex v0.1.1/go.mod h1:zPw1dOFwvLPKStUy9g2BYKanI6bsQMATzDMYQQybo3o= +github.com/rsteube/carapace v0.49.1 h1:ljGQpEkSATpvAnP6eO7Bc1iLJljxgfpCJoCPA/EkQlU= +github.com/rsteube/carapace v0.49.1/go.mod h1:syVOvI8e2rEEK/9aMZxfWuHvcnQK/EcnTV4roClEnLE= +github.com/rsteube/carapace-shlex v0.1.2 h1:ZKjhIfXoCkEnzensMglTaLbkNOaLkmM8SCRshpJKx6s= +github.com/rsteube/carapace-shlex v0.1.2/go.mod h1:zPw1dOFwvLPKStUy9g2BYKanI6bsQMATzDMYQQybo3o= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= diff --git a/pkg/actions/bridge/bash.go b/pkg/actions/bridge/bash.go index 0433e4a..256a298 100644 --- a/pkg/actions/bridge/bash.go +++ b/pkg/actions/bridge/bash.go @@ -3,9 +3,11 @@ package bridge import ( _ "embed" "fmt" + "os" "strings" "github.com/rsteube/carapace" + shlex "github.com/rsteube/carapace-shlex" "github.com/rsteube/carapace/pkg/style" "github.com/rsteube/carapace/pkg/xdg" ) @@ -18,7 +20,7 @@ var bashSnippet string func ActionBash(command ...string) carapace.Action { return carapace.ActionCallback(func(c carapace.Context) carapace.Action { if len(command) == 0 { - return carapace.ActionMessage("missing argument [ActionFish]") + return carapace.ActionMessage("missing argument [ActionBash]") } configDir, err := xdg.UserConfigDir() @@ -26,25 +28,29 @@ func ActionBash(command ...string) carapace.Action { return carapace.ActionMessage(err.Error()) } - // replacer := strings.NewReplacer( - // ` `, `\ `, - // `"`, `\""`, - // ) - args := append(command, c.Args...) args = append(args, c.Value) - // for index, arg := range args { - // args[index] = replacer.Replace(arg) - // } - configPath := fmt.Sprintf("%v/carapace/bridge/bash/.bashrc", configDir) if err := ensureExists(configPath); err != nil { return carapace.ActionMessage(err.Error()) } - c.Setenv("COMP_LINE", strings.Join(args, " ")) - return carapace.ActionExecCommand("bash", "--rcfile", configPath, "-i", "-c", bashSnippet, strings.Join(args, " "))(func(output []byte) carapace.Action { + joined := shlex.Join(args) + if c.Value == "" { + joined = strings.TrimSuffix(joined, `""`) + } + c.Setenv("COMP_LINE", joined) + + file, err := os.CreateTemp(os.TempDir(), "carapace-bridge_bash_*") + if err != nil { + return carapace.ActionMessage(err.Error()) + } + defer os.Remove(file.Name()) + + os.WriteFile(file.Name(), []byte(bashSnippet), os.ModePerm) + + return carapace.ActionExecCommand("bash", "--rcfile", configPath, "-i", file.Name())(func(output []byte) carapace.Action { lines := strings.Split(string(output), "\n") return carapace.ActionValues(lines[:len(lines)-1]...).StyleF(style.ForPath) }).Invoke(c).ToA().NoSpace([]rune("/=@:.,")...) // TODO check compopt for nospace diff --git a/pkg/actions/bridge/bash.sh b/pkg/actions/bridge/bash.sh index 009f071..05ea3d6 100755 --- a/pkg/actions/bridge/bash.sh +++ b/pkg/actions/bridge/bash.sh @@ -1,4 +1,9 @@ -#!/bin/bash -i +#!/bin/bash + +[ -f /usr/local/etc/bash_completion ] && source /usr/local/etc/bash_completion # osx +[ -f /usr/share/bash-completion/bash_completion ] && source /usr/share/bash-completion/bash_completion # linux +[ -f /data/data/com.termux/files/usr/share/bash-completion/bash_completion ] && source /data/data/com.termux/files/usr/share/bash-completion/bash_completion # termux + # COMP_LINE="$1" COMP_WORDS=($COMP_LINE) if [ "${COMP_LINE: -1}" = " " ]; then diff --git a/pkg/actions/bridge/click.go b/pkg/actions/bridge/click.go index 28b7ca9..ab6bfa3 100644 --- a/pkg/actions/bridge/click.go +++ b/pkg/actions/bridge/click.go @@ -7,6 +7,7 @@ import ( "strings" "github.com/rsteube/carapace" + shlex "github.com/rsteube/carapace-shlex" ) // ActionClick bridges https://github.com/pallets/click @@ -42,7 +43,7 @@ func ActionClick(command ...string) carapace.Action { args := append(command[1:], c.Args...) current := c.Value - compLine := command[0] + " " + strings.Join(append(args, current), " ") // TODO escape/quote special characters + compLine := command[0] + " " + shlex.Join(append(args, current)) c.Setenv(fmt.Sprintf("_%v_COMPLETE", strings.ToUpper(command[0])), "zsh_complete") c.Setenv("COMP_WORDS", compLine) c.Setenv("COMP_CWORD", strconv.Itoa(len(args)+1)) diff --git a/pkg/actions/bridge/fish.go b/pkg/actions/bridge/fish.go index d3c977e..21697f0 100644 --- a/pkg/actions/bridge/fish.go +++ b/pkg/actions/bridge/fish.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/rsteube/carapace" + shlex "github.com/rsteube/carapace-shlex" "github.com/rsteube/carapace/pkg/style" "github.com/rsteube/carapace/pkg/xdg" ) @@ -22,23 +23,15 @@ func ActionFish(command ...string) carapace.Action { return carapace.ActionMessage(err.Error()) } - replacer := strings.NewReplacer( - ` `, `\ `, - `"`, `\""`, - ) - args := append(command, c.Args...) args = append(args, c.Value) - for index, arg := range args { - args[index] = replacer.Replace(arg) - } configPath := fmt.Sprintf("%v/carapace/bridge/fish/config.fish", configDir) if err := ensureExists(configPath); err != nil { return carapace.ActionMessage(err.Error()) } - snippet := fmt.Sprintf(`source %#v;complete --do-complete="%v"`, configPath, strings.Join(args, " ")) // TODO needs custom escaping + snippet := fmt.Sprintf(`source "$__fish_data_dir/config.fish";source %#v;complete --do-complete="%v"`, configPath, shlex.Join(args)) // TODO needs custom escaping return carapace.ActionExecCommand("fish", "--no-config", "--command", snippet)(func(output []byte) carapace.Action { lines := strings.Split(string(output), "\n") diff --git a/pkg/actions/bridge/inshellisense.go b/pkg/actions/bridge/inshellisense.go index 6e341d8..305cf81 100644 --- a/pkg/actions/bridge/inshellisense.go +++ b/pkg/actions/bridge/inshellisense.go @@ -5,6 +5,7 @@ import ( "strings" "github.com/rsteube/carapace" + shlex "github.com/rsteube/carapace-shlex" ) // ActionInshellisense bridges https://github.com/microsoft/inshellisense @@ -16,7 +17,14 @@ func ActionInshellisense(command ...string) carapace.Action { args := append(command, c.Args...) args = append(args, c.Value) - input := strings.Join(args, " ") // TODO simple join for now as the lexer in inshellisense can't handle quotes and spaces anyway + + input := shlex.Join(args) + + if strings.HasSuffix(input, `""`) { + // TODO temporary fix as inshellisense can't handle quotes yet (won't work for those within) + input = input[:len(input)-2] + " " + } + return carapace.ActionExecCommand("inshellisense", "complete", input)(func(output []byte) carapace.Action { var r struct { Suggestions []struct { diff --git a/pkg/actions/bridge/powershell.go b/pkg/actions/bridge/powershell.go index d35023e..2e9591b 100644 --- a/pkg/actions/bridge/powershell.go +++ b/pkg/actions/bridge/powershell.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/rsteube/carapace" + shlex "github.com/rsteube/carapace-shlex" "github.com/rsteube/carapace/pkg/style" "github.com/rsteube/carapace/pkg/xdg" ) @@ -48,7 +49,7 @@ func ActionPowershell(command ...string) carapace.Action { // args[index] = strings.Replace(arg, " ", "` ", -1) // } - line := strings.Join(args, " ") + line := shlex.Join(args) snippet := []string{ fmt.Sprintf(`Get-Content "%v/carapace/bridge/powershell/Microsoft.PowerShell_profile.ps1" | Out-String | Invoke-Expression`, configDir), fmt.Sprintf(`[System.Management.Automation.CommandCompletion]::CompleteInput("%v", %v, $null).CompletionMatches | ConvertTo-Json `, line, len(line)), diff --git a/third_party/github.com/Valodim/zsh-capture-completion/capture.zsh b/third_party/github.com/Valodim/zsh-capture-completion/capture.zsh index 27a11db..6f4a8e7 100755 --- a/third_party/github.com/Valodim/zsh-capture-completion/capture.zsh +++ b/third_party/github.com/Valodim/zsh-capture-completion/capture.zsh @@ -22,10 +22,11 @@ setopt rcquotes PROMPT= # load completion system -autoload compinit +autoload -U compinit && compinit compinit -d "${CARAPACE_BRIDGE_CONFIG_HOME:-~/.config}/carapace/bridge/zsh/.zcompdump_capture" source "${CARAPACE_BRIDGE_CONFIG_HOME:-~/.config}/carapace/bridge/zsh/.zshrc" + # never run a command bindkey ''^M'' undefined bindkey ''^J'' undefined