Skip to content

Commit

Permalink
Merge pull request #75 from rsteube/add-actions
Browse files Browse the repository at this point in the history
added more actions
  • Loading branch information
rsteube authored Mar 28, 2023
2 parents 830f768 + 0505ccf commit 572cfdd
Show file tree
Hide file tree
Showing 8 changed files with 509 additions and 8 deletions.
42 changes: 41 additions & 1 deletion cmd/carapace-bridge/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ package cmd

import (
"github.com/rsteube/carapace"
"github.com/rsteube/carapace-bridge/pkg/actions/bridge"
"github.com/rsteube/carapace-bridge/pkg/actions/os"
"github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
Use: "carapace-bridge",
Short: "",
Short: "bridge completions",
Run: func(cmd *cobra.Command, args []string) {},
}

Expand All @@ -17,4 +19,42 @@ func Execute(version string) error {
}
func init() {
carapace.Gen(rootCmd).Standalone()
addSubCommand("argcomplete", "bridges https://github.com/kislyuk/argcomplete", bridge.ActionArgcomplete)
addSubCommand("carapace-bin", "bridges completions registered in carapace-bin", bridge.ActionCarapaceBin)
addSubCommand("carapace", "bridges https://github.com/rsteube/carapace", bridge.ActionCarapace)
addSubCommand("click", "bridges https://github.com/pallets/click", bridge.ActionClick)
addSubCommand("click", "bridges https://github.com/pallets/click", bridge.ActionClick)
addSubCommand("cobra", "bridges https://github.com/spf13/cobra", bridge.ActionCobra)
addSubCommand("complete", "bridges https://github.com/spf13/cobra", bridge.ActionComplete)
addSubCommand("fish", "bridges completions registered in fish shell", bridge.ActionFish)
addSubCommand("yargs", "bridges https://github.com/yargs/yargs", bridge.ActionYargs)
}

func addSubCommand(use, short string, f func(s ...string) carapace.Action) {
cmd := &cobra.Command{
Use: use,
Short: short,
Args: cobra.MinimumNArgs(1),
Run: func(cmd *cobra.Command, args []string) {
rootCmd.SetArgs(append([]string{"_carapace", "export", "", "fish"}, args...))
rootCmd.Execute()
},
DisableFlagParsing: true,
}

carapace.Gen(cmd).Standalone()

rootCmd.AddCommand(cmd)

carapace.Gen(cmd).PositionalCompletion(
os.ActionPathExecutables(),
)

carapace.Gen(cmd).PositionalAnyCompletion(
carapace.ActionCallback(func(c carapace.Context) carapace.Action {
command := c.Args[0]
c.Args = c.Args[1:]
return f(command).Invoke(c).ToA()
}),
)
}
105 changes: 105 additions & 0 deletions pkg/actions/bridge/argcomplete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package bridge

import (
"os/exec"
"strconv"
"strings"

"github.com/rsteube/carapace"
)

// ActionArgcomplete bridges https://github.com/kislyuk/argcomplete
//
// var rootCmd = &cobra.Command{
// Use: "az",
// Short: "Azure Command-Line Interface",
// Run: func(cmd *cobra.Command, args []string) {},
// DisableFlagParsing: true,
// }
//
// func Execute() error {
// return rootCmd.Execute()
// }
//
// func init() {
// carapace.Gen(rootCmd).Standalone()
//
// carapace.Gen(rootCmd).PositionalAnyCompletion(
// argcomplete.ActionArgcomplete("az"),
// )
// }
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())
}

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

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 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.CallbackValue, "-")
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.CallbackValue, "="); index > -1 {
return carapace.ActionFiles().Invoke(carapace.Context{CallbackValue: c.CallbackValue[index+1:]}).ToA()
}
return carapace.ActionFiles()
}
return carapace.ActionValuesDescribed(vals...)
}).Invoke(c).Prefix(prefix).ToA() // re-add optarg prefix
if nospace {
return a.NoSpace()
}
return a
})
}
45 changes: 45 additions & 0 deletions pkg/actions/bridge/carapace.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package bridge

import (
"github.com/rsteube/carapace"
)

// 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.CallbackValue)
return carapace.ActionExecCommand(command[0], args...)(func(output []byte) carapace.Action {
if string(output) == "" {
return carapace.ActionValues()
}
return carapace.ActionImport(output)
})
})
}

// ActionCarapaceBin bridges completions registered in carapace-bin
func ActionCarapaceBin(command ...string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
if len(command) == 0 {
return carapace.ActionMessage("missing argument [ActionCarapaceBin]")
}

args := []string{command[0], "export", ""}
args = append(args, command[1:]...)
args = append(args, c.Args...)
args = append(args, c.CallbackValue)
return carapace.ActionExecCommand("carapace", args...)(func(output []byte) carapace.Action {
if string(output) == "" {
return carapace.ActionFiles()
}
return carapace.ActionImport(output)
})
})
}
71 changes: 71 additions & 0 deletions pkg/actions/bridge/click.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package bridge

import (
"fmt"
"os/exec"
"strconv"
"strings"

"github.com/rsteube/carapace"
)

// ActionClick bridges https://github.com/pallets/click
//
// var rootCmd = &cobra.Command{
// Use: "watson",
// Short: "Watson is a tool aimed at helping you monitoring your time",
// Run: func(cmd *cobra.Command, args []string) {},
// DisableFlagParsing: true,
// }
//
// func Execute() error {
// return rootCmd.Execute()
// }
//
// func init() {
// carapace.Gen(rootCmd).Standalone()
//
// carapace.Gen(rootCmd).PositionalAnyCompletion(
// bridge.ActionClick("watson"),
// )
// }
func ActionClick(command ...string) carapace.Action {
return carapace.ActionCallback(func(c carapace.Context) carapace.Action {
if len(command) == 0 {
return carapace.ActionMessage("missing argument [ActionClick]")
}

if _, err := exec.LookPath(command[0]); err != nil {
return carapace.ActionMessage(err.Error())
}

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

compLine := command[0] + " " + strings.Join(append(args, current), " ") // TODO escape/quote special characters
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))
return carapace.ActionExecCommand(command[0])(func(output []byte) carapace.Action {
lines := strings.Split(string(output), "\n")

vals := make([]string, 0)
for i := 0; i+3 < len(lines); i += 3 {
switch lines[i] { // type
case "dir":
return carapace.ActionDirectories()
case "file":
return carapace.ActionFiles()
case "plain":
value := lines[i+1]
description := lines[i+2]
if description == "_" {
description = ""
}
vals = append(vals, value, description)
}
}
return carapace.ActionValuesDescribed(vals...)
}).Invoke(c).ToA()
})
}
Loading

0 comments on commit 572cfdd

Please sign in to comment.