Skip to content

Commit

Permalink
Merge pull request #41 from rsteube/wildcard-copmletion
Browse files Browse the repository at this point in the history
added wildcard completion
  • Loading branch information
rsteube authored May 2, 2020
2 parents e77c43f + 762447f commit 0cd5fbc
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 41 deletions.
70 changes: 45 additions & 25 deletions carapace.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ func (c Carapace) PositionalCompletion(action ...Action) {
}
}

func (c Carapace) PositionalAnyCompletion(action Action) {
completions.actions[uid.Positional(c.cmd, 0)] = action.finalize(c.cmd, uid.Positional(c.cmd, 0))
}

func (c Carapace) FlagCompletion(actions ActionMap) {
for name, action := range actions {
if flag := c.cmd.LocalFlags().Lookup(name); flag == nil {
Expand Down Expand Up @@ -147,36 +151,25 @@ func addCompletionCommand(cmd *cobra.Command) {
fmt.Println(Gen(cmd).Snippet(args[0]))
}
} else {
callback := args[1]
origArg := []string{}
if len(os.Args) > 5 {
origArg = os.Args[5:]
}
targetCmd, targetArgs := traverse(cmd, origArg)
if callback == "_" {
if len(targetArgs) == 0 {
callback = uid.Positional(targetCmd, 1)
} else {
lastArg := targetArgs[len(targetArgs)-1]
if strings.HasSuffix(lastArg, " ") {
callback = uid.Positional(targetCmd, len(targetArgs)+1)
} else {
callback = uid.Positional(targetCmd, len(targetArgs))
}
}
if action, ok := completions.actions[callback]; !ok {
os.Exit(0) // ensure no message for missing action on positional completion // TODO this was only for bash, maybe enable for other shells?
} else {
targetCmd, targetArgs := findTarget(cmd)

shell := args[0]
id := args[1]

switch id {
case "_":
if action, ok := findAction(targetCmd, targetArgs); ok {
if action.Callback == nil {
fmt.Println(action.Value(args[0]))
os.Exit(0)
fmt.Println(action.Value(shell))
} else {
fmt.Println(action.Callback(targetArgs).Value(shell))
}
}
} else if callback == "state" {
case "state":
fmt.Println(uid.Command(targetCmd))
os.Exit(0) // TODO
default:
fmt.Println(completions.invokeCallback(id, targetArgs).Value(shell))
}
fmt.Println(completions.invokeCallback(callback, targetArgs).Value(args[0]))
}
}
},
Expand All @@ -187,6 +180,33 @@ func addCompletionCommand(cmd *cobra.Command) {
})
}

func findAction(targetCmd *cobra.Command, targetArgs []string) (action Action, ok bool) {
var id string
if len(targetArgs) == 0 {
id = uid.Positional(targetCmd, 1)
} else {
lastArg := targetArgs[len(targetArgs)-1]
if strings.HasSuffix(lastArg, " ") {
id = uid.Positional(targetCmd, len(targetArgs)+1)
} else {
id = uid.Positional(targetCmd, len(targetArgs))
}
}
if action, ok = completions.actions[id]; !ok {
id = uid.Positional(targetCmd, 0)
action, ok = completions.actions[id]
}
return
}

func findTarget(cmd *cobra.Command) (*cobra.Command, []string) {
origArg := []string{}
if len(os.Args) > 5 {
origArg = os.Args[5:]
}
return traverse(cmd, origArg)
}

func traverse(cmd *cobra.Command, args []string) (*cobra.Command, []string) {
// ignore flag parse errors (like a missing argument for the flag currently being completed)
targetCmd, targetArgs, _ := cmd.Root().Traverse(args)
Expand Down
4 changes: 4 additions & 0 deletions elvish/snippet.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ func snippetFunctions(cmd *cobra.Command, actions map[string]string) string {
break // TODO only consistent entries for now
}
}
if action, ok := actions[uid.Positional(cmd, 0)]; ok {
positionals = append(positionals, " "+snippetPositionalCompletion(action))
positionals = append(positionals, " "+"...")
}
if len(positionals) == 0 {
if cmd.ValidArgs != nil {
positionals = []string{" " + snippetPositionalCompletion(ActionValues(cmd.ValidArgs...))}
Expand Down
6 changes: 6 additions & 0 deletions example/cmd/callback.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,10 @@ func init() {
return carapace.ActionValues("callback1", "callback2")
}),
)

carapace.Gen(callbackCmd).PositionalAnyCompletion(
carapace.ActionCallback(func(args []string) carapace.Action {
return carapace.ActionMessage(fmt.Sprintf("POS_%v", len(args)))
}),
)
}
29 changes: 16 additions & 13 deletions example/cmd/root_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,8 @@ edit:complex-candidate example &display-suffix=' (exampleDescription)'
]
arg-handlers = [
[_]{ _example_callback '_example__callback#1' }
[_]{ _example_callback '_example__callback#0' }
...
]
subargs = $arg[(subindex callback):]
if (> (count $subargs) 0) {
Expand Down Expand Up @@ -672,20 +674,21 @@ function _example__action {
"(-u --users)"{-u,--users}"[users flag]: :_users" \
"(-v --values)"{-v,--values}"[values flag]: :_values '' values example" \
"(-d --values_described)"{-d,--values_described}"[values with description flag]: :_values '' 'values[valueDescription]' 'example[exampleDescription]' " \
"1:: :_values '' positional1 p1" \
"2:: :_values '' positional2 p2"
"1: :_values '' positional1 p1" \
"2: :_values '' positional2 p2"
}
function _example__callback {
_arguments -C \
"(-c --callback)"{-c,--callback}"[Help message for callback]: : eval \$(example _carapace zsh '_example__callback##callback' ${${os_args:1:gs/\"/\\\"}:gs/\'/\\\"})" \
"1:: : eval \$(example _carapace zsh '_example__callback#1' ${${os_args:1:gs/\"/\\\"}:gs/\'/\\\"})"
"1: : eval \$(example _carapace zsh '_example__callback#1' ${${os_args:1:gs/\"/\\\"}:gs/\'/\\\"})" \
"*: : eval \$(example _carapace zsh '_example__callback#0' ${${os_args:1:gs/\"/\\\"}:gs/\'/\\\"})"
}
function _example__condition {
_arguments -C \
"(-r --required)"{-r,--required}"[required flag]: :_values '' valid invalid" \
"1:: : eval \$(example _carapace zsh '_example__condition#1' ${${os_args:1:gs/\"/\\\"}:gs/\'/\\\"})"
"1: : eval \$(example _carapace zsh '_example__condition#1' ${${os_args:1:gs/\"/\\\"}:gs/\'/\\\"})"
}
function _example__help {
Expand All @@ -695,15 +698,15 @@ function _example__help {
function _example__injection {
_arguments -C \
"1:: :_values '' echo\ fail" \
"2:: :_values '' echo\ fail" \
"3:: :_values '' echo\ fail" \
"4:: :_values '' \ echo\ fail\ " \
"5:: :_values '' \ echo\ fail\ " \
"6:: :_values '' \ echo\ fail\ " \
"7:: :_values '' echo\ fail" \
"8:: : _message -r 'no values to complete'" \
"9:: :_values '' LAST\ POSITIONAL\ VALUE"
"1: :_values '' echo\ fail" \
"2: :_values '' echo\ fail" \
"3: :_values '' echo\ fail" \
"4: :_values '' \ echo\ fail\ " \
"5: :_values '' \ echo\ fail\ " \
"6: :_values '' \ echo\ fail\ " \
"7: :_values '' echo\ fail" \
"8: : _message -r 'no values to complete'" \
"9: :_values '' LAST\ POSITIONAL\ VALUE"
}
if compquote '' 2>/dev/null; then _example; else compdef _example example; fi
`
Expand Down
9 changes: 8 additions & 1 deletion zsh/snippet.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ func snippetFunctions(cmd *cobra.Command, actions map[string]string) string {
positionals = append(positionals, " "+snippetPositionalCompletion(pos, action))
pos++
} else {
if action, ok := actions[uid.Positional(cmd, 0)]; ok {
positionals = append(positionals, " "+snippetPositionalAnyCompletion(action))
}
break // TODO only consisten entriess for now
}
}
Expand Down Expand Up @@ -115,7 +118,11 @@ func snippetFlagCompletion(flag *pflag.Flag, action *string) (snippet string) {
}

func snippetPositionalCompletion(position int, action string) string {
return fmt.Sprintf(`"%v:: :%v"`, position, action)
return fmt.Sprintf(`"%v: :%v"`, position, action)
}

func snippetPositionalAnyCompletion(action string) string {
return fmt.Sprintf(`"*: :%v"`, action)
}

func zshCompFlagCouldBeSpecifiedMoreThenOnce(f *pflag.Flag) bool {
Expand Down
4 changes: 2 additions & 2 deletions zsh/snippet_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ func TestSnippetFlagCompletion(t *testing.T) {

func TestSnippetPositionalCompletion(t *testing.T) {
pos1 := snippetPositionalCompletion(1, ActionValues("a", "b", "c"))
assert.Equal(t, `"1:: :_values '' a b c"`, pos1)
assert.Equal(t, `"1: :_values '' a b c"`, pos1)

pos2 := snippetPositionalCompletion(2, ActionMessage("test"))
assert.Equal(t, `"2:: : _message -r 'test'"`, pos2)
assert.Equal(t, `"2: : _message -r 'test'"`, pos2)
}

func TestSnippetSubcommands(t *testing.T) {
Expand Down

0 comments on commit 0cd5fbc

Please sign in to comment.