command_kit-completion 0.2.0 → 0.3.0

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