highline 1.6.1 → 1.6.14

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.
data/lib/highline.rb CHANGED
@@ -1,5 +1,3 @@
1
- #!/usr/local/bin/ruby -w
2
-
3
1
  # highline.rb
4
2
  #
5
3
  # Created by James Edward Gray II on 2005-04-26.
@@ -17,6 +15,7 @@ require "highline/system_extensions"
17
15
  require "highline/question"
18
16
  require "highline/menu"
19
17
  require "highline/color_scheme"
18
+ require "highline/style"
20
19
 
21
20
  #
22
21
  # A HighLine object is a "high-level line oriented" shell over an input and an
@@ -29,7 +28,7 @@ require "highline/color_scheme"
29
28
  #
30
29
  class HighLine
31
30
  # The version of the installed library.
32
- VERSION = "1.6.1".freeze
31
+ VERSION = "1.6.14".freeze
33
32
 
34
33
  # An internal HighLine error. User code does not need to trap this.
35
34
  class QuestionError < StandardError
@@ -49,6 +48,13 @@ class HighLine
49
48
  @@use_color
50
49
  end
51
50
 
51
+ # For checking if the current version of HighLine supports RGB colors
52
+ # Usage: HighLine.supports_rgb_color? rescue false # rescue for compatibility with older versions
53
+ # Note: color usage also depends on HighLine.use_color being set
54
+ def self.supports_rgb_color?
55
+ true
56
+ end
57
+
52
58
  # The setting used to disable EOF tracking.
53
59
  @@track_eof = true
54
60
 
@@ -83,62 +89,85 @@ class HighLine
83
89
  #
84
90
  # Embed in a String to clear all previous ANSI sequences. This *MUST* be
85
91
  # done before the program exits!
86
- #
87
- CLEAR = "\e[0m"
88
- # An alias for CLEAR.
89
- RESET = CLEAR
90
- # Erase the current line of terminal output.
91
- ERASE_LINE = "\e[K"
92
- # Erase the character under the cursor.
93
- ERASE_CHAR = "\e[P"
94
- # The start of an ANSI bold sequence.
95
- BOLD = "\e[1m"
96
- # The start of an ANSI dark sequence. (Terminal support uncommon.)
97
- DARK = "\e[2m"
98
- # The start of an ANSI underline sequence.
99
- UNDERLINE = "\e[4m"
100
- # An alias for UNDERLINE.
101
- UNDERSCORE = UNDERLINE
102
- # The start of an ANSI blink sequence. (Terminal support uncommon.)
103
- BLINK = "\e[5m"
104
- # The start of an ANSI reverse sequence.
105
- REVERSE = "\e[7m"
106
- # The start of an ANSI concealed sequence. (Terminal support uncommon.)
107
- CONCEALED = "\e[8m"
108
-
109
- # Set the terminal's foreground ANSI color to black.
110
- BLACK = "\e[30m"
111
- # Set the terminal's foreground ANSI color to red.
112
- RED = "\e[31m"
113
- # Set the terminal's foreground ANSI color to green.
114
- GREEN = "\e[32m"
115
- # Set the terminal's foreground ANSI color to yellow.
116
- YELLOW = "\e[33m"
117
- # Set the terminal's foreground ANSI color to blue.
118
- BLUE = "\e[34m"
119
- # Set the terminal's foreground ANSI color to magenta.
120
- MAGENTA = "\e[35m"
121
- # Set the terminal's foreground ANSI color to cyan.
122
- CYAN = "\e[36m"
123
- # Set the terminal's foreground ANSI color to white.
124
- WHITE = "\e[37m"
125
-
126
- # Set the terminal's background ANSI color to black.
127
- ON_BLACK = "\e[40m"
128
- # Set the terminal's background ANSI color to red.
129
- ON_RED = "\e[41m"
130
- # Set the terminal's background ANSI color to green.
131
- ON_GREEN = "\e[42m"
132
- # Set the terminal's background ANSI color to yellow.
133
- ON_YELLOW = "\e[43m"
134
- # Set the terminal's background ANSI color to blue.
135
- ON_BLUE = "\e[44m"
136
- # Set the terminal's background ANSI color to magenta.
137
- ON_MAGENTA = "\e[45m"
138
- # Set the terminal's background ANSI color to cyan.
139
- ON_CYAN = "\e[46m"
140
- # Set the terminal's background ANSI color to white.
141
- ON_WHITE = "\e[47m"
92
+ #
93
+
94
+ ERASE_LINE_STYLE = Style.new(:name=>:erase_line, :builtin=>true, :code=>"\e[K") # Erase the current line of terminal output
95
+ ERASE_CHAR_STYLE = Style.new(:name=>:erase_char, :builtin=>true, :code=>"\e[P") # Erase the character under the cursor.
96
+ CLEAR_STYLE = Style.new(:name=>:clear, :builtin=>true, :code=>"\e[0m") # Clear color settings
97
+ RESET_STYLE = Style.new(:name=>:reset, :builtin=>true, :code=>"\e[0m") # Alias for CLEAR.
98
+ BOLD_STYLE = Style.new(:name=>:bold, :builtin=>true, :code=>"\e[1m") # Bold; Note: bold + a color works as you'd expect,
99
+ # for example bold black. Bold without a color displays
100
+ # the system-defined bold color (e.g. red on Mac iTerm)
101
+ DARK_STYLE = Style.new(:name=>:dark, :builtin=>true, :code=>"\e[2m") # Dark; support uncommon
102
+ UNDERLINE_STYLE = Style.new(:name=>:underline, :builtin=>true, :code=>"\e[4m") # Underline
103
+ UNDERSCORE_STYLE = Style.new(:name=>:underscore, :builtin=>true, :code=>"\e[4m") # Alias for UNDERLINE
104
+ BLINK_STYLE = Style.new(:name=>:blink, :builtin=>true, :code=>"\e[5m") # Blink; support uncommon
105
+ REVERSE_STYLE = Style.new(:name=>:reverse, :builtin=>true, :code=>"\e[7m") # Reverse foreground and background
106
+ CONCEALED_STYLE = Style.new(:name=>:concealed, :builtin=>true, :code=>"\e[8m") # Concealed; support uncommon
107
+
108
+ STYLES = %w{CLEAR RESET BOLD DARK UNDERLINE UNDERSCORE BLINK REVERSE CONCEALED}
109
+
110
+ # These RGB colors are approximate; see http://en.wikipedia.org/wiki/ANSI_escape_code
111
+ BLACK_STYLE = Style.new(:name=>:black, :builtin=>true, :code=>"\e[30m", :rgb=>[ 0, 0, 0])
112
+ RED_STYLE = Style.new(:name=>:red, :builtin=>true, :code=>"\e[31m", :rgb=>[128, 0, 0])
113
+ GREEN_STYLE = Style.new(:name=>:green, :builtin=>true, :code=>"\e[32m", :rgb=>[ 0,128, 0])
114
+ BLUE_STYLE = Style.new(:name=>:blue, :builtin=>true, :code=>"\e[34m", :rgb=>[ 0, 0,128])
115
+ YELLOW_STYLE = Style.new(:name=>:yellow, :builtin=>true, :code=>"\e[33m", :rgb=>[128,128, 0])
116
+ MAGENTA_STYLE = Style.new(:name=>:magenta, :builtin=>true, :code=>"\e[35m", :rgb=>[128, 0,128])
117
+ CYAN_STYLE = Style.new(:name=>:cyan, :builtin=>true, :code=>"\e[36m", :rgb=>[ 0,128,128])
118
+ # On Mac OSX Terminal, white is actually gray
119
+ WHITE_STYLE = Style.new(:name=>:white, :builtin=>true, :code=>"\e[37m", :rgb=>[192,192,192])
120
+ # Alias for WHITE, since WHITE is actually a light gray on Macs
121
+ GRAY_STYLE = Style.new(:name=>:gray, :builtin=>true, :code=>"\e[37m", :rgb=>[192,192,192])
122
+ # On Mac OSX Terminal, this is black foreground, or bright white background.
123
+ # Also used as base for RGB colors, if available
124
+ NONE_STYLE = Style.new(:name=>:none, :builtin=>true, :code=>"\e[38m", :rgb=>[ 0, 0, 0])
125
+
126
+ BASIC_COLORS = %w{BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE GRAY NONE}
127
+
128
+ colors = BASIC_COLORS.dup
129
+ BASIC_COLORS.each do |color|
130
+ bright_color = "BRIGHT_#{color}"
131
+ colors << bright_color
132
+ const_set bright_color+'_STYLE', const_get(color + '_STYLE').bright
133
+ end
134
+ COLORS = colors
135
+
136
+ colors.each do |color|
137
+ const_set color, const_get("#{color}_STYLE").code
138
+ const_set "ON_#{color}_STYLE", const_get("#{color}_STYLE").on
139
+ const_set "ON_#{color}", const_get("ON_#{color}_STYLE").code
140
+ end
141
+ ON_NONE_STYLE.rgb = [255,255,255] # Override; white background
142
+
143
+ STYLES.each do |style|
144
+ const_set style, const_get("#{style}_STYLE").code
145
+ end
146
+
147
+ # For RGB colors:
148
+ def self.const_missing(name)
149
+ if name.to_s =~ /^(ON_)?(RGB_)([A-F0-9]{6})(_STYLE)?$/ # RGB color
150
+ on = $1
151
+ suffix = $4
152
+ if suffix
153
+ code_name = $1.to_s + $2 + $3
154
+ else
155
+ code_name = name.to_s
156
+ end
157
+ style_name = code_name + '_STYLE'
158
+ style = Style.rgb($3)
159
+ style = style.on if on
160
+ const_set(style_name, style)
161
+ const_set(code_name, style.code)
162
+ if suffix
163
+ style
164
+ else
165
+ style.code
166
+ end
167
+ else
168
+ raise NameError, "Bad color or uninitialized constant #{name}"
169
+ end
170
+ end
142
171
 
143
172
  #
144
173
  # Create an instance of HighLine, connected to the streams _input_
@@ -146,6 +175,7 @@ class HighLine
146
175
  #
147
176
  def initialize( input = $stdin, output = $stdout,
148
177
  wrap_at = nil, page_at = nil )
178
+ super()
149
179
  @input = input
150
180
  @output = output
151
181
 
@@ -211,7 +241,9 @@ class HighLine
211
241
  # readline() needs to handle it's own output, but readline only supports
212
242
  # full line reading. Therefore if @question.echo is anything but true,
213
243
  # the prompt will not be issued. And we have to account for that now.
214
- say(@question) unless (@question.readline and @question.echo == true)
244
+ # Also, JRuby-1.7's ConsoleReader.readLine() needs to be passed the prompt
245
+ # to handle line editing properly.
246
+ say(@question) unless ((JRUBY or @question.readline) and @question.echo == true)
215
247
  begin
216
248
  @answer = @question.answer_or_default(get_response)
217
249
  unless @question.valid_answer?(@answer)
@@ -328,19 +360,34 @@ class HighLine
328
360
  # This method returns the original _string_ unchanged if HighLine::use_color?
329
361
  # is +false+.
330
362
  #
331
- def color( string, *colors )
332
- return string unless self.class.use_color?
333
-
334
- colors.map! do |c|
335
- if self.class.using_color_scheme? and self.class.color_scheme.include? c
336
- self.class.color_scheme[c]
337
- elsif c.is_a? Symbol
338
- self.class.const_get(c.to_s.upcase)
339
- else
340
- c
341
- end
342
- end
343
- "#{colors.flatten.join}#{string}#{CLEAR}"
363
+ def self.color( string, *colors )
364
+ return string unless self.use_color?
365
+ Style(*colors).color(string)
366
+ end
367
+
368
+ # In case you just want the color code, without the embedding and the CLEAR
369
+ def self.color_code(*colors)
370
+ Style(*colors).code
371
+ end
372
+
373
+ # Works as an instance method, same as the class method
374
+ def color_code(*colors)
375
+ self.class.color_code(*colors)
376
+ end
377
+
378
+ # Works as an instance method, same as the class method
379
+ def color(*args)
380
+ self.class.color(*args)
381
+ end
382
+
383
+ # Remove color codes from a string
384
+ def self.uncolor(string)
385
+ Style.uncolor(string)
386
+ end
387
+
388
+ # Works as an instance method, same as the class method
389
+ def uncolor(string)
390
+ self.class.uncolor(string)
344
391
  end
345
392
 
346
393
  #
@@ -352,20 +399,25 @@ class HighLine
352
399
  # to list. A specified _mode_ controls how that list is formed and _option_
353
400
  # has different effects, depending on the _mode_. Recognized modes are:
354
401
  #
355
- # <tt>:columns_across</tt>:: _items_ will be placed in columns, flowing
356
- # from left to right. If given, _option_ is the
357
- # number of columns to be used. When absent,
358
- # columns will be determined based on _wrap_at_
359
- # or a default of 80 characters.
360
- # <tt>:columns_down</tt>:: Identical to <tt>:columns_across</tt>, save
361
- # flow goes down.
362
- # <tt>:inline</tt>:: All _items_ are placed on a single line. The
363
- # last two _items_ are separated by _option_ or
364
- # a default of " or ". All other _items_ are
365
- # separated by ", ".
366
- # <tt>:rows</tt>:: The default mode. Each of the _items_ is
367
- # placed on it's own line. The _option_
368
- # parameter is ignored in this mode.
402
+ # <tt>:columns_across</tt>:: _items_ will be placed in columns,
403
+ # flowing from left to right. If given,
404
+ # _option_ is the number of columns to be
405
+ # used. When absent, columns will be
406
+ # determined based on _wrap_at_ or a
407
+ # default of 80 characters.
408
+ # <tt>:columns_down</tt>:: Identical to <tt>:columns_across</tt>,
409
+ # save flow goes down.
410
+ # <tt>:uneven_columns_across</tt>:: Like <tt>:columns_across</tt> but each
411
+ # column is sized independently.
412
+ # <tt>:uneven_columns_down</tt>:: Like <tt>:columns_down</tt> but each
413
+ # column is sized independently.
414
+ # <tt>:inline</tt>:: All _items_ are placed on a single line.
415
+ # The last two _items_ are separated by
416
+ # _option_ or a default of " or ". All
417
+ # other _items_ are separated by ", ".
418
+ # <tt>:rows</tt>:: The default mode. Each of the _items_ is
419
+ # placed on it's own line. The _option_
420
+ # parameter is ignored in this mode.
369
421
  #
370
422
  # Each member of the _items_ Array is passed through ERb and thus can contain
371
423
  # their own expansions. Color escape expansions do not contribute to the
@@ -376,58 +428,160 @@ class HighLine
376
428
  ERB.new(item, nil, "%").result(binding)
377
429
  end
378
430
 
379
- case mode
380
- when :inline
381
- option = " or " if option.nil?
431
+ if items.empty?
432
+ ""
433
+ else
434
+ case mode
435
+ when :inline
436
+ option = " or " if option.nil?
437
+
438
+ if items.size == 1
439
+ items.first
440
+ else
441
+ items[0..-2].join(", ") + "#{option}#{items.last}"
442
+ end
443
+ when :columns_across, :columns_down
444
+ max_length = actual_length(
445
+ items.max { |a, b| actual_length(a) <=> actual_length(b) }
446
+ )
447
+
448
+ if option.nil?
449
+ limit = @wrap_at || 80
450
+ option = (limit + 2) / (max_length + 2)
451
+ end
452
+
453
+ items = items.map do |item|
454
+ pad = max_length + (item.to_s.length - actual_length(item))
455
+ "%-#{pad}s" % item
456
+ end
457
+ row_count = (items.size / option.to_f).ceil
382
458
 
383
- case items.size
384
- when 0
385
- ""
386
- when 1
387
- items.first
388
- when 2
389
- "#{items.first}#{option}#{items.last}"
390
- else
391
- items[0..-2].join(", ") + "#{option}#{items.last}"
392
- end
393
- when :columns_across, :columns_down
394
- max_length = actual_length(
395
- items.max { |a, b| actual_length(a) <=> actual_length(b) }
396
- )
397
-
398
- if option.nil?
399
- limit = @wrap_at || 80
400
- option = (limit + 2) / (max_length + 2)
401
- end
459
+ if mode == :columns_across
460
+ rows = Array.new(row_count) { Array.new }
461
+ items.each_with_index do |item, index|
462
+ rows[index / option] << item
463
+ end
402
464
 
403
- items = items.map do |item|
404
- pad = max_length + (item.length - actual_length(item))
405
- "%-#{pad}s" % item
406
- end
407
- row_count = (items.size / option.to_f).ceil
465
+ rows.map { |row| row.join(" ") + "\n" }.join
466
+ else
467
+ columns = Array.new(option) { Array.new }
468
+ items.each_with_index do |item, index|
469
+ columns[index / row_count] << item
470
+ end
408
471
 
409
- if mode == :columns_across
410
- rows = Array.new(row_count) { Array.new }
411
- items.each_with_index do |item, index|
412
- rows[index / option] << item
472
+ list = ""
473
+ columns.first.size.times do |index|
474
+ list << columns.map { |column| column[index] }.
475
+ compact.join(" ") + "\n"
476
+ end
477
+ list
413
478
  end
479
+ when :uneven_columns_across
480
+ if option.nil?
481
+ limit = @wrap_at || 80
482
+ items.size.downto(1) do |column_count|
483
+ row_count = (items.size / column_count.to_f).ceil
484
+ rows = Array.new(row_count) { Array.new }
485
+ items.each_with_index do |item, index|
486
+ rows[index / column_count] << item
487
+ end
414
488
 
415
- rows.map { |row| row.join(" ") + "\n" }.join
416
- else
417
- columns = Array.new(option) { Array.new }
418
- items.each_with_index do |item, index|
419
- columns[index / row_count] << item
489
+ widths = Array.new(column_count, 0)
490
+ rows.each do |row|
491
+ row.each_with_index do |field, column|
492
+ size = actual_length(field)
493
+ widths[column] = size if size > widths[column]
494
+ end
495
+ end
496
+
497
+ if column_count == 1 or
498
+ widths.inject(0) { |sum, n| sum + n + 2 } <= limit + 2
499
+ return rows.map { |row|
500
+ row.zip(widths).map { |field, i|
501
+ "%-#{i + (field.to_s.length - actual_length(field))}s" % field
502
+ }.join(" ") + "\n"
503
+ }.join
504
+ end
505
+ end
506
+ else
507
+ row_count = (items.size / option.to_f).ceil
508
+ rows = Array.new(row_count) { Array.new }
509
+ items.each_with_index do |item, index|
510
+ rows[index / option] << item
511
+ end
512
+
513
+ widths = Array.new(option, 0)
514
+ rows.each do |row|
515
+ row.each_with_index do |field, column|
516
+ size = actual_length(field)
517
+ widths[column] = size if size > widths[column]
518
+ end
519
+ end
520
+
521
+ return rows.map { |row|
522
+ row.zip(widths).map { |field, i|
523
+ "%-#{i + (field.to_s.length - actual_length(field))}s" % field
524
+ }.join(" ") + "\n"
525
+ }.join
420
526
  end
421
-
422
- list = ""
423
- columns.first.size.times do |index|
424
- list << columns.map { |column| column[index] }.
425
- compact.join(" ") + "\n"
527
+ when :uneven_columns_down
528
+ if option.nil?
529
+ limit = @wrap_at || 80
530
+ items.size.downto(1) do |column_count|
531
+ row_count = (items.size / column_count.to_f).ceil
532
+ columns = Array.new(column_count) { Array.new }
533
+ items.each_with_index do |item, index|
534
+ columns[index / row_count] << item
535
+ end
536
+
537
+ widths = Array.new(column_count, 0)
538
+ columns.each_with_index do |column, i|
539
+ column.each do |field|
540
+ size = actual_length(field)
541
+ widths[i] = size if size > widths[i]
542
+ end
543
+ end
544
+
545
+ if column_count == 1 or
546
+ widths.inject(0) { |sum, n| sum + n + 2 } <= limit + 2
547
+ list = ""
548
+ columns.first.size.times do |index|
549
+ list << columns.zip(widths).map { |column, width|
550
+ field = column[index]
551
+ "%-#{width + (field.to_s.length - actual_length(field))}s" %
552
+ field
553
+ }.compact.join(" ").strip + "\n"
554
+ end
555
+ return list
556
+ end
557
+ end
558
+ else
559
+ row_count = (items.size / option.to_f).ceil
560
+ columns = Array.new(option) { Array.new }
561
+ items.each_with_index do |item, index|
562
+ columns[index / row_count] << item
563
+ end
564
+
565
+ widths = Array.new(option, 0)
566
+ columns.each_with_index do |column, i|
567
+ column.each do |field|
568
+ size = actual_length(field)
569
+ widths[i] = size if size > widths[i]
570
+ end
571
+ end
572
+
573
+ list = ""
574
+ columns.first.size.times do |index|
575
+ list << columns.zip(widths).map { |column, width|
576
+ field = column[index]
577
+ "%-#{width + (field.to_s.length - actual_length(field))}s" % field
578
+ }.compact.join(" ").strip + "\n"
579
+ end
580
+ return list
426
581
  end
427
- list
582
+ else
583
+ items.map { |i| "#{i}\n" }.join
428
584
  end
429
- else
430
- items.map { |i| "#{i}\n" }.join
431
585
  end
432
586
  end
433
587
 
@@ -523,44 +677,68 @@ class HighLine
523
677
  # Raises EOFError if input is exhausted.
524
678
  #
525
679
  def gather( )
526
- @gather = @question.gather
527
- @answers = [ ]
528
680
  original_question = @question
529
-
681
+ original_question_string = @question.question
682
+ original_gather = @question.gather
683
+
684
+ verify_match = @question.verify_match
530
685
  @question.gather = false
531
-
532
- case @gather
533
- when Integer
534
- @answers << ask(@question)
535
- @gather -= 1
536
686
 
537
- original_question.question = ""
538
- until @gather.zero?
539
- @question = original_question
540
- @answers << ask(@question)
541
- @gather -= 1
542
- end
543
- when String, Regexp
544
- @answers << ask(@question)
687
+ begin # when verify_match is set this loop will repeat until unique_answers == 1
688
+ @answers = [ ]
689
+ @gather = original_gather
690
+ original_question.question = original_question_string
691
+
692
+ case @gather
693
+ when Integer
694
+ @answers << ask(@question)
695
+ @gather -= 1
696
+
697
+ original_question.question = ""
698
+ until @gather.zero?
699
+ @question = original_question
700
+ @answers << ask(@question)
701
+ @gather -= 1
702
+ end
703
+ when ::String, Regexp
704
+ @answers << ask(@question)
545
705
 
546
- original_question.question = ""
547
- until (@gather.is_a?(String) and @answers.last.to_s == @gather) or
706
+ original_question.question = ""
707
+ until (@gather.is_a?(::String) and @answers.last.to_s == @gather) or
548
708
  (@gather.is_a?(Regexp) and @answers.last.to_s =~ @gather)
549
- @question = original_question
550
- @answers << ask(@question)
709
+ @question = original_question
710
+ @answers << ask(@question)
711
+ end
712
+
713
+ @answers.pop
714
+ when Hash
715
+ @answers = { }
716
+ @gather.keys.sort.each do |key|
717
+ @question = original_question
718
+ @key = key
719
+ @answers[key] = ask(@question)
720
+ end
551
721
  end
552
-
553
- @answers.pop
554
- when Hash
555
- @answers = { }
556
- @gather.keys.sort.each do |key|
557
- @question = original_question
558
- @key = key
559
- @answers[key] = ask(@question)
722
+
723
+ if verify_match && (unique_answers(@answers).size > 1)
724
+ @question = original_question
725
+ explain_error(:mismatch)
726
+ else
727
+ verify_match = false
560
728
  end
561
- end
562
-
563
- @answers
729
+
730
+ end while verify_match
731
+
732
+ original_question.verify_match ? @answer : @answers
733
+ end
734
+
735
+ #
736
+ # A helper method used by HighLine::Question.verify_match
737
+ # for finding whether a list of answers match or differ
738
+ # from each other.
739
+ #
740
+ def unique_answers(list = @answers)
741
+ (list.respond_to?(:values) ? list.values : list).uniq
564
742
  end
565
743
 
566
744
  #
@@ -591,20 +769,32 @@ class HighLine
591
769
  # work-around ugly readline() warnings
592
770
  old_verbose = $VERBOSE
593
771
  $VERBOSE = nil
772
+ raw_answer = Readline.readline(question, true)
773
+ if raw_answer.nil?
774
+ if @@track_eof
775
+ raise EOFError, "The input stream is exhausted."
776
+ else
777
+ raw_answer = String.new # Never return nil
778
+ end
779
+ end
594
780
  answer = @question.change_case(
595
- @question.remove_whitespace(
596
- Readline.readline(question, true) ) )
781
+ @question.remove_whitespace(raw_answer))
597
782
  $VERBOSE = old_verbose
598
783
 
599
784
  answer
600
785
  else
601
- raise EOFError, "The input stream is exhausted." if @@track_eof and
602
- @input.eof?
786
+ if JRUBY
787
+ raw_answer = @java_console.readLine(@question.question, nil)
788
+ else
789
+ raise EOFError, "The input stream is exhausted." if @@track_eof and
790
+ @input.eof?
791
+ raw_answer = @input.gets
792
+ end
603
793
 
604
- @question.change_case(@question.remove_whitespace(@input.gets))
794
+ @question.change_case(@question.remove_whitespace(raw_answer))
605
795
  end
606
796
  end
607
-
797
+
608
798
  #
609
799
  # Return a line or character of input, as requested for this question.
610
800
  # Character input will be returned as a single character String,
@@ -616,18 +806,20 @@ class HighLine
616
806
  #
617
807
  def get_response( )
618
808
  return @question.first_answer if @question.first_answer?
619
-
809
+
810
+ stty = (CHARACTER_MODE == "stty")
811
+
620
812
  if @question.character.nil?
621
813
  if @question.echo == true and @question.limit.nil?
622
814
  get_line
623
815
  else
624
- raw_no_echo_mode if stty = CHARACTER_MODE == "stty"
625
-
816
+ raw_no_echo_mode
817
+
626
818
  line = ""
627
819
  backspace_limit = 0
628
820
  begin
629
821
 
630
- while character = (stty ? @input.getbyte : get_character(@input))
822
+ while character = get_character(@input)
631
823
  # honor backspace and delete
632
824
  if character == 127 or character == 8
633
825
  line.slice!(-1, 1)
@@ -638,17 +830,16 @@ class HighLine
638
830
  end
639
831
  # looking for carriage return (decimal 13) or
640
832
  # newline (decimal 10) in raw input
641
- break if character == 13 or character == 10 or
642
- (@question.limit and line.size == @question.limit)
833
+ break if character == 13 or character == 10
643
834
  if @question.echo != false
644
- if character == 127 or character == 8
645
- # only backspace if we have characters on the line to
646
- # eliminate, otherwise we'll tromp over the prompt
647
- if backspace_limit >= 0 then
648
- @output.print("\b#{ERASE_CHAR}")
649
- else
650
- # do nothing
651
- end
835
+ if character == 127 or character == 8
836
+ # only backspace if we have characters on the line to
837
+ # eliminate, otherwise we'll tromp over the prompt
838
+ if backspace_limit >= 0 then
839
+ @output.print("\b#{HighLine.Style(:erase_char).code}")
840
+ else
841
+ # do nothing
842
+ end
652
843
  else
653
844
  if @question.echo == true
654
845
  @output.print(character.chr)
@@ -658,35 +849,43 @@ class HighLine
658
849
  end
659
850
  @output.flush
660
851
  end
852
+ break if @question.limit and line.size == @question.limit
661
853
  end
662
854
  ensure
663
- restore_mode if stty
855
+ restore_mode
664
856
  end
665
857
  if @question.overwrite
666
- @output.print("\r#{ERASE_LINE}")
858
+ @output.print("\r#{HighLine.Style(:erase_line).code}")
667
859
  @output.flush
668
860
  else
669
861
  say("\n")
670
862
  end
671
-
863
+
672
864
  @question.change_case(@question.remove_whitespace(line))
673
865
  end
674
- elsif @question.character == :getc
675
- @question.change_case(@input.getbyte.chr)
676
866
  else
677
- response = get_character(@input).chr
678
- if @question.overwrite
679
- @output.print("\r#{ERASE_LINE}")
680
- @output.flush
681
- else
682
- echo = if @question.echo == true
683
- response
684
- elsif @question.echo != false
685
- @question.echo
867
+ raw_no_echo_mode
868
+ begin
869
+ if @question.character == :getc
870
+ response = @input.getbyte.chr
686
871
  else
687
- ""
872
+ response = get_character(@input).chr
873
+ if @question.overwrite
874
+ @output.print("\r#{HighLine.Style(:erase_line).code}")
875
+ @output.flush
876
+ else
877
+ echo = if @question.echo == true
878
+ response
879
+ elsif @question.echo != false
880
+ @question.echo
881
+ else
882
+ ""
883
+ end
884
+ say("#{echo}\n")
885
+ end
688
886
  end
689
- say("#{echo}\n")
887
+ ensure
888
+ restore_mode
690
889
  end
691
890
  @question.change_case(response)
692
891
  end
@@ -730,15 +929,17 @@ class HighLine
730
929
  def wrap( text )
731
930
  wrapped = [ ]
732
931
  text.each_line do |line|
733
- while line =~ /([^\n]{#{@wrap_at + 1},})/
932
+ # take into account color escape sequences when wrapping
933
+ wrap_at = @wrap_at + (line.length - actual_length(line))
934
+ while line =~ /([^\n]{#{wrap_at + 1},})/
734
935
  search = $1.dup
735
936
  replace = $1.dup
736
- if index = replace.rindex(" ", @wrap_at)
937
+ if index = replace.rindex(" ", wrap_at)
737
938
  replace[index, 1] = "\n"
738
939
  replace.sub!(/\n[ \t]+/, "\n")
739
940
  line.sub!(search, replace)
740
941
  else
741
- line[$~.begin(1) + @wrap_at, 0] = "\n"
942
+ line[$~.begin(1) + wrap_at, 0] = "\n"
742
943
  end
743
944
  end
744
945
  wrapped << line
@@ -751,6 +952,9 @@ class HighLine
751
952
  # sequence escapes.
752
953
  #
753
954
  def actual_length( string_with_escapes )
754
- string_with_escapes.gsub(/\e\[\d{1,2}m/, "").length
955
+ string_with_escapes.to_s.gsub(/\e\[\d{1,2}m/, "").length
755
956
  end
756
957
  end
958
+
959
+ require "highline/string_extensions"
960
+