Repository: rsteube/carapace
Pull request #97 (2020-10-07 22:27:25)

added multiparts test
- updated Action util functions
- added InvokedAction type


Patch:
diff --git a/README.md b/README.md
index f8772b7..2986783 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Completion script generator for [cobra] with support for:
 - [Fish](https://fishshell.com/docs/current/#writing-your-own-completions)
 - [Oil](http://www.oilshell.org/blog/2018/10/10.html) *broken* ([#86](https://github.com/rsteube/carapace/issues/86))
 - [Powershell](https://docs.microsoft.com/en-us/powershell/module/microsoft.powershell.core/register-argumentcompleter)
-- [Xonsh](https://xon.sh/tutorial_completers.html#writing-a-new-completer)
+- [Xonsh](https://xon.sh/tutorial_completers.html#writing-a-new-completer) *experimental*
 - [Zsh](https://github.com/zsh-users/zsh-completions/blob/master/zsh-completions-howto.org)
 
 
@@ -110,7 +110,7 @@ Since callbacks are simply invocations of the program they can be tested directl
 
 ```sh
 ./example _carapace bash '_example__condition#1' example condition --required invalid
-#compgen -W $'_\nERR (flag --required must be set to valid: invalid)' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"
+#compgen -W "ERR flag_--required_must_be_set_to_valid:_invalid" -- $last
 
 ./example _carapace elvish '_example__condition#1' example condition --required invalid
 #edit:complex-candidate ERR &display-suffix=' (flag --required must be set to valid: invalid)'
@@ -130,7 +130,7 @@ Since callbacks are simply invocations of the program they can be tested directl
 #}
 
 ./example _carapace zsh '_example__condition#1' example condition --required invalid
-#{local _comp_desc=('_' 'ERR (flag --required must be set to valid: invalid)');compadd -S '' -d _comp_desc '_' 'ERR'}
+# _message -r 'flag --required must be set to valid: invalid'
 ```
 
 ### ActionMultiParts
diff --git a/action.go b/action.go
index 99a6b03..e0f0bcb 100644
--- a/action.go
+++ b/action.go
@@ -21,14 +21,14 @@ type Action struct {
 	powershell func() string
 	xonsh      func() string
 	zsh        func() string
-	Callback   CompletionCallback
+	callback   CompletionCallback
 }
 type ActionMap map[string]Action
 type CompletionCallback func(args []string) Action
 
 // finalize replaces value if a callback function is set
 func (a Action) finalize(cmd *cobra.Command, uid string) Action {
-	if a.Callback != nil {
+	if a.callback != nil {
 		if a.bash == nil {
 			a.bash = func() string { return bash.Callback(cmd.Root().Name(), uid) }
 		}
@@ -51,34 +51,47 @@ func (a Action) finalize(cmd *cobra.Command, uid string) Action {
 	return a
 }
 
-// TODO maybe use Invoke(args) and work on []Candidate
-func (a Action) Prefix(prefix string, args []string) Action {
-	if nestedAction := a.NestedAction(args, 5); nestedAction.rawValues != nil {
-		for index, val := range nestedAction.rawValues {
-			nestedAction.rawValues[index].Value = prefix + val.Value // TODO check if val.Value can be assigned directly
+type InvokedAction Action
+
+func (a Action) Invoke(args []string) InvokedAction {
+	return InvokedAction(a.nestedAction(args, 5))
+}
+
+func (a InvokedAction) Filter(values []string) InvokedAction {
+	toremove := make(map[string]bool)
+	for _, v := range values {
+		toremove[v] = true
+	}
+	filtered := make(common.Candidates, 0)
+	for _, candidate := range a.rawValues {
+		if _, ok := toremove[candidate.Value]; !ok {
+			filtered = append(filtered, candidate)
 		}
-		return nestedAction
-	} else {
-		return ActionMessage("TODO Prefix(str) failed")
 	}
+	return InvokedAction(actionCandidates(filtered...))
 }
 
-// TODO maybe use Invoke(args) and work on []Candidate
-func (a Action) Suffix(suffix string, args []string) Action {
-	if nestedAction := a.NestedAction(args, 5); nestedAction.rawValues != nil {
-		for index, val := range nestedAction.rawValues {
-			nestedAction.rawValues[index].Value = val.Value + suffix // TODO check if val.Value can be assigned directly
-		}
-		return nestedAction
-	} else {
-		return ActionMessage("TODO Prefix(str) failed")
+func (a InvokedAction) Prefix(prefix string) InvokedAction {
+	for index, val := range a.rawValues {
+		a.rawValues[index].Value = prefix + val.Value
+	}
+	return a
+}
+
+func (a InvokedAction) Suffix(suffix string) InvokedAction {
+	for index, val := range a.rawValues {
+		a.rawValues[index].Value = val.Value + suffix
 	}
+	return a
 }
 
-// TODO maybe rename to Invoke(args)
-func (a Action) NestedAction(args []string, maxDepth int) Action {
-	if a.rawValues == nil && a.Callback != nil && maxDepth > 0 {
-		return a.Callback(args).NestedAction(args, maxDepth-1)
+func (a InvokedAction) ToA() Action {
+	return Action(a)
+}
+
+func (a Action) nestedAction(args []string, maxDepth int) Action {
+	if a.rawValues == nil && a.callback != nil && maxDepth > 0 {
+		return a.callback(args).nestedAction(args, maxDepth-1)
 	} else {
 		return a
 	}
@@ -119,7 +132,7 @@ func (m *ActionMap) Shell(shell string) map[string]string {
 
 // ActionCallback invokes a go function during completion
 func ActionCallback(callback CompletionCallback) Action {
-	return Action{Callback: callback}
+	return Action{callback: callback}
 }
 
 // ActionBool completes true/false
@@ -169,25 +182,27 @@ func ActionValuesDescribed(values ...string) Action {
 			vals[index/2] = common.Candidate{Value: val, Display: val, Description: values[index+1]}
 		}
 	}
+	return actionCandidates(vals...)
+}
+
+func actionCandidates(candidates ...common.Candidate) Action {
 	return Action{
-		rawValues:  vals,
-		bash:       func() string { return bash.ActionCandidates(vals...) },
-		elvish:     func() string { return elvish.ActionCandidates(vals...) },
-		fish:       func() string { return fish.ActionCandidates(vals...) },
-		powershell: func() string { return powershell.ActionCandidates(vals...) },
-		xonsh:      func() string { return xonsh.ActionCandidates(vals...) },
-		zsh:        func() string { return zsh.ActionCandidates(vals...) },
+		rawValues:  candidates,
+		bash:       func() string { return bash.ActionCandidates(candidates...) },
+		elvish:     func() string { return elvish.ActionCandidates(candidates...) },
+		fish:       func() string { return fish.ActionCandidates(candidates...) },
+		powershell: func() string { return powershell.ActionCandidates(candidates...) },
+		xonsh:      func() string { return xonsh.ActionCandidates(candidates...) },
+		zsh:        func() string { return zsh.ActionCandidates(candidates...) },
 	}
 }
 
 // ActionMessage displays a help messages in places where no completions can be generated
-func ActionMessage(msg string) Action { // TODO somehow handle this differently for Prefix/Suffix
+func ActionMessage(msg string) Action {
 	return ActionValuesDescribed("_", "", "ERR", msg)
-	// TODO muss not be filtered if value already contains a submatch (so that it is always shown)
-	// TODO zsh is the only one with actual message function		zsh:        func() string { return zsh.ActionMessage(msg) },
 }
 
-// TODO find a better solution for this
+// CCallbackValue is set to the currently completed flag/positional value during callback
 var CallbackValue string
 
 // ActionMultiParts completes multiple parts of words separately where each part is separated by some char
@@ -206,6 +221,6 @@ func ActionMultiParts(divider string, callback func(args []string, parts []strin
 			parts = parts[0 : len(parts)-1]
 		}
 
-		return callback(args, parts).Prefix(prefix, args)
+		return callback(args, parts).Invoke(args).Prefix(prefix).ToA()
 	})
 }
diff --git a/bash/action.go b/bash/action.go
index 083d61c..17a68c1 100644
--- a/bash/action.go
+++ b/bash/action.go
@@ -48,11 +48,11 @@ func ActionCandidates(values ...common.Candidate) string {
 	vals := make([]string, len(values))
 	for index, val := range values {
 		if val.Description == "" {
-			vals[index] = strings.Replace(sanitizer.Replace(val.Value), ` `, `\\\ `, -1)
+			vals[index] = sanitizer.Replace(val.Value)
 		} else {
-			vals[index] = fmt.Sprintf(`%v (%v)`, strings.Replace(sanitizer.Replace(val.Value), ` `, `\\\ `, -1), sanitizer.Replace(val.Description))
+			vals[index] = fmt.Sprintf(`%v (%v)`, sanitizer.Replace(val.Value), sanitizer.Replace(val.Description))
 		}
 	}
 
-	return fmt.Sprintf(`compgen -W $'%v' -- "$cur" | sed "s!^$curprefix!!"`, strings.Join(vals, `\n`))
+	return fmt.Sprintf(`compgen -W $'%v' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"`, strings.Join(vals, `\n`))
 }
diff --git a/bash/snippet.go b/bash/snippet.go
index 9be79e5..d453524 100644
--- a/bash/snippet.go
+++ b/bash/snippet.go
@@ -25,7 +25,7 @@ _%v_callback() {
 
 _%v_completions() {
   local cur prev #words cword split
-  _init_completion -n :=
+  _init_completion -n /=:.,
   local curprefix
   curprefix="$(echo "$cur" | sed -r 's_^(.*[:/=])?.*_\1_')"
   local compline="${COMP_LINE:0:${COMP_POINT}}"
diff --git a/carapace.go b/carapace.go
index a2aafec..6c58a4e 100644
--- a/carapace.go
+++ b/carapace.go
@@ -22,8 +22,8 @@ type Completions struct {
 
 func (c Completions) invokeCallback(uid string, args []string) Action {
 	if action, ok := c.actions[uid]; ok {
-		if action.Callback != nil {
-			return action.Callback(args)
+		if action.callback != nil {
+			return action.callback(args)
 		}
 	}
 	return ActionMessage(fmt.Sprintf("callback %v unknown", uid))
@@ -132,17 +132,17 @@ func addCompletionCommand(cmd *cobra.Command) {
 					case "_":
 						if _uid, action, ok := findAction(targetCmd, targetArgs); ok {
 							CallbackValue = uid.Value(targetCmd, targetArgs, _uid)
-							if action.Callback == nil {
+							if action.callback == nil {
 								fmt.Println(action.Value(shell))
 							} else {
-								fmt.Println(action.Callback(targetArgs).NestedAction(targetArgs, 2).Value(shell))
+								fmt.Println(action.callback(targetArgs).nestedAction(targetArgs, 2).Value(shell))
 							}
 						}
 					case "state":
 						fmt.Println(uid.Command(targetCmd))
 					default:
 						CallbackValue = uid.Value(targetCmd, targetArgs, id)
-						fmt.Println(completions.invokeCallback(id, targetArgs).NestedAction(targetArgs, 2).Value(shell))
+						fmt.Println(completions.invokeCallback(id, targetArgs).nestedAction(targetArgs, 2).Value(shell))
 					}
 				}
 			}
diff --git a/common/value.go b/common/value.go
index 0e75699..1a2b854 100644
--- a/common/value.go
+++ b/common/value.go
@@ -6,7 +6,9 @@ type Candidate struct {
 	Description string
 }
 
-func CandidateFromValues(values ...string) []Candidate {
+type Candidates []Candidate
+
+func CandidateFromValues(values ...string) Candidates {
 	candidates := make([]Candidate, len(values))
 	for index, val := range values {
 		candidates[index] = Candidate{Value: val, Display: val}
diff --git a/example/cmd/action/os.go b/example/cmd/action/os.go
index 5c81d98..1705175 100644
--- a/example/cmd/action/os.go
+++ b/example/cmd/action/os.go
@@ -128,7 +128,7 @@ func ActionUserGroup() carapace.Action {
 	return carapace.ActionMultiParts(":", func(args []string, parts []string) carapace.Action {
 		switch len(parts) {
 		case 0:
-			return ActionUsers().Suffix(":", args)
+			return ActionUsers().Invoke(args).Suffix(":").ToA()
 		case 1:
 			return ActionGroups()
 		default:
diff --git a/example/cmd/multiparts.go b/example/cmd/multiparts.go
new file mode 100644
index 0000000..de9c54f
--- /dev/null
+++ b/example/cmd/multiparts.go
@@ -0,0 +1,56 @@
+package cmd
+
+import (
+	"strings"
+
+	"github.com/rsteube/carapace"
+	"github.com/spf13/cobra"
+)
+
+var multipartsCmd = &cobra.Command{
+	Use:   "multiparts",
+	Short: "multiparts example",
+}
+
+func init() {
+	multipartsCmd.Flags().String("at", "", "multiparts with @ as divider")
+	multipartsCmd.Flags().String("colon", "", "multiparts with : as divider ")
+	multipartsCmd.Flags().String("comma", "", "multiparts with , as divider")
+	multipartsCmd.Flags().String("dot", "", "multiparts with . as divider")
+	multipartsCmd.Flags().String("equals", "", "multiparts with = as divider")
+	multipartsCmd.Flags().String("slash", "", "multiparts with / as divider")
+	multipartsCmd.Flags().String("none", "", "multiparts without divider")
+
+	rootCmd.AddCommand(multipartsCmd)
+
+	carapace.Gen(multipartsCmd).FlagCompletion(carapace.ActionMap{
+		"at":     ActionMultipartsTest("@"),
+		"colon":  ActionMultipartsTest(":"),
+		"comma":  ActionMultipartsTest(","),
+		"dot":    ActionMultipartsTest("."),
+		"equals": ActionMultipartsTest("="),
+		"slash":  ActionMultipartsTest("/"),
+		"none": carapace.ActionMultiParts("", func(args, parts []string) carapace.Action {
+			return carapace.ActionValuesDescribed("a", "first", "b", "second", "c", "third", "d", "fourth").Invoke(args).Filter(strings.Split(carapace.CallbackValue, "")).ToA()
+		}),
+	})
+}
+
+func ActionMultipartsTest(divider string) carapace.Action {
+	return carapace.ActionMultiParts(divider, func(args, parts []string) carapace.Action {
+		switch len(parts) {
+		case 0:
+			return ActionTestValues().Invoke(args).Suffix(divider).ToA()
+		case 1:
+			return ActionTestValues().Invoke(args).Filter(parts).Suffix(divider).ToA()
+		case 2:
+			return ActionTestValues().Invoke(args).Filter(parts).ToA()
+		default:
+			return carapace.ActionValues()
+		}
+	})
+}
+
+func ActionTestValues() carapace.Action {
+	return carapace.ActionValuesDescribed("first", "first value", "second", "second value", "third with space", "third value")
+}
diff --git a/go.sum b/go.sum
index 4cc9a7d..faca3e1 100644
--- a/go.sum
+++ b/go.sum
@@ -2,6 +2,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
 github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
 github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
 github.com/alecthomas/assert v0.0.0-20170929043011-405dbfeb8e38/go.mod h1:r7bzyVFMNntcxPZXK3/+KdruV1H5KSlyVY0gc+NgInI=
+github.com/alecthomas/chroma v0.7.1 h1:G1i02OhUbRi2nJxcNkwJaY/J1gHXj9tt72qN6ZouLFQ=
 github.com/alecthomas/chroma v0.7.1/go.mod h1:gHw09mkX1Qp80JlYbmN9L3+4R5o6DJJ3GRShh+AICNc=
 github.com/alecthomas/colour v0.0.0-20160524082231-60882d9e2721/go.mod h1:QO9JBoKquHd+jz9nshCh40fOfO+JzsoXy8qTHF68zU0=
 github.com/alecthomas/kong v0.2.1-0.20190708041108-0548c6b1afae/go.mod h1:+inYUSluD+p4L8KdviBSgzcqEjUQOfC5fQDRFuc36lI=
@@ -23,11 +24,13 @@ github.com/cornfeedhobo/pflag v1.0.2-0.20200824165833-dd6f6588b61d h1:048MNx9ATp
 github.com/cornfeedhobo/pflag v1.0.2-0.20200824165833-dd6f6588b61d/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964 h1:y5HC9v93H5EPKqaS1UYVg1uYah5Xf51mBfIoWehClUQ=
 github.com/danwakefield/fnmatch v0.0.0-20160403171240-cbb64ac3d964/go.mod h1:Xd9hchkHSWYkEqJwUGisez3G1QY8Ryz0sdWrLPMGjLk=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
+github.com/dlclark/regexp2 v1.1.6 h1:CqB4MjHw0MFCDj+PHHjiESmHX+N7t0tJzKvC6M97BRg=
 github.com/dlclark/regexp2 v1.1.6/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
 github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
diff --git a/uid/uid.go b/uid/uid.go
index d49e6fd..cbf9b62 100644
--- a/uid/uid.go
+++ b/uid/uid.go
@@ -54,7 +54,15 @@ func Value(cmd *cobra.Command, args []string, uid string) string {
 	if strings.Contains(uid, "##") {
 		split := strings.Split(uid, "##")
 		if flag := cmd.Flag(split[len(split)-1]); flag != nil {
-			return flag.Value.String()
+			if flag.Value.Type() == "stringSlice" {
+				slice, _ := cmd.Flags().GetStringSlice(split[len(split)-1])
+				return strings.Join(slice, ",")
+			} else if flag.Value.Type() == "stringArray" {
+				slice, _ := cmd.Flags().GetStringArray(split[len(split)-1])
+				return strings.Join(slice, ",")
+			} else {
+				return flag.Value.String()
+			}
 		}
 
 	} else if strings.Contains(uid, "#") {


Test patch:
diff --git a/example/cmd/root_test.go b/example/cmd/root_test.go
index 661a1b4..441359b 100644
--- a/example/cmd/root_test.go
+++ b/example/cmd/root_test.go
@@ -22,7 +22,7 @@ _example_callback() {
 
 _example_completions() {
   local cur prev #words cword split
-  _init_completion -n :=
+  _init_completion -n /=:.,
   local curprefix
   curprefix="$(echo "$cur" | sed -r 's_^(.*[:/=])?.*_\1_')"
   local compline="${COMP_LINE:0:${COMP_POINT}}"
@@ -52,7 +52,7 @@ _example_completions() {
             ;;
 
           *)
-            COMPREPLY=($(compgen -W $'--array (multiflag)\n-a (multiflag)\n--persistentFlag (Help message for persistentFlag)\n-p (Help message for persistentFlag)\n--toggle (Help message for toggle)\n-t (Help message for toggle)' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'--array (multiflag)\n-a (multiflag)\n--persistentFlag (Help message for persistentFlag)\n-p (Help message for persistentFlag)\n--toggle (Help message for toggle)\n-t (Help message for toggle)' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
         esac
       else
@@ -62,7 +62,7 @@ _example_completions() {
             ;;
 
           *)
-            COMPREPLY=($(compgen -W $'action (action example)\nalias (action example)\ncallback (callback example)\ncondition (condition example)\nhelp (Help about any command)\ninjection (just trying to break things)' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'action (action example)\nalias (action example)\ncallback (callback example)\ncondition (condition example)\nhelp (Help about any command)\ninjection (just trying to break things)\nmultiparts (multiparts example)' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
         esac
       fi
@@ -75,11 +75,11 @@ _example_completions() {
           -o=* | --optarg=*)
             cur=${cur#*=}
             curprefix=${curprefix#*=}
-            COMPREPLY=($(compgen -W $'blue\nred\ngreen\nyellow' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'blue\nred\ngreen\nyellow' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
 
           *)
-            COMPREPLY=($(compgen -W $'--directories (files flag)\n--files (files flag)\n-f (files flag)\n--groups (groups flag)\n-g (groups flag)\n--hosts (hosts flag)\n--kill (kill signals)\n-k (kill signals)\n--message (message flag)\n-m (message flag)\n--net_interfaces (net_interfaces flag)\n-n (net_interfaces flag)\n--optarg (optional arg with default value blue)\n-o (optional arg with default value blue)\n--usergroup (user:group flag)\n--users (users flag)\n-u (users flag)\n--values (values flag)\n-v (values flag)\n--values_described (values with description flag)\n-d (values with description flag)' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'--directories (files flag)\n--files (files flag)\n-f (files flag)\n--groups (groups flag)\n-g (groups flag)\n--hosts (hosts flag)\n--kill (kill signals)\n-k (kill signals)\n--message (message flag)\n-m (message flag)\n--net_interfaces (net_interfaces flag)\n-n (net_interfaces flag)\n--optarg (optional arg with default value blue)\n-o (optional arg with default value blue)\n--usergroup (user:group flag)\n--users (users flag)\n-u (users flag)\n--values (values flag)\n-v (values flag)\n--values_described (values with description flag)\n-d (values with description flag)' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
         esac
       else
@@ -101,11 +101,11 @@ _example_completions() {
             ;;
 
           -k | --kill)
-            COMPREPLY=($(compgen -W $'ABRT (Abnormal termination)\nALRM (Virtual alarm clock)\nBUS (BUS error)\nCHLD (Child status has changed)\nCONT (Continue stopped process)\nFPE (Floating-point exception)\nHUP (Hangup detected on controlling terminal)\nILL (Illegal instruction)\nINT (Interrupt from keyboard)\nKILL (Kill, unblockable)\nPIPE (Broken pipe)\nPOLL (Pollable event occurred)\nPROF (Profiling alarm clock timer expired)\nPWR (Power failure restart)\nQUIT (Quit from keyboard)\nSEGV (Segmentation violation)\nSTKFLT (Stack fault on coprocessor)\nSTOP (Stop process, unblockable)\nSYS (Bad system call)\nTERM (Termination request)\nTRAP (Trace/breakpoint trap)\nTSTP (Stop typed at keyboard)\nTTIN (Background read from tty)\nTTOU (Background write to tty)\nURG (Urgent condition on socket)\nUSR1 (User-defined signal 1)\nUSR2 (User-defined signal 2)\nVTALRM (Virtual alarm clock)\nWINCH (Window size change)\nXCPU (CPU time limit exceeded)\nXFSZ (File size limit exceeded)' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'ABRT (Abnormal termination)\nALRM (Virtual alarm clock)\nBUS (BUS error)\nCHLD (Child status has changed)\nCONT (Continue stopped process)\nFPE (Floating-point exception)\nHUP (Hangup detected on controlling terminal)\nILL (Illegal instruction)\nINT (Interrupt from keyboard)\nKILL (Kill, unblockable)\nPIPE (Broken pipe)\nPOLL (Pollable event occurred)\nPROF (Profiling alarm clock timer expired)\nPWR (Power failure restart)\nQUIT (Quit from keyboard)\nSEGV (Segmentation violation)\nSTKFLT (Stack fault on coprocessor)\nSTOP (Stop process, unblockable)\nSYS (Bad system call)\nTERM (Termination request)\nTRAP (Trace/breakpoint trap)\nTSTP (Stop typed at keyboard)\nTTIN (Background read from tty)\nTTOU (Background write to tty)\nURG (Urgent condition on socket)\nUSR1 (User-defined signal 1)\nUSR2 (User-defined signal 2)\nVTALRM (Virtual alarm clock)\nWINCH (Window size change)\nXCPU (CPU time limit exceeded)\nXFSZ (File size limit exceeded)' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
 
           -m | --message)
-            COMPREPLY=($(compgen -W $'_\nERR (message example)' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'_\nERR (message example)' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
 
           -n | --net_interfaces)
@@ -121,11 +121,11 @@ _example_completions() {
             ;;
 
           -v | --values)
-            COMPREPLY=($(compgen -W $'values\nexample' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'values\nexample' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
 
           -d | --values_described)
-            COMPREPLY=($(compgen -W $'values (valueDescription)\nexample (exampleDescription)' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'values (valueDescription)\nexample (exampleDescription)' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
 
           *)
@@ -141,7 +141,7 @@ _example_completions() {
         case $cur in
 
           *)
-            COMPREPLY=($(compgen -W $'--callback (Help message for callback)\n-c (Help message for callback)' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'--callback (Help message for callback)\n-c (Help message for callback)' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
         esac
       else
@@ -163,13 +163,13 @@ _example_completions() {
         case $cur in
 
           *)
-            COMPREPLY=($(compgen -W $'--required (required flag)\n-r (required flag)' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'--required (required flag)\n-r (required flag)' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
         esac
       else
         case $prev in
           -r | --required)
-            COMPREPLY=($(compgen -W $'valid\ninvalid' -- "$cur" | sed "s!^$curprefix!!"))
+            COMPREPLY=($(compgen -W $'valid\ninvalid' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
             ;;
 
           *)
@@ -217,6 +217,52 @@ _example_completions() {
       fi
       ;;
 
+
+    '_example__multiparts' )
+      if [[ $cur == -* ]]; then
+        case $cur in
+
+          *)
+            COMPREPLY=($(compgen -W $'--at (multiparts with @ as divider)\n--colon (multiparts with : as divider )\n--comma (multiparts with , as divider)\n--dot (multiparts with . as divider)\n--equals (multiparts with = as divider)\n--none (multiparts without divider)\n--slash (multiparts with / as divider)' -- "${cur//\\ / }" | sed "s!^${curprefix//\\ / }!!"))
+            ;;
+        esac
+      else
+        case $prev in
+          --at)
+            COMPREPLY=($(eval $(_example_callback '_example__multiparts##at')))
+            ;;
+
+          --colon)
+            COMPREPLY=($(eval $(_example_callback '_example__multiparts##colon')))
+            ;;
+
+          --comma)
+            COMPREPLY=($(eval $(_example_callback '_example__multiparts##comma')))
+            ;;
+
+          --dot)
+            COMPREPLY=($(eval $(_example_callback '_example__multiparts##dot')))
+            ;;
+
+          --equals)
+            COMPREPLY=($(eval $(_example_callback '_example__multiparts##equals')))
+            ;;
+
+          --none)
+            COMPREPLY=($(eval $(_example_callback '_example__multiparts##none')))
+            ;;
+
+          --slash)
+            COMPREPLY=($(eval $(_example_callback '_example__multiparts##slash')))
+            ;;
+
+          *)
+            COMPREPLY=($(eval $(_example_callback '_')))
+            ;;
+        esac
+      fi
+      ;;
+
   esac
 
   [[ $cur =~ ^[\"\'] ]] && COMPREPLY=("${COMPREPLY[@]//\\ /\ }")
@@ -263,7 +309,8 @@ edit:complex-candidate 'alias' &display='alias (action example)'
 edit:complex-candidate 'callback' &display='callback (callback example)'
 edit:complex-candidate 'condition' &display='condition (condition example)'
 edit:complex-candidate 'help' &display='help (Help about any command)'
-edit:complex-candidate 'injection' &display='injection (just trying to break things)' }
+edit:complex-candidate 'injection' &display='injection (just trying to break things)'
+edit:complex-candidate 'multiparts' &display='multiparts (multiparts example)' }
     ]
     subargs = $arg[(subindex example):] 
     if (> (count $subargs) 0) {
@@ -386,6 +433,23 @@ edit:complex-candidate 'invalid' &display='invalid' }]
     if (> (count $subargs) 0) {
       edit:complete-getopt $subargs $opt-specs $arg-handlers
     }
+  }  elif (eq $state '_example__multiparts') {
+    opt-specs = [
+        [&long='at' &desc='multiparts with @ as divider' &arg-required=$true &completer=[_]{ _example_callback '_example__multiparts##at' }]
+        [&long='colon' &desc='multiparts with \: as divider ' &arg-required=$true &completer=[_]{ _example_callback '_example__multiparts##colon' }]
+        [&long='comma' &desc='multiparts with , as divider' &arg-required=$true &completer=[_]{ _example_callback '_example__multiparts##comma' }]
+        [&long='dot' &desc='multiparts with . as divider' &arg-required=$true &completer=[_]{ _example_callback '_example__multiparts##dot' }]
+        [&long='equals' &desc='multiparts with = as divider' &arg-required=$true &completer=[_]{ _example_callback '_example__multiparts##equals' }]
+        [&long='none' &desc='multiparts without divider' &arg-required=$true &completer=[_]{ _example_callback '_example__multiparts##none' }]
+        [&long='slash' &desc='multiparts with / as divider' &arg-required=$true &completer=[_]{ _example_callback '_example__multiparts##slash' }]
+    ]
+    arg-handlers = [
+
+    ]
+    subargs = $arg[(subindex multiparts):] 
+    if (> (count $subargs) 0) {
+      edit:complete-getopt $subargs $opt-specs $arg-handlers
+    }
   }
 }
 `
@@ -431,6 +495,7 @@ complete -c 'example' -f -n '_example_state _example ' -a 'callback ' -d 'callba
 complete -c 'example' -f -n '_example_state _example ' -a 'condition ' -d 'condition example'
 complete -c 'example' -f -n '_example_state _example ' -a 'help ' -d 'Help about any command'
 complete -c 'example' -f -n '_example_state _example ' -a 'injection ' -d 'just trying to break things'
+complete -c 'example' -f -n '_example_state _example ' -a 'multiparts ' -d 'multiparts example'
 
 
 complete -c 'example' -f -n '_example_state _example__action' -l 'directories' -d 'files flag' -a '(__fish_complete_directories)' -r
@@ -460,6 +525,16 @@ complete -c 'example' -f -n '_example_state _example__help' -a '(_example_callba
 
 
 complete -c 'example' -f -n '_example_state _example__injection' -a '(_example_callback _)'
+
+
+complete -c 'example' -f -n '_example_state _example__multiparts' -l 'at' -d 'multiparts with @ as divider' -a '(_example_callback _example__multiparts##at)' -r
+complete -c 'example' -f -n '_example_state _example__multiparts' -l 'colon' -d 'multiparts with \: as divider ' -a '(_example_callback _example__multiparts##colon)' -r
+complete -c 'example' -f -n '_example_state _example__multiparts' -l 'comma' -d 'multiparts with , as divider' -a '(_example_callback _example__multiparts##comma)' -r
+complete -c 'example' -f -n '_example_state _example__multiparts' -l 'dot' -d 'multiparts with . as divider' -a '(_example_callback _example__multiparts##dot)' -r
+complete -c 'example' -f -n '_example_state _example__multiparts' -l 'equals' -d 'multiparts with = as divider' -a '(_example_callback _example__multiparts##equals)' -r
+complete -c 'example' -f -n '_example_state _example__multiparts' -l 'none' -d 'multiparts without divider' -a '(_example_callback _example__multiparts##none)' -r
+complete -c 'example' -f -n '_example_state _example__multiparts' -l 'slash' -d 'multiparts with / as divider' -a '(_example_callback _example__multiparts##slash)' -r
+complete -c 'example' -f -n '_example_state _example__multiparts' -a '(_example_callback _)'
 `
 	rootCmd.InitDefaultHelpCmd()
 	assert.Equal(t, expected, carapace.Gen(rootCmd).Snippet("fish"))
@@ -518,6 +593,7 @@ Register-ArgumentCompleter -Native -CommandName 'example' -ScriptBlock {
                 [CompletionResult]::new('condition ', 'condition', [CompletionResultType]::Command, 'condition example')
                 [CompletionResult]::new('help ', 'help', [CompletionResultType]::Command, 'Help about any command')
                 [CompletionResult]::new('injection ', 'injection', [CompletionResultType]::Command, 'just trying to break things')
+                [CompletionResult]::new('multiparts ', 'multiparts', [CompletionResultType]::Command, 'multiparts example')
             }
             break
         }
@@ -742,6 +818,60 @@ Register-ArgumentCompleter -Native -CommandName 'example' -ScriptBlock {
                 }
             }
 
+        '_example__multiparts' {
+            switch -regex ($previous) {
+                '^(--at)$' {
+                        _example_callback '_example__multiparts##at' 
+                        break
+                      }
+                '^(--colon)$' {
+                        _example_callback '_example__multiparts##colon' 
+                        break
+                      }
+                '^(--comma)$' {
+                        _example_callback '_example__multiparts##comma' 
+                        break
+                      }
+                '^(--dot)$' {
+                        _example_callback '_example__multiparts##dot' 
+                        break
+                      }
+                '^(--equals)$' {
+                        _example_callback '_example__multiparts##equals' 
+                        break
+                      }
+                '^(--none)$' {
+                        _example_callback '_example__multiparts##none' 
+                        break
+                      }
+                '^(--slash)$' {
+                        _example_callback '_example__multiparts##slash' 
+                        break
+                      }
+                default {
+                    switch -regex ($wordToComplete) {
+
+
+                        default {
+
+            if ($wordToComplete -like "-*") {
+                [CompletionResult]::new('--at ', '--at', [CompletionResultType]::ParameterName, 'multiparts with @ as divider')
+                [CompletionResult]::new('--colon ', '--colon', [CompletionResultType]::ParameterName, 'multiparts with : as divider ')
+                [CompletionResult]::new('--comma ', '--comma', [CompletionResultType]::ParameterName, 'multiparts with , as divider')
+                [CompletionResult]::new('--dot ', '--dot', [CompletionResultType]::ParameterName, 'multiparts with . as divider')
+                [CompletionResult]::new('--equals ', '--equals', [CompletionResultType]::ParameterName, 'multiparts with = as divider')
+                [CompletionResult]::new('--none ', '--none', [CompletionResultType]::ParameterName, 'multiparts without divider')
+                [CompletionResult]::new('--slash ', '--slash', [CompletionResultType]::ParameterName, 'multiparts with / as divider')
+            } else {
+                _example_callback '_'
+            }
+            break
+        }
+                        }
+                    }
+                }
+            }
+
     })
 
     if ($completions.count -eq 0) {
@@ -827,6 +957,7 @@ def _example_completer(prefix, line, begidx, endidx, ctx):
                 RichCompletion('condition', display='condition', description='condition example', prefix_len=0),
                 RichCompletion('help', display='help', description='Help about any command', prefix_len=0),
                 RichCompletion('injection', display='injection', description='just trying to break things', prefix_len=0),
+                RichCompletion('multiparts', display='multiparts', description='multiparts example', prefix_len=0),
                 }
 
 
@@ -1023,6 +1154,49 @@ def _example_completer(prefix, line, begidx, endidx, ctx):
                 result = _example_callback('_')
 
 
+    elif state == '_example__multiparts':
+        if False: # switch previous
+            pass
+        elif re.search('^(--at)$',previous):
+            result = _example_callback('_example__multiparts##at')
+                  
+        elif re.search('^(--colon)$',previous):
+            result = _example_callback('_example__multiparts##colon')
+                  
+        elif re.search('^(--comma)$',previous):
+            result = _example_callback('_example__multiparts##comma')
+                  
+        elif re.search('^(--dot)$',previous):
+            result = _example_callback('_example__multiparts##dot')
+                  
+        elif re.search('^(--equals)$',previous):
+            result = _example_callback('_example__multiparts##equals')
+                  
+        elif re.search('^(--none)$',previous):
+            result = _example_callback('_example__multiparts##none')
+                  
+        elif re.search('^(--slash)$',previous):
+            result = _example_callback('_example__multiparts##slash')
+                  
+        else:
+            if False:
+                pass
+    
+
+            elif re.search("-.*",current):
+                result = {
+                    RichCompletion('--at', display='--at', description='multiparts with @ as divider', prefix_len=0),
+                    RichCompletion('--colon', display='--colon', description='multiparts with : as divider ', prefix_len=0),
+                    RichCompletion('--comma', display='--comma', description='multiparts with , as divider', prefix_len=0),
+                    RichCompletion('--dot', display='--dot', description='multiparts with . as divider', prefix_len=0),
+                    RichCompletion('--equals', display='--equals', description='multiparts with = as divider', prefix_len=0),
+                    RichCompletion('--none', display='--none', description='multiparts without divider', prefix_len=0),
+                    RichCompletion('--slash', display='--slash', description='multiparts with / as divider', prefix_len=0),
+                }
+            else:
+                result = _example_callback('_')
+
+
 
     result = set(filter(lambda x: x.startswith(current) and x.endswith(suffix), result))
     if len(result) == 0:
@@ -1066,6 +1240,7 @@ function _example {
         "condition:condition example"
         "help:Help about any command"
         "injection:just trying to break things"
+        "multiparts:multiparts example"
       )
       _describe "command" commands
       ;;
@@ -1090,6 +1265,9 @@ function _example {
     injection)
       _example__injection
       ;;
+    multiparts)
+      _example__multiparts
+      ;;
   esac
 }
 
@@ -1142,6 +1320,18 @@ function _example__injection {
     "8: :{local _comp_desc=('');compadd -S '' -d _comp_desc ''}" \
     "9: :{local _comp_desc=('LAST POSITIONAL VALUE');compadd -S '' -d _comp_desc 'LAST POSITIONAL VALUE'}"
 }
+
+function _example__multiparts {
+    _arguments -C \
+    "--at[multiparts with @ as divider]: :_example_callback '_example__multiparts##at'" \
+    "--colon[multiparts with \: as divider ]: :_example_callback '_example__multiparts##colon'" \
+    "--comma[multiparts with , as divider]: :_example_callback '_example__multiparts##comma'" \
+    "--dot[multiparts with . as divider]: :_example_callback '_example__multiparts##dot'" \
+    "--equals[multiparts with = as divider]: :_example_callback '_example__multiparts##equals'" \
+    "--none[multiparts without divider]: :_example_callback '_example__multiparts##none'" \
+    "--slash[multiparts with / as divider]: :_example_callback '_example__multiparts##slash'" \
+    "*::arg:->args"
+}
 if compquote '' 2>/dev/null; then _example; else compdef _example example; fi
 `
 	rootCmd.InitDefaultHelpCmd()