malysz87-highline 1.5.9 → 1.5.10

Sign up to get free protection for your applications and to get access to all the features.
data/lib/highline.rb ADDED
@@ -0,0 +1,762 @@
1
+ #!/usr/local/bin/ruby -w
2
+
3
+ # highline.rb
4
+ #
5
+ # Created by James Edward Gray II on 2005-04-26.
6
+ # Copyright 2005 Gray Productions. All rights reserved.
7
+ #
8
+ # See HighLine for documentation.
9
+ #
10
+ # This is Free Software. See LICENSE and COPYING for details.
11
+
12
+ require "highline/system_extensions"
13
+ require "highline/question"
14
+ require "highline/menu"
15
+ require "highline/color_scheme"
16
+ require "erb"
17
+ require "optparse"
18
+ require "stringio"
19
+ require "abbrev"
20
+
21
+ #
22
+ # A HighLine object is a "high-level line oriented" shell over an input and an
23
+ # output stream. HighLine simplifies common console interaction, effectively
24
+ # replacing puts() and gets(). User code can simply specify the question to ask
25
+ # and any details about user interaction, then leave the rest of the work to
26
+ # HighLine. When HighLine.ask() returns, you'll have the answer you requested,
27
+ # even if HighLine had to ask many times, validate results, perform range
28
+ # checking, convert types, etc.
29
+ #
30
+ class HighLine
31
+ # The version of the installed library.
32
+ VERSION = "1.5.8".freeze
33
+
34
+ # An internal HighLine error. User code does not need to trap this.
35
+ class QuestionError < StandardError
36
+ # do nothing, just creating a unique error type
37
+ end
38
+
39
+ # The setting used to disable color output.
40
+ @@use_color = true
41
+
42
+ # Pass +false+ to _setting_ to turn off HighLine's color escapes.
43
+ def self.use_color=( setting )
44
+ @@use_color = setting
45
+ end
46
+
47
+ # Returns true if HighLine is currently using color escapes.
48
+ def self.use_color?
49
+ @@use_color
50
+ end
51
+
52
+ # The setting used to disable EOF tracking.
53
+ @@track_eof = true
54
+
55
+ # Pass +false+ to _setting_ to turn off HighLine's EOF tracking.
56
+ def self.track_eof=( setting )
57
+ @@track_eof = setting
58
+ end
59
+
60
+ # Returns true if HighLine is currently tracking EOF for input.
61
+ def self.track_eof?
62
+ @@track_eof
63
+ end
64
+
65
+ # The setting used to control color schemes.
66
+ @@color_scheme = nil
67
+
68
+ # Pass ColorScheme to _setting_ to turn set a HighLine color scheme.
69
+ def self.color_scheme=( setting )
70
+ @@color_scheme = setting
71
+ end
72
+
73
+ # Returns the current color scheme.
74
+ def self.color_scheme
75
+ @@color_scheme
76
+ end
77
+
78
+ # Returns +true+ if HighLine is currently using a color scheme.
79
+ def self.using_color_scheme?
80
+ not @@color_scheme.nil?
81
+ end
82
+
83
+ #
84
+ # Embed in a String to clear all previous ANSI sequences. This *MUST* be
85
+ # 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"
142
+
143
+ #
144
+ # Create an instance of HighLine, connected to the streams _input_
145
+ # and _output_.
146
+ #
147
+ def initialize( input = $stdin, output = $stdout,
148
+ wrap_at = nil, page_at = nil )
149
+ @input = input
150
+ @output = output
151
+
152
+ self.wrap_at = wrap_at
153
+ self.page_at = page_at
154
+
155
+ @question = nil
156
+ @answer = nil
157
+ @menu = nil
158
+ @header = nil
159
+ @prompt = nil
160
+ @gather = nil
161
+ @answers = nil
162
+ @key = nil
163
+ end
164
+
165
+ include HighLine::SystemExtensions
166
+
167
+ # The current column setting for wrapping output.
168
+ attr_reader :wrap_at
169
+ # The current row setting for paging output.
170
+ attr_reader :page_at
171
+
172
+ #
173
+ # A shortcut to HighLine.ask() a question that only accepts "yes" or "no"
174
+ # answers ("y" and "n" are allowed) and returns +true+ or +false+
175
+ # (+true+ for "yes"). If provided a +true+ value, _character_ will cause
176
+ # HighLine to fetch a single character response. A block can be provided
177
+ # to further configure the question as in HighLine.ask()
178
+ #
179
+ # Raises EOFError if input is exhausted.
180
+ #
181
+ def agree( yes_or_no_question, character = nil )
182
+ ask(yes_or_no_question, lambda { |yn| yn.downcase[0] == ?y}) do |q|
183
+ q.validate = /\Ay(?:es)?|no?\Z/i
184
+ q.responses[:not_valid] = 'Please enter "yes" or "no".'
185
+ q.responses[:ask_on_error] = :question
186
+ q.character = character
187
+
188
+ yield q if block_given?
189
+ end
190
+ end
191
+
192
+ #
193
+ # This method is the primary interface for user input. Just provide a
194
+ # _question_ to ask the user, the _answer_type_ you want returned, and
195
+ # optionally a code block setting up details of how you want the question
196
+ # handled. See HighLine.say() for details on the format of _question_, and
197
+ # HighLine::Question for more information about _answer_type_ and what's
198
+ # valid in the code block.
199
+ # actions is a hash with proc or lambdas which will be called on errors,
200
+ # default action is explain_error. Type of error, answer, and
201
+ # default action are send to the action.
202
+ #
203
+ # If <tt>@question</tt> is set before ask() is called, parameters are
204
+ # ignored and that object (must be a HighLine::Question) is used to drive
205
+ # the process instead.
206
+ #
207
+ # Raises EOFError if input is exhausted.
208
+ #
209
+ def ask( question, answer_type = String, actions = Hash.new, &details ) # :yields: question
210
+ actions.default = Proc.new { |error, answer, default_action| explain_error(error) } unless actions.default
211
+
212
+ @question ||= Question.new(question, answer_type, &details)
213
+
214
+ return gather if @question.gather
215
+
216
+ # readline() needs to handle it's own output, but readline only supports
217
+ # full line reading. Therefore if @question.echo is anything but true,
218
+ # the prompt will not be issued. And we have to account for that now.
219
+ say(@question) unless (@question.readline and @question.echo == true)
220
+ begin
221
+ @answer = @question.answer_or_default(get_response)
222
+ unless @question.valid_answer?(@answer)
223
+ explain_error(:not_valid)
224
+ raise QuestionError
225
+ end
226
+
227
+ @answer = @question.convert(@answer)
228
+
229
+ if @question.in_range?(@answer)
230
+ if @question.confirm
231
+ # need to add a layer of scope to ask a question inside a
232
+ # question, without destroying instance data
233
+ context_change = self.class.new(@input, @output, @wrap_at, @page_at)
234
+ if @question.confirm == true
235
+ confirm_question = "Are you sure? "
236
+ else
237
+ # evaluate ERb under initial scope, so it will have
238
+ # access to @question and @answer
239
+ template = ERB.new(@question.confirm, nil, "%")
240
+ confirm_question = template.result(binding)
241
+ end
242
+ unless context_change.agree(confirm_question)
243
+ explain_error(nil)
244
+ raise QuestionError
245
+ end
246
+ end
247
+
248
+ @answer
249
+ else
250
+ actions[:not_in_range].call(:not_in_range, @answer, actions.default)
251
+ raise QuestionError
252
+ end
253
+ rescue QuestionError
254
+ retry
255
+ rescue ArgumentError
256
+ actions[:invalid_type].call(:invalid_type, @answer, actions.default)
257
+ retry
258
+ rescue Question::NoAutoCompleteMatch
259
+ actions[:no_completion].call(:no_completion, @answer, actions.default)
260
+ retry
261
+ rescue Question::NotEnoughAnswers
262
+ actions[:not_enough_answers].call(:not_enough_answers, @answer, actions.default)
263
+ retry
264
+ rescue NameError
265
+ raise if $!.is_a?(NoMethodError)
266
+ actions[:ambiguous_completion].call(:ambiguous_completion, @answer, actions.default)
267
+ retry
268
+ ensure
269
+ @question = nil # Reset Question object.
270
+ end
271
+ end
272
+
273
+ #
274
+ # This method is HighLine's menu handler. For simple usage, you can just
275
+ # pass all the menu items you wish to display. At that point, choose() will
276
+ # build and display a menu, walk the user through selection, and return
277
+ # their choice amoung the provided items. You might use this in a case
278
+ # statement for quick and dirty menus.
279
+ #
280
+ # However, choose() is capable of much more. If provided, a block will be
281
+ # passed a HighLine::Menu object to configure. Using this method, you can
282
+ # customize all the details of menu handling from index display, to building
283
+ # a complete shell-like menuing system. See HighLine::Menu for all the
284
+ # methods it responds to.
285
+ #
286
+ # Raises EOFError if input is exhausted.
287
+ #
288
+ def choose( *items, &details )
289
+ @menu = @question = Menu.new(&details)
290
+ @menu.choices(*items) unless items.empty?
291
+
292
+ # Set _answer_type_ so we can double as the Question for ask().
293
+ @menu.answer_type = if @menu.shell
294
+ lambda do |command| # shell-style selection
295
+ first_word = command.to_s.split.first || ""
296
+
297
+ options = @menu.options
298
+ options.extend(OptionParser::Completion)
299
+ answer = options.complete(first_word)
300
+
301
+ if answer.nil?
302
+ raise Question::NoAutoCompleteMatch
303
+ end
304
+
305
+ [answer.last, command.sub(/^\s*#{first_word}\s*/, "")]
306
+ end
307
+ else
308
+ @menu.options # normal menu selection, by index or name
309
+ end
310
+
311
+ # Provide hooks for ERb layouts.
312
+ @header = @menu.header
313
+ @prompt = @menu.prompt
314
+
315
+ if @menu.shell
316
+ selected = ask("Ignored", @menu.answer_type)
317
+ @menu.select(self, *selected)
318
+ else
319
+ selected = ask("Ignored", @menu.answer_type)
320
+ @menu.select(self, selected)
321
+ end
322
+ end
323
+
324
+ #
325
+ # This method provides easy access to ANSI color sequences, without the user
326
+ # needing to remember to CLEAR at the end of each sequence. Just pass the
327
+ # _string_ to color, followed by a list of _colors_ you would like it to be
328
+ # affected by. The _colors_ can be HighLine class constants, or symbols
329
+ # (:blue for BLUE, for example). A CLEAR will automatically be embedded to
330
+ # the end of the returned String.
331
+ #
332
+ # This method returns the original _string_ unchanged if HighLine::use_color?
333
+ # is +false+.
334
+ #
335
+ def color( string, *colors )
336
+ return string unless self.class.use_color?
337
+
338
+ colors.map! do |c|
339
+ if self.class.using_color_scheme? and self.class.color_scheme.include? c
340
+ self.class.color_scheme[c]
341
+ elsif c.is_a? Symbol
342
+ self.class.const_get(c.to_s.upcase)
343
+ else
344
+ c
345
+ end
346
+ end
347
+ "#{colors.flatten.join}#{string}#{CLEAR}"
348
+ end
349
+
350
+ #
351
+ # This method is a utility for quickly and easily laying out lists. It can
352
+ # be accessed within ERb replacements of any text that will be sent to the
353
+ # user.
354
+ #
355
+ # The only required parameter is _items_, which should be the Array of items
356
+ # to list. A specified _mode_ controls how that list is formed and _option_
357
+ # has different effects, depending on the _mode_. Recognized modes are:
358
+ #
359
+ # <tt>:columns_across</tt>:: _items_ will be placed in columns, flowing
360
+ # from left to right. If given, _option_ is the
361
+ # number of columns to be used. When absent,
362
+ # columns will be determined based on _wrap_at_
363
+ # or a default of 80 characters.
364
+ # <tt>:columns_down</tt>:: Identical to <tt>:columns_across</tt>, save
365
+ # flow goes down.
366
+ # <tt>:inline</tt>:: All _items_ are placed on a single line. The
367
+ # last two _items_ are separated by _option_ or
368
+ # a default of " or ". All other _items_ are
369
+ # separated by ", ".
370
+ # <tt>:rows</tt>:: The default mode. Each of the _items_ is
371
+ # placed on it's own line. The _option_
372
+ # parameter is ignored in this mode.
373
+ #
374
+ # Each member of the _items_ Array is passed through ERb and thus can contain
375
+ # their own expansions. Color escape expansions do not contribute to the
376
+ # final field width.
377
+ #
378
+ def list( items, mode = :rows, option = nil )
379
+ items = items.to_ary.map do |item|
380
+ ERB.new(item, nil, "%").result(binding)
381
+ end
382
+
383
+ case mode
384
+ when :inline
385
+ option = " or " if option.nil?
386
+
387
+ case items.size
388
+ when 0
389
+ ""
390
+ when 1
391
+ items.first
392
+ when 2
393
+ "#{items.first}#{option}#{items.last}"
394
+ else
395
+ items[0..-2].join(", ") + "#{option}#{items.last}"
396
+ end
397
+ when :columns_across, :columns_down
398
+ max_length = actual_length(
399
+ items.max { |a, b| actual_length(a) <=> actual_length(b) }
400
+ )
401
+
402
+ if option.nil?
403
+ limit = @wrap_at || 80
404
+ option = (limit + 2) / (max_length + 2)
405
+ end
406
+
407
+ items = items.map do |item|
408
+ pad = max_length + (item.length - actual_length(item))
409
+ "%-#{pad}s" % item
410
+ end
411
+ row_count = (items.size / option.to_f).ceil
412
+
413
+ if mode == :columns_across
414
+ rows = Array.new(row_count) { Array.new }
415
+ items.each_with_index do |item, index|
416
+ rows[index / option] << item
417
+ end
418
+
419
+ rows.map { |row| row.join(" ") + "\n" }.join
420
+ else
421
+ columns = Array.new(option) { Array.new }
422
+ items.each_with_index do |item, index|
423
+ columns[index / row_count] << item
424
+ end
425
+
426
+ list = ""
427
+ columns.first.size.times do |index|
428
+ list << columns.map { |column| column[index] }.
429
+ compact.join(" ") + "\n"
430
+ end
431
+ list
432
+ end
433
+ else
434
+ items.map { |i| "#{i}\n" }.join
435
+ end
436
+ end
437
+
438
+ #
439
+ # The basic output method for HighLine objects. If the provided _statement_
440
+ # ends with a space or tab character, a newline will not be appended (output
441
+ # will be flush()ed). All other cases are passed straight to Kernel.puts().
442
+ #
443
+ # The _statement_ parameter is processed as an ERb template, supporting
444
+ # embedded Ruby code. The template is evaluated with a binding inside
445
+ # the HighLine instance, providing easy access to the ANSI color constants
446
+ # and the HighLine.color() method.
447
+ #
448
+ def say( statement )
449
+ statement = statement.to_str
450
+ return unless statement.length > 0
451
+
452
+ template = ERB.new(statement, nil, "%")
453
+ statement = template.result(binding)
454
+
455
+ statement = wrap(statement) unless @wrap_at.nil?
456
+ statement = page_print(statement) unless @page_at.nil?
457
+
458
+ if statement[-1, 1] == " " or statement[-1, 1] == "\t"
459
+ @output.print(statement)
460
+ @output.flush
461
+ else
462
+ @output.puts(statement)
463
+ end
464
+ end
465
+
466
+ #
467
+ # Set to an integer value to cause HighLine to wrap output lines at the
468
+ # indicated character limit. When +nil+, the default, no wrapping occurs. If
469
+ # set to <tt>:auto</tt>, HighLine will attempt to determing the columns
470
+ # available for the <tt>@output</tt> or use a sensible default.
471
+ #
472
+ def wrap_at=( setting )
473
+ @wrap_at = setting == :auto ? output_cols : setting
474
+ end
475
+
476
+ #
477
+ # Set to an integer value to cause HighLine to page output lines over the
478
+ # indicated line limit. When +nil+, the default, no paging occurs. If
479
+ # set to <tt>:auto</tt>, HighLine will attempt to determing the rows available
480
+ # for the <tt>@output</tt> or use a sensible default.
481
+ #
482
+ def page_at=( setting )
483
+ @page_at = setting == :auto ? output_rows : setting
484
+ end
485
+
486
+ #
487
+ # Returns the number of columns for the console, or a default it they cannot
488
+ # be determined.
489
+ #
490
+ def output_cols
491
+ return 80 unless @output.tty?
492
+ terminal_size.first
493
+ rescue
494
+ return 80
495
+ end
496
+
497
+ #
498
+ # Returns the number of rows for the console, or a default if they cannot be
499
+ # determined.
500
+ #
501
+ def output_rows
502
+ return 24 unless @output.tty?
503
+ terminal_size.last
504
+ rescue
505
+ return 24
506
+ end
507
+
508
+ private
509
+
510
+ #
511
+ # A helper method for sending the output stream and error and repeat
512
+ # of the question.
513
+ #
514
+ def explain_error( error )
515
+ say(@question.responses[error]) unless error.nil?
516
+ if @question.responses[:ask_on_error] == :question
517
+ say(@question)
518
+ elsif @question.responses[:ask_on_error]
519
+ say(@question.responses[:ask_on_error])
520
+ end
521
+ end
522
+
523
+ #
524
+ # Collects an Array/Hash full of answers as described in
525
+ # HighLine::Question.gather().
526
+ #
527
+ # Raises EOFError if input is exhausted.
528
+ #
529
+ def gather( )
530
+ @gather = @question.gather
531
+ @answers = [ ]
532
+ original_question = @question
533
+
534
+ @question.gather = false
535
+
536
+ case @gather
537
+ when Integer
538
+ @answers << ask(@question)
539
+ @gather -= 1
540
+
541
+ original_question.question = ""
542
+ until @gather.zero?
543
+ @question = original_question
544
+ @answers << ask(@question)
545
+ @gather -= 1
546
+ end
547
+ when String, Regexp
548
+ @answers << ask(@question)
549
+
550
+ original_question.question = ""
551
+ until (@gather.is_a?(String) and @answers.last.to_s == @gather) or
552
+ (@gather.is_a?(Regexp) and @answers.last.to_s =~ @gather)
553
+ @question = original_question
554
+ @answers << ask(@question)
555
+ end
556
+
557
+ @answers.pop
558
+ when Hash
559
+ @answers = { }
560
+ @gather.keys.sort.each do |key|
561
+ @question = original_question
562
+ @key = key
563
+ @answers[key] = ask(@question)
564
+ end
565
+ end
566
+
567
+ @answers
568
+ end
569
+
570
+ #
571
+ # Read a line of input from the input stream and process whitespace as
572
+ # requested by the Question object.
573
+ #
574
+ # If Question's _readline_ property is set, that library will be used to
575
+ # fetch input. *WARNING*: This ignores the currently set input stream.
576
+ #
577
+ # Raises EOFError if input is exhausted.
578
+ #
579
+ def get_line( )
580
+ if @question.readline
581
+ require "readline" # load only if needed
582
+
583
+ # capture say()'s work in a String to feed to readline()
584
+ old_output = @output
585
+ @output = StringIO.new
586
+ say(@question)
587
+ question = @output.string
588
+ @output = old_output
589
+
590
+ # prep auto-completion
591
+ # I found this version in plugin
592
+ # The previous version wasn`t warking properly, readline wasn`t
593
+ # showing possible answers if you push tab 2 times
594
+ Readline.completion_proc = lambda do |string|
595
+ @question.selection.grep(/\A#{Regexp.escape(string)}/)
596
+ end
597
+
598
+
599
+
600
+
601
+ # work-around ugly readline() warnings
602
+ old_verbose = $VERBOSE
603
+ $VERBOSE = nil
604
+ answer = @question.change_case(
605
+ @question.remove_whitespace(
606
+ Readline.readline(question, true) ) )
607
+ $VERBOSE = old_verbose
608
+
609
+ answer
610
+ else
611
+ raise EOFError, "The input stream is exhausted." if @@track_eof and
612
+ @input.eof?
613
+
614
+ @question.change_case(@question.remove_whitespace(@input.gets))
615
+ end
616
+ end
617
+
618
+ #
619
+ # Return a line or character of input, as requested for this question.
620
+ # Character input will be returned as a single character String,
621
+ # not an Integer.
622
+ #
623
+ # This question's _first_answer_ will be returned instead of input, if set.
624
+ #
625
+ # Raises EOFError if input is exhausted.
626
+ #
627
+ def get_response( )
628
+ return @question.first_answer if @question.first_answer?
629
+
630
+ if @question.character.nil?
631
+ if @question.echo == true and @question.limit.nil?
632
+ get_line
633
+ else
634
+ raw_no_echo_mode if stty = CHARACTER_MODE == "stty"
635
+
636
+ line = ""
637
+ backspace_limit = 0
638
+ begin
639
+
640
+ while character = (stty ? @input.getc : get_character(@input))
641
+ # honor backspace and delete
642
+ if character == 127 or character == 8
643
+ line.slice!(-1, 1)
644
+ backspace_limit -= 1
645
+ else
646
+ line << character.chr
647
+ backspace_limit = line.size
648
+ end
649
+ # looking for carriage return (decimal 13) or
650
+ # newline (decimal 10) in raw input
651
+ break if character == 13 or character == 10 or
652
+ (@question.limit and line.size == @question.limit)
653
+ if @question.echo != false
654
+ if character == 127 or character == 8
655
+ # only backspace if we have characters on the line to
656
+ # eliminate, otherwise we'll tromp over the prompt
657
+ if backspace_limit >= 0 then
658
+ @output.print("\b#{ERASE_CHAR}")
659
+ else
660
+ # do nothing
661
+ end
662
+ else
663
+ @output.print(@question.echo)
664
+ end
665
+ @output.flush
666
+ end
667
+ end
668
+ ensure
669
+ restore_mode if stty
670
+ end
671
+ if @question.overwrite
672
+ @output.print("\r#{ERASE_LINE}")
673
+ @output.flush
674
+ else
675
+ say("\n")
676
+ end
677
+
678
+ @question.change_case(@question.remove_whitespace(line))
679
+ end
680
+ elsif @question.character == :getc
681
+ @question.change_case(@input.getc.chr)
682
+ else
683
+ response = get_character(@input).chr
684
+ if @question.overwrite
685
+ @output.print("\r#{ERASE_LINE}")
686
+ @output.flush
687
+ else
688
+ echo = if @question.echo == true
689
+ response
690
+ elsif @question.echo != false
691
+ @question.echo
692
+ else
693
+ ""
694
+ end
695
+ say("#{echo}\n")
696
+ end
697
+ @question.change_case(response)
698
+ end
699
+ end
700
+
701
+ #
702
+ # Page print a series of at most _page_at_ lines for _output_. After each
703
+ # page is printed, HighLine will pause until the user presses enter/return
704
+ # then display the next page of data.
705
+ #
706
+ # Note that the final page of _output_ is *not* printed, but returned
707
+ # instead. This is to support any special handling for the final sequence.
708
+ #
709
+ def page_print( output )
710
+ lines = output.scan(/[^\n]*\n?/)
711
+ while lines.size > @page_at
712
+ @output.puts lines.slice!(0...@page_at).join
713
+ @output.puts
714
+ # Return last line if user wants to abort paging
715
+ return (["...\n"] + lines.slice(-2,1)).join unless continue_paging?
716
+ end
717
+ return lines.join
718
+ end
719
+
720
+ #
721
+ # Ask user if they wish to continue paging output. Allows them to type "q" to
722
+ # cancel the paging process.
723
+ #
724
+ def continue_paging?
725
+ command = HighLine.new(@input, @output).ask(
726
+ "-- press enter/return to continue or q to stop -- "
727
+ ) { |q| q.character = true }
728
+ command !~ /\A[qQ]\Z/ # Only continue paging if Q was not hit.
729
+ end
730
+
731
+ #
732
+ # Wrap a sequence of _lines_ at _wrap_at_ characters per line. Existing
733
+ # newlines will not be affected by this process, but additional newlines
734
+ # may be added.
735
+ #
736
+ def wrap( lines )
737
+ wrapped = [ ]
738
+ lines.each do |line|
739
+ while line =~ /([^\n]{#{@wrap_at + 1},})/
740
+ search = $1.dup
741
+ replace = $1.dup
742
+ if index = replace.rindex(" ", @wrap_at)
743
+ replace[index, 1] = "\n"
744
+ replace.sub!(/\n[ \t]+/, "\n")
745
+ line.sub!(search, replace)
746
+ else
747
+ line[@wrap_at, 0] = "\n"
748
+ end
749
+ end
750
+ wrapped << line
751
+ end
752
+ return wrapped.join
753
+ end
754
+
755
+ #
756
+ # Returns the length of the passed +string_with_escapes+, minus and color
757
+ # sequence escapes.
758
+ #
759
+ def actual_length( string_with_escapes )
760
+ string_with_escapes.gsub(/\e\[\d{1,2}m/, "").length
761
+ end
762
+ end