tabry 0.2.2 → 0.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/tabry/config_builder/arg_or_flag_builder.rb +5 -0
- data/lib/tabry/config_builder/top_level_builder.rb +2 -0
- data/lib/tabry/machine.rb +1 -1
- data/lib/tabry/models/method_option.rb +14 -0
- data/lib/tabry/models/option.rb +5 -5
- data/lib/tabry/replty/base.rb +23 -0
- data/lib/tabry/replty/builder.rb +100 -0
- data/lib/tabry/shell_tokenizer.rb +50 -0
- data/lib/tabry/shells/bash/wrapper.rb +2 -2
- data/sh/fish/tabry_fish.fish +7 -4
- data/spec/fixtures/config_builder/underscoresdashes.yml +1 -1
- data/spec/fixtures/vehicles-expectations.json +351 -0
- data/spec/tabry/machine_spec.rb +5 -82
- data/spec/tabry/shell_tokenizer_spec.rb +34 -0
- data/tabry.gemspec +1 -1
- data/treesitter/corpus/opts.txt +2 -0
- data/treesitter/grammar.js +7 -0
- data/treesitter/src/grammar.json +21 -0
- data/treesitter/src/node-types.json +23 -0
- data/treesitter/src/parser.c +959 -893
- data/treesitter/tabry-compile.js +6 -0
- metadata +9 -5
- data/lib/tabry/shell_splitter.rb +0 -34
- data/spec/tabry/shell_splitter_spec.rb +0 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 53cb186e419ee3ddbecddaf09ac32b39d93d5668c58fd81bc8d08ff16e1b168d
|
4
|
+
data.tar.gz: c5d7fa4e944df15ff727eb0a9d38e2550da6988bdcf83f2dbaa743252f3c9669
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1da3914992fd9ca409800f0183992fe6a742f0d55f04573e20c119cace1b00538294113c675a8281ff881703cbb13fb5a1802d4d164d9343dd4d5543cd9f2ca5
|
7
|
+
data.tar.gz: 6a9797aaf76219131bcb3a6faa780ebedd0cd7971ad98f91e0d9f56d3d3302f4c29b62c9c4db0025fea4a1fea9047a40e54bf55400d2de1a7837915ddb7edc06
|
@@ -21,6 +21,11 @@ module Tabry
|
|
21
21
|
[{ "type" => "shell", "value" => cmd.to_s }]
|
22
22
|
end
|
23
23
|
|
24
|
+
# maybe I should rename this ... and include... ?
|
25
|
+
def method(met_name)
|
26
|
+
[{ "type" => "method", "value" => met_name.to_s }]
|
27
|
+
end
|
28
|
+
|
24
29
|
def file
|
25
30
|
[{ "type" => "file" }]
|
26
31
|
end
|
@@ -10,11 +10,13 @@ module Tabry
|
|
10
10
|
|
11
11
|
def defargs(name, &blk)
|
12
12
|
name = name.to_s.gsub(/^@/, "")
|
13
|
+
name = name.gsub("_", "-") if _opts[:names_underscores_to_dashes]
|
13
14
|
_set_hash :arg_includes, name, _build(SubBuilder, &blk)
|
14
15
|
end
|
15
16
|
|
16
17
|
def defopts(name, &blk)
|
17
18
|
name = name.to_s.gsub(/^@/, "")
|
19
|
+
name = name.gsub("_", "-") if _opts[:names_underscores_to_dashes]
|
18
20
|
_set_hash :option_includes, name, _build(ArgOrFlagBuilder, &blk)["options"]
|
19
21
|
end
|
20
22
|
|
data/lib/tabry/machine.rb
CHANGED
@@ -20,7 +20,7 @@ module Tabry
|
|
20
20
|
|
21
21
|
def initialize(config)
|
22
22
|
@config = config
|
23
|
-
@state = State.new(mode: :subcommand, subcommand_stack: [], args: [], flags: {})
|
23
|
+
@state = State.new(mode: :subcommand, subcommand_stack: [], args: [], flags: {}, help: false, dashdash: false)
|
24
24
|
end
|
25
25
|
|
26
26
|
def run(tokens)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "option_base"
|
4
|
+
|
5
|
+
module Tabry
|
6
|
+
module Models
|
7
|
+
class MethodOption < OptionBase
|
8
|
+
# TODO: Handled upstream for now, could change later.
|
9
|
+
def options(_token)
|
10
|
+
[value.to_sym]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/lib/tabry/models/option.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require_relative "config_error"
|
4
|
-
|
5
|
-
require_relative "
|
6
|
-
|
7
|
-
require_relative "file_option"
|
8
|
-
require_relative "dir_option"
|
4
|
+
%w[const shell include file dir method].each do |type|
|
5
|
+
require_relative "#{type}_option"
|
6
|
+
end
|
9
7
|
|
10
8
|
module Tabry
|
11
9
|
module Models
|
@@ -18,6 +16,8 @@ module Tabry
|
|
18
16
|
ConstOption.new(**args)
|
19
17
|
when "shell"
|
20
18
|
ShellOption.new(**args)
|
19
|
+
when "method"
|
20
|
+
MethodOption.new(**args)
|
21
21
|
when "include"
|
22
22
|
IncludeOption.new(**args)
|
23
23
|
when "file"
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../cli/arg_proxy" # TODO: shared code now, probably move out of cli dir
|
4
|
+
require_relative "../cli/flag_proxy" # TODO: shared code now, probably move out of cli dir
|
5
|
+
|
6
|
+
module Tabry
|
7
|
+
module Replty
|
8
|
+
class Base
|
9
|
+
attr_reader :internals, :args, :flags
|
10
|
+
|
11
|
+
def internals=(internals)
|
12
|
+
@internals = internals
|
13
|
+
@args = ::Tabry::CLI::ArgProxy.new(internals.state.args, internals.result.named_args)
|
14
|
+
@flags = ::Tabry::CLI::FlagProxy.new(internals.state.flags)
|
15
|
+
end
|
16
|
+
|
17
|
+
# NOTE: internal functions (besides internal/args/flags) start with "tabry_"; reconsider scheme
|
18
|
+
def tabry_prompt
|
19
|
+
"> "
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "readline"
|
4
|
+
require_relative "../cli/internals" # TODO: bring out of cli if shared code now, probably
|
5
|
+
require_relative "../shell_tokenizer"
|
6
|
+
require_relative "../runner"
|
7
|
+
|
8
|
+
module Tabry
|
9
|
+
module Replty
|
10
|
+
class Builder
|
11
|
+
attr_reader :runner, :repl
|
12
|
+
|
13
|
+
# TODO: implement before_action, after_action, sub-REPLs; share code in run_result with CLI::Builder
|
14
|
+
|
15
|
+
def initialize(config, repl)
|
16
|
+
@runner = Tabry::Runner.new(config: config)
|
17
|
+
@repl = repl
|
18
|
+
end
|
19
|
+
|
20
|
+
def readline
|
21
|
+
Readline.readline(repl.tabry_prompt, true)
|
22
|
+
rescue Interrupt
|
23
|
+
puts "^C"
|
24
|
+
retry
|
25
|
+
end
|
26
|
+
|
27
|
+
def load_history(file)
|
28
|
+
return unless file
|
29
|
+
|
30
|
+
File.open(file) do |f|
|
31
|
+
f.each do |l|
|
32
|
+
Readline::HISTORY.push l.chomp
|
33
|
+
end
|
34
|
+
end
|
35
|
+
rescue Errno::ENOENT # rubocop:disable Lint/SuppressedException
|
36
|
+
end
|
37
|
+
|
38
|
+
def save_history(file, max_size = 10000)
|
39
|
+
return unless file
|
40
|
+
|
41
|
+
history = Readline::HISTORY.each
|
42
|
+
|
43
|
+
# Skip over excess elements:
|
44
|
+
(Readline::HISTORY.length - max_size).times do
|
45
|
+
history.next
|
46
|
+
end
|
47
|
+
|
48
|
+
File.open(file, "w") do |f|
|
49
|
+
loop { f.puts history.next }
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def run(history_file: nil, tokenizer: ShellTokenizer)
|
54
|
+
history_file = history_file&.gsub(%r{^~/}, "#{Dir.home}/")
|
55
|
+
load_history(history_file)
|
56
|
+
|
57
|
+
Readline.completion_proc = proc do
|
58
|
+
cmd, args, last_arg = tokenizer.split_with_comppoint(Readline.line_buffer, Readline.point)
|
59
|
+
options = runner.options([cmd, *args].compact, last_arg)
|
60
|
+
options.map do |opt|
|
61
|
+
# if opt is a symbol, it's a "opts method" option type -- where a REPL method returns the options.
|
62
|
+
# TODO: a bit weird since the REPL's "args" and "flags" are wrong/old when that completion method is run
|
63
|
+
# also filter to start_with? is more appropriate in tabry, not here, but whatever
|
64
|
+
opt.is_a?(Symbol) ? repl.send(opt)&.select { |x| x.start_with?(last_arg) } : opt
|
65
|
+
end.flatten
|
66
|
+
end
|
67
|
+
|
68
|
+
while (cmdline = readline)
|
69
|
+
raw_args = tokenizer.split(cmdline)
|
70
|
+
next if raw_args.empty?
|
71
|
+
|
72
|
+
result = runner.parse(raw_args)
|
73
|
+
# TODO: usage has "myrepl" (command name) in; also, "got 1 args" is confusing when command doesn't exist
|
74
|
+
if result.help?
|
75
|
+
puts result.usage
|
76
|
+
elsif result.invalid_usage_reason
|
77
|
+
puts "Invalid usage: #{result.invalid_usage_reason}"
|
78
|
+
puts
|
79
|
+
puts result.usage
|
80
|
+
else
|
81
|
+
run_result(raw_args, result)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
save_history(history_file)
|
86
|
+
end
|
87
|
+
|
88
|
+
def run_result(raw_args, result)
|
89
|
+
met = result.state.subcommand_stack.join("__").gsub("-", "_")
|
90
|
+
repl.internals = ::Tabry::CLI::Internals.new(
|
91
|
+
runner: runner, raw_args: raw_args,
|
92
|
+
state: result.state, met: met, result: result
|
93
|
+
)
|
94
|
+
catch(:tabry_replty_command_exit) do
|
95
|
+
repl.send(met.to_sym)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "shellwords"
|
4
|
+
|
5
|
+
# Code to tokenize whole command lines into command and argument
|
6
|
+
# This may be adjustable in some cases
|
7
|
+
module Tabry
|
8
|
+
module ShellTokenizer
|
9
|
+
module_function
|
10
|
+
|
11
|
+
COMP_POINT_SENTINEL = "\uFFFF"
|
12
|
+
|
13
|
+
# Use Shellwords.split() to split a command line + comp point (index of the
|
14
|
+
# cursor in the command line) into the args up to the current token plus
|
15
|
+
# current token
|
16
|
+
#
|
17
|
+
# Returns [cmd_name, args, last_arg]
|
18
|
+
# cmd_name is the basename of the command run
|
19
|
+
#
|
20
|
+
def split_with_comppoint(cmd_line, comp_point)
|
21
|
+
# TODO: in weird scenarios this acts weird: namely, special shell operators like <(ls), $$((1 + 1))
|
22
|
+
# Also it crashed on unbalanced quotes, like: foo "bar<TAB>
|
23
|
+
# however, this will handle the common scenarios of escaping with quotes, single quotes, and backslashes
|
24
|
+
# Split up args and put the argument that comp_point is in in the `last_arg` variable.
|
25
|
+
# Just cutting off everything after comp_point might have worked, although
|
26
|
+
# maybe we wanted the whole arg? Not sure this is the best.
|
27
|
+
cmd_line = cmd_line.dup
|
28
|
+
cmd_line[comp_point.to_i...comp_point.to_i] = COMP_POINT_SENTINEL
|
29
|
+
all_tokens = Shellwords.split(cmd_line)
|
30
|
+
|
31
|
+
# ignore all tokens after the one with the sentinel, then replace the COMP_POINT_SENTINEL.
|
32
|
+
# the last token is now the token which the cursor is on (the entire token, not just before the cursor)
|
33
|
+
last_arg_index = all_tokens.index { |arg| arg.include?(COMP_POINT_SENTINEL) }
|
34
|
+
all_tokens = all_tokens[0..last_arg_index]
|
35
|
+
all_tokens.last.gsub! COMP_POINT_SENTINEL, ""
|
36
|
+
|
37
|
+
# take last_arg first -- will always be non-null (it will be empty if input string is empty).
|
38
|
+
last_arg = all_tokens.pop
|
39
|
+
cmd = all_tokens.shift
|
40
|
+
|
41
|
+
cmd_name = cmd&.gsub(%r{.*/}, "")
|
42
|
+
|
43
|
+
[cmd_name, all_tokens, last_arg]
|
44
|
+
end
|
45
|
+
|
46
|
+
def split(str)
|
47
|
+
Shellwords.split(str)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -4,14 +4,14 @@ require "shellwords"
|
|
4
4
|
require "yaml"
|
5
5
|
require_relative "../../util"
|
6
6
|
require_relative "../../runner"
|
7
|
-
require_relative "../../
|
7
|
+
require_relative "../../shell_tokenizer"
|
8
8
|
|
9
9
|
# Bash-specific entrypoint, taking COMP_WORDS and COMP_CWORDS and returning possible options
|
10
10
|
module Tabry
|
11
11
|
module Bash
|
12
12
|
module Wrapper
|
13
13
|
def self.run(cmd_line, comp_point, config: nil)
|
14
|
-
cmd_name, args, last_arg = Tabry::
|
14
|
+
cmd_name, args, last_arg = Tabry::ShellTokenizer.split_with_comppoint(cmd_line, comp_point)
|
15
15
|
opts = Tabry::Runner.new(config: config || cmd_name).options(args, last_arg)
|
16
16
|
|
17
17
|
if Tabry::Util.debug?
|
data/sh/fish/tabry_fish.fish
CHANGED
@@ -29,7 +29,7 @@ function __fish_tabry_check_only_args
|
|
29
29
|
set args (echo "$result"|sed 's/ .*//')
|
30
30
|
set specials (echo "$result"|grep -o ' file')
|
31
31
|
|
32
|
-
# https://github.com/fish-shell/fish-shell/issues/5186#issuecomment-421244106
|
32
|
+
# https://github.com/fish-shell/fish-shell/issues/5186#issuecomment-421244106 (the random "x")
|
33
33
|
if test "x$args" != "x" -a "$specials" != " file"
|
34
34
|
# echo "confirming only args: [$result,$args,$specials]" 1>&2
|
35
35
|
return 0;
|
@@ -45,7 +45,8 @@ function __fish_tabry_check_only_file
|
|
45
45
|
|
46
46
|
set args (echo "$result"|sed 's/ .*//')
|
47
47
|
|
48
|
-
|
48
|
+
# The '--' arg is needed in case $result contains flag-like strings
|
49
|
+
if test "x$args" = "x" -a (string match -ra -- ' file' $result)
|
49
50
|
# echo "confirming only file" 1>&2
|
50
51
|
return 0;
|
51
52
|
else
|
@@ -59,7 +60,8 @@ function __fish_tabry_check_args_and_file
|
|
59
60
|
|
60
61
|
set args (echo "$result"|sed 's/ .*//')
|
61
62
|
|
62
|
-
|
63
|
+
# The '--' arg is needed in case $result contains flag-like strings
|
64
|
+
if test "x$args" != "x" -a (string match -ra -- ' file' $result)
|
63
65
|
# echo "confirming args and file" 1>&2
|
64
66
|
return 0;
|
65
67
|
else
|
@@ -73,7 +75,8 @@ function __fish_tabry_completions
|
|
73
75
|
|
74
76
|
set args (echo "$result"|sed 's/ .*//')
|
75
77
|
|
76
|
-
|
78
|
+
# The '--' arg is needed in case $result contains flag-like strings
|
79
|
+
set args_parsed (string split -- ' ' $args)
|
77
80
|
|
78
81
|
if test "x$args" = "x"
|
79
82
|
# Don't offer anything if we don't have any completions
|
@@ -0,0 +1,351 @@
|
|
1
|
+
{
|
2
|
+
"handles subcommands": [
|
3
|
+
[
|
4
|
+
"build"
|
5
|
+
],
|
6
|
+
{
|
7
|
+
"subs": [
|
8
|
+
"build"
|
9
|
+
],
|
10
|
+
"mode": "subcommand"
|
11
|
+
}
|
12
|
+
],
|
13
|
+
"handles bogus subcommands": [
|
14
|
+
[
|
15
|
+
"bogus"
|
16
|
+
],
|
17
|
+
{
|
18
|
+
"subs": [
|
19
|
+
|
20
|
+
],
|
21
|
+
"args": [
|
22
|
+
"bogus"
|
23
|
+
]
|
24
|
+
}
|
25
|
+
],
|
26
|
+
"handles bogus subcommands of subcommands are treated as args": [
|
27
|
+
[
|
28
|
+
"move",
|
29
|
+
"bogus",
|
30
|
+
"bogus2"
|
31
|
+
],
|
32
|
+
{
|
33
|
+
"subs": [
|
34
|
+
"move"
|
35
|
+
],
|
36
|
+
"args": [
|
37
|
+
"bogus",
|
38
|
+
"bogus2"
|
39
|
+
],
|
40
|
+
"mode": "subcommand"
|
41
|
+
}
|
42
|
+
],
|
43
|
+
"handles subcommands of subcommands": [
|
44
|
+
[
|
45
|
+
"move",
|
46
|
+
"go"
|
47
|
+
],
|
48
|
+
{
|
49
|
+
"subs": [
|
50
|
+
"move",
|
51
|
+
"go"
|
52
|
+
]
|
53
|
+
}
|
54
|
+
],
|
55
|
+
"handles subcommands aliases": [
|
56
|
+
[
|
57
|
+
"move",
|
58
|
+
"g"
|
59
|
+
],
|
60
|
+
{
|
61
|
+
"subs": [
|
62
|
+
"move",
|
63
|
+
"go"
|
64
|
+
]
|
65
|
+
}
|
66
|
+
],
|
67
|
+
"handles argsuments": [
|
68
|
+
[
|
69
|
+
"build",
|
70
|
+
"arg1",
|
71
|
+
"arg2"
|
72
|
+
],
|
73
|
+
{
|
74
|
+
"subs": [
|
75
|
+
"build"
|
76
|
+
],
|
77
|
+
"args": [
|
78
|
+
"arg1",
|
79
|
+
"arg2"
|
80
|
+
]
|
81
|
+
}
|
82
|
+
],
|
83
|
+
"handles flags": [
|
84
|
+
[
|
85
|
+
"build",
|
86
|
+
"-v"
|
87
|
+
],
|
88
|
+
{
|
89
|
+
"subs": [
|
90
|
+
"build"
|
91
|
+
],
|
92
|
+
"flags": {
|
93
|
+
"verbose": true
|
94
|
+
},
|
95
|
+
"args": [
|
96
|
+
|
97
|
+
],
|
98
|
+
"mode": "subcommand"
|
99
|
+
}
|
100
|
+
],
|
101
|
+
"handles long flags": [
|
102
|
+
[
|
103
|
+
"--verbose",
|
104
|
+
"build"
|
105
|
+
],
|
106
|
+
{
|
107
|
+
"subs": [
|
108
|
+
"build"
|
109
|
+
],
|
110
|
+
"flags": {
|
111
|
+
"verbose": true
|
112
|
+
},
|
113
|
+
"args": [
|
114
|
+
|
115
|
+
],
|
116
|
+
"mode": "subcommand"
|
117
|
+
}
|
118
|
+
],
|
119
|
+
"handles flags interspersed with arguments": [
|
120
|
+
[
|
121
|
+
"move",
|
122
|
+
"-v",
|
123
|
+
"crash",
|
124
|
+
"arg1",
|
125
|
+
"--dry-run",
|
126
|
+
"arg2",
|
127
|
+
"arg3"
|
128
|
+
],
|
129
|
+
{
|
130
|
+
"subs": [
|
131
|
+
"move",
|
132
|
+
"crash"
|
133
|
+
],
|
134
|
+
"args": [
|
135
|
+
"arg1",
|
136
|
+
"arg2",
|
137
|
+
"arg3"
|
138
|
+
],
|
139
|
+
"flags": {
|
140
|
+
"dry-run": true,
|
141
|
+
"verbose": true
|
142
|
+
},
|
143
|
+
"mode": "subcommand"
|
144
|
+
}
|
145
|
+
],
|
146
|
+
"handles flags with an argument": [
|
147
|
+
[
|
148
|
+
"move",
|
149
|
+
"crash",
|
150
|
+
"--dry-run",
|
151
|
+
"arg1",
|
152
|
+
"-f",
|
153
|
+
"file",
|
154
|
+
"arg2",
|
155
|
+
"arg3"
|
156
|
+
],
|
157
|
+
{
|
158
|
+
"subs": [
|
159
|
+
"move",
|
160
|
+
"crash"
|
161
|
+
],
|
162
|
+
"args": [
|
163
|
+
"arg1",
|
164
|
+
"arg2",
|
165
|
+
"arg3"
|
166
|
+
],
|
167
|
+
"flags": {
|
168
|
+
"dry-run": true,
|
169
|
+
"output-to-file": "file"
|
170
|
+
},
|
171
|
+
"mode": "subcommand"
|
172
|
+
}
|
173
|
+
],
|
174
|
+
"handles double-dash to stop parsing of flags": [
|
175
|
+
[
|
176
|
+
"move",
|
177
|
+
"crash",
|
178
|
+
"--",
|
179
|
+
"--dry-run",
|
180
|
+
"arg1",
|
181
|
+
"-f",
|
182
|
+
"file",
|
183
|
+
"arg2",
|
184
|
+
"arg3"
|
185
|
+
],
|
186
|
+
{
|
187
|
+
"subs": [
|
188
|
+
"move",
|
189
|
+
"crash"
|
190
|
+
],
|
191
|
+
"args": [
|
192
|
+
"--dry-run",
|
193
|
+
"arg1",
|
194
|
+
"-f",
|
195
|
+
"file",
|
196
|
+
"arg2",
|
197
|
+
"arg3"
|
198
|
+
],
|
199
|
+
"mode": "subcommand",
|
200
|
+
"dashdash": true
|
201
|
+
}
|
202
|
+
],
|
203
|
+
"handles unknown long flags as args": [
|
204
|
+
[
|
205
|
+
"move",
|
206
|
+
"crash",
|
207
|
+
"--notaflag",
|
208
|
+
"arg2"
|
209
|
+
],
|
210
|
+
{
|
211
|
+
"subs": [
|
212
|
+
"move",
|
213
|
+
"crash"
|
214
|
+
],
|
215
|
+
"args": [
|
216
|
+
"--notaflag",
|
217
|
+
"arg2"
|
218
|
+
],
|
219
|
+
"mode": "subcommand"
|
220
|
+
}
|
221
|
+
],
|
222
|
+
"handles unknown short flags as args": [
|
223
|
+
[
|
224
|
+
"move",
|
225
|
+
"crash",
|
226
|
+
"-x",
|
227
|
+
"arg2",
|
228
|
+
"--dry-run"
|
229
|
+
],
|
230
|
+
{
|
231
|
+
"subs": [
|
232
|
+
"move",
|
233
|
+
"crash"
|
234
|
+
],
|
235
|
+
"args": [
|
236
|
+
"-x",
|
237
|
+
"arg2"
|
238
|
+
],
|
239
|
+
"flags": {
|
240
|
+
"dry-run": true
|
241
|
+
},
|
242
|
+
"mode": "subcommand"
|
243
|
+
}
|
244
|
+
],
|
245
|
+
"handles --help": [
|
246
|
+
[
|
247
|
+
"move",
|
248
|
+
"--help"
|
249
|
+
],
|
250
|
+
{
|
251
|
+
"subs": [
|
252
|
+
"move"
|
253
|
+
],
|
254
|
+
"flags": {
|
255
|
+
},
|
256
|
+
"args": [
|
257
|
+
|
258
|
+
],
|
259
|
+
"help": true,
|
260
|
+
"mode": "subcommand"
|
261
|
+
}
|
262
|
+
],
|
263
|
+
"handles -?": [
|
264
|
+
[
|
265
|
+
"move",
|
266
|
+
"foo",
|
267
|
+
"bar",
|
268
|
+
"--help",
|
269
|
+
"waz"
|
270
|
+
],
|
271
|
+
{
|
272
|
+
"subs": [
|
273
|
+
"move"
|
274
|
+
],
|
275
|
+
"flags": {
|
276
|
+
},
|
277
|
+
"args": [
|
278
|
+
"foo",
|
279
|
+
"bar",
|
280
|
+
"waz"
|
281
|
+
],
|
282
|
+
"help": true
|
283
|
+
}
|
284
|
+
],
|
285
|
+
"ignores -?/--help after double-dash": [
|
286
|
+
[
|
287
|
+
"move",
|
288
|
+
"--",
|
289
|
+
"--help",
|
290
|
+
"-?",
|
291
|
+
"--",
|
292
|
+
"-"
|
293
|
+
],
|
294
|
+
{
|
295
|
+
"subs": [
|
296
|
+
"move"
|
297
|
+
],
|
298
|
+
"flags": {
|
299
|
+
},
|
300
|
+
"args": [
|
301
|
+
"--help",
|
302
|
+
"-?",
|
303
|
+
"--",
|
304
|
+
"-"
|
305
|
+
],
|
306
|
+
"help": false,
|
307
|
+
"dashdash": true
|
308
|
+
}
|
309
|
+
],
|
310
|
+
"sets mode to flagarg when waiting for a flag argument": [
|
311
|
+
[
|
312
|
+
"move",
|
313
|
+
"crash",
|
314
|
+
"--dry-run",
|
315
|
+
"arg1",
|
316
|
+
"-f"
|
317
|
+
],
|
318
|
+
{
|
319
|
+
"subs": [
|
320
|
+
"move",
|
321
|
+
"crash"
|
322
|
+
],
|
323
|
+
"args": [
|
324
|
+
"arg1"
|
325
|
+
],
|
326
|
+
"flags": {
|
327
|
+
"dry-run": true
|
328
|
+
},
|
329
|
+
"mode": "flagarg",
|
330
|
+
"current_flag": "output-to-file"
|
331
|
+
}
|
332
|
+
],
|
333
|
+
"most specific sub's flag takes precedence in case of multiple matching": [
|
334
|
+
[
|
335
|
+
"sub-with-mandatory-flag",
|
336
|
+
"--verbose",
|
337
|
+
"foo",
|
338
|
+
"--mandatory",
|
339
|
+
"abc"
|
340
|
+
],
|
341
|
+
{
|
342
|
+
"subs": [
|
343
|
+
"sub-with-mandatory-flag"
|
344
|
+
],
|
345
|
+
"flags": {
|
346
|
+
"verbose": "foo",
|
347
|
+
"mandatory": "abc"
|
348
|
+
}
|
349
|
+
}
|
350
|
+
]
|
351
|
+
}
|