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
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
|
data/tabry.gemspec
CHANGED
@@ -6,7 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "tabry"
|
9
|
-
s.version = "0.2.
|
9
|
+
s.version = "0.2.3"
|
10
10
|
s.summary = "Tab completion and CLIs extraordinaire"
|
11
11
|
s.authors = ["Evan Battaglia"]
|
12
12
|
s.email = "battaglia.evan@gmail.com"
|
data/treesitter/corpus/opts.txt
CHANGED
@@ -5,6 +5,7 @@ Options
|
|
5
5
|
arg {
|
6
6
|
opts const a
|
7
7
|
opts shell "ls"
|
8
|
+
opts method foo
|
8
9
|
opts file
|
9
10
|
opts dir
|
10
11
|
}
|
@@ -17,5 +18,6 @@ arg {
|
|
17
18
|
(block
|
18
19
|
(opts_const_statement (string))
|
19
20
|
(opts_shell_statement (string))
|
21
|
+
(opts_method_statement (string))
|
20
22
|
(opts_file_statement)
|
21
23
|
(opts_dir_statement))))
|
data/treesitter/grammar.js
CHANGED
@@ -27,6 +27,7 @@ module.exports = grammar({
|
|
27
27
|
$._common_statement,
|
28
28
|
$.opts_const_statement,
|
29
29
|
$.opts_shell_statement,
|
30
|
+
$.opts_method_statement,
|
30
31
|
$.opts_file_statement,
|
31
32
|
$.opts_dir_statement,
|
32
33
|
$.title_statement,
|
@@ -121,6 +122,12 @@ module.exports = grammar({
|
|
121
122
|
'dir',
|
122
123
|
),
|
123
124
|
|
125
|
+
opts_method_statement: $ => seq(
|
126
|
+
'opts',
|
127
|
+
'method',
|
128
|
+
$.string
|
129
|
+
),
|
130
|
+
|
124
131
|
flagarg_statement: $ => seq(
|
125
132
|
optional($.flag_modifier),
|
126
133
|
'flagarg',
|
data/treesitter/src/grammar.json
CHANGED
@@ -77,6 +77,10 @@
|
|
77
77
|
"type": "SYMBOL",
|
78
78
|
"name": "opts_shell_statement"
|
79
79
|
},
|
80
|
+
{
|
81
|
+
"type": "SYMBOL",
|
82
|
+
"name": "opts_method_statement"
|
83
|
+
},
|
80
84
|
{
|
81
85
|
"type": "SYMBOL",
|
82
86
|
"name": "opts_file_statement"
|
@@ -389,6 +393,23 @@
|
|
389
393
|
}
|
390
394
|
]
|
391
395
|
},
|
396
|
+
"opts_method_statement": {
|
397
|
+
"type": "SEQ",
|
398
|
+
"members": [
|
399
|
+
{
|
400
|
+
"type": "STRING",
|
401
|
+
"value": "opts"
|
402
|
+
},
|
403
|
+
{
|
404
|
+
"type": "STRING",
|
405
|
+
"value": "method"
|
406
|
+
},
|
407
|
+
{
|
408
|
+
"type": "SYMBOL",
|
409
|
+
"name": "string"
|
410
|
+
}
|
411
|
+
]
|
412
|
+
},
|
392
413
|
"flagarg_statement": {
|
393
414
|
"type": "SEQ",
|
394
415
|
"members": [
|
@@ -101,6 +101,10 @@
|
|
101
101
|
"type": "opts_file_statement",
|
102
102
|
"named": true
|
103
103
|
},
|
104
|
+
{
|
105
|
+
"type": "opts_method_statement",
|
106
|
+
"named": true
|
107
|
+
},
|
104
108
|
{
|
105
109
|
"type": "opts_shell_statement",
|
106
110
|
"named": true
|
@@ -332,6 +336,21 @@
|
|
332
336
|
"named": true,
|
333
337
|
"fields": {}
|
334
338
|
},
|
339
|
+
{
|
340
|
+
"type": "opts_method_statement",
|
341
|
+
"named": true,
|
342
|
+
"fields": {},
|
343
|
+
"children": {
|
344
|
+
"multiple": false,
|
345
|
+
"required": true,
|
346
|
+
"types": [
|
347
|
+
{
|
348
|
+
"type": "string",
|
349
|
+
"named": true
|
350
|
+
}
|
351
|
+
]
|
352
|
+
}
|
353
|
+
},
|
335
354
|
{
|
336
355
|
"type": "opts_shell_statement",
|
337
356
|
"named": true,
|
@@ -524,6 +543,10 @@
|
|
524
543
|
"type": "include",
|
525
544
|
"named": false
|
526
545
|
},
|
546
|
+
{
|
547
|
+
"type": "method",
|
548
|
+
"named": false
|
549
|
+
},
|
527
550
|
{
|
528
551
|
"type": "opts",
|
529
552
|
"named": false
|