atli 0.1.9 → 0.1.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,48 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # =======================================================================
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
9
+
10
+ require 'nrser'
11
+ require 'nrser/labs/i8'
12
+
13
+
14
+ # Refinements
15
+ # =======================================================================
16
+
17
+ require 'nrser/refinements/types'
18
+ using NRSER::Types
19
+
20
+
21
+ # Namespace
22
+ # =======================================================================
23
+
24
+ class Thor
25
+ module Completion
26
+ module Bash
27
+
28
+ # Definitions
29
+ # =======================================================================
30
+
31
+ # Structre to hold Bash complete request parameters
32
+ class Request < I8::Struct.new(
33
+ cur: t.str,
34
+ prev: t.str,
35
+ cword: t.non_neg_int,
36
+ split: t.bool,
37
+ words: t.array( t.str )
38
+ )
39
+
40
+ end # class Request
41
+
42
+
43
+ # /Namespace
44
+ # =======================================================================
45
+
46
+ end # module Bash
47
+ end # module Completion
48
+ end # class Thor
@@ -0,0 +1,136 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # ========================================================================
6
+
7
+ # Project / Package
8
+ # ------------------------------------------------------------------------
9
+
10
+ # Need to make sure {Thor} is loaded first in case a user file requires
11
+ # us here beforehand
12
+ require 'thor'
13
+
14
+
15
+ # Refinements
16
+ # =======================================================================
17
+
18
+ require 'nrser/refinements/types'
19
+ using NRSER::Types
20
+
21
+
22
+ # Namespace
23
+ # =======================================================================
24
+
25
+ class Thor
26
+ module Completion
27
+ module Bash
28
+
29
+
30
+ # Definitions
31
+ # =======================================================================
32
+
33
+ # {Thor} added as a {Thor.subcommand} to the includer of
34
+ # {Thor::Completion::Bash} to expose Bash completion endpoints.
35
+ #
36
+ class Subcmd < ::Thor
37
+
38
+ # Commands
39
+ # ========================================================================
40
+
41
+ desc 'complete -- CUR PREV CWORD SPLIT WORDS...',
42
+ "Provide Bash completions"
43
+
44
+ # Execute Bash completion.
45
+ #
46
+ # @param [Array] *_
47
+ # Ignored - input custom read from `ARGV`.
48
+ #
49
+ # @return [void]
50
+ # Never returns - manually calls `exit` when done.
51
+ #
52
+ def complete *_
53
+ # logger.level = :trace
54
+
55
+ logger.trace "Starting Bash complete...",
56
+ ARGV: ARGV,
57
+ args: args
58
+
59
+ args = ARGV.dup
60
+
61
+ args.shift while args[0] != '--'
62
+ args.shift
63
+
64
+ cur, prev, cword, split, *words = args
65
+
66
+ request = Request.new \
67
+ cur: cur, # options[:cur],
68
+ prev: prev, # options[:prev],
69
+ cword: cword.to_i, # options[:cword],
70
+ split: split.truthy?, # options[:split],
71
+ words: words
72
+
73
+ logger.trace "Bash complete Request loaded",
74
+ request: request
75
+
76
+ results = begin
77
+ self.class.target.bash_complete( request: request, index: 1 )
78
+ rescue StandardError => error
79
+ logger.error "Error raised processing Bash complete request",
80
+ { request: request },
81
+ error
82
+
83
+ []
84
+ end
85
+
86
+ joined = results.shelljoin
87
+
88
+ logger.trace "Sending Bash compelte response",
89
+ request: request,
90
+ results: results,
91
+ joined: joined
92
+
93
+ puts joined
94
+ exit true
95
+ end # #complete
96
+
97
+
98
+ desc 'setup',
99
+ "Source this output in your shell or profile to install."
100
+
101
+ long_desc <<~END
102
+ Prints Bash source code to STDOUT that hooks `complete` into the exe.
103
+ END
104
+
105
+ example "source <(#{ $0 } bash-complete setup)"
106
+
107
+ # Print Bash source code to hook into `complete` to `$stdout`.
108
+ #
109
+ # @return [nil]
110
+ #
111
+ def setup
112
+ bin = File.basename $0
113
+ name = bin.underscore
114
+
115
+ erb_src_path = ::Thor::ROOT.join 'support',
116
+ 'completion',
117
+ 'complete.inc.bash.erb'
118
+
119
+ erb_src = erb_src_path.read
120
+
121
+ bash_src = binding.erb erb_src
122
+
123
+ puts bash_src
124
+
125
+ nil
126
+ end # #setup
127
+
128
+ end # class Subcmd
129
+
130
+
131
+ # /Namespace
132
+ # =======================================================================
133
+
134
+ end # module Bash
135
+ end # module Completion
136
+ end # class Thor
@@ -0,0 +1,250 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+
5
+ # Refinements
6
+ # =======================================================================
7
+
8
+ require 'nrser/refinements/types'
9
+ using NRSER::Types
10
+
11
+
12
+ # Namespace
13
+ # =======================================================================
14
+
15
+ class Thor
16
+ module Completion
17
+ module Bash
18
+
19
+
20
+ # Definitions
21
+ # =======================================================================
22
+
23
+ # To be mixed in to {Thor}. It's all class methods at the moment.
24
+ #
25
+ # @todo
26
+ # Deal with that {Thor::Group} thing? I never use it...
27
+ #
28
+ module ThorMixin
29
+
30
+ # Methods to be mixed as class methods in to {Thor}.
31
+ #
32
+ module ClassMethods
33
+
34
+ # Handles Bash completion requests at the {Thor} *class* level.
35
+ #
36
+ # This consists of:
37
+ #
38
+ # 1. Finding the next word in the `request` at of after `index` that
39
+ # identifies next the command or subcommand.
40
+ #
41
+ # **NOTE** This is done by simply scanning over words that start
42
+ # with `-`, which will not work for options that take values as
43
+ # separate shell words.
44
+ #
45
+ # 2. Looking for an exact match command or subcommand to the word from
46
+ # (1), and passing processing off to the respective {Thor::Command}
47
+ # instance or {Thor::Base} subclass.
48
+ #
49
+ # 3. Or returning an array of "starts with" matches for available
50
+ # {Thor::Base::ClassMethods#all_commands} and any partial input.
51
+ #
52
+ # Checks against both {Thor::Command#name} ("method" name) and
53
+ # {Thor::Command#usage_name}, preferring usage name.
54
+ #
55
+ # @param [Request] request:
56
+ #
57
+ #
58
+ def bash_complete request:, index:
59
+ # logger.level = :trace
60
+
61
+ logger.trace __method__,
62
+ request: request,
63
+ index: index,
64
+ word: request.words[index]
65
+
66
+ # Index we'll incremenet as we scan past options
67
+ scan_index = index
68
+
69
+ while scan_index < request.cword &&
70
+ scan_index < request.words.length &&
71
+ request.words[scan_index].start_with?( '-' )
72
+ scan_index += 1
73
+ end
74
+
75
+ if scan_index == request.cword
76
+ return bash_complete_cur request: request
77
+ end
78
+
79
+ unless scan_index < request.words.length
80
+ # We ran out of words without hitting a command name or ending
81
+ # empty string (for which we would provide all commands as options)
82
+ #
83
+ # TODO In the future we can deal with half-written class options
84
+ # I guess? Maybe? But for now we just give up.
85
+ #
86
+ return [].tap { |results|
87
+ logger.trace "No command or empty string found",
88
+ results: []
89
+ }
90
+ end
91
+
92
+ # OK, we should have either '' or something we can match
93
+ match_word = request.words[scan_index]
94
+
95
+ # We have nothing, return all commands
96
+ if match_word == ''
97
+ return all_commands.values.map( &:usage_name ).tap { |results|
98
+ logger.trace "Empty match word, returning all commands",
99
+ results: results
100
+ }
101
+ end
102
+
103
+ # See what possibilities we have
104
+ possibilities = find_command_possibilities match_word.tr( '-', '_' )
105
+
106
+ case possibilities.length
107
+ when 0
108
+ # We couldn't match anything
109
+ logger.trace "Found no command possibilities",
110
+ match_word: match_word,
111
+ results: []
112
+
113
+ return []
114
+
115
+ when 1
116
+ # We have a unique match
117
+
118
+ logger.trace "Unique command matched",
119
+ match_word: match_word,
120
+ match: possibilities[0]
121
+
122
+ # pass to below
123
+
124
+ else
125
+ # There is more than one possbility, but we're trying to fill in
126
+ # something later on, so we're SO
127
+ return []
128
+ end
129
+
130
+ # See if we're got an extact match
131
+ cmd = all_commands[ possibilities[0].underscore ]
132
+
133
+ # Bump the index to the scan index + 1 to go past the word we just
134
+ # used to find `cmd`
135
+ index = scan_index + 1
136
+
137
+ # is it a subcommand?
138
+ if subcommand_classes.key? cmd.name
139
+ # It is, hand it off to there
140
+ subcommand_classes[cmd.name].bash_complete \
141
+ request: request,
142
+ index: index
143
+ else
144
+ cmd.bash_complete request: request, index: index, klass: self
145
+ end
146
+ end # #bash_complete
147
+
148
+
149
+
150
+ private
151
+ # ========================================================================
152
+
153
+ def bash_complete_cur request:
154
+ logger.trace "START #{ self.class.safe_name }.#{ __method__ }",
155
+ cur: request.cur
156
+
157
+ cur_method_name = if request.cur.start_with? '-'
158
+ request.cur
159
+ else
160
+ request.cur.tr( '-', '_' )
161
+ end
162
+
163
+ method_name_matches = find_command_possibilities( cur_method_name ).map &:to_s
164
+
165
+ logger.trace "Matched #{ method_name_matches.length } method name(s)",
166
+ matches: method_name_matches # ,
167
+ # all_method_names: all_commands.keys,
168
+ # map: map
169
+
170
+ case method_name_matches.length
171
+ when 0
172
+ return [].tap { |results|
173
+ logger.trace "No commands matched",
174
+ results: results
175
+ }
176
+
177
+ when 1
178
+ method_name = method_name_matches[0]
179
+
180
+ logger.trace "Found unique method name",
181
+ method_name: method_name
182
+
183
+ cmd = all_commands[ method_name ] || all_commands[ map[ method_name ].to_s ]
184
+
185
+ logger.trace "Got command",
186
+ command: cmd
187
+
188
+ cmd.names_by_format.each { |format, name|
189
+ if name.start_with?( request.cur )
190
+ return [ name ].tap { |results|
191
+ logger.trace \
192
+ "Prefix-matched cur against cmd's #{ format } name",
193
+ name_format: format,
194
+ results: results,
195
+ cmd: cmd
196
+ }
197
+ end
198
+ }
199
+
200
+ matching_mappings = map.map { |map_name, cmd_name|
201
+ map_name.to_s if cmd.name == cmd_name.to_s &&
202
+ map_name.to_s.start_with?( request.cur )
203
+ }.compact
204
+
205
+ return matching_mappings unless matching_mappings.empty?
206
+
207
+ return [ request.cur ]
208
+
209
+ else
210
+ # There is more than one possbility...
211
+
212
+ cmd_matches = method_name_matches.map { |method_name|
213
+ all_commands[ method_name ] || all_commands[ map[ method_name ].to_s ]
214
+ }.uniq
215
+
216
+ logger.trace "Unique command matches",
217
+ cmd_matches: cmd_matches
218
+
219
+ usage_name_results = cmd_matches.map( &:usage_name ).select { |usage_name|
220
+ usage_name.start_with? request.cur
221
+ }
222
+
223
+ return usage_name_results unless usage_name_results.empty?
224
+
225
+ return method_name_matches.select { |method_name|
226
+ method_name.start_with? request.cur
227
+ }
228
+ end
229
+ end # #bash_complete_cur
230
+
231
+ public # end private *****************************************************
232
+
233
+ end # module ClassMethods
234
+
235
+
236
+ # Hook to mix {ClassMethods} in on include.
237
+ #
238
+ def self.included base
239
+ base.extend ClassMethods
240
+ end
241
+
242
+ end # module ThorMixin
243
+
244
+
245
+ # /Namespace
246
+ # =======================================================================
247
+
248
+ end # module Bash
249
+ end # module Completion
250
+ end # class Thor
@@ -1,5 +1,12 @@
1
+ # Namespace
2
+ # ========================================================================
3
+
1
4
  class Thor
2
5
  module CoreExt
6
+
7
+ # Definitions
8
+ # ========================================================================
9
+
3
10
  # A hash with indifferent access and magic predicates.
4
11
  #
5
12
  # hash = HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
@@ -34,4 +41,11 @@ class HashWithIndifferentAccess < ::HashWithIndifferentAccess #:nodoc:
34
41
 
35
42
  public # end protected ***************************************************
36
43
 
37
- end; end; end
44
+ end
45
+
46
+
47
+ # /Namespace
48
+ # ========================================================================
49
+
50
+ end # module CoreExt
51
+ end # class Thor