keybox 1.1.1 → 1.2.0

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