Skip to content

Commit

Permalink
compat: added cobra bridge
Browse files Browse the repository at this point in the history
  • Loading branch information
rsteube committed Sep 25, 2023
1 parent 0e0f241 commit 924a68e
Show file tree
Hide file tree
Showing 9 changed files with 292 additions and 13 deletions.
4 changes: 2 additions & 2 deletions carapace.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func (c Carapace) PositionalCompletion(action ...Action) {

// PositionalAnyCompletion defines completion for any positional arguments not already defined.
func (c Carapace) PositionalAnyCompletion(action Action) {
storage.get(c.cmd).positionalAny = action
storage.get(c.cmd).positionalAny = &action
}

// DashCompletion defines completion for positional arguments after dash (`--`) using a list of Actions.
Expand All @@ -67,7 +67,7 @@ func (c Carapace) DashCompletion(action ...Action) {

// DashAnyCompletion defines completion for any positional arguments after dash (`--`) not already defined.
func (c Carapace) DashAnyCompletion(action Action) {
storage.get(c.cmd).dashAny = action
storage.get(c.cmd).dashAny = &action
}

// FlagCompletion defines completion for flags using a map consisting of name and Action.
Expand Down
71 changes: 70 additions & 1 deletion compat.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package carapace

import (
"fmt"
"strings"

"github.com/spf13/cobra"
"github.com/spf13/pflag"
Expand All @@ -10,14 +11,25 @@ import (
func registerValidArgsFunction(cmd *cobra.Command) {
if cmd.ValidArgsFunction == nil {
cmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
action := storage.getPositional(cmd, len(args)).Invoke(Context{Args: args, Value: toComplete})
// TODO check storage.hasPositional to prevent loop
action := Action{}.Invoke(Context{Args: args, Value: toComplete}) // TODO just IvokedAction{} ok?
if storage.hasPositional(cmd, len(args)) {
action = storage.getPositional(cmd, len(args)).Invoke(Context{Args: args, Value: toComplete})
}
return cobraValuesFor(action), cobraDirectiveFor(action)
}
}
}

func registerFlagCompletion(cmd *cobra.Command) {
cmd.LocalFlags().VisitAll(func(f *pflag.Flag) {
if !storage.hasFlag(cmd, f.Name) {
return // skip if not defined in carapace
}
if _, ok := cmd.GetFlagCompletionByName(f.Name); ok {
return // skip if already defined in cobra
}

err := cmd.RegisterFlagCompletionFunc(f.Name, func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
a := storage.getFlag(cmd, f.Name)
action := a.Invoke(Context{Args: args, Value: toComplete})
Expand Down Expand Up @@ -51,3 +63,60 @@ func cobraDirectiveFor(action InvokedAction) cobra.ShellCompDirective {
}
return directive
}

func actionCobra(cmd *cobra.Command, f func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective)) Action {
return ActionCallback(func(c Context) Action {
if f == nil {
return ActionValues()
}

c.Args = cmd.Flags().Args() // TODO verify - ensure it contains all args, this might not be correct at all times (e.g. changed Context.Args)
values, directive := f(cmd, c.Args, c.Value)
return compDirective(directive).ToA(values...)
})
}

type compDirective cobra.ShellCompDirective

func (d compDirective) matches(cobraDirective cobra.ShellCompDirective) bool {
return d&compDirective(cobraDirective) != 0
}

func (d compDirective) ToA(values ...string) Action {
var action Action
switch {
case d.matches(cobra.ShellCompDirectiveError):
return ActionMessage("an error occured")
case d.matches(cobra.ShellCompDirectiveFilterDirs):
switch len(values) {
case 0:
action = ActionDirectories()
default:
action = ActionDirectories().Chdir(values[0])
}
case d.matches(cobra.ShellCompDirectiveFilterFileExt):
extensions := make([]string, 0)
for _, v := range values {
extensions = append(extensions, "."+v)
}
return ActionFiles(extensions...)
case len(values) == 0 && !d.matches(cobra.ShellCompDirectiveNoFileComp):
action = ActionFiles()
default:
vals := make([]string, 0)
for _, v := range values {
if splitted := strings.SplitN(v, "\t", 2); len(splitted) == 2 {
vals = append(vals, splitted[0], splitted[1])
} else {
vals = append(vals, splitted[0], "")
}
}
action = ActionValuesDescribed(vals...)
}

if d.matches(cobra.ShellCompDirectiveNoSpace) {
action = action.NoSpace()
}

return action
}
6 changes: 5 additions & 1 deletion defaultActions.go
Original file line number Diff line number Diff line change
Expand Up @@ -473,7 +473,11 @@ func ActionPositional(cmd *cobra.Command) Action {
c.Args = cmd.Flags().Args()
entry := storage.get(cmd)

a := entry.positionalAny
var a Action
if entry.positionalAny != nil {
a = *entry.positionalAny
}

if index := len(c.Args); index < len(entry.positional) {
a = entry.positional[len(c.Args)]
}
Expand Down
72 changes: 72 additions & 0 deletions example/cmd/compat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package cmd

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

var compatCmd = &cobra.Command{
Use: "compat",
Short: "",
Run: func(cmd *cobra.Command, args []string) {},
}

func init() {
carapace.Gen(compatCmd).Standalone()

compatCmd.Flags()

compatCmd.Flags().String("error", "", "ShellCompDirectiveError")
compatCmd.Flags().String("nospace", "", "ShellCompDirectiveNoSpace")
compatCmd.Flags().String("nofilecomp", "", "ShellCompDirectiveNoFileComp")
compatCmd.Flags().String("filterfileext", "", "ShellCompDirectiveFilterFileExt")
compatCmd.Flags().String("filterdirs", "", "ShellCompDirectiveFilterDirs")
compatCmd.Flags().String("filterdirs-chdir", "", "ShellCompDirectiveFilterDirs")
compatCmd.Flags().String("keeporder", "", "ShellCompDirectiveKeepOrder")
compatCmd.Flags().String("default", "", "ShellCompDirectiveDefault")

compatCmd.Flags().String("unset", "", "no completions defined")

rootCmd.AddCommand(compatCmd)

compatCmd.RegisterFlagCompletionFunc("error", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

Check failure on line 32 in example/cmd/compat.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `compatCmd.RegisterFlagCompletionFunc` is not checked (errcheck)
return nil, cobra.ShellCompDirectiveError
})
compatCmd.RegisterFlagCompletionFunc("nospace", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

Check failure on line 35 in example/cmd/compat.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `compatCmd.RegisterFlagCompletionFunc` is not checked (errcheck)
return []string{"one", "two"}, cobra.ShellCompDirectiveNoSpace
})
compatCmd.RegisterFlagCompletionFunc("nofilecomp", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {

Check failure on line 38 in example/cmd/compat.go

View workflow job for this annotation

GitHub Actions / lint

Error return value of `compatCmd.RegisterFlagCompletionFunc` is not checked (errcheck)
return nil, cobra.ShellCompDirectiveNoFileComp
})

compatCmd.RegisterFlagCompletionFunc("filterfileext", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"mod", "sum"}, cobra.ShellCompDirectiveFilterFileExt
})

compatCmd.RegisterFlagCompletionFunc("filterdirs", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveFilterDirs
})

compatCmd.RegisterFlagCompletionFunc("filterdirs-chdir", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"subdir"}, cobra.ShellCompDirectiveFilterDirs
})

compatCmd.RegisterFlagCompletionFunc("keeporder", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"one", "three", "two"}, cobra.ShellCompDirectiveKeepOrder
})

compatCmd.RegisterFlagCompletionFunc("default", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return nil, cobra.ShellCompDirectiveDefault
})

compatCmd.ValidArgsFunction = func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
switch len(args) {
case 0:
return []string{"p1", "positional1"}, cobra.ShellCompDirectiveDefault
case 1:
return nil, cobra.ShellCompDirectiveDefault
default:
return nil, cobra.ShellCompDirectiveNoFileComp
}
}
}
84 changes: 84 additions & 0 deletions example/cmd/compat_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package cmd

import (
"testing"

"github.com/rsteube/carapace"
"github.com/rsteube/carapace/pkg/sandbox"
"github.com/rsteube/carapace/pkg/style"
)

func TestCompat(t *testing.T) {
sandbox.Package(t, "github.com/rsteube/carapace/example")(func(s *sandbox.Sandbox) {
s.Files(
"subdir/file1.txt", "",
"subdir/subdir2/file2.txt", "",
"go.mod", "",
"go.sum", "",
"README.md", "",
)

s.Run("compat", "--error", "").
Expect(carapace.ActionMessage("an error occured").
Usage("ShellCompDirectiveError"))

s.Run("compat", "--nospace", "").
Expect(carapace.ActionValues(
"one",
"two",
).NoSpace().
Usage("ShellCompDirectiveNoSpace"))

s.Run("compat", "--nofilecomp", "").
Expect(carapace.ActionValues().
Usage("ShellCompDirectiveNoFileComp"))

s.Run("compat", "--filterfileext", "").
Expect(carapace.ActionValues(
"subdir/",
"go.mod",
"go.sum",
).NoSpace('/').
Tag("files").
StyleF(style.ForPath).
Usage("ShellCompDirectiveFilterFileExt"))

s.Run("compat", "--filterdirs", "").
Expect(carapace.ActionValues(
"subdir/",
).NoSpace('/').
Tag("directories").
StyleF(style.ForPath).
Usage("ShellCompDirectiveFilterDirs"))

s.Run("compat", "--filterdirs-chdir", "").
Expect(carapace.ActionValues(
"subdir2/",
).NoSpace('/').
Tag("directories").
StyleF(style.ForPathExt).
Usage("ShellCompDirectiveFilterDirs"))

s.Run("compat", "--keeporder", "").
Expect(carapace.ActionValues(
"one",
"two",
"three",
).Usage("ShellCompDirectiveKeepOrder"))

s.Run("compat", "--default", "").
Expect(carapace.ActionValues(
"subdir/",
"go.mod",
"go.sum",
"README.md",
).NoSpace('/').
Tag("files").
StyleF(style.ForPath).
Usage("ShellCompDirectiveDefault"))

s.Run("compat", "--unset", "").
Expect(carapace.ActionValues().
Usage("no completions defined"))
})
}
1 change: 1 addition & 0 deletions example/cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ func TestRoot(t *testing.T) {
).Style(style.Magenta).Tag("plugin commands"),
carapace.ActionValuesDescribed(
"chain", "shorthand chain",
"compat", "",
"completion", "Generate the autocompletion script for the specified shell",
"group", "group example",
"help", "Help about any command",
Expand Down
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,5 @@ require (
github.com/spf13/pflag v1.0.5
gopkg.in/yaml.v3 v3.0.1
)

replace github.com/spf13/cobra => github.com/avirtopeanu-ionos/cobra v0.0.0-20230330100513-ad4838bd46b0
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
github.com/avirtopeanu-ionos/cobra v0.0.0-20230330100513-ad4838bd46b0 h1:rZ2JtTLPDPTwQVMbU+VYpG8Tz2JfWFS3NW/YdV8vqXs=
github.com/avirtopeanu-ionos/cobra v0.0.0-20230330100513-ad4838bd46b0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/cpuguy83/go-md2man/v2 v2.0.2/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-shlex v0.0.4 h1:3GVn8PaM2RCxPTAiwVy9vDQI8Mi7DqrbdpDgf5ZzQmY=
github.com/rsteube/carapace-shlex v0.0.4/go.mod h1:zPw1dOFwvLPKStUy9g2BYKanI6bsQMATzDMYQQybo3o=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down
Loading

0 comments on commit 924a68e

Please sign in to comment.