tabry 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 = :subcommand_stack if key == :subs
105
- expect(result.send(key)).to eq(val)
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.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"
@@ -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))))
@@ -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',
@@ -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