From 92c6d90132d8ada1cf68f8e9e81244780aa75609 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Mon, 28 Apr 2025 11:27:33 +0530 Subject: [PATCH] Allow single char options to take =value in kittens Matches kitty cli parsing behavior --- tools/cli/parse-args.go | 22 ++++++++++++---------- tools/cli/types_test.go | 15 +++++++++------ 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/tools/cli/parse-args.go b/tools/cli/parse-args.go index fc34f10eb..1ed9ead9a 100644 --- a/tools/cli/parse-args.go +++ b/tools/cli/parse-args.go @@ -51,7 +51,7 @@ func (self *Command) parse_args(ctx *Context, args []string) error { opt.seen_option = opt_str needs_arg := opt.needs_argument() if needs_arg && val_not_allowed { - return &ParseError{Message: fmt.Sprintf("The option : :yellow:`%s` must be followed by a value not another option", opt_str)} + return &ParseError{Message: fmt.Sprintf("The option: :yellow:`%s` must be followed by a value not another option", opt_str)} } if has_val { if !needs_arg { @@ -77,27 +77,29 @@ func (self *Command) parse_args(ctx *Context, args []string) error { continue } opt_str := arg - opt_val := "" - has_val := false if strings.HasPrefix(opt_str, "--") { - parts := strings.SplitN(arg, "=", 2) - if len(parts) > 1 { - has_val = true - opt_val = parts[1] - } - opt_str = parts[0] + opt_str, opt_val, has_val := strings.Cut(opt_str, "=") err := handle_option(opt_str, has_val, opt_val, false) if err != nil { return err } } else { - runes := []rune(opt_str[1:]) + prefix, payload, has_payload := strings.Cut(opt_str, "=") + runes := []rune(prefix[1:]) for i, sl := range runes { err := handle_option("-"+string(sl), false, "", i < len(runes)-1) if err != nil { return err } } + if has_payload { + if expecting_arg_for == nil { + return &ParseError{Message: fmt.Sprintf("The option: :yellow:`-%c` does not take values", prefix[len(prefix)-1])} + } else { + expecting_arg_for.add_value(payload) + expecting_arg_for = nil + } + } } } else { // handle non option arg diff --git a/tools/cli/types_test.go b/tools/cli/types_test.go index d9c392420..32ebe7360 100644 --- a/tools/cli/types_test.go +++ b/tools/cli/types_test.go @@ -9,6 +9,8 @@ import ( "testing" "kitty/tools/utils/shlex" + + "github.com/google/go-cmp/cmp" ) var _ = fmt.Print @@ -54,14 +56,14 @@ func TestCLIParsing(t *testing.T) { if err != nil { t.Fatal(err) } - if !reflect.DeepEqual(expected_options, actual_options) { - t.Fatalf("Option values incorrect (expected != actual):\nCommand line: %s\n%#v != %#v", cmdline, expected_options, actual_options) + if diff := cmp.Diff(expected_options, actual_options); diff != "" { + t.Fatalf("Option values incorrect (expected != actual):\nCommand line: %s\n%s", cmdline, diff) } if expected_args == nil { expected_args = []string{} } - if !reflect.DeepEqual(expected_args, cmd.Args) { - t.Fatalf("Argument values incorrect (expected != actual):\nCommand line: %s\n%#v != %#v", cmdline, expected_args, cmd.Args) + if diff := cmp.Diff(expected_args, cmd.Args); diff != "" { + t.Fatalf("Argument values incorrect (expected != actual):\nCommand line: %s\n%s", cmdline, diff) } cmd.Root().ResetAfterParseArgs() } @@ -71,8 +73,8 @@ func TestCLIParsing(t *testing.T) { child1 := root.AddSubCommand(&Command{Name: "child1"}) child1.Add(OptionSpec{Name: "--choices", Choices: "a b c"}) child1.Add(OptionSpec{Name: "--simple-string -s"}) - child1.Add(OptionSpec{Name: "--set-me", Type: "bool-set"}) - child1.Add(OptionSpec{Name: "--int", Type: "int"}) + child1.Add(OptionSpec{Name: "--set-me -b", Type: "bool-set"}) + child1.Add(OptionSpec{Name: "--int -i", Type: "int"}) child1.Add(OptionSpec{Name: "--float", Type: "float"}) child1.Add(OptionSpec{Name: "--list", Type: "list"}) child1.SubCommandIsOptional = true @@ -88,6 +90,7 @@ func TestCLIParsing(t *testing.T) { rt(child1, "test child1 --set-me --simp=foo one", &options{SimpleString: "foo", SetMe: true}, "one") rt(child1, "test child1 --set-me --simple-string= one", &options{SetMe: true}, "one") rt(child1, "test child1 --int -3 --simple-s -s --float=3.3", &options{SimpleString: "-s", Int: -3, Float: 3.3}) + rt(child1, "test child1 -bi=3 --float=3.3", &options{SetMe: true, Int: 3, Float: 3.3}) rt(child1, "test child1 --list -3 -p --list one", &options{FromParent: 1, List: []string{"-3", "one"}}) rt(gc1, "test -p child1 -p gc1 xxx", &empty_options{}, "xxx")