File: parse-args.go

package info (click to toggle)
kitty 0.42.1-1
  • links: PTS, VCS
  • area: main
  • in suites: experimental
  • size: 28,564 kB
  • sloc: ansic: 82,787; python: 55,191; objc: 5,122; sh: 1,295; xml: 364; makefile: 143; javascript: 78
file content (143 lines) | stat: -rw-r--r-- 4,400 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// License: GPLv3 Copyright: 2022, Kovid Goyal, <kovid at kovidgoyal.net>

package cli

import (
	"fmt"
	"strings"
)

var _ = fmt.Print

func (self *Command) parse_args(ctx *Context, args []string) error {
	args_to_parse := make([]string, len(args))
	copy(args_to_parse, args)
	ctx.SeenCommands = append(ctx.SeenCommands, self)
	if self.IgnoreAllArgs {
		self.Args = args
		return nil
	}

	var expecting_arg_for *Option
	options_allowed := true

	consume_arg := func() string { ans := args_to_parse[0]; args_to_parse = args_to_parse[1:]; return ans }

	handle_option := func(opt_str string, has_val bool, opt_val string, val_not_allowed bool) error {
		possible_options := self.FindOptions(opt_str)
		var opt *Option
		if len(possible_options) == 1 {
			opt = possible_options[0]
			opt_str = opt.MatchingAlias(NormalizeOptionName(opt_str), !strings.HasPrefix(opt_str, "--"))
		} else if len(possible_options) == 0 {
			possibles := self.SuggestionsForOption(opt_str, 2)
			if len(possibles) > 0 {
				return &ParseError{Message: fmt.Sprintf("Unknown option: :yellow:`%s`. Did you mean:\n\t%s", opt_str, strings.Join(possibles, "\n\t"))}
			}
			return &ParseError{Message: fmt.Sprintf("Unknown option: :yellow:`%s`", opt_str)}
		} else {
			ambi := make([]string, len(possible_options))
			for i, o := range possible_options {
				ambi[i] = o.MatchingAlias(NormalizeOptionName(opt_str), !strings.HasPrefix(opt_str, "--"))
				if ambi[i] == opt_str {
					opt = o
					break
				}
			}
			if opt == nil {
				return &ParseError{Message: fmt.Sprintf("Ambiguous option: :yellow:`%s` could be any of: %s", opt_str, strings.Join(ambi, ", "))}
			}
		}
		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)}
		}
		if has_val {
			if !needs_arg {
				if opt.OptionType == BoolOption {
					if err := opt.add_value(opt_val); err != nil {
						return err
					}
				} else {
					return &ParseError{Message: fmt.Sprintf("The option: :yellow:`%s` does not take values", opt_str)}
				}
			}
			return opt.add_value(opt_val)
		} else if needs_arg {
			expecting_arg_for = opt
		} else {
			opt.add_value("")
		}
		return nil
	}

	for len(args_to_parse) > 0 {
		arg := consume_arg()

		if expecting_arg_for == nil {
			if options_allowed && strings.HasPrefix(arg, "-") && arg != "-" {
				// handle option arg
				if arg == "--" {
					options_allowed = false
					continue
				}
				opt_str, opt_val, has_val := strings.Cut(arg, "=")
				if strings.HasPrefix(opt_str, "--") {
					err := handle_option(opt_str, has_val, opt_val, false)
					if err != nil {
						return err
					}
				} else {
					runes := []rune(opt_str[1:])
					for i, sl := range runes {
						var err error
						if i == len(runes)-1 {
							err = handle_option("-"+string(sl), has_val, opt_val, false)
						} else {
							err = handle_option("-"+string(sl), false, "", true)
						}
						if err != nil {
							return err
						}
					}
				}
			} else {
				// handle non option arg
				if self.AllowOptionsAfterArgs <= len(self.Args) {
					options_allowed = false
				}
				if self.HasSubCommands() {
					possible_cmds := self.FindSubCommands(arg)
					if len(possible_cmds) == 1 {
						return possible_cmds[0].parse_args(ctx, args_to_parse)
					}
					if !self.SubCommandIsOptional {
						if len(possible_cmds) == 0 {
							possibles := self.SuggestionsForCommand(arg, 2)
							if len(possibles) > 0 {
								return &ParseError{Message: fmt.Sprintf("Unknown subcommand: :yellow:`%s`. Did you mean:\n\t%s", arg, strings.Join(possibles, "\n\t"))}
							}
							return &ParseError{Message: fmt.Sprintf(":yellow:`%s` is not a known subcommand for :emph:`%s`. Use --help to get a list of valid subcommands.", arg, self.Name)}
						}
						cn := make([]string, len(possible_cmds))
						for i, x := range possible_cmds {
							cn[i] = x.Name
						}
						return &ParseError{Message: fmt.Sprintf(
							":yellow:`%s` is not a known subcommand for :emph:`%s`. Did you mean:\n\t%s", arg, self.Name, strings.Join(cn, "\n\t"))}
					}
				}
				self.Args = append(self.Args, arg)
			}
		} else {
			// handle option value
			err := expecting_arg_for.add_value(arg)
			if err != nil {
				return err
			}
			expecting_arg_for = nil
		}
	}
	return nil
}