command_kit-completion 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d38eb48761e8c78265e4fe3de35b056a4b20a1c709ae92d1748b55bdaf26d78
4
- data.tar.gz: 632bd4541f75fadeff3ecb7f754c865e387a2282c4329719e47fa628b049ebab
3
+ metadata.gz: 5f85e448a2df37a8bc6231b839b8e56992432498384e6ea4c2e1060a9485c874
4
+ data.tar.gz: 898168ab4ed70e3e595de27c933432a085976ea81980c7bcc3572e6e2cc4c38c
5
5
  SHA512:
6
- metadata.gz: 671fd188f0ba01c1a32bf02d3d06ed5e9fd2e9e78f7e055d0cde9f687bb732a1913bef999f9e15e2d8d29837db8e996ea40dd7e65891bad03e92440999ba2839
7
- data.tar.gz: a4dd29acbf852ad3a746bb8ea47595a6b613ac8e12f407a1966165d3a3f86aadb7893295c743448574f12c9e417a7fef084845ceacac8f3782484fe33d7c1da6
6
+ metadata.gz: 45ceb26079e0d5caf31e94ea0851fc30e5175661491bed0d6da4f57feb3bc37352ef48e9f7d817f5dced1825db2aa94e0500387a8175dde6b805921c62453ab6
7
+ data.tar.gz: 8858593122d06c2e4fff8d5f78fae75cd8f23665732131d36eb2fd9c52a51e94a8aa5fe3920b5b622a4f03a16360f442b79e6fd5a9b1c96cf78389c1871a6872
data/ChangeLog.md CHANGED
@@ -1,3 +1,17 @@
1
+ ### 0.2.0 / 2024-04-26
2
+
3
+ * Also generate completion rules for option's short flags.
4
+ * Also generate `<file>`, `<directory>`, `<hostname>`, and `<user>` completion
5
+ rules for options who's value is named `FILE`, `DIR`, `HOST`, `USER`
6
+ (or ends in `_FILE`, `_DIR`, `_HOST`, `_USER`), respectively.
7
+ * Also generate `<file>`, `<directory>`, `<hostname>`, and `<user>` completion
8
+ rules for the command's first argument if it's named `FILE`, `DIR`, `HOST`,
9
+ `USER` (or ends in `_FILE`, `_DIR`, `_HOST`, `_USER`), respectively.
10
+
11
+ ### 0.1.2 / 2023-12-18
12
+
13
+ * Fix namespace conflict between `FileUtils` and `CommandKit::FileUtils`.
14
+
1
15
  ### 0.1.1 / 2023-12-18
2
16
 
3
17
  * Ensure that the parent directory of the output file exists before writing to
@@ -90,7 +90,7 @@ module CommandKit
90
90
  completions.script
91
91
  end
92
92
 
93
- FileUtils.mkdir_p(File.dirname(@output_file))
93
+ ::FileUtils.mkdir_p(File.dirname(@output_file))
94
94
  File.write(@output_file,shell_script)
95
95
  end
96
96
 
@@ -110,13 +110,25 @@ module CommandKit
110
110
  Object.const_get(@class_name)
111
111
  end
112
112
 
113
- # Mapping of command usage strings to completely `<keyword>`s.
114
- USAGE_COMPLETIONS = {
115
- 'FILE' => '<file>',
116
- 'DIR' => '<directory>',
117
- 'HOST' => '<hostname>',
118
- 'USER' => '<user>'
119
- }
113
+ #
114
+ # Maps the argument name strings to completely suggestion `<keyword>`s.
115
+ #
116
+ # @param [String] arg
117
+ # The argument name.
118
+ #
119
+ # @return [String, nil]
120
+ # The suggestion keyword for the argument name.
121
+ #
122
+ # @since 0.2.0
123
+ #
124
+ def suggestion_for_argument(arg)
125
+ case arg
126
+ when /\AFILE\z|_FILE\z/ then '<file>'
127
+ when /\ADIR\z|_DIR\z/ then '<directory>'
128
+ when /\AHOST\z|_HOST\z/ then '<hostname>'
129
+ when /\AUSER\z|_USER\z/ then '<user>'
130
+ end
131
+ end
120
132
 
121
133
  #
122
134
  # Generates the completion rules for the given [command_kit] command
@@ -139,20 +151,26 @@ module CommandKit
139
151
  # add all long option flags
140
152
  command_class.options.each_value do |option|
141
153
  completions[command_name] << option.long
154
+ completions[command_name] << option.short if option.short
142
155
 
143
156
  if option.value
144
- if (option_value_completion = USAGE_COMPLETIONS[option.value.usage])
157
+ if (suggestion = suggestion_for_argument(option.value.usage))
158
+ command_pattern = "#{command_name}*#{option.long}"
159
+
145
160
  # add a special rule if the option's value USAGE maps to a
146
161
  # 'completely' completion keyword (ex: `FILE` -> `<file>`).
147
- completions["#{command_name}*#{option.long}"] = [
148
- option_value_completion
149
- ]
162
+ completions[command_pattern] = [suggestion]
163
+
164
+ if option.short
165
+ # also add another rule with the option's short flag
166
+ completions["#{command_name}*#{option.short}"] = [suggestion]
167
+ end
150
168
  end
151
169
  end
152
170
  end
153
171
  end
154
172
 
155
- # sub-commands
173
+ # sub-commands / first argument
156
174
  if command_class.include?(CommandKit::Commands)
157
175
  command_class.commands.each do |subcommand_name,subcommand|
158
176
  # add all sub-command names
@@ -165,6 +183,13 @@ module CommandKit
165
183
  end
166
184
 
167
185
  completions[command_name].concat(command_class.command_aliases.keys)
186
+ elsif command_class.include?(CommandKit::Arguments)
187
+ if (argument = command_class.arguments.values.first)
188
+ if (suggestion = suggestion_for_argument(argument.usage))
189
+ # add a suggestion for the first argument
190
+ completions[command_name] << suggestion
191
+ end
192
+ end
168
193
  end
169
194
 
170
195
  # filter out any command's that have no options/sub-commands
@@ -3,6 +3,6 @@
3
3
  module CommandKit
4
4
  module Completion
5
5
  # command_kit-completion version
6
- VERSION = '0.1.1'
6
+ VERSION = '0.2.0'
7
7
  end
8
8
  end
data/spec/task_spec.rb CHANGED
@@ -219,6 +219,140 @@ describe CommandKit::Completion::Task do
219
219
  end
220
220
  end
221
221
 
222
+ context "when the command class includes CommandKit::Arguments" do
223
+ context "and has at least one argument" do
224
+ context "and it's named FILE" do
225
+ class TestCommandWithFILEArgument < CommandKit::Command
226
+
227
+ command_name 'test'
228
+
229
+ option :foo, desc: 'Foo option'
230
+
231
+ argument :bar, usage: 'FILE', desc: 'Bar option'
232
+
233
+ end
234
+
235
+ let(:command_class) { TestCommandWithFILEArgument }
236
+
237
+ it "must add '<file>' to the command's completion rule" do
238
+ expect(subject.completion_rules_for(command_class)).to eq(
239
+ {
240
+ "test" => %w[--foo <file>]
241
+ }
242
+ )
243
+ end
244
+ end
245
+
246
+ context "and it's named DIR" do
247
+ class TestCommandWithDIRArgument < CommandKit::Command
248
+
249
+ command_name 'test'
250
+
251
+ option :foo, desc: 'Foo option'
252
+
253
+ argument :bar, usage: 'DIR', desc: 'Bar option'
254
+
255
+ end
256
+
257
+ let(:command_class) { TestCommandWithDIRArgument }
258
+
259
+ it "must add '<directory>' to the command's completion rule" do
260
+ expect(subject.completion_rules_for(command_class)).to eq(
261
+ {
262
+ "test" => %w[--foo <directory>]
263
+ }
264
+ )
265
+ end
266
+ end
267
+
268
+ context "and it's named HOST" do
269
+ class TestCommandWithHOSTArgument < CommandKit::Command
270
+
271
+ command_name 'test'
272
+
273
+ option :foo, desc: 'Foo option'
274
+
275
+ argument :bar, usage: 'HOST', desc: 'Bar option'
276
+
277
+ end
278
+
279
+ let(:command_class) { TestCommandWithHOSTArgument }
280
+
281
+ it "must add '<hostname>' to the command's completion rule" do
282
+ expect(subject.completion_rules_for(command_class)).to eq(
283
+ {
284
+ "test" => %w[--foo <hostname>]
285
+ }
286
+ )
287
+ end
288
+ end
289
+
290
+ context "and it's named USER" do
291
+ class TestCommandWithUSERArgument < CommandKit::Command
292
+
293
+ command_name 'test'
294
+
295
+ option :foo, desc: 'Foo option'
296
+
297
+ argument :bar, usage: 'USER', desc: 'Bar option'
298
+
299
+ end
300
+
301
+ let(:command_class) { TestCommandWithUSERArgument }
302
+
303
+ it "must add '<user>' to the command's completion rule" do
304
+ expect(subject.completion_rules_for(command_class)).to eq(
305
+ {
306
+ "test" => %w[--foo <user>]
307
+ }
308
+ )
309
+ end
310
+ end
311
+
312
+ context "but it's named something else" do
313
+ class TestCommandWithArgument < CommandKit::Command
314
+
315
+ command_name 'test'
316
+
317
+ option :foo, desc: 'Foo option'
318
+
319
+ argument :bar, usage: 'BAR', desc: 'Bar option'
320
+
321
+ end
322
+
323
+ let(:command_class) { TestCommandWithArgument }
324
+
325
+ it "must not add additional suggestions to the command's completion rule" do
326
+ expect(subject.completion_rules_for(command_class)).to eq(
327
+ {
328
+ "test" => %w[--foo]
329
+ }
330
+ )
331
+ end
332
+ end
333
+ end
334
+
335
+ context "but has no arguments" do
336
+ class TestCommandWithoutArguments < CommandKit::Command
337
+
338
+ command_name 'test'
339
+
340
+ option :foo, desc: 'Foo option'
341
+
342
+ end
343
+
344
+ let(:command_class) { TestCommandWithoutArguments }
345
+
346
+ it "must not add additional suggestions" do
347
+ expect(subject.completion_rules_for(command_class)).to eq(
348
+ {
349
+ "test" => %w[--foo]
350
+ }
351
+ )
352
+ end
353
+ end
354
+ end
355
+
222
356
  context "when the command class includes CommandKit::Commands" do
223
357
  class TestCommandWithSubCommands < CommandKit::Command
224
358
  include CommandKit::Commands
@@ -259,7 +393,7 @@ describe CommandKit::Completion::Task do
259
393
  it "must add completion rules for the other commands" do
260
394
  expect(subject.completion_rules_for(command_class)).to eq(
261
395
  {
262
- "test" => %w[--global-option help foo bar],
396
+ "test" => %w[--global-option -g help foo bar],
263
397
  "test foo" => %w[--foo-opt1 --foo-opt2],
264
398
  "test bar" => %w[--bar-opt1 --bar-opt2]
265
399
  }
@@ -309,7 +443,7 @@ describe CommandKit::Completion::Task do
309
443
  it "must include the command aliases in the completion rules" do
310
444
  expect(subject.completion_rules_for(command_class)).to eq(
311
445
  {
312
- "test" => %w[--global-option help foo bar foo2 bar2],
446
+ "test" => %w[--global-option -g help foo bar foo2 bar2],
313
447
  "test foo" => %w[--foo-opt1 --foo-opt2],
314
448
  "test bar" => %w[--bar-opt1 --bar-opt2]
315
449
  }
@@ -349,7 +483,7 @@ describe CommandKit::Completion::Task do
349
483
  it "must omit the command from the completion rules" do
350
484
  expect(subject.completion_rules_for(command_class)).to eq(
351
485
  {
352
- "test" => %w[--global-option help foo bar],
486
+ "test" => %w[--global-option -g help foo bar],
353
487
  "test foo" => %w[--foo-opt1 --foo-opt2]
354
488
  }
355
489
  )
@@ -423,7 +557,7 @@ describe CommandKit::Completion::Task do
423
557
  it "must recursively include completion rules for the sub-sub-commands" do
424
558
  expect(subject.completion_rules_for(command_class)).to eq(
425
559
  {
426
- "test" => %w[--global-option help foo bar],
560
+ "test" => %w[--global-option -g help foo bar],
427
561
  "test foo" => %w[--foo-opt1 --foo-opt2],
428
562
  "test bar" => %w[--bar-opt1 --bar-opt2 help baz qux],
429
563
  "test bar baz" => %w[--baz-opt1 --baz-opt2],
@@ -435,14 +569,65 @@ describe CommandKit::Completion::Task do
435
569
  end
436
570
  end
437
571
 
572
+ describe "#suggestion_for_argument" do
573
+ context "when given 'FILE'" do
574
+ it "must return '<file>'" do
575
+ expect(subject.suggestion_for_argument('FILE')).to eq('<file>')
576
+ end
577
+ end
578
+
579
+ context "when the string ends with '_FILE'" do
580
+ it "must return '<file>'" do
581
+ expect(subject.suggestion_for_argument('FOO_FILE')).to eq('<file>')
582
+ end
583
+ end
584
+
585
+ context "when given 'DIR'" do
586
+ it "must return '<directory>'" do
587
+ expect(subject.suggestion_for_argument('DIR')).to eq('<directory>')
588
+ end
589
+ end
590
+
591
+ context "when the string ends with '_DIR'" do
592
+ it "must return '<directory>'" do
593
+ expect(subject.suggestion_for_argument('FOO_DIR')).to eq('<directory>')
594
+ end
595
+ end
596
+
597
+ context "when given 'HOST'" do
598
+ it "must return '<hostname>'" do
599
+ expect(subject.suggestion_for_argument('HOST')).to eq('<hostname>')
600
+ end
601
+ end
602
+
603
+ context "when the string ends with '_HOST'" do
604
+ it "must return '<hostname>'" do
605
+ expect(subject.suggestion_for_argument('FOO_HOST')).to eq('<hostname>')
606
+ end
607
+ end
608
+
609
+ context "when given 'USER'" do
610
+ it "must return '<user>'" do
611
+ expect(subject.suggestion_for_argument('USER')).to eq('<user>')
612
+ end
613
+ end
614
+
615
+ context "when the string ends with '_USER'" do
616
+ it "must return '<user>'" do
617
+ expect(subject.suggestion_for_argument('FOO_USER')).to eq('<user>')
618
+ end
619
+ end
620
+ end
621
+
438
622
  describe "#completion_rules" do
439
623
  it "must load the class from #class_file and return the generated completion rules for it" do
440
624
  expect(subject.completion_rules).to eq(
441
625
  {
442
- "foo" => %w[--config-file help config list update ls up],
626
+ "foo" => %w[--config-file -C help config list update ls up],
443
627
  "foo config" => %w[help get set],
444
- "foo update" => %w[--quiet],
445
- "foo*--config-file" => %w[<file>]
628
+ "foo update" => %w[--quiet -q],
629
+ "foo*--config-file" => %w[<file>],
630
+ "foo*-C" => %w[<file>]
446
631
  }
447
632
  )
448
633
  end
@@ -462,10 +647,11 @@ describe CommandKit::Completion::Task do
462
647
  it "must merge the additional completion rules with the generated ones" do
463
648
  expect(subject.completion_rules).to eq(
464
649
  {
465
- "foo" => %w[--config-file help config list update ls up],
650
+ "foo" => %w[--config-file -C help config list update ls up],
466
651
  "foo config" => %w[help get set],
467
- "foo update" => ['--quiet', '$(foo list)'],
468
- "foo*--config-file" => %w[<file>]
652
+ "foo update" => ['--quiet', '-q', '$(foo list)'],
653
+ "foo*--config-file" => %w[<file>],
654
+ "foo*-C" => %w[<file>]
469
655
  }
470
656
  )
471
657
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: command_kit-completion
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Postmodern
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-12-18 00:00:00.000000000 Z
11
+ date: 2024-04-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: command_kit
@@ -110,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
110
  - !ruby/object:Gem::Version
111
111
  version: '0'
112
112
  requirements: []
113
- rubygems_version: 3.4.10
113
+ rubygems_version: 3.4.19
114
114
  signing_key:
115
115
  specification_version: 4
116
116
  summary: Generate shell completions for command_kit commands