Repository: rsteube/carapace
Pull request #867 (2023-07-30 19:01:23)

ActionMultiParts: fix c.Parts for empty dividier


Patch:
diff --git a/defaultActions.go b/defaultActions.go
index 2b66c42..3a254ca 100644
--- a/defaultActions.go
+++ b/defaultActions.go
@@ -219,18 +219,40 @@ func ActionMultiParts(sep string, callback func(c Context) Action) Action {
 // ActionMultiPartsN is like ActionMultiParts but limits the number of parts to `n`.
 func ActionMultiPartsN(sep string, n int, callback func(c Context) Action) Action {
 	return ActionCallback(func(c Context) Action {
+		switch n {
+		case 0:
+			return ActionMessage("invalid value for n [ActionValuesDescribed]: %v", n)
+		case 1:
+			return callback(c).Invoke(c).ToA()
+		}
+
 		splitted := strings.SplitN(c.Value, sep, n)
 		prefix := ""
 		c.Parts = []string{}
 
 		switch {
 		case len(sep) == 0:
-			prefix = c.Value
-			c.Value = ""
-		case len(splitted) > 1:
-			c.Value = splitted[len(splitted)-1]
-			c.Parts = splitted[:len(splitted)-1]
-			prefix = strings.Join(c.Parts, sep) + sep
+			switch {
+			case n < 0:
+				prefix = c.Value
+				c.Value = ""
+				c.Parts = splitted
+			default:
+				prefix = c.Value
+				if n-1 < len(prefix) {
+					prefix = c.Value[:n-1]
+					c.Value = c.Value[n-1:]
+				} else {
+					c.Value = ""
+				}
+				c.Parts = strings.Split(prefix, "")
+			}
+		default:
+			if len(splitted) > 1 {
+				c.Value = splitted[len(splitted)-1]
+				c.Parts = splitted[:len(splitted)-1]
+				prefix = strings.Join(c.Parts, sep) + sep
+			}
 		}
 
 		nospace := '*'
diff --git a/example/cmd/action.go b/example/cmd/action.go
index 45852d6..4500a5b 100644
--- a/example/cmd/action.go
+++ b/example/cmd/action.go
@@ -33,6 +33,7 @@ func init() {
 	actionCmd.Flags().String("multiparts", "", "ActionMultiParts()")
 	actionCmd.Flags().String("multiparts-nested", "", "ActionMultiParts(...ActionMultiParts...)")
 	actionCmd.Flags().String("multipartsn", "", "ActionMultiPartsN()")
+	actionCmd.Flags().String("multipartsn-empty", "", "ActionMultiPartsN()")
 	actionCmd.Flags().String("styles", "", "ActionStyles()")
 	actionCmd.Flags().String("styleconfig", "", "ActionStyleConfig()")
 	actionCmd.Flags().String("styled-values", "", "ActionStyledValues()")
@@ -146,6 +147,16 @@ func init() {
 				return carapace.ActionMessage("should never happen")
 			}
 		}),
+		"multipartsn-empty": carapace.ActionMultiPartsN("", 2, func(c carapace.Context) carapace.Action {
+			switch len(c.Parts) {
+			case 0:
+				return carapace.ActionValues("a", "b")
+			case 1:
+				return carapace.ActionValues("c", "d", "e").UniqueList("")
+			default:
+				return carapace.ActionMessage("should never happen")
+			}
+		}),
 		"styles":      carapace.ActionStyles(),
 		"styleconfig": carapace.ActionStyleConfig(),
 		"styled-values": carapace.ActionStyledValues(
diff --git a/example/cmd/multiparts.go b/example/cmd/multiparts.go
index 8e554ed..56d7cf9 100644
--- a/example/cmd/multiparts.go
+++ b/example/cmd/multiparts.go
@@ -4,6 +4,7 @@ import (
 	"strings"
 
 	"github.com/rsteube/carapace"
+	"github.com/rsteube/carapace/pkg/style"
 	"github.com/spf13/cobra"
 )
 
@@ -21,6 +22,10 @@ func init() {
 	multipartsCmd.Flags().String("dotdotdot", "", "multiparts with ... as divider")
 	multipartsCmd.Flags().String("equals", "", "multiparts with = as divider")
 	multipartsCmd.Flags().String("none", "", "multiparts without divider")
+	multipartsCmd.Flags().String("none-zero", "", "multiparts without divider limited to 0")
+	multipartsCmd.Flags().String("none-one", "", "multiparts without divider limited to 1")
+	multipartsCmd.Flags().String("none-two", "", "multiparts without divider limited to 2")
+	multipartsCmd.Flags().String("none-three", "", "multiparts without divider limited to 3")
 	multipartsCmd.Flags().String("slash", "", "multiparts with / as divider")
 
 	rootCmd.AddCommand(multipartsCmd)
@@ -35,6 +40,51 @@ func init() {
 		"none": carapace.ActionMultiParts("", func(c carapace.Context) carapace.Action {
 			return carapace.ActionValuesDescribed("a", "first", "b", "second", "c", "third", "d", "fourth").Invoke(c).Filter(c.Parts).ToA()
 		}),
+		"none-zero": carapace.ActionMultiPartsN("", 0, func(c carapace.Context) carapace.Action {
+			return carapace.ActionMessage("unreachable")
+		}),
+		"none-one": carapace.ActionMultiPartsN("", 1, func(c carapace.Context) carapace.Action {
+			return carapace.ActionValues("a", "b")
+		}),
+		"none-two": carapace.ActionMultiPartsN("", 2, func(c carapace.Context) carapace.Action {
+			switch len(c.Parts) {
+			case 0:
+				return carapace.ActionValuesDescribed(
+					"a", "zero",
+					"b", "zero",
+				).Style(style.Blue)
+			default:
+				return carapace.ActionValuesDescribed(
+					"a", "default",
+					"b", "default",
+					"c", "default",
+				).Style(style.Red).
+					UniqueList("")
+			}
+		}),
+		"none-three": carapace.ActionMultiPartsN("", 3, func(c carapace.Context) carapace.Action {
+			switch len(c.Parts) {
+			case 0:
+				return carapace.ActionValuesDescribed(
+					"a", "zero",
+					"b", "zero",
+				).Style(style.Blue)
+			case 1:
+				return carapace.ActionValuesDescribed(
+					"a", "one",
+					"b", "one",
+					"c", "one",
+				).Style(style.Red)
+			default:
+				return carapace.ActionValuesDescribed(
+					"a", "default",
+					"b", "default",
+					"c", "default",
+					"d", "default",
+				).Style(style.Green).
+					UniqueList("")
+			}
+		}),
 		"slash": actionMultipartsTest("/"),
 	})
 


Test patch:
diff --git a/example/cmd/multiparts_test.go b/example/cmd/multiparts_test.go
index b24b9d1..d0621c3 100644
--- a/example/cmd/multiparts_test.go
+++ b/example/cmd/multiparts_test.go
@@ -8,6 +8,7 @@ import (
 	"github.com/rsteube/carapace/pkg/style"
 )
 
+// TODO rename
 func TestMultiparts(t *testing.T) {
 	sandbox.Package(t, "github.com/rsteube/carapace/example")(func(s *sandbox.Sandbox) {
 		s.Files(
@@ -75,5 +76,100 @@ func TestMultiparts(t *testing.T) {
 				StyleF(style.ForPath).
 				Prefix("VALUE=one,FILE=").
 				NoSpace(',', '/', '='))
+
+		s.Run("multiparts", "--none-zero", "").
+			Expect(carapace.ActionMessage("invalid value for n [ActionValuesDescribed]: 0").
+				Usage("multiparts without divider limited to 0"))
+
+		s.Run("multiparts", "--none-one", "").
+			Expect(carapace.ActionValues("a", "b").
+				Usage("multiparts without divider limited to 1"))
+
+		s.Run("multiparts", "--none-one", "a").
+			Expect(carapace.ActionValues("a").
+				Usage("multiparts without divider limited to 1"))
+
+		s.Run("multiparts", "--none-two", "").
+			Expect(carapace.ActionValuesDescribed(
+				"a", "zero",
+				"b", "zero",
+			).
+				NoSpace().
+				Style(style.Blue).
+				Usage("multiparts without divider limited to 2"))
+
+		s.Run("multiparts", "--none-two", "a").
+			Expect(carapace.ActionValuesDescribed(
+				"a", "default",
+				"b", "default",
+				"c", "default",
+			).
+				Prefix("a").
+				NoSpace().
+				Style(style.Red).
+				Usage("multiparts without divider limited to 2"))
+
+		s.Run("multiparts", "--none-two", "ab").
+			Expect(carapace.ActionValuesDescribed(
+				"a", "default",
+				"c", "default",
+			).
+				Prefix("ab").
+				NoSpace().
+				Style(style.Red).
+				Usage("multiparts without divider limited to 2"))
+
+		s.Run("multiparts", "--none-two", "abc").
+			Expect(carapace.ActionValuesDescribed(
+				"a", "default",
+			).
+				Prefix("abc").
+				NoSpace().
+				Style(style.Red).
+				Usage("multiparts without divider limited to 2"))
+
+		s.Run("multiparts", "--none-three", "a").
+			Expect(carapace.ActionValuesDescribed(
+				"a", "one",
+				"b", "one",
+				"c", "one",
+			).
+				Prefix("a").
+				NoSpace().
+				Style(style.Red).
+				Usage("multiparts without divider limited to 3"))
+
+		s.Run("multiparts", "--none-three", "ab").
+			Expect(carapace.ActionValuesDescribed(
+				"a", "default",
+				"b", "default",
+				"c", "default",
+				"d", "default",
+			).
+				Prefix("ab").
+				NoSpace().
+				Style(style.Green).
+				Usage("multiparts without divider limited to 3"))
+
+		s.Run("multiparts", "--none-three", "abc").
+			Expect(carapace.ActionValuesDescribed(
+				"a", "default",
+				"b", "default",
+				"d", "default",
+			).
+				Prefix("abc").
+				NoSpace().
+				Style(style.Green).
+				Usage("multiparts without divider limited to 3"))
+
+		s.Run("multiparts", "--none-three", "abcd").
+			Expect(carapace.ActionValuesDescribed(
+				"a", "default",
+				"b", "default",
+			).
+				Prefix("abcd").
+				NoSpace().
+				Style(style.Green).
+				Usage("multiparts without divider limited to 3"))
 	})
 }