tabry 0.2.2 → 0.2.4
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/bin/tabry-fish +9 -0
- data/bin/tabry-generate-fish-complete +31 -0
- data/lib/tabry/cli/all_in_one.rb +27 -6
- data/lib/tabry/config_builder/arg_or_flag_builder.rb +5 -0
- data/lib/tabry/config_builder/top_level_builder.rb +3 -0
- data/lib/tabry/machine.rb +1 -1
- data/lib/tabry/models/const_option.rb +1 -1
- data/lib/tabry/models/flags_list.rb +6 -2
- data/lib/tabry/models/include_option.rb +2 -2
- data/lib/tabry/models/method_option.rb +14 -0
- data/lib/tabry/models/option.rb +5 -5
- data/lib/tabry/models/options_list.rb +2 -2
- data/lib/tabry/models/shell_option.rb +1 -1
- data/lib/tabry/models/subs_list.rb +13 -2
- data/lib/tabry/options_finder.rb +13 -8
- data/lib/tabry/replty/base.rb +23 -0
- data/lib/tabry/replty/builder.rb +100 -0
- data/lib/tabry/runner.rb +2 -2
- data/lib/tabry/shell_tokenizer.rb +50 -0
- data/lib/tabry/shells/bash/wrapper.rb +2 -2
- data/lib/tabry/shells/fish/wrapper.rb +51 -0
- data/lib/tabry/shells/fish.rb +82 -0
- data/lib/tabry/version.rb +3 -0
- data/sh/fish/README.md +2 -1
- data/sh/fish/tabry_fish.fish +17 -11
- data/spec/fixtures/config_builder/underscoresdashes.yml +1 -1
- data/spec/fixtures/vehicles-expectations.json +351 -0
- data/spec/tabry/cli/all_in_one_spec.rb +12 -0
- data/spec/tabry/machine_spec.rb +5 -82
- data/spec/tabry/shell_tokenizer_spec.rb +34 -0
- data/tabry.gemspec +2 -4
- 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 +16 -5
- data/lib/tabry/shell_splitter.rb +0 -34
- data/spec/tabry/shell_splitter_spec.rb +0 -22
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Used to generate a tab-completion function for a Tabry CLI using absolute
|
4
|
+
# paths to the tabry-bash script in this repo and to the Tabry JSON/YML file.
|
5
|
+
# Using uniquely-named tab-completion functions and absolute paths means you can
|
6
|
+
# have different Tabry-based CLIs using different versions of Tabry without any
|
7
|
+
# conflicts.
|
8
|
+
# See sh/bash/README.md and "Adding Tab Completion to your CLI" in main README
|
9
|
+
|
10
|
+
require "shellwords"
|
11
|
+
|
12
|
+
module Tabry
|
13
|
+
module Shells
|
14
|
+
module Fish
|
15
|
+
# NOTE! This code uses sh/fish/tabry_fish.fish and is described in
|
16
|
+
# sh/fish/README.md; see that README for more info
|
17
|
+
def self.generate(cmd_name, tabry_file_path, uniq_fn_id: nil)
|
18
|
+
generate_internal(
|
19
|
+
cmd_name: cmd_name,
|
20
|
+
import_path: File.expand_path(tabry_file_path),
|
21
|
+
tabry_fish_executable: File.expand_path("#{path_to_tabry}/bin/tabry-fish"),
|
22
|
+
tabry_fish_arg: nil,
|
23
|
+
uniq_fn_id: uniq_fn_id
|
24
|
+
)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Generate fish completion code that will run the currently running
|
28
|
+
# command ($0) with "completion" to get completion options. "cmd_name"
|
29
|
+
# is used to tell fish which command to make options for
|
30
|
+
def self.generate_self(cmd_name: nil)
|
31
|
+
cmd_name ||= File.basename($0)
|
32
|
+
generate_internal(
|
33
|
+
cmd_name: cmd_name,
|
34
|
+
import_path: "",
|
35
|
+
tabry_fish_executable: File.expand_path($0),
|
36
|
+
tabry_fish_arg: "completion",
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.path_to_tabry
|
41
|
+
File.expand_path("#{__dir__}/../../../")
|
42
|
+
end
|
43
|
+
|
44
|
+
def self.esc(s)
|
45
|
+
Shellwords.escape(s)
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.add_uniq_id(str, pattern, uniq_id)
|
49
|
+
str.gsub! pattern, "#{pattern}_#{uniq_id}"
|
50
|
+
end
|
51
|
+
|
52
|
+
def self.generate_internal(cmd_name:, import_path:, tabry_fish_executable:, tabry_fish_arg:, uniq_fn_id: nil)
|
53
|
+
# uniq_fn_id is added to the bash functions to ensure they are unique
|
54
|
+
# -- by default this is the capitalized command name
|
55
|
+
uniq_fn_id ||= cmd_name
|
56
|
+
uniq_fn_id = uniq_fn_id.upcase.gsub(/[^A-Z0-9_]/, "_")
|
57
|
+
script = File.read("#{__dir__}/../../../sh/fish/tabry_fish.fish")
|
58
|
+
|
59
|
+
add_uniq_id(script, "tabry_completion_init", uniq_fn_id)
|
60
|
+
add_uniq_id(script, "__fish_tabry_internal_invoke", uniq_fn_id)
|
61
|
+
add_uniq_id(script, "__fish_tabry_check_only_args", uniq_fn_id)
|
62
|
+
add_uniq_id(script, "__fish_tabry_check_only_file", uniq_fn_id)
|
63
|
+
add_uniq_id(script, "__fish_tabry_check_args_and_file", uniq_fn_id)
|
64
|
+
add_uniq_id(script, "__fish_tabry_completions", uniq_fn_id)
|
65
|
+
|
66
|
+
script.gsub! "# TABRY_IMPORT_PATH_REPLACE (DO NOT REMOVE)", "set TABRY_IMPORTS_PATH #{esc import_path}"
|
67
|
+
script.gsub! "# TABRY_EXECUTABLE_REPLACE (DO NOT REMOVE)", "set TABRY_EXECUTABLE #{esc tabry_fish_executable}"
|
68
|
+
if !tabry_fish_arg.nil?
|
69
|
+
script.gsub! "# TABRY_ARG_REPLACE (DO NOT REMOVE)", "set TABRY_ARG #{esc tabry_fish_arg}"
|
70
|
+
end
|
71
|
+
|
72
|
+
<<~END_FISH_CODE_TEMPLATE
|
73
|
+
# The following Autocomplete is for a Tabry-powered command. It was
|
74
|
+
# generated by the command itself. See the documentation located in
|
75
|
+
# #{esc path_to_tabry}/sh/fish/README.md
|
76
|
+
#{script}
|
77
|
+
tabry_completion_init_#{uniq_fn_id} #{esc cmd_name}
|
78
|
+
END_FISH_CODE_TEMPLATE
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
data/sh/fish/README.md
CHANGED
@@ -2,8 +2,9 @@ This directory includes fish completions for Tabry
|
|
2
2
|
|
3
3
|
There are two steps to add to your ~/.config/fish/config.fish to use:
|
4
4
|
|
5
|
-
1. Source
|
5
|
+
1. Source `tabry_fish.fish`
|
6
6
|
2. Add a call to `tabry_completion_init`, for each command
|
7
|
+
3. Ensure the `TABRY_IMPORTS_PATH` environment variable is defined and points to where your compiled tabry files are located.
|
7
8
|
|
8
9
|
```sh
|
9
10
|
source tabry_fish.fish
|
data/sh/fish/tabry_fish.fish
CHANGED
@@ -12,14 +12,19 @@ end
|
|
12
12
|
|
13
13
|
# return true if tabry only reports commands
|
14
14
|
function __fish_tabry_internal_invoke
|
15
|
-
|
16
|
-
|
15
|
+
# TABRY_IMPORT_PATH_REPLACE (DO NOT REMOVE)
|
16
|
+
# TABRY_EXECUTABLE_REPLACE (DO NOT REMOVE)
|
17
|
+
# TABRY_ARG_REPLACE (DO NOT REMOVE)
|
18
|
+
set -x TABRY_FISH_MODE true
|
17
19
|
|
18
20
|
# -C "Get cursor position"
|
19
21
|
set cursor_position (commandline -C)
|
20
22
|
set cmd (commandline)
|
21
|
-
|
22
|
-
|
23
|
+
|
24
|
+
set result ($TABRY_EXECUTABLE $TABRY_ARG "$cmd" "$cursor_position")
|
25
|
+
for arg in $result
|
26
|
+
echo $arg
|
27
|
+
end
|
23
28
|
end
|
24
29
|
|
25
30
|
# return true if tabry only reports file
|
@@ -29,7 +34,7 @@ function __fish_tabry_check_only_args
|
|
29
34
|
set args (echo "$result"|sed 's/ .*//')
|
30
35
|
set specials (echo "$result"|grep -o ' file')
|
31
36
|
|
32
|
-
# https://github.com/fish-shell/fish-shell/issues/5186#issuecomment-421244106
|
37
|
+
# https://github.com/fish-shell/fish-shell/issues/5186#issuecomment-421244106 (the random "x")
|
33
38
|
if test "x$args" != "x" -a "$specials" != " file"
|
34
39
|
# echo "confirming only args: [$result,$args,$specials]" 1>&2
|
35
40
|
return 0;
|
@@ -45,7 +50,8 @@ function __fish_tabry_check_only_file
|
|
45
50
|
|
46
51
|
set args (echo "$result"|sed 's/ .*//')
|
47
52
|
|
48
|
-
|
53
|
+
# The '--' arg is needed in case $result contains flag-like strings
|
54
|
+
if test "x$args" = "x" -a (string match -ra -- ' file' $result)
|
49
55
|
# echo "confirming only file" 1>&2
|
50
56
|
return 0;
|
51
57
|
else
|
@@ -59,7 +65,8 @@ function __fish_tabry_check_args_and_file
|
|
59
65
|
|
60
66
|
set args (echo "$result"|sed 's/ .*//')
|
61
67
|
|
62
|
-
|
68
|
+
# The '--' arg is needed in case $result contains flag-like strings
|
69
|
+
if test "x$args" != "x" -a (string match -ra -- ' file' $result)
|
63
70
|
# echo "confirming args and file" 1>&2
|
64
71
|
return 0;
|
65
72
|
else
|
@@ -73,15 +80,14 @@ function __fish_tabry_completions
|
|
73
80
|
|
74
81
|
set args (echo "$result"|sed 's/ .*//')
|
75
82
|
|
76
|
-
set args_parsed (string split ' ' $args)
|
77
|
-
|
78
83
|
if test "x$args" = "x"
|
79
84
|
# Don't offer anything if we don't have any completions
|
80
85
|
return 1;
|
81
86
|
else
|
82
|
-
# $
|
87
|
+
# $result will be something like: ["foo 'this is foo'" "bar" "baz" "" "file"]
|
83
88
|
# where "file" is special, since it's after the space.
|
84
|
-
for arg in $
|
89
|
+
for arg in $result
|
90
|
+
set clean_arg (echo "$arg"|sed 's/ .*//')
|
85
91
|
if test "x$arg" != "x"
|
86
92
|
echo "$arg"
|
87
93
|
else
|
@@ -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
|
+
}
|
@@ -3,6 +3,7 @@
|
|
3
3
|
require_relative "../../../lib/tabry/cli/all_in_one"
|
4
4
|
require_relative "../../../lib/tabry/shells/bash"
|
5
5
|
require_relative "../../../lib/tabry/shells/bash/wrapper"
|
6
|
+
require_relative "../../../lib/tabry/shells/fish"
|
6
7
|
|
7
8
|
module Tabry::Spec
|
8
9
|
module Cli
|
@@ -124,6 +125,17 @@ describe Tabry::CLI::AllInOne do
|
|
124
125
|
end
|
125
126
|
end
|
126
127
|
|
128
|
+
it "creates a #completion__fish method which generates completion" do
|
129
|
+
stub_const("ARGV", %w[completion fish])
|
130
|
+
expect(Tabry::Shells::Fish).to receive(:generate_self).with(cmd_name: "foo").and_return("fish completion stuff")
|
131
|
+
expect_any_instance_of(Kernel).to receive(:puts).with("fish completion stuff")
|
132
|
+
|
133
|
+
described_class.completion_only do
|
134
|
+
cmd :foo
|
135
|
+
sub :bar
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
127
139
|
it "creates a #completion method which generates options" do
|
128
140
|
stub_const("ARGV", ["completion", "cmd line", "6"])
|
129
141
|
expect(Tabry::Bash::Wrapper).to receive(:run) do |cmd_line, comp_point, config:|
|
data/spec/tabry/machine_spec.rb
CHANGED
@@ -15,94 +15,17 @@ describe Tabry::Machine do
|
|
15
15
|
expect(subject.args).to eq(arr)
|
16
16
|
end
|
17
17
|
|
18
|
-
examples = {
|
19
|
-
"handles subcommands" => [
|
20
|
-
%w[build],
|
21
|
-
subs: %w[build], mode: :subcommand
|
22
|
-
],
|
23
|
-
"handles bogus subcommands" => [
|
24
|
-
%w[bogus],
|
25
|
-
subs: [], args: %w[bogus]
|
26
|
-
],
|
27
|
-
"handles bogus subcommands of subcommands are treated as args" => [
|
28
|
-
%w[move bogus bogus2],
|
29
|
-
subs: %w[move], args: %w[bogus bogus2], mode: :subcommand
|
30
|
-
],
|
31
|
-
"handles subcommands of subcommands" => [
|
32
|
-
%w[move go],
|
33
|
-
subs: %w[move go],
|
34
|
-
],
|
35
|
-
"handles subcommands aliases" => [
|
36
|
-
%w[move g],
|
37
|
-
subs: %w[move go],
|
38
|
-
],
|
39
|
-
"handles argsuments" => [
|
40
|
-
%w[build arg1 arg2],
|
41
|
-
subs: %w[build], args: %w[arg1 arg2]
|
42
|
-
],
|
43
|
-
"handles flags" => [
|
44
|
-
%w[build -v],
|
45
|
-
subs: %w[build], flags: { "verbose" => true }, args: [], mode: :subcommand
|
46
|
-
],
|
47
|
-
"handles long flags" => [
|
48
|
-
%w[--verbose build],
|
49
|
-
subs: %w[build], flags: { "verbose" => true }, args: [], mode: :subcommand
|
50
|
-
],
|
51
|
-
"handles flags interspersed with arguments" => [
|
52
|
-
%w[move -v crash arg1 --dry-run arg2 arg3],
|
53
|
-
subs: %w[move crash], args: %w[arg1 arg2 arg3],
|
54
|
-
flags: { "dry-run" => true, "verbose" => true }, mode: :subcommand
|
55
|
-
],
|
56
|
-
"handles flags with an argument" => [
|
57
|
-
%w[move crash --dry-run arg1 -f file arg2 arg3],
|
58
|
-
subs: %w[move crash], args: %w[arg1 arg2 arg3],
|
59
|
-
flags: { "dry-run" => true, "output-to-file" => "file" }, mode: :subcommand
|
60
|
-
],
|
61
|
-
"handles double-dash to stop parsing of flags" => [
|
62
|
-
%w[move crash -- --dry-run arg1 -f file arg2 arg3],
|
63
|
-
subs: %w[move crash], args: %w[--dry-run arg1 -f file arg2 arg3], mode: :subcommand
|
64
|
-
],
|
65
|
-
"handles unknown long flags as args" => [
|
66
|
-
%w[move crash --notaflag arg2],
|
67
|
-
subs: %w[move crash], args: %w[--notaflag arg2], mode: :subcommand
|
68
|
-
],
|
69
|
-
"handles unknown short flags as args" => [
|
70
|
-
%w[move crash -x arg2 --dry-run],
|
71
|
-
subs: %w[move crash], args: %w[-x arg2], flags: { "dry-run" => true }, mode: :subcommand
|
72
|
-
],
|
73
|
-
"handles --help" => [
|
74
|
-
%w[move --help],
|
75
|
-
subs: %w[move], flags: {}, args: [], help: true, mode: :subcommand
|
76
|
-
],
|
77
|
-
"handles -?" => [
|
78
|
-
%w[move foo bar --help waz],
|
79
|
-
subs: %w[move], flags: {}, args: %w[foo bar waz], help: true
|
80
|
-
],
|
81
|
-
"ignores -?/--help after double-dash" => [
|
82
|
-
%w[move -- --help -? -- -],
|
83
|
-
subs: %w[move], flags: {}, args: %w[--help -? -- -], help: nil
|
84
|
-
],
|
85
|
-
"sets mode to flagarg when waiting for a flag argument" => [
|
86
|
-
%w[move crash --dry-run arg1 -f],
|
87
|
-
subs: %w[move crash], args: %w[arg1], flags: { "dry-run" => true },
|
88
|
-
mode: :flagarg, current_flag: "output-to-file"
|
89
|
-
],
|
90
|
-
# TODO: might want behavior to be different; currently I think if --verbose is before the
|
91
|
-
# subcommand it use's the main command's --verbose flag
|
92
|
-
"most specific sub's flag takes precedence in case of multiple matching" => [
|
93
|
-
%w[sub-with-mandatory-flag --verbose foo --mandatory abc],
|
94
|
-
subs: %w[sub-with-mandatory-flag],
|
95
|
-
flags: { "verbose" => "foo", "mandatory" => "abc" }
|
96
|
-
]
|
97
|
-
}
|
18
|
+
examples = JSON.parse(File.read("#{__dir__}/../fixtures/vehicles-expectations.json"))
|
98
19
|
|
99
20
|
examples.each do |test_name, (tokens, expectation)|
|
100
21
|
it test_name do
|
101
22
|
pending unless expectation
|
102
23
|
result = described_class.run(config, tokens)
|
103
24
|
expectation.each do |key, val|
|
104
|
-
key =
|
105
|
-
|
25
|
+
key = 'subcommand_stack' if key == 'subs'
|
26
|
+
res = result.send(key.to_sym)
|
27
|
+
res = res.to_s if res.is_a?(Symbol)
|
28
|
+
expect(res).to eq(val)
|
106
29
|
end
|
107
30
|
end
|
108
31
|
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "../../lib/tabry/shell_tokenizer"
|
4
|
+
|
5
|
+
describe Tabry::ShellTokenizer do
|
6
|
+
describe ".split_with_comppoint" do
|
7
|
+
it "returns the command basename, argument, and last argument" do
|
8
|
+
str = "foo/bar abc def ghi"
|
9
|
+
expect(described_class.split_with_comppoint(str, 13)).to eq(["bar", ["abc"], "def"])
|
10
|
+
end
|
11
|
+
|
12
|
+
it "returns [] as arguments and the last argument as non-nil when there is only one arg" do
|
13
|
+
expect(described_class.split_with_comppoint("foo bar", 5)).to eq(["foo", [], "bar"])
|
14
|
+
end
|
15
|
+
|
16
|
+
it "handles quotes and backslashes like a shell" do
|
17
|
+
expect(described_class.split_with_comppoint('"/foo bar/waz" a\'b \'c\\ d "ef g" "hi jk" lmn', 38)).to eq(
|
18
|
+
[
|
19
|
+
"waz",
|
20
|
+
["ab c d", "ef g"],
|
21
|
+
"hi jk"
|
22
|
+
]
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "supports tokens with argument (tab on command), yielding the last arg" do
|
27
|
+
expect(described_class.split_with_comppoint("abc", 2)).to eq([nil, [], "abc"])
|
28
|
+
end
|
29
|
+
|
30
|
+
it "supports empty strings" do
|
31
|
+
expect(described_class.split_with_comppoint("", 0)).to eq([nil, [], ""])
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|