atli 0.1.9 → 0.1.10
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 +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
|