Skip to content

Commit

Permalink
bash: split COMP_LINE with shlex
Browse files Browse the repository at this point in the history
  • Loading branch information
rsteube committed Aug 9, 2023
1 parent bff086c commit 9d5749e
Show file tree
Hide file tree
Showing 6 changed files with 69 additions and 2 deletions.
17 changes: 15 additions & 2 deletions complete.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package carapace

import (
"github.com/rsteube/carapace/internal/config"
"github.com/rsteube/carapace/internal/shell/bash"
"github.com/rsteube/carapace/internal/shell/nushell"
"github.com/rsteube/carapace/pkg/ps"
"github.com/spf13/cobra"
Expand All @@ -16,9 +17,21 @@ func complete(cmd *cobra.Command, args []string) (string, error) {
default:
initHelpCompletion(cmd)

if shell := ps.DetermineShell(); shell == "nushell" {
args = nushell.Patch(args)
switch ps.DetermineShell() {
case "nushell":
args = nushell.Patch(args) // handle open quotes
LOG.Printf("patching args to %#v", args)
case "bash": // TODO what about oil and such?
var err error
args, err = bash.Patch(args) // handle redirects
LOG.Printf("patching args to %#v", args)
if _, ok := err.(bash.RedirectError); ok {
LOG.Printf("completing redirect target for %#v", args)
context := NewContext(args...)
return ActionFiles().Invoke(context).value(args[0], args[len(args)-1]), nil
}
}

action, context := traverse(cmd, args[2:])
if err := config.Load(); err != nil {
action = ActionMessage("failed to load config: " + err.Error())
Expand Down
1 change: 1 addition & 0 deletions example/cmd/_test/bash-ble.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/bin/bash
_example_completion() {
export COMP_WORDBREAKS
export COMP_LINE

local compline="${COMP_LINE:0:${COMP_POINT}}"
local IFS=$'\n'
Expand Down
1 change: 1 addition & 0 deletions example/cmd/_test/bash.sh
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#!/bin/bash
_example_completion() {
export COMP_WORDBREAKS
export COMP_LINE

local compline="${COMP_LINE:0:${COMP_POINT}}"
local IFS=$'\n'
Expand Down
2 changes: 2 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@ github.com/rsteube/carapace-pflag v0.1.0 h1:CPJRlj3jbyOnxuMf5pdrM76hEwdQ0STDDmkA
github.com/rsteube/carapace-pflag v0.1.0/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/rsteube/carapace-shlex v0.0.1 h1:8uvsc+ISKw7uoITSp92nNisFUOulYMz+Uu7N5nbHTiM=
github.com/rsteube/carapace-shlex v0.0.1/go.mod h1:zPw1dOFwvLPKStUy9g2BYKanI6bsQMATzDMYQQybo3o=
github.com/rsteube/carapace-shlex v0.0.3 h1:QzcD31o9L4EK0ga9AxUU1QrfvfYb9TCdgOYUhpIstpQ=
github.com/rsteube/carapace-shlex v0.0.3/go.mod h1:zPw1dOFwvLPKStUy9g2BYKanI6bsQMATzDMYQQybo3o=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
49 changes: 49 additions & 0 deletions internal/shell/bash/patch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package bash

import (
"os"

shlex "github.com/rsteube/carapace-shlex"
)

// RedirectError current position is a redirect like `echo test >[TAB]`.
type RedirectError struct{}

func (r RedirectError) Error() string {
return "current position is a redirect like `echo test >[TAB]`"
}

// Patch patches args if `COMP_LINE` environment variable is set.
//
// Bash passes redirects to the completion function so these need to be filtered out.
//
// `example action >/tmp/stdout.txt --values 2>/tmp/stderr.txt fi[TAB]`
// ["example", "action", ">", "/tmp/stdout.txt", "--values", "2", ">", "/tmp/stderr.txt", "fi"]
// ["example", "action", "--values", "fi"]
func Patch(args []string) ([]string, error) { // TODO document and fix wordbreak splitting (e.g. `:`)
compline, ok := os.LookupEnv("COMP_LINE")
if !ok {
return args, nil
}

if err := os.Unsetenv("COMP_LINE"); err != nil { // prevent it being passes along to embedded completions
return nil, err
}

if compline == "" {
return args, nil
}

tokens, err := shlex.Split(compline)
if err != nil {
return nil, err
}

if len(tokens) > 1 {
if previous := tokens[len(tokens)-2]; previous.WordbreakType.IsRedirect() {
return append(args[:1], tokens[len(tokens)-1].Value), RedirectError{}
}
}
args = append(args[:1], tokens.CurrentPipeline().FilterRedirects().Words().Strings()...)
return args, nil
}
1 change: 1 addition & 0 deletions internal/shell/bash/snippet.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func Snippet(cmd *cobra.Command) string {
result := fmt.Sprintf(`#!/bin/bash
_%v_completion() {
export COMP_WORDBREAKS
export COMP_LINE
local compline="${COMP_LINE:0:${COMP_POINT}}"
local IFS=$'\n'
Expand Down

0 comments on commit 9d5749e

Please sign in to comment.