Skip to content

Commit

Permalink
Merge pull request #190 from rsteube/bridge-allow-empty
Browse files Browse the repository at this point in the history
bridge: alle empty arguments
  • Loading branch information
rsteube committed Feb 24, 2024
2 parents 5eda4c1 + b20e52a commit 98e8f0a
Show file tree
Hide file tree
Showing 15 changed files with 514 additions and 544 deletions.
120 changes: 59 additions & 61 deletions pkg/actions/bridge/argcomplete.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,77 +29,75 @@ import (
// )
// }
func ActionArgcomplete(command ...string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
if len(command) == 0 {
return carapace.ActionMessage("missing argument [ActionArgcomplete]")
}

if _, err := exec.LookPath(command[0]); err != nil {
return carapace.ActionMessage(err.Error())
}
return actionCommand(command...)(func(command ...string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
if _, err := exec.LookPath(command[0]); err != nil {
return carapace.ActionMessage(err.Error())
}

args := append(command[1:], c.Args...)
current := c.Value
args := append(command[1:], c.Args...)
current := c.Value

prefix := ""
if strings.HasPrefix(current, "--") {
if strings.Contains(current, "=") { // optarg flag which is handled as normal arg by the completer
splitted := strings.SplitN(current, "=", 2)
prefix = splitted[0] + "="
args = append(args, splitted[0]) // add flag as arg
current = "" // seem partial optarg value isn't completed
prefix := ""
if strings.HasPrefix(current, "--") {
if strings.Contains(current, "=") { // optarg flag which is handled as normal arg by the completer
splitted := strings.SplitN(current, "=", 2)
prefix = splitted[0] + "="
args = append(args, splitted[0]) // add flag as arg
current = "" // seem partial optarg value isn't completed

} else {
current = "--" // seems partial flag names aren't completed so get all
}
} else {
current = "--" // seems partial flag names aren't completed so get all
current = "" // seems partial positional arguments aren't completed as well
}
} else {
current = "" // seems partial positional arguments aren't completed as well
}

compLine := command[0] + " " + strings.Join(append(args, current), " ") // TODO escape/quote special characters
c.Setenv("_ARGCOMPLETE", "1")
c.Setenv("_ARGCOMPLETE_DFS", "\t")
c.Setenv("_ARGCOMPLETE_IFS", "\n")
c.Setenv("_ARGCOMPLETE_SHELL", "fish")
c.Setenv("_ARGCOMPLETE_SUPPRESS_SPACE", "1") // TODO needed? relevant for nospace detection?
// c.Setenv("_ARGCOMPLETE_COMP_WORDBREAKS", " ") // TODO set to space-only for multiparts?
c.Setenv("_ARGCOMPLETE", "1")
c.Setenv("COMP_LINE", compLine)
c.Setenv("COMP_POINT", strconv.Itoa(len(compLine)))
nospace := false
a := carapace.ActionExecCommand("sh", "-c", command[0]+" 8>&1 9>&2 1>/dev/null 2>/dev/null")(func(output []byte) carapace.Action {
lines := strings.Split(string(output), "\n")
vals := make([]string, 0)
isFlag := strings.HasPrefix(c.Value, "-")
for _, line := range lines[:len(lines)-1] {
if !isFlag && strings.HasPrefix(line, "-") {
continue
}
if strings.HasSuffix(line, "=") ||
strings.HasSuffix(line, "/") ||
strings.HasSuffix(line, ",") {
nospace = true
}
if splitted := strings.SplitN(line, "\t", 2); splitted[0] != "" {
vals = append(vals, splitted...)
if len(splitted) < 2 {
vals = append(vals, "")
compLine := command[0] + " " + strings.Join(append(args, current), " ") // TODO escape/quote special characters
c.Setenv("_ARGCOMPLETE", "1")
c.Setenv("_ARGCOMPLETE_DFS", "\t")
c.Setenv("_ARGCOMPLETE_IFS", "\n")
c.Setenv("_ARGCOMPLETE_SHELL", "fish")
c.Setenv("_ARGCOMPLETE_SUPPRESS_SPACE", "1") // TODO needed? relevant for nospace detection?
// c.Setenv("_ARGCOMPLETE_COMP_WORDBREAKS", " ") // TODO set to space-only for multiparts?
c.Setenv("_ARGCOMPLETE", "1")
c.Setenv("COMP_LINE", compLine)
c.Setenv("COMP_POINT", strconv.Itoa(len(compLine)))
nospace := false
a := carapace.ActionExecCommand("sh", "-c", command[0]+" 8>&1 9>&2 1>/dev/null 2>/dev/null")(func(output []byte) carapace.Action {
lines := strings.Split(string(output), "\n")
vals := make([]string, 0)
isFlag := strings.HasPrefix(c.Value, "-")
for _, line := range lines[:len(lines)-1] {
if !isFlag && strings.HasPrefix(line, "-") {
continue
}
if strings.HasSuffix(line, "=") ||
strings.HasSuffix(line, "/") ||
strings.HasSuffix(line, ",") {
nospace = true
}
if splitted := strings.SplitN(line, "\t", 2); splitted[0] != "" {
vals = append(vals, splitted...)
if len(splitted) < 2 {
vals = append(vals, "")
}
}
}
}

if len(vals) == 0 {
// fallback to file completions when no values returned
if index := strings.Index(c.Value, "="); index > -1 {
return carapace.ActionFiles().Invoke(carapace.Context{Value: c.Value[index+1:]}).ToA()
if len(vals) == 0 {
// fallback to file completions when no values returned
if index := strings.Index(c.Value, "="); index > -1 {
return carapace.ActionFiles().Invoke(carapace.Context{Value: c.Value[index+1:]}).ToA()
}
return carapace.ActionFiles()
}
return carapace.ActionFiles()
return carapace.ActionValuesDescribed(vals...)
}).Invoke(c).Prefix(prefix).ToA() // re-add optarg prefix
if nospace {
return a.NoSpace()
}
return carapace.ActionValuesDescribed(vals...)
}).Invoke(c).Prefix(prefix).ToA() // re-add optarg prefix
if nospace {
return a.NoSpace()
}
return a
return a
})
})
}
90 changes: 44 additions & 46 deletions pkg/actions/bridge/bash.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,59 +18,57 @@ var bashSnippet string
// ActionBash bridges completions registered in bash
// (uses custom `.bashrc` in “~/.config/carapace/bridge/bash`)
func ActionBash(command ...string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
if len(command) == 0 {
return carapace.ActionMessage("missing argument [ActionBash]")
}

configDir, err := xdg.UserConfigDir()
if err != nil {
return carapace.ActionMessage(err.Error())
}
return actionCommand(command...)(func(command ...string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
configDir, err := xdg.UserConfigDir()
if err != nil {
return carapace.ActionMessage(err.Error())
}

args := append(command, c.Args...)
args = append(args, c.Value)
args := append(command, c.Args...)
args = append(args, c.Value)

configPath := fmt.Sprintf("%v/carapace/bridge/bash/.bashrc", configDir)
if err := ensureExists(configPath); err != nil {
return carapace.ActionMessage(err.Error())
}
configPath := fmt.Sprintf("%v/carapace/bridge/bash/.bashrc", configDir)
if err := ensureExists(configPath); err != nil {
return carapace.ActionMessage(err.Error())
}

joined := shlex.Join(args)
if c.Value == "" {
joined = strings.TrimSuffix(joined, `""`)
}
c.Setenv("COMP_LINE", joined)
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())
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)
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.ActionExecCommand("bash", "--rcfile", configPath, "-i", file.Name())(func(output []byte) carapace.Action {
lines := strings.Split(string(output), "\n")

vals := make([]string, 0)
for _, line := range lines[:len(lines)-1] {
if splitted := strings.SplitN(line, "(", 2); len(splitted) == 2 {
// assume results contain descriptions in the format `value (description)` (spf13/cobra, rsteube/carapace)
vals = append(vals,
strings.TrimSpace(splitted[0]),
strings.TrimSpace(strings.TrimSuffix(splitted[1], ")")),
)
} else {
vals = append(vals, strings.TrimSpace(line), "")
vals := make([]string, 0)
for _, line := range lines[:len(lines)-1] {
if splitted := strings.SplitN(line, "(", 2); len(splitted) == 2 {
// assume results contain descriptions in the format `value (description)` (spf13/cobra, rsteube/carapace)
vals = append(vals,
strings.TrimSpace(splitted[0]),
strings.TrimSpace(strings.TrimSuffix(splitted[1], ")")),
)
} else {
vals = append(vals, strings.TrimSpace(line), "")
}
}
}
switch len(vals) {
case 0:
return carapace.ActionFiles()
default:
return carapace.ActionValuesDescribed(vals...).StyleF(style.ForPath)
}
}).Invoke(c).ToA().NoSpace([]rune("/=@:.,")...) // TODO check compopt for nospace
switch len(vals) {
case 0:
return carapace.ActionFiles()
default:
return carapace.ActionValuesDescribed(vals...).StyleF(style.ForPath)
}
}).Invoke(c).ToA().NoSpace([]rune("/=@:.,")...) // TODO check compopt for nospace
})
})
}
108 changes: 53 additions & 55 deletions pkg/actions/bridge/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,66 +10,64 @@ import (

// Bridges bridges completions as defined in bridges.yaml and CARAPACE_BRIDGE environment variable
func ActionBridges(command ...string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
if len(command) == 0 {
return carapace.ActionMessage("missing argument [ActionBridges]")
}

if bridge, ok := bridges.Config()[command[0]]; ok {
switch bridge {
case "argcomplete":
return ActionArgcomplete(command...)
case "bash":
return ActionBash(command...)
case "carapace":
return ActionCarapace(command...)
case "clap":
return ActionClap(command...)
case "click":
return ActionClick(command...)
case "cobra":
return ActionCobra(command...)
case "complete":
return ActionComplete(command...)
case "fish":
return ActionFish(command...)
case "inshellisense":
return ActionInshellisense(command...)
case "kingpin":
return ActionKingpin(command...)
case "powershell":
return ActionPowershell(command...)
case "urfavecli":
return ActionUrfavecli(command...)
case "yargs":
return ActionYargs(command...)
case "zsh":
return ActionZsh(command...)
default:
return carapace.ActionMessage("unknown bridge: %v", bridge)
}
}

for _, b := range env.Bridges() {
switch b {
case "bash":
if slices.Contains(bridges.Bash(), command[0]) {
return actionCommand(command...)(func(command ...string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
if bridge, ok := bridges.Config()[command[0]]; ok {
switch bridge {
case "argcomplete":
return ActionArgcomplete(command...)
case "bash":
return ActionBash(command...)
}
case "fish":
if slices.Contains(bridges.Fish(), command[0]) {
case "carapace":
return ActionCarapace(command...)
case "clap":
return ActionClap(command...)
case "click":
return ActionClick(command...)
case "cobra":
return ActionCobra(command...)
case "complete":
return ActionComplete(command...)
case "fish":
return ActionFish(command...)
}
case "inshellisense":
if slices.Contains(bridges.Inshellisense(), command[0]) {
case "inshellisense":
return ActionInshellisense(command...)
}
case "zsh":
if slices.Contains(bridges.Zsh(), command[0]) {
case "kingpin":
return ActionKingpin(command...)
case "powershell":
return ActionPowershell(command...)
case "urfavecli":
return ActionUrfavecli(command...)
case "yargs":
return ActionYargs(command...)
case "zsh":
return ActionZsh(command...)
default:
return carapace.ActionMessage("unknown bridge: %v", bridge)
}
}

for _, b := range env.Bridges() {
switch b {
case "bash":
if slices.Contains(bridges.Bash(), command[0]) {
return ActionBash(command...)
}
case "fish":
if slices.Contains(bridges.Fish(), command[0]) {
return ActionFish(command...)
}
case "inshellisense":
if slices.Contains(bridges.Inshellisense(), command[0]) {
return ActionInshellisense(command...)
}
case "zsh":
if slices.Contains(bridges.Zsh(), command[0]) {
return ActionZsh(command...)
}
}
}
}
return carapace.ActionValues()
return carapace.ActionValues()
})
})
}
26 changes: 12 additions & 14 deletions pkg/actions/bridge/carapace.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,20 +11,18 @@ import (

// ActionCarapace bridges https://github.com/rsteube/carapace
func ActionCarapace(command ...string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
if len(command) == 0 {
return carapace.ActionMessage("missing argument [ActionCarapace]")
}

args := []string{"_carapace", "export", ""}
args = append(args, command[1:]...)
args = append(args, c.Args...)
args = append(args, c.Value)
return carapace.ActionExecCommand(command[0], args...)(func(output []byte) carapace.Action {
if string(output) == "" {
return carapace.ActionValues()
}
return carapace.ActionImport(output)
return actionCommand(command...)(func(command ...string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
args := []string{"_carapace", "export", ""}
args = append(args, command[1:]...)
args = append(args, c.Args...)
args = append(args, c.Value)
return carapace.ActionExecCommand(command[0], args...)(func(output []byte) carapace.Action {
if string(output) == "" {
return carapace.ActionValues()
}
return carapace.ActionImport(output)
})
})
})
}
Expand Down
Loading

0 comments on commit 98e8f0a

Please sign in to comment.