command_kit-completion 0.2.0 → 0.3.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: 5f85e448a2df37a8bc6231b839b8e56992432498384e6ea4c2e1060a9485c874
4
- data.tar.gz: 898168ab4ed70e3e595de27c933432a085976ea81980c7bcc3572e6e2cc4c38c
3
+ metadata.gz: 721a31416806fcf1375dfa9060827013fbde3ffc06f0d9916f31c5602d4386f7
4
+ data.tar.gz: e6bb27f5dd650bfb26b9fda270bb66a97c7c260cbaa45b3a520a93aa53594567
5
5
  SHA512:
6
- metadata.gz: 45ceb26079e0d5caf31e94ea0851fc30e5175661491bed0d6da4f57feb3bc37352ef48e9f7d817f5dced1825db2aa94e0500387a8175dde6b805921c62453ab6
7
- data.tar.gz: 8858593122d06c2e4fff8d5f78fae75cd8f23665732131d36eb2fd9c52a51e94a8aa5fe3920b5b622a4f03a16360f442b79e6fd5a9b1c96cf78389c1871a6872
6
+ metadata.gz: 6e9820c5f5ae32b4f9672d9b05bec71b86e0a66a9a8ae873e74346d65fe2d5b8d37c205921689d7c842d30e1d8411328fc5181da61b57abe71fbd31518485229
7
+ data.tar.gz: 855fd91e1a677a359f62f1ebcc38350e6308e5451cfa7d69dea172ec3d88c015a2323e1b7d6559f64cd6667bd1c5ac2489d62644523e39bf80f8a2628bd64621
@@ -9,9 +9,10 @@ jobs:
9
9
  fail-fast: false
10
10
  matrix:
11
11
  ruby:
12
- - 3.0
13
- - 3.1
14
- - 3.2
12
+ - '3.0'
13
+ - '3.1'
14
+ - '3.2'
15
+ - '3.3'
15
16
  - jruby
16
17
  - truffleruby
17
18
  name: OS ${{ matrix.os }} / Ruby ${{ matrix.ruby }}
data/ChangeLog.md CHANGED
@@ -1,3 +1,13 @@
1
+ ### 0.3.0 / 2024-12-16
2
+
3
+ * Generate `<file>` and `<directory>` completion rules for arguments named
4
+ `PATH` or ending in `_PATH`, so they can tab complete both files and
5
+ directories.
6
+
7
+ ### 0.2.1 / 2024-04-29
8
+
9
+ * Support loading YAML input files that contain YAML aliases.
10
+
1
11
  ### 0.2.0 / 2024-04-26
2
12
 
3
13
  * Also generate completion rules for option's short flags.
data/LICENSE.txt CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2023 Hal Brodigan
1
+ Copyright (c) 2023-2024 Hal Brodigan
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -45,7 +45,7 @@ rake command_kit:completion
45
45
 
46
46
  ## License
47
47
 
48
- Copyright (c) 2023 Hal Brodigan
48
+ Copyright (c) 2023-2024 Hal Brodigan
49
49
 
50
50
  See {file:LICENSE.txt} for details.
51
51
 
@@ -10,37 +10,61 @@ require 'fileutils'
10
10
 
11
11
  module CommandKit
12
12
  module Completion
13
+ #
14
+ # `command_kit-completion` rake task.
15
+ #
16
+ # ## Example
17
+ #
18
+ # require 'command_kit/completion/task'
19
+ # CommandKit::Completion::Task.new(
20
+ # class_file: './examples/cli',
21
+ # class_name: 'Foo::CLI',
22
+ # output_file: 'completion.sh'
23
+ # )
24
+ #
13
25
  class Task < Rake::TaskLib
14
26
 
15
27
  # The file that the command_kit CLI is defined in.
16
28
  #
17
29
  # @return [String]
30
+ #
31
+ # @api private
18
32
  attr_reader :class_file
19
33
 
20
34
  # The class name of the command_kit CLI.
21
35
  #
22
36
  # @return [String]
37
+ #
38
+ # @api private
23
39
  attr_reader :class_name
24
40
 
25
41
  # The output file to write the shell completions to.
26
42
  #
27
43
  # @return [String]
44
+ #
45
+ # @api private
28
46
  attr_reader :output_file
29
47
 
30
48
  # Optional input YAML file to read additional shell completions from.
31
49
  #
32
50
  # @return [String, nil]
51
+ #
52
+ # @api private
33
53
  attr_reader :input_file
34
54
 
35
55
  # Specifies whether the shell completion logic should be wrapped in a
36
56
  # function.
37
57
  #
38
58
  # @return [Boolean]
59
+ #
60
+ # @api private
39
61
  attr_reader :wrap_function
40
62
 
41
63
  # Optional function name to wrap the shell completions within.
42
64
  #
43
65
  # @return [String, nil]
66
+ #
67
+ # @api private
44
68
  attr_reader :function_name
45
69
 
46
70
  #
@@ -61,6 +85,8 @@ module CommandKit
61
85
  #
62
86
  # [completely examples]: https://github.com/DannyBen/completely?tab=readme-ov-file#using-the-completely-command-line
63
87
  #
88
+ # @api public
89
+ #
64
90
  def initialize(class_file: ,
65
91
  class_name: ,
66
92
  output_file: ,
@@ -81,6 +107,8 @@ module CommandKit
81
107
  #
82
108
  # Defines the `command_kit:completion` task.
83
109
  #
110
+ # @api private
111
+ #
84
112
  def define
85
113
  task(@output_file) do
86
114
  completions = Completely::Completions.new(completion_rules)
@@ -105,28 +133,45 @@ module CommandKit
105
133
  #
106
134
  # @return [Class]
107
135
  #
136
+ # @api private
137
+ #
108
138
  def load_class
109
139
  require(@class_file)
110
140
  Object.const_get(@class_name)
111
141
  end
112
142
 
143
+ #
144
+ # Loads the completion rules from the {#input_file}.
145
+ #
146
+ # @return [Hash]
147
+ # The completion rules from the {#input_file}.
148
+ #
149
+ # @api private
150
+ #
151
+ def load_input_file
152
+ YAML.load_file(@input_file, aliases: true)
153
+ end
154
+
113
155
  #
114
156
  # Maps the argument name strings to completely suggestion `<keyword>`s.
115
157
  #
116
158
  # @param [String] arg
117
159
  # The argument name.
118
160
  #
119
- # @return [String, nil]
161
+ # @return [Array<String>, nil]
120
162
  # The suggestion keyword for the argument name.
121
163
  #
122
- # @since 0.2.0
164
+ # @since 0.3.0
165
+ #
166
+ # @api private
123
167
  #
124
- def suggestion_for_argument(arg)
168
+ def suggestions_for_argument(arg)
125
169
  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>'
170
+ when /\AFILE\z|_FILE\z/ then %w[<file>]
171
+ when /\ADIR\z|_DIR\z/ then %w[<directory>]
172
+ when /\APATH\z|_PATH\z/ then %w[<file> <directory>]
173
+ when /\AHOST\z|_HOST\z/ then %w[<hostname>]
174
+ when /\AUSER\z|_USER\z/ then %w[<user>]
130
175
  end
131
176
  end
132
177
 
@@ -142,6 +187,8 @@ module CommandKit
142
187
  # @return [Hash{String => Array<String>}]
143
188
  # The completion rules for the command class and any sub-commands.
144
189
  #
190
+ # @api private
191
+ #
145
192
  def completion_rules_for(command_class)
146
193
  command_name = command_class.command_name
147
194
  completions = {command_name => []}
@@ -154,16 +201,16 @@ module CommandKit
154
201
  completions[command_name] << option.short if option.short
155
202
 
156
203
  if option.value
157
- if (suggestion = suggestion_for_argument(option.value.usage))
204
+ if (suggestions = suggestions_for_argument(option.value.usage))
158
205
  command_pattern = "#{command_name}*#{option.long}"
159
206
 
160
207
  # add a special rule if the option's value USAGE maps to a
161
208
  # 'completely' completion keyword (ex: `FILE` -> `<file>`).
162
- completions[command_pattern] = [suggestion]
209
+ completions[command_pattern] = suggestions
163
210
 
164
211
  if option.short
165
212
  # also add another rule with the option's short flag
166
- completions["#{command_name}*#{option.short}"] = [suggestion]
213
+ completions["#{command_name}*#{option.short}"] = suggestions
167
214
  end
168
215
  end
169
216
  end
@@ -185,9 +232,9 @@ module CommandKit
185
232
  completions[command_name].concat(command_class.command_aliases.keys)
186
233
  elsif command_class.include?(CommandKit::Arguments)
187
234
  if (argument = command_class.arguments.values.first)
188
- if (suggestion = suggestion_for_argument(argument.usage))
235
+ if (suggestions = suggestions_for_argument(argument.usage))
189
236
  # add a suggestion for the first argument
190
- completions[command_name] << suggestion
237
+ completions[command_name].concat(suggestions)
191
238
  end
192
239
  end
193
240
  end
@@ -206,12 +253,14 @@ module CommandKit
206
253
  #
207
254
  # @return [Hash{String => Array<String>}]
208
255
  #
256
+ # @api private
257
+ #
209
258
  def completion_rules
210
259
  completion_rules = completion_rules_for(load_class)
211
260
 
212
261
  if @input_file
213
262
  # load the additional rules from the input file
214
- additional_completion_rules = YAML.load_file(@input_file)
263
+ additional_completion_rules = load_input_file
215
264
 
216
265
  # merge the additional completion rules
217
266
  additional_completion_rules.each do |command_string,completions|
@@ -3,6 +3,6 @@
3
3
  module CommandKit
4
4
  module Completion
5
5
  # command_kit-completion version
6
- VERSION = '0.2.0'
6
+ VERSION = '0.3.0'
7
7
  end
8
8
  end
@@ -0,0 +1,4 @@
1
+ ---
2
+ foo update: &update
3
+ - $(foo list)
4
+ foo up: *update
data/spec/task_spec.rb CHANGED
@@ -69,6 +69,42 @@ describe CommandKit::Completion::Task do
69
69
  end
70
70
  end
71
71
 
72
+ describe "#load_input_file" do
73
+ let(:input_file) { File.join(fixtures_dir,'additional_rules.yml') }
74
+
75
+ subject do
76
+ described_class.new(
77
+ class_file: class_file,
78
+ class_name: class_name,
79
+ input_file: input_file,
80
+ output_file: output_file
81
+ )
82
+ end
83
+
84
+ it "must load the YAML from the input file" do
85
+ expect(subject.load_input_file).to eq(
86
+ {
87
+ 'foo update' => ['$(foo list)']
88
+ }
89
+ )
90
+ end
91
+
92
+ context "when the input file contains YAML aliases" do
93
+ let(:input_file) do
94
+ File.join(fixtures_dir,'additional_rules_with_aliases.yml')
95
+ end
96
+
97
+ it "must support parsing YAML aliases" do
98
+ expect(subject.load_input_file).to eq(
99
+ {
100
+ 'foo update' => ['$(foo list)'],
101
+ 'foo up'=> ['$(foo list)']
102
+ }
103
+ )
104
+ end
105
+ end
106
+ end
107
+
72
108
  describe "#completion_rules_for" do
73
109
  context "when given a simple CommandKit::Command class" do
74
110
  class TestBasicCommand < CommandKit::Command
@@ -569,52 +605,90 @@ describe CommandKit::Completion::Task do
569
605
  end
570
606
  end
571
607
 
572
- describe "#suggestion_for_argument" do
608
+ describe "#suggestions_for_argument" do
573
609
  context "when given 'FILE'" do
574
- it "must return '<file>'" do
575
- expect(subject.suggestion_for_argument('FILE')).to eq('<file>')
610
+ it "must return ['<file>']" do
611
+ expect(subject.suggestions_for_argument('FILE')).to eq(
612
+ %w[<file>]
613
+ )
576
614
  end
577
615
  end
578
616
 
579
617
  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>')
618
+ it "must return ['<file>']" do
619
+ expect(subject.suggestions_for_argument('FOO_FILE')).to eq(
620
+ %w[<file>]
621
+ )
582
622
  end
583
623
  end
584
624
 
585
625
  context "when given 'DIR'" do
586
- it "must return '<directory>'" do
587
- expect(subject.suggestion_for_argument('DIR')).to eq('<directory>')
626
+ it "must return ['<directory>']" do
627
+ expect(subject.suggestions_for_argument('DIR')).to eq(
628
+ %w[<directory>]
629
+ )
588
630
  end
589
631
  end
590
632
 
591
633
  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>')
634
+ it "must return ['<directory>']" do
635
+ expect(subject.suggestions_for_argument('FOO_DIR')).to eq(
636
+ %w[<directory>]
637
+ )
638
+ end
639
+ end
640
+
641
+ context "when given 'PATH'" do
642
+ it "must return ['<file>', '<directory>']" do
643
+ expect(subject.suggestions_for_argument('PATH')).to eq(
644
+ %w[
645
+ <file>
646
+ <directory>
647
+ ]
648
+ )
649
+ end
650
+ end
651
+
652
+ context "when the string ends with '_PATH'" do
653
+ it "must return ['<file>', '<directory>']" do
654
+ expect(subject.suggestions_for_argument('FOO_PATH')).to eq(
655
+ %w[
656
+ <file>
657
+ <directory>
658
+ ]
659
+ )
594
660
  end
595
661
  end
596
662
 
597
663
  context "when given 'HOST'" do
598
- it "must return '<hostname>'" do
599
- expect(subject.suggestion_for_argument('HOST')).to eq('<hostname>')
664
+ it "must return ['<hostname>']" do
665
+ expect(subject.suggestions_for_argument('HOST')).to eq(
666
+ %w[<hostname>]
667
+ )
600
668
  end
601
669
  end
602
670
 
603
671
  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>')
672
+ it "must return ['<hostname>']" do
673
+ expect(subject.suggestions_for_argument('FOO_HOST')).to eq(
674
+ %w[<hostname>]
675
+ )
606
676
  end
607
677
  end
608
678
 
609
679
  context "when given 'USER'" do
610
- it "must return '<user>'" do
611
- expect(subject.suggestion_for_argument('USER')).to eq('<user>')
680
+ it "must return ['<user>']" do
681
+ expect(subject.suggestions_for_argument('USER')).to eq(
682
+ %w[<user>]
683
+ )
612
684
  end
613
685
  end
614
686
 
615
687
  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>')
688
+ it "must return ['<user>']" do
689
+ expect(subject.suggestions_for_argument('FOO_USER')).to eq(
690
+ %w[<user>]
691
+ )
618
692
  end
619
693
  end
620
694
  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.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Postmodern
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-04-27 00:00:00.000000000 Z
11
+ date: 2024-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: command_kit
@@ -84,6 +84,7 @@ files:
84
84
  - lib/command_kit/completion/task.rb
85
85
  - lib/command_kit/completion/version.rb
86
86
  - spec/fixtures/additional_rules.yml
87
+ - spec/fixtures/additional_rules_with_aliases.yml
87
88
  - spec/spec_helper.rb
88
89
  - spec/task_spec.rb
89
90
  homepage: https://github.com/postmodern/command_kit-completion#readme
@@ -110,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
110
111
  - !ruby/object:Gem::Version
111
112
  version: '0'
112
113
  requirements: []
113
- rubygems_version: 3.4.19
114
+ rubygems_version: 3.5.22
114
115
  signing_key:
115
116
  specification_version: 4
116
117
  summary: Generate shell completions for command_kit commands