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.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/CONTRIBUTING.md +15 -0
  3. data/README.md +23 -6
  4. data/bin/thor +1 -1
  5. data/lib/thor/actions/create_file.rb +34 -35
  6. data/lib/thor/actions/create_link.rb +9 -5
  7. data/lib/thor/actions/directory.rb +33 -23
  8. data/lib/thor/actions/empty_directory.rb +75 -85
  9. data/lib/thor/actions/file_manipulation.rb +103 -36
  10. data/lib/thor/actions/inject_into_file.rb +46 -36
  11. data/lib/thor/actions.rb +90 -68
  12. data/lib/thor/base.rb +302 -244
  13. data/lib/thor/command.rb +142 -0
  14. data/lib/thor/core_ext/hash_with_indifferent_access.rb +52 -24
  15. data/lib/thor/error.rb +90 -10
  16. data/lib/thor/group.rb +70 -74
  17. data/lib/thor/invocation.rb +63 -55
  18. data/lib/thor/line_editor/basic.rb +37 -0
  19. data/lib/thor/line_editor/readline.rb +88 -0
  20. data/lib/thor/line_editor.rb +17 -0
  21. data/lib/thor/nested_context.rb +29 -0
  22. data/lib/thor/parser/argument.rb +24 -28
  23. data/lib/thor/parser/arguments.rb +110 -102
  24. data/lib/thor/parser/option.rb +53 -15
  25. data/lib/thor/parser/options.rb +174 -97
  26. data/lib/thor/parser.rb +4 -4
  27. data/lib/thor/rake_compat.rb +12 -11
  28. data/lib/thor/runner.rb +159 -155
  29. data/lib/thor/shell/basic.rb +216 -93
  30. data/lib/thor/shell/color.rb +53 -40
  31. data/lib/thor/shell/html.rb +61 -58
  32. data/lib/thor/shell.rb +29 -36
  33. data/lib/thor/util.rb +231 -213
  34. data/lib/thor/version.rb +1 -1
  35. data/lib/thor.rb +303 -166
  36. data/thor.gemspec +27 -24
  37. metadata +36 -226
  38. data/.gitignore +0 -44
  39. data/.rspec +0 -2
  40. data/.travis.yml +0 -7
  41. data/CHANGELOG.rdoc +0 -134
  42. data/Gemfile +0 -15
  43. data/Thorfile +0 -30
  44. data/bin/rake2thor +0 -86
  45. data/lib/thor/core_ext/dir_escape.rb +0 -0
  46. data/lib/thor/core_ext/file_binary_read.rb +0 -9
  47. data/lib/thor/core_ext/ordered_hash.rb +0 -100
  48. data/lib/thor/task.rb +0 -132
  49. data/spec/actions/create_file_spec.rb +0 -170
  50. data/spec/actions/create_link_spec.rb +0 -81
  51. data/spec/actions/directory_spec.rb +0 -149
  52. data/spec/actions/empty_directory_spec.rb +0 -130
  53. data/spec/actions/file_manipulation_spec.rb +0 -370
  54. data/spec/actions/inject_into_file_spec.rb +0 -135
  55. data/spec/actions_spec.rb +0 -331
  56. data/spec/base_spec.rb +0 -279
  57. data/spec/core_ext/hash_with_indifferent_access_spec.rb +0 -43
  58. data/spec/core_ext/ordered_hash_spec.rb +0 -115
  59. data/spec/exit_condition_spec.rb +0 -19
  60. data/spec/fixtures/application.rb +0 -2
  61. data/spec/fixtures/app{1}/README +0 -3
  62. data/spec/fixtures/bundle/execute.rb +0 -6
  63. data/spec/fixtures/bundle/main.thor +0 -1
  64. data/spec/fixtures/doc/%file_name%.rb.tt +0 -1
  65. data/spec/fixtures/doc/COMMENTER +0 -10
  66. data/spec/fixtures/doc/README +0 -3
  67. data/spec/fixtures/doc/block_helper.rb +0 -3
  68. data/spec/fixtures/doc/components/.empty_directory +0 -0
  69. data/spec/fixtures/doc/config.rb +0 -1
  70. data/spec/fixtures/doc/config.yaml.tt +0 -1
  71. data/spec/fixtures/enum.thor +0 -10
  72. data/spec/fixtures/group.thor +0 -114
  73. data/spec/fixtures/invoke.thor +0 -112
  74. data/spec/fixtures/path with spaces +0 -0
  75. data/spec/fixtures/script.thor +0 -190
  76. data/spec/fixtures/task.thor +0 -10
  77. data/spec/group_spec.rb +0 -216
  78. data/spec/invocation_spec.rb +0 -100
  79. data/spec/parser/argument_spec.rb +0 -53
  80. data/spec/parser/arguments_spec.rb +0 -66
  81. data/spec/parser/option_spec.rb +0 -202
  82. data/spec/parser/options_spec.rb +0 -330
  83. data/spec/rake_compat_spec.rb +0 -72
  84. data/spec/register_spec.rb +0 -135
  85. data/spec/runner_spec.rb +0 -241
  86. data/spec/shell/basic_spec.rb +0 -300
  87. data/spec/shell/color_spec.rb +0 -81
  88. data/spec/shell/html_spec.rb +0 -32
  89. data/spec/shell_spec.rb +0 -47
  90. data/spec/spec_helper.rb +0 -59
  91. data/spec/task_spec.rb +0 -80
  92. data/spec/thor_spec.rb +0 -418
  93. data/spec/util_spec.rb +0 -196
@@ -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, @mute, @padding = nil, false, 0
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
- options[:limited_to] ? ask_filtered(statement, options[:limited_to], *args) : ask_simply(statement, *args)
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
- message = message.to_s
96
+ def say(message = "", color = nil, force_new_line = (message.to_s !~ /( |\t)\Z/))
97
+ return if quiet?
63
98
 
64
- message = set_color(message, *color) if color
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
- if force_new_line
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
- stdout.puts "#{status}#{spaces}#{message}"
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
- !yes?(statement, color)
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, indent, colwidth = [], options[:indent].to_i, options[:colwidth]
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
- # Don't output 2 trailing spaces when printing the last column
152
- formats << "%-s"
153
- else
154
- formats << "%-#{maxima + 2}s"
155
- end
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
- f = "%#{maxima}s"
223
+ "%#{maxima}s"
171
224
  else
172
- f = "%#{maxima}s "
225
+ "%#{maxima}s "
173
226
  end
174
227
  else
175
- f = formats[index]
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
- unwrapped.strip.gsub(/\n/, " ").squeeze(" ").
201
- gsub(/.{1,#{width}}(?:\s|\Z)/){($& + 5.chr).
202
- gsub(/\n\005/,"\n").gsub(/\005/,"\n")}
203
- end
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? ? "[Ynaqdh]" : "[Ynaqh]"
287
+ options = block_given? ? "[Ynaqdhm]" : "[Ynaqh]"
224
288
 
225
- while true
226
- answer = ask %[Overwrite #{destination}? (enter "h" for help) #{options}]
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
- when is?(:yes), is?(:force), ""
230
- return true
231
- when is?(:no), is?(:skip)
232
- return false
233
- when is?(:always)
234
- return @always_force = true
235
- when is?(:quit)
236
- say 'Aborting...'
237
- raise SystemExit
238
- when is?(:diff)
239
- show_diff(destination, yield) if block_given?
240
- say 'Retrying...'
241
- else
242
- say file_collision_help
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['THOR_COLUMNS']
251
- result = ENV['THOR_COLUMNS'].to_i
327
+ result = if ENV["THOR_COLUMNS"]
328
+ ENV["THOR_COLUMNS"].to_i
252
329
  else
253
- result = unix? ? dynamic_width : 80
330
+ unix? ? dynamic_width : DEFAULT_TERMINAL_WIDTH
254
331
  end
255
- (result < 10) ? 80 : result
332
+ result < 10 ? DEFAULT_TERMINAL_WIDTH : result
256
333
  rescue
257
- 80
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, *args) #:nodoc:
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
- <<HELP
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
- HELP
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['THOR_DIFF'] || ENV['RAILS_DIFF'] || 'diff -u'
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
- %x{stty size 2>/dev/null}.split[1].to_i
420
+ `stty size 2>/dev/null`.split[1].to_i
337
421
  end
338
422
 
339
423
  def dynamic_width_tput
340
- %x{tput cols 2>/dev/null}.to_i
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
- ( chars[0, width-3].join ) + "..."
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, $KCODE = $KCODE, "U"
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=nil)
372
- say("#{statement} ", color)
373
- stdin.gets.strip
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, answer_set, *args)
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
- answer = ask_simply("#{statement} #{answer_set.inspect}", *args)
380
- correct_answer = answer_set.include?(answer) ? answer : nil
381
- answers = answer_set.map(&:inspect).join(", ")
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
@@ -1,4 +1,4 @@
1
- require 'thor/shell/basic'
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.all? { |color| color.is_a?(Symbol) || color.is_a?(String) }
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
- protected
97
+ protected
96
98
 
97
- # Overwrite show_diff to show diff with colors if Diff::LCS is
98
- # available.
99
- #
100
- def show_diff(destination, content) #:nodoc:
101
- if diff_lcs_loaded? && ENV['THOR_DIFF'].nil? && ENV['RAILS_DIFF'].nil?
102
- actual = File.binread(destination).to_s.split("\n")
103
- content = content.to_s.split("\n")
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
- Diff::LCS.sdiff(actual, content).each do |diff|
106
- output_diff_line(diff)
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
- def output_diff_line(diff) #:nodoc:
114
- case diff.action
115
- when '-'
116
- say "- #{diff.old_element.chomp}", :red, true
117
- when '+'
118
- say "+ #{diff.new_element.chomp}", :green, true
119
- when '!'
120
- say "- #{diff.old_element.chomp}", :red, true
121
- say "+ #{diff.new_element.chomp}", :green, true
122
- else
123
- say " #{diff.old_element.chomp}", nil, true
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
- # Check if Diff::LCS is loaded. If it is, use it to create pretty output
128
- # for diff.
129
- #
130
- def diff_lcs_loaded? #:nodoc:
131
- return true if defined?(Diff::LCS)
132
- return @diff_lcs_loaded unless @diff_lcs_loaded.nil?
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
- @diff_lcs_loaded = begin
135
- require 'diff/lcs'
136
- true
137
- rescue LoadError
138
- false
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