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.
@@ -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