diff --git a/defaultActions.go b/defaultActions.go index 2b66c42e..3a254ca4 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 45852d60..4500a5b3 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 8e554ed8..56d7cf9c 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("/"), }) diff --git a/example/cmd/multiparts_test.go b/example/cmd/multiparts_test.go index b24b9d13..d0621c3e 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")) }) }