atli 0.1.9 → 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.yardopts +1 -0
- data/atli.gemspec +3 -1
- data/lib/thor.rb +748 -732
- data/lib/thor/base.rb +160 -92
- data/lib/thor/base/arguments_concern.rb +298 -0
- data/lib/thor/base/class_methods.rb +31 -120
- data/lib/thor/base/shared_concern.rb +123 -0
- data/lib/thor/base/shared_options_concern.rb +235 -0
- data/lib/thor/command.rb +51 -6
- data/lib/thor/completion/bash.rb +71 -126
- data/lib/thor/completion/bash/argument_mixin.rb +83 -0
- data/lib/thor/completion/bash/command_mixin.rb +236 -0
- data/lib/thor/completion/bash/request.rb +48 -0
- data/lib/thor/completion/bash/subcmd.rb +136 -0
- data/lib/thor/completion/bash/thor_mixin.rb +250 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +15 -1
- data/lib/thor/execution.rb +2 -1
- data/lib/thor/parser/argument.rb +61 -10
- data/lib/thor/parser/arguments.rb +3 -1
- data/lib/thor/parser/option.rb +49 -0
- data/lib/thor/parser/options.rb +10 -7
- data/lib/thor/version.rb +13 -2
- metadata +26 -5
- data/lib/thor/core_ext/ordered_hash.rb +0 -129
@@ -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
|
44
|
+
end
|
45
|
+
|
46
|
+
|
47
|
+
# /Namespace
|
48
|
+
# ========================================================================
|
49
|
+
|
50
|
+
end # module CoreExt
|
51
|
+
end # class Thor
|