thor 0.16.0 → 1.2.1
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 +7 -0
- data/CONTRIBUTING.md +15 -0
- data/README.md +23 -6
- data/bin/thor +1 -1
- data/lib/thor/actions/create_file.rb +34 -35
- data/lib/thor/actions/create_link.rb +9 -5
- data/lib/thor/actions/directory.rb +33 -23
- data/lib/thor/actions/empty_directory.rb +75 -85
- data/lib/thor/actions/file_manipulation.rb +103 -36
- data/lib/thor/actions/inject_into_file.rb +46 -36
- data/lib/thor/actions.rb +90 -68
- data/lib/thor/base.rb +302 -244
- data/lib/thor/command.rb +142 -0
- data/lib/thor/core_ext/hash_with_indifferent_access.rb +52 -24
- data/lib/thor/error.rb +90 -10
- data/lib/thor/group.rb +70 -74
- data/lib/thor/invocation.rb +63 -55
- data/lib/thor/line_editor/basic.rb +37 -0
- data/lib/thor/line_editor/readline.rb +88 -0
- data/lib/thor/line_editor.rb +17 -0
- data/lib/thor/nested_context.rb +29 -0
- data/lib/thor/parser/argument.rb +24 -28
- data/lib/thor/parser/arguments.rb +110 -102
- data/lib/thor/parser/option.rb +53 -15
- data/lib/thor/parser/options.rb +174 -97
- data/lib/thor/parser.rb +4 -4
- data/lib/thor/rake_compat.rb +12 -11
- data/lib/thor/runner.rb +159 -155
- data/lib/thor/shell/basic.rb +216 -93
- data/lib/thor/shell/color.rb +53 -40
- data/lib/thor/shell/html.rb +61 -58
- data/lib/thor/shell.rb +29 -36
- data/lib/thor/util.rb +231 -213
- data/lib/thor/version.rb +1 -1
- data/lib/thor.rb +303 -166
- data/thor.gemspec +27 -24
- metadata +36 -226
- data/.gitignore +0 -44
- data/.rspec +0 -2
- data/.travis.yml +0 -7
- data/CHANGELOG.rdoc +0 -134
- data/Gemfile +0 -15
- data/Thorfile +0 -30
- data/bin/rake2thor +0 -86
- data/lib/thor/core_ext/dir_escape.rb +0 -0
- data/lib/thor/core_ext/file_binary_read.rb +0 -9
- data/lib/thor/core_ext/ordered_hash.rb +0 -100
- data/lib/thor/task.rb +0 -132
- data/spec/actions/create_file_spec.rb +0 -170
- data/spec/actions/create_link_spec.rb +0 -81
- data/spec/actions/directory_spec.rb +0 -149
- data/spec/actions/empty_directory_spec.rb +0 -130
- data/spec/actions/file_manipulation_spec.rb +0 -370
- data/spec/actions/inject_into_file_spec.rb +0 -135
- data/spec/actions_spec.rb +0 -331
- data/spec/base_spec.rb +0 -279
- data/spec/core_ext/hash_with_indifferent_access_spec.rb +0 -43
- data/spec/core_ext/ordered_hash_spec.rb +0 -115
- data/spec/exit_condition_spec.rb +0 -19
- data/spec/fixtures/application.rb +0 -2
- data/spec/fixtures/app{1}/README +0 -3
- data/spec/fixtures/bundle/execute.rb +0 -6
- data/spec/fixtures/bundle/main.thor +0 -1
- data/spec/fixtures/doc/%file_name%.rb.tt +0 -1
- data/spec/fixtures/doc/COMMENTER +0 -10
- data/spec/fixtures/doc/README +0 -3
- data/spec/fixtures/doc/block_helper.rb +0 -3
- data/spec/fixtures/doc/components/.empty_directory +0 -0
- data/spec/fixtures/doc/config.rb +0 -1
- data/spec/fixtures/doc/config.yaml.tt +0 -1
- data/spec/fixtures/enum.thor +0 -10
- data/spec/fixtures/group.thor +0 -114
- data/spec/fixtures/invoke.thor +0 -112
- data/spec/fixtures/path with spaces +0 -0
- data/spec/fixtures/script.thor +0 -190
- data/spec/fixtures/task.thor +0 -10
- data/spec/group_spec.rb +0 -216
- data/spec/invocation_spec.rb +0 -100
- data/spec/parser/argument_spec.rb +0 -53
- data/spec/parser/arguments_spec.rb +0 -66
- data/spec/parser/option_spec.rb +0 -202
- data/spec/parser/options_spec.rb +0 -330
- data/spec/rake_compat_spec.rb +0 -72
- data/spec/register_spec.rb +0 -135
- data/spec/runner_spec.rb +0 -241
- data/spec/shell/basic_spec.rb +0 -300
- data/spec/shell/color_spec.rb +0 -81
- data/spec/shell/html_spec.rb +0 -32
- data/spec/shell_spec.rb +0 -47
- data/spec/spec_helper.rb +0 -59
- data/spec/task_spec.rb +0 -80
- data/spec/thor_spec.rb +0 -418
- data/spec/util_spec.rb +0 -196
data/lib/thor/shell/basic.rb
CHANGED
@@ -1,15 +1,18 @@
|
|
1
|
-
require 'tempfile'
|
2
|
-
|
3
1
|
class Thor
|
4
2
|
module Shell
|
5
3
|
class Basic
|
4
|
+
DEFAULT_TERMINAL_WIDTH = 80
|
5
|
+
|
6
6
|
attr_accessor :base
|
7
7
|
attr_reader :padding
|
8
8
|
|
9
9
|
# Initialize base, mute and padding to nil.
|
10
10
|
#
|
11
11
|
def initialize #:nodoc:
|
12
|
-
@base
|
12
|
+
@base = nil
|
13
|
+
@mute = false
|
14
|
+
@padding = 0
|
15
|
+
@always_force = false
|
13
16
|
end
|
14
17
|
|
15
18
|
# Mute everything that's inside given block
|
@@ -33,22 +36,54 @@ class Thor
|
|
33
36
|
@padding = [0, value].max
|
34
37
|
end
|
35
38
|
|
39
|
+
# Sets the output padding while executing a block and resets it.
|
40
|
+
#
|
41
|
+
def indent(count = 1)
|
42
|
+
orig_padding = padding
|
43
|
+
self.padding = padding + count
|
44
|
+
yield
|
45
|
+
self.padding = orig_padding
|
46
|
+
end
|
47
|
+
|
36
48
|
# Asks something to the user and receives a response.
|
37
49
|
#
|
50
|
+
# If a default value is specified it will be presented to the user
|
51
|
+
# and allows them to select that value with an empty response. This
|
52
|
+
# option is ignored when limited answers are supplied.
|
53
|
+
#
|
38
54
|
# If asked to limit the correct responses, you can pass in an
|
39
55
|
# array of acceptable answers. If one of those is not supplied,
|
40
56
|
# they will be shown a message stating that one of those answers
|
41
57
|
# must be given and re-asked the question.
|
42
58
|
#
|
59
|
+
# If asking for sensitive information, the :echo option can be set
|
60
|
+
# to false to mask user input from $stdin.
|
61
|
+
#
|
62
|
+
# If the required input is a path, then set the path option to
|
63
|
+
# true. This will enable tab completion for file paths relative
|
64
|
+
# to the current working directory on systems that support
|
65
|
+
# Readline.
|
66
|
+
#
|
43
67
|
# ==== Example
|
44
68
|
# ask("What is your name?")
|
45
69
|
#
|
70
|
+
# ask("What is the planet furthest from the sun?", :default => "Pluto")
|
71
|
+
#
|
46
72
|
# ask("What is your favorite Neopolitan flavor?", :limited_to => ["strawberry", "chocolate", "vanilla"])
|
47
73
|
#
|
74
|
+
# ask("What is your password?", :echo => false)
|
75
|
+
#
|
76
|
+
# ask("Where should the file be saved?", :path => true)
|
77
|
+
#
|
48
78
|
def ask(statement, *args)
|
49
79
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
80
|
+
color = args.first
|
50
81
|
|
51
|
-
|
82
|
+
if options[:limited_to]
|
83
|
+
ask_filtered(statement, color, options)
|
84
|
+
else
|
85
|
+
ask_simply(statement, color, options)
|
86
|
+
end
|
52
87
|
end
|
53
88
|
|
54
89
|
# Say (print) something to the user. If the sentence ends with a whitespace
|
@@ -58,50 +93,66 @@ class Thor
|
|
58
93
|
# ==== Example
|
59
94
|
# say("I know you knew that.")
|
60
95
|
#
|
61
|
-
def say(message="", color=nil, force_new_line=(message.to_s !~ /( |\t)
|
62
|
-
|
96
|
+
def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
|
97
|
+
return if quiet?
|
63
98
|
|
64
|
-
|
65
|
-
|
66
|
-
spaces = " " * padding
|
99
|
+
buffer = prepare_message(message, *color)
|
100
|
+
buffer << "\n" if force_new_line && !message.to_s.end_with?("\n")
|
67
101
|
|
68
|
-
|
69
|
-
stdout.puts(spaces + message)
|
70
|
-
else
|
71
|
-
stdout.print(spaces + message)
|
72
|
-
end
|
102
|
+
stdout.print(buffer)
|
73
103
|
stdout.flush
|
74
104
|
end
|
75
105
|
|
106
|
+
# Say (print) an error to the user. If the sentence ends with a whitespace
|
107
|
+
# or tab character, a new line is not appended (print + flush). Otherwise
|
108
|
+
# are passed straight to puts (behavior got from Highline).
|
109
|
+
#
|
110
|
+
# ==== Example
|
111
|
+
# say_error("error: something went wrong")
|
112
|
+
#
|
113
|
+
def say_error(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
|
114
|
+
return if quiet?
|
115
|
+
|
116
|
+
buffer = prepare_message(message, *color)
|
117
|
+
buffer << "\n" if force_new_line && !message.to_s.end_with?("\n")
|
118
|
+
|
119
|
+
stderr.print(buffer)
|
120
|
+
stderr.flush
|
121
|
+
end
|
122
|
+
|
76
123
|
# Say a status with the given color and appends the message. Since this
|
77
124
|
# method is used frequently by actions, it allows nil or false to be given
|
78
125
|
# in log_status, avoiding the message from being shown. If a Symbol is
|
79
126
|
# given in log_status, it's used as the color.
|
80
127
|
#
|
81
|
-
def say_status(status, message, log_status=true)
|
128
|
+
def say_status(status, message, log_status = true)
|
82
129
|
return if quiet? || log_status == false
|
83
130
|
spaces = " " * (padding + 1)
|
84
|
-
color = log_status.is_a?(Symbol) ? log_status : :green
|
85
|
-
|
86
131
|
status = status.to_s.rjust(12)
|
132
|
+
margin = " " * status.length + spaces
|
133
|
+
|
134
|
+
color = log_status.is_a?(Symbol) ? log_status : :green
|
87
135
|
status = set_color status, color, true if color
|
88
136
|
|
89
|
-
|
137
|
+
message = message.to_s.chomp.gsub(/(?<!\A)^/, margin)
|
138
|
+
buffer = "#{status}#{spaces}#{message}\n"
|
139
|
+
|
140
|
+
stdout.print(buffer)
|
90
141
|
stdout.flush
|
91
142
|
end
|
92
143
|
|
93
144
|
# Make a question the to user and returns true if the user replies "y" or
|
94
145
|
# "yes".
|
95
146
|
#
|
96
|
-
def yes?(statement, color=nil)
|
97
|
-
!!(ask(statement, color) =~ is?(:yes))
|
147
|
+
def yes?(statement, color = nil)
|
148
|
+
!!(ask(statement, color, :add_to_history => false) =~ is?(:yes))
|
98
149
|
end
|
99
150
|
|
100
151
|
# Make a question the to user and returns true if the user replies "n" or
|
101
152
|
# "no".
|
102
153
|
#
|
103
|
-
def no?(statement, color=nil)
|
104
|
-
|
154
|
+
def no?(statement, color = nil)
|
155
|
+
!!(ask(statement, color, :add_to_history => false) =~ is?(:no))
|
105
156
|
end
|
106
157
|
|
107
158
|
# Prints values in columns
|
@@ -111,7 +162,7 @@ class Thor
|
|
111
162
|
#
|
112
163
|
def print_in_columns(array)
|
113
164
|
return if array.empty?
|
114
|
-
colwidth = (array.map{|el| el.to_s.size}.max || 0) + 2
|
165
|
+
colwidth = (array.map { |el| el.to_s.size }.max || 0) + 2
|
115
166
|
array.each_with_index do |value, index|
|
116
167
|
# Don't output trailing spaces when printing the last column
|
117
168
|
if ((((index + 1) % (terminal_width / colwidth))).zero? && !index.zero?) || index + 1 == array.length
|
@@ -131,48 +182,50 @@ class Thor
|
|
131
182
|
# indent<Integer>:: Indent the first column by indent value.
|
132
183
|
# colwidth<Integer>:: Force the first column to colwidth spaces wide.
|
133
184
|
#
|
134
|
-
def print_table(array, options={})
|
185
|
+
def print_table(array, options = {}) # rubocop:disable MethodLength
|
135
186
|
return if array.empty?
|
136
187
|
|
137
|
-
formats
|
188
|
+
formats = []
|
189
|
+
indent = options[:indent].to_i
|
190
|
+
colwidth = options[:colwidth]
|
138
191
|
options[:truncate] = terminal_width if options[:truncate] == true
|
139
192
|
|
140
|
-
formats << "%-#{colwidth + 2}s" if colwidth
|
193
|
+
formats << "%-#{colwidth + 2}s".dup if colwidth
|
141
194
|
start = colwidth ? 1 : 0
|
142
195
|
|
143
|
-
colcount = array.max{|a,b| a.size <=> b.size }.size
|
196
|
+
colcount = array.max { |a, b| a.size <=> b.size }.size
|
144
197
|
|
145
198
|
maximas = []
|
146
199
|
|
147
200
|
start.upto(colcount - 1) do |index|
|
148
|
-
maxima = array.map {|row| row[index] ? row[index].to_s.size : 0 }.max
|
201
|
+
maxima = array.map { |row| row[index] ? row[index].to_s.size : 0 }.max
|
149
202
|
maximas << maxima
|
150
|
-
if index == colcount - 1
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
203
|
+
formats << if index == colcount - 1
|
204
|
+
# Don't output 2 trailing spaces when printing the last column
|
205
|
+
"%-s".dup
|
206
|
+
else
|
207
|
+
"%-#{maxima + 2}s".dup
|
208
|
+
end
|
156
209
|
end
|
157
210
|
|
158
211
|
formats[0] = formats[0].insert(0, " " * indent)
|
159
212
|
formats << "%s"
|
160
213
|
|
161
214
|
array.each do |row|
|
162
|
-
sentence = ""
|
215
|
+
sentence = "".dup
|
163
216
|
|
164
217
|
row.each_with_index do |column, index|
|
165
218
|
maxima = maximas[index]
|
166
219
|
|
167
|
-
if column.is_a?(Numeric)
|
220
|
+
f = if column.is_a?(Numeric)
|
168
221
|
if index == row.size - 1
|
169
222
|
# Don't output 2 trailing spaces when printing the last column
|
170
|
-
|
223
|
+
"%#{maxima}s"
|
171
224
|
else
|
172
|
-
|
225
|
+
"%#{maxima}s "
|
173
226
|
end
|
174
227
|
else
|
175
|
-
|
228
|
+
formats[index]
|
176
229
|
end
|
177
230
|
sentence << f % column.to_s
|
178
231
|
end
|
@@ -191,16 +244,27 @@ class Thor
|
|
191
244
|
# ==== Options
|
192
245
|
# indent<Integer>:: Indent each line of the printed paragraph by indent value.
|
193
246
|
#
|
194
|
-
def print_wrapped(message, options={})
|
247
|
+
def print_wrapped(message, options = {})
|
195
248
|
indent = options[:indent] || 0
|
196
249
|
width = terminal_width - indent
|
197
250
|
paras = message.split("\n\n")
|
198
251
|
|
199
252
|
paras.map! do |unwrapped|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
253
|
+
words = unwrapped.split(" ")
|
254
|
+
counter = words.first.length
|
255
|
+
words.inject do |memo, word|
|
256
|
+
word = word.gsub(/\n\005/, "\n").gsub(/\005/, "\n")
|
257
|
+
counter = 0 if word.include? "\n"
|
258
|
+
if (counter + word.length + 1) < width
|
259
|
+
memo = "#{memo} #{word}"
|
260
|
+
counter += (word.length + 1)
|
261
|
+
else
|
262
|
+
memo = "#{memo}\n#{word}"
|
263
|
+
counter = word.length
|
264
|
+
end
|
265
|
+
memo
|
266
|
+
end
|
267
|
+
end.compact!
|
204
268
|
|
205
269
|
paras.each do |para|
|
206
270
|
para.split("\n").each do |line|
|
@@ -216,30 +280,43 @@ class Thor
|
|
216
280
|
#
|
217
281
|
# ==== Parameters
|
218
282
|
# destination<String>:: the destination file to solve conflicts
|
219
|
-
# block<Proc>:: an optional block that returns the value to be used in diff
|
283
|
+
# block<Proc>:: an optional block that returns the value to be used in diff and merge
|
220
284
|
#
|
221
285
|
def file_collision(destination)
|
222
286
|
return true if @always_force
|
223
|
-
options = block_given? ? "[
|
287
|
+
options = block_given? ? "[Ynaqdhm]" : "[Ynaqh]"
|
224
288
|
|
225
|
-
|
226
|
-
answer = ask
|
289
|
+
loop do
|
290
|
+
answer = ask(
|
291
|
+
%[Overwrite #{destination}? (enter "h" for help) #{options}],
|
292
|
+
:add_to_history => false
|
293
|
+
)
|
227
294
|
|
228
295
|
case answer
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
296
|
+
when nil
|
297
|
+
say ""
|
298
|
+
return true
|
299
|
+
when is?(:yes), is?(:force), ""
|
300
|
+
return true
|
301
|
+
when is?(:no), is?(:skip)
|
302
|
+
return false
|
303
|
+
when is?(:always)
|
304
|
+
return @always_force = true
|
305
|
+
when is?(:quit)
|
306
|
+
say "Aborting..."
|
307
|
+
raise SystemExit
|
308
|
+
when is?(:diff)
|
309
|
+
show_diff(destination, yield) if block_given?
|
310
|
+
say "Retrying..."
|
311
|
+
when is?(:merge)
|
312
|
+
if block_given? && !merge_tool.empty?
|
313
|
+
merge(destination, yield)
|
314
|
+
return nil
|
315
|
+
end
|
316
|
+
|
317
|
+
say "Please specify merge tool to `THOR_MERGE` env."
|
318
|
+
else
|
319
|
+
say file_collision_help
|
243
320
|
end
|
244
321
|
end
|
245
322
|
end
|
@@ -247,14 +324,14 @@ class Thor
|
|
247
324
|
# This code was copied from Rake, available under MIT-LICENSE
|
248
325
|
# Copyright (c) 2003, 2004 Jim Weirich
|
249
326
|
def terminal_width
|
250
|
-
if ENV[
|
251
|
-
|
327
|
+
result = if ENV["THOR_COLUMNS"]
|
328
|
+
ENV["THOR_COLUMNS"].to_i
|
252
329
|
else
|
253
|
-
|
330
|
+
unix? ? dynamic_width : DEFAULT_TERMINAL_WIDTH
|
254
331
|
end
|
255
|
-
|
332
|
+
result < 10 ? DEFAULT_TERMINAL_WIDTH : result
|
256
333
|
rescue
|
257
|
-
|
334
|
+
DEFAULT_TERMINAL_WIDTH
|
258
335
|
end
|
259
336
|
|
260
337
|
# Called if something goes wrong during the execution. This is used by Thor
|
@@ -269,12 +346,21 @@ class Thor
|
|
269
346
|
# Apply color to the given string with optional bold. Disabled in the
|
270
347
|
# Thor::Shell::Basic class.
|
271
348
|
#
|
272
|
-
def set_color(string, *
|
349
|
+
def set_color(string, *) #:nodoc:
|
273
350
|
string
|
274
351
|
end
|
275
352
|
|
276
353
|
protected
|
277
354
|
|
355
|
+
def prepare_message(message, *color)
|
356
|
+
spaces = " " * padding
|
357
|
+
spaces + set_color(message.to_s, *color)
|
358
|
+
end
|
359
|
+
|
360
|
+
def can_display_colors?
|
361
|
+
false
|
362
|
+
end
|
363
|
+
|
278
364
|
def lookup_color(color)
|
279
365
|
return color unless color.is_a?(Symbol)
|
280
366
|
self.class.const_get(color.to_s.upcase)
|
@@ -284,10 +370,6 @@ class Thor
|
|
284
370
|
$stdout
|
285
371
|
end
|
286
372
|
|
287
|
-
def stdin
|
288
|
-
$stdin
|
289
|
-
end
|
290
|
-
|
291
373
|
def stderr
|
292
374
|
$stderr
|
293
375
|
end
|
@@ -298,24 +380,26 @@ class Thor
|
|
298
380
|
if value.size == 1
|
299
381
|
/\A#{value}\z/i
|
300
382
|
else
|
301
|
-
/\A(#{value}|#{value[0,1]})\z/i
|
383
|
+
/\A(#{value}|#{value[0, 1]})\z/i
|
302
384
|
end
|
303
385
|
end
|
304
386
|
|
305
387
|
def file_collision_help #:nodoc:
|
306
|
-
|
307
|
-
Y - yes, overwrite
|
308
|
-
n - no, do not overwrite
|
309
|
-
a - all, overwrite this and all others
|
310
|
-
q - quit, abort
|
311
|
-
d - diff, show the differences between the old and the new
|
312
|
-
h - help, show this help
|
313
|
-
|
388
|
+
<<-HELP
|
389
|
+
Y - yes, overwrite
|
390
|
+
n - no, do not overwrite
|
391
|
+
a - all, overwrite this and all others
|
392
|
+
q - quit, abort
|
393
|
+
d - diff, show the differences between the old and the new
|
394
|
+
h - help, show this help
|
395
|
+
m - merge, run merge tool
|
396
|
+
HELP
|
314
397
|
end
|
315
398
|
|
316
399
|
def show_diff(destination, content) #:nodoc:
|
317
|
-
diff_cmd = ENV[
|
400
|
+
diff_cmd = ENV["THOR_DIFF"] || ENV["RAILS_DIFF"] || "diff -u"
|
318
401
|
|
402
|
+
require "tempfile"
|
319
403
|
Tempfile.open(File.basename(destination), File.dirname(destination)) do |temp|
|
320
404
|
temp.write content
|
321
405
|
temp.rewind
|
@@ -333,11 +417,11 @@ HELP
|
|
333
417
|
end
|
334
418
|
|
335
419
|
def dynamic_width_stty
|
336
|
-
|
420
|
+
`stty size 2>/dev/null`.split[1].to_i
|
337
421
|
end
|
338
422
|
|
339
423
|
def dynamic_width_tput
|
340
|
-
|
424
|
+
`tput cols 2>/dev/null`.to_i
|
341
425
|
end
|
342
426
|
|
343
427
|
def unix?
|
@@ -350,7 +434,7 @@ HELP
|
|
350
434
|
if chars.length <= width
|
351
435
|
chars.join
|
352
436
|
else
|
353
|
-
|
437
|
+
chars[0, width - 3].join + "..."
|
354
438
|
end
|
355
439
|
end
|
356
440
|
end
|
@@ -361,29 +445,68 @@ HELP
|
|
361
445
|
end
|
362
446
|
else
|
363
447
|
def as_unicode
|
364
|
-
old
|
448
|
+
old = $KCODE
|
449
|
+
$KCODE = "U"
|
365
450
|
yield
|
366
451
|
ensure
|
367
452
|
$KCODE = old
|
368
453
|
end
|
369
454
|
end
|
370
455
|
|
371
|
-
def ask_simply(statement, color
|
372
|
-
|
373
|
-
|
456
|
+
def ask_simply(statement, color, options)
|
457
|
+
default = options[:default]
|
458
|
+
message = [statement, ("(#{default})" if default), nil].uniq.join(" ")
|
459
|
+
message = prepare_message(message, *color)
|
460
|
+
result = Thor::LineEditor.readline(message, options)
|
461
|
+
|
462
|
+
return unless result
|
463
|
+
|
464
|
+
result = result.strip
|
465
|
+
|
466
|
+
if default && result == ""
|
467
|
+
default
|
468
|
+
else
|
469
|
+
result
|
470
|
+
end
|
374
471
|
end
|
375
472
|
|
376
|
-
def ask_filtered(statement,
|
473
|
+
def ask_filtered(statement, color, options)
|
474
|
+
answer_set = options[:limited_to]
|
475
|
+
case_insensitive = options.fetch(:case_insensitive, false)
|
377
476
|
correct_answer = nil
|
378
477
|
until correct_answer
|
379
|
-
|
380
|
-
|
381
|
-
|
478
|
+
answers = answer_set.join(", ")
|
479
|
+
answer = ask_simply("#{statement} [#{answers}]", color, options)
|
480
|
+
correct_answer = answer_match(answer_set, answer, case_insensitive)
|
382
481
|
say("Your response must be one of: [#{answers}]. Please try again.") unless correct_answer
|
383
482
|
end
|
384
483
|
correct_answer
|
385
484
|
end
|
386
485
|
|
486
|
+
def answer_match(possibilities, answer, case_insensitive)
|
487
|
+
if case_insensitive
|
488
|
+
possibilities.detect{ |possibility| possibility.downcase == answer.downcase }
|
489
|
+
else
|
490
|
+
possibilities.detect{ |possibility| possibility == answer }
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
def merge(destination, content) #:nodoc:
|
495
|
+
require "tempfile"
|
496
|
+
Tempfile.open([File.basename(destination), File.extname(destination)], File.dirname(destination)) do |temp|
|
497
|
+
temp.write content
|
498
|
+
temp.rewind
|
499
|
+
system %(#{merge_tool} "#{temp.path}" "#{destination}")
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
def merge_tool #:nodoc:
|
504
|
+
@merge_tool ||= ENV["THOR_MERGE"] || git_merge_tool
|
505
|
+
end
|
506
|
+
|
507
|
+
def git_merge_tool #:nodoc:
|
508
|
+
`git config merge.tool`.rstrip rescue ""
|
509
|
+
end
|
387
510
|
end
|
388
511
|
end
|
389
512
|
end
|
data/lib/thor/shell/color.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
require_relative "basic"
|
2
2
|
|
3
3
|
class Thor
|
4
4
|
module Shell
|
@@ -77,7 +77,9 @@ class Thor
|
|
77
77
|
# :on_cyan
|
78
78
|
# :on_white
|
79
79
|
def set_color(string, *colors)
|
80
|
-
if colors.
|
80
|
+
if colors.compact.empty? || !can_display_colors?
|
81
|
+
string
|
82
|
+
elsif colors.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
|
81
83
|
ansi_colors = colors.map { |color| lookup_color(color) }
|
82
84
|
"#{ansi_colors.join}#{string}#{CLEAR}"
|
83
85
|
else
|
@@ -92,53 +94,64 @@ class Thor
|
|
92
94
|
end
|
93
95
|
end
|
94
96
|
|
95
|
-
|
97
|
+
protected
|
96
98
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
99
|
+
def can_display_colors?
|
100
|
+
are_colors_supported? && !are_colors_disabled?
|
101
|
+
end
|
102
|
+
|
103
|
+
def are_colors_supported?
|
104
|
+
stdout.tty? && ENV["TERM"] != "dumb"
|
105
|
+
end
|
106
|
+
|
107
|
+
def are_colors_disabled?
|
108
|
+
!ENV['NO_COLOR'].nil?
|
109
|
+
end
|
110
|
+
|
111
|
+
# Overwrite show_diff to show diff with colors if Diff::LCS is
|
112
|
+
# available.
|
113
|
+
#
|
114
|
+
def show_diff(destination, content) #:nodoc:
|
115
|
+
if diff_lcs_loaded? && ENV["THOR_DIFF"].nil? && ENV["RAILS_DIFF"].nil?
|
116
|
+
actual = File.binread(destination).to_s.split("\n")
|
117
|
+
content = content.to_s.split("\n")
|
104
118
|
|
105
|
-
|
106
|
-
|
107
|
-
end
|
108
|
-
else
|
109
|
-
super
|
119
|
+
Diff::LCS.sdiff(actual, content).each do |diff|
|
120
|
+
output_diff_line(diff)
|
110
121
|
end
|
122
|
+
else
|
123
|
+
super
|
111
124
|
end
|
125
|
+
end
|
112
126
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
end
|
127
|
+
def output_diff_line(diff) #:nodoc:
|
128
|
+
case diff.action
|
129
|
+
when "-"
|
130
|
+
say "- #{diff.old_element.chomp}", :red, true
|
131
|
+
when "+"
|
132
|
+
say "+ #{diff.new_element.chomp}", :green, true
|
133
|
+
when "!"
|
134
|
+
say "- #{diff.old_element.chomp}", :red, true
|
135
|
+
say "+ #{diff.new_element.chomp}", :green, true
|
136
|
+
else
|
137
|
+
say " #{diff.old_element.chomp}", nil, true
|
125
138
|
end
|
139
|
+
end
|
126
140
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
141
|
+
# Check if Diff::LCS is loaded. If it is, use it to create pretty output
|
142
|
+
# for diff.
|
143
|
+
#
|
144
|
+
def diff_lcs_loaded? #:nodoc:
|
145
|
+
return true if defined?(Diff::LCS)
|
146
|
+
return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
|
133
147
|
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
end
|
148
|
+
@diff_lcs_loaded = begin
|
149
|
+
require "diff/lcs"
|
150
|
+
true
|
151
|
+
rescue LoadError
|
152
|
+
false
|
140
153
|
end
|
141
|
-
|
154
|
+
end
|
142
155
|
end
|
143
156
|
end
|
144
157
|
end
|