highline 1.7.10 → 2.0.3

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 (65) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +5 -0
  3. data/.rubocop.yml +84 -0
  4. data/.simplecov +5 -0
  5. data/.travis.yml +35 -9
  6. data/Changelog.md +214 -15
  7. data/Gemfile +16 -5
  8. data/README.md +202 -0
  9. data/Rakefile +8 -20
  10. data/appveyor.yml +37 -0
  11. data/examples/ansi_colors.rb +6 -11
  12. data/examples/asking_for_arrays.rb +6 -2
  13. data/examples/basic_usage.rb +31 -21
  14. data/examples/color_scheme.rb +11 -10
  15. data/examples/get_character.rb +8 -4
  16. data/examples/limit.rb +4 -0
  17. data/examples/menus.rb +16 -10
  18. data/examples/overwrite.rb +9 -5
  19. data/examples/page_and_wrap.rb +5 -4
  20. data/examples/password.rb +4 -0
  21. data/examples/repeat_entry.rb +10 -5
  22. data/examples/trapping_eof.rb +2 -1
  23. data/examples/using_readline.rb +2 -1
  24. data/highline.gemspec +25 -27
  25. data/lib/highline/builtin_styles.rb +129 -0
  26. data/lib/highline/color_scheme.rb +49 -32
  27. data/lib/highline/compatibility.rb +11 -4
  28. data/lib/highline/custom_errors.rb +57 -0
  29. data/lib/highline/import.rb +19 -12
  30. data/lib/highline/io_console_compatible.rb +37 -0
  31. data/lib/highline/list.rb +177 -0
  32. data/lib/highline/list_renderer.rb +261 -0
  33. data/lib/highline/menu/item.rb +32 -0
  34. data/lib/highline/menu.rb +306 -111
  35. data/lib/highline/paginator.rb +52 -0
  36. data/lib/highline/question/answer_converter.rb +103 -0
  37. data/lib/highline/question.rb +281 -131
  38. data/lib/highline/question_asker.rb +150 -0
  39. data/lib/highline/simulate.rb +24 -13
  40. data/lib/highline/statement.rb +88 -0
  41. data/lib/highline/string.rb +36 -0
  42. data/lib/highline/string_extensions.rb +83 -64
  43. data/lib/highline/style.rb +196 -63
  44. data/lib/highline/template_renderer.rb +62 -0
  45. data/lib/highline/terminal/io_console.rb +36 -0
  46. data/lib/highline/terminal/ncurses.rb +38 -0
  47. data/lib/highline/terminal/unix_stty.rb +51 -0
  48. data/lib/highline/terminal.rb +190 -0
  49. data/lib/highline/version.rb +3 -1
  50. data/lib/highline/wrapper.rb +53 -0
  51. data/lib/highline.rb +390 -788
  52. metadata +56 -35
  53. data/INSTALL +0 -59
  54. data/README.rdoc +0 -74
  55. data/lib/highline/system_extensions.rb +0 -254
  56. data/setup.rb +0 -1360
  57. data/test/string_methods.rb +0 -32
  58. data/test/tc_color_scheme.rb +0 -96
  59. data/test/tc_highline.rb +0 -1402
  60. data/test/tc_import.rb +0 -52
  61. data/test/tc_menu.rb +0 -439
  62. data/test/tc_simulator.rb +0 -33
  63. data/test/tc_string_extension.rb +0 -33
  64. data/test/tc_string_highline.rb +0 -38
  65. data/test/tc_style.rb +0 -578
@@ -1,108 +1,206 @@
1
- # color_scheme.rb
1
+ # coding: utf-8
2
+
3
+ #--
4
+ # originally color_scheme.rb
2
5
  #
3
6
  # Created by Richard LeBer on 2011-06-27.
4
7
  # Copyright 2011. All rights reserved
5
8
  #
6
9
  # This is Free Software. See LICENSE and COPYING for details
7
10
 
8
- class HighLine
9
-
11
+ class HighLine #:nodoc:
12
+ # Creates a style using {.find_or_create_style} or
13
+ # {.find_or_create_style_list}
14
+ # @param args [Array<Style, Hash, String>] style properties
15
+ # @return [Style]
10
16
  def self.Style(*args)
11
17
  args = args.compact.flatten
12
- if args.size==1
13
- arg = args.first
14
- if arg.is_a?(Style)
15
- Style.list[arg.name] || Style.index(arg)
16
- elsif arg.is_a?(::String) && arg =~ /^\e\[/ # arg is a code
17
- if styles = Style.code_index[arg]
18
- styles.first
19
- else
20
- Style.new(:code=>arg)
21
- end
22
- elsif style = Style.list[arg]
23
- style
24
- elsif HighLine.color_scheme && HighLine.color_scheme[arg]
25
- HighLine.color_scheme[arg]
26
- elsif arg.is_a?(Hash)
27
- Style.new(arg)
28
- elsif arg.to_s.downcase =~ /^rgb_([a-f0-9]{6})$/
29
- Style.rgb($1)
30
- elsif arg.to_s.downcase =~ /^on_rgb_([a-f0-9]{6})$/
31
- Style.rgb($1).on
18
+ if args.size == 1
19
+ find_or_create_style(args.first)
20
+ else
21
+ find_or_create_style_list(*args)
22
+ end
23
+ end
24
+
25
+ # Search for a Style with the given properties and return it.
26
+ # If there's no matched Style, it creates one.
27
+ # You can pass a Style, String or a Hash.
28
+ # @param arg [Style, String, Hash] style properties
29
+ # @return [Style] found or creted Style
30
+ def self.find_or_create_style(arg)
31
+ if arg.is_a?(Style)
32
+ Style.list[arg.name] || Style.index(arg)
33
+ elsif arg.is_a?(::String) && arg =~ /^\e\[/ # arg is a code
34
+ styles = Style.code_index[arg]
35
+ if styles
36
+ styles.first
32
37
  else
33
- raise NameError, "#{arg.inspect} is not a defined Style"
38
+ Style.new(code: arg)
34
39
  end
40
+ elsif Style.list[arg]
41
+ Style.list[arg]
42
+ elsif HighLine.color_scheme && HighLine.color_scheme[arg]
43
+ HighLine.color_scheme[arg]
44
+ elsif arg.is_a?(Hash)
45
+ Style.new(arg)
46
+ elsif arg.to_s.downcase =~ /^rgb_([a-f0-9]{6})$/
47
+ Style.rgb(Regexp.last_match(1))
48
+ elsif arg.to_s.downcase =~ /^on_rgb_([a-f0-9]{6})$/
49
+ Style.rgb(Regexp.last_match(1)).on
35
50
  else
36
- name = args
37
- Style.list[name] || Style.new(:list=>args)
51
+ raise NameError, "#{arg.inspect} is not a defined Style"
38
52
  end
39
53
  end
40
54
 
41
- class Style
55
+ # Find a Style list or create a new one.
56
+ # @param args [Array<Symbol>] an Array of Symbols of each style
57
+ # that will be on the style list.
58
+ # @return [Style] Style list
59
+ # @example Creating a Style list of the combined RED and BOLD styles.
60
+ # style_list = HighLine.find_or_create_style_list(:red, :bold)
61
+
62
+ def self.find_or_create_style_list(*args)
63
+ name = args
64
+ Style.list[name] || Style.new(list: args)
65
+ end
42
66
 
67
+ # ANSI styles to be used by HighLine.
68
+ class Style
69
+ # Index the given style.
70
+ # Uses @code_index (Hash) as repository.
71
+ # @param style [Style]
72
+ # @return [Style] the given style
43
73
  def self.index(style)
44
74
  if style.name
45
- @@styles ||= {}
46
- @@styles[style.name] = style
75
+ @styles ||= {}
76
+ @styles[style.name] = style
47
77
  end
48
- if !style.list
49
- @@code_index ||= {}
50
- @@code_index[style.code] ||= []
51
- @@code_index[style.code].reject!{|indexed_style| indexed_style.name == style.name}
52
- @@code_index[style.code] << style
78
+ unless style.list
79
+ @code_index ||= {}
80
+ @code_index[style.code] ||= []
81
+ @code_index[style.code].reject! do |indexed_style|
82
+ indexed_style.name == style.name
83
+ end
84
+ @code_index[style.code] << style
53
85
  end
54
86
  style
55
87
  end
56
88
 
89
+ # Clear all custom Styles, restoring the Style index to
90
+ # builtin styles only.
91
+ # @return [void]
92
+ def self.clear_index
93
+ # reset to builtin only styles
94
+ @styles = list.select { |_name, style| style.builtin }
95
+ @code_index = {}
96
+ @styles.each_value { |style| index(style) }
97
+ end
98
+
99
+ # Converts all given color codes to hexadecimal and
100
+ # join them in a single string. If any given color code
101
+ # is already a String, doesn't perform any convertion.
102
+ #
103
+ # @param colors [Array<Numeric, String>] color codes
104
+ # @return [String] all color codes joined
105
+ # @example
106
+ # HighLine::Style.rgb_hex(9, 10, "11") # => "090a11"
57
107
  def self.rgb_hex(*colors)
58
108
  colors.map do |color|
59
- color.is_a?(Numeric) ? '%02x'%color : color.to_s
109
+ color.is_a?(Numeric) ? format("%02x", color) : color.to_s
60
110
  end.join
61
111
  end
62
112
 
113
+ # Split an rgb code string into its 3 numerical compounds.
114
+ # @param hex [String] rgb code string like "010F0F"
115
+ # @return [Array<Numeric>] numerical compounds like [1, 15, 15]
116
+ # @example
117
+ # HighLine::Style.rgb_parts("090A0B") # => [9, 10, 11]
118
+
63
119
  def self.rgb_parts(hex)
64
- hex.scan(/../).map{|part| part.to_i(16)}
120
+ hex.scan(/../).map { |part| part.to_i(16) }
65
121
  end
66
122
 
123
+ # Search for or create a new Style from the colors provided.
124
+ # @param colors (see .rgb_hex)
125
+ # @return [Style] a Style with the rgb colors provided.
126
+ # @example Creating a new Style based on rgb code
127
+ # rgb_style = HighLine::Style.rgb(9, 10, 11)
128
+ #
129
+ # rgb_style.name # => :rgb_090a0b
130
+ # rgb_style.code # => "\e[38;5;16m"
131
+ # rgb_style.rgb # => [9, 10, 11]
132
+ #
67
133
  def self.rgb(*colors)
68
134
  hex = rgb_hex(*colors)
69
- name = ('rgb_' + hex).to_sym
70
- if style = list[name]
71
- style
72
- else
73
- parts = rgb_parts(hex)
74
- new(:name=>name, :code=>"\e[38;5;#{rgb_number(parts)}m", :rgb=>parts)
75
- end
135
+ name = ("rgb_" + hex).to_sym
136
+ style = list[name]
137
+ return style if style
138
+
139
+ parts = rgb_parts(hex)
140
+ new(name: name, code: "\e[38;5;#{rgb_number(parts)}m", rgb: parts)
76
141
  end
77
142
 
143
+ # Returns the rgb number to be used as escape code on ANSI terminals.
144
+ # @param parts [Array<Numeric>] three numerical codes for red, green
145
+ # and blue
146
+ # @return [Numeric] to be used as escape code on ANSI terminals
78
147
  def self.rgb_number(*parts)
79
148
  parts = parts.flatten
80
- 16 + parts.inject(0) {|kode, part| kode*6 + (part/256.0*6.0).floor}
149
+ 16 + parts.reduce(0) do |kode, part|
150
+ kode * 6 + (part / 256.0 * 6.0).floor
151
+ end
81
152
  end
82
153
 
154
+ # From an ANSI number (color escape code), craft an 'rgb_hex' code of it
155
+ # @param ansi_number [Integer] ANSI escape code
156
+ # @return [String] all color codes joined as {.rgb_hex}
83
157
  def self.ansi_rgb_to_hex(ansi_number)
84
- raise "Invalid ANSI rgb code #{ansi_number}" unless (16..231).include?(ansi_number)
85
- parts = (ansi_number-16).to_s(6).rjust(3,'0').scan(/./).map{|d| (d.to_i*255.0/6.0).ceil}
158
+ raise "Invalid ANSI rgb code #{ansi_number}" unless
159
+ (16..231).cover?(ansi_number)
160
+ parts = (ansi_number - 16).
161
+ to_s(6).
162
+ rjust(3, "0").
163
+ scan(/./).
164
+ map { |d| (d.to_i * 255.0 / 6.0).ceil }
165
+
86
166
  rgb_hex(*parts)
87
167
  end
88
168
 
169
+ # @return [Hash] list of all cached Styles
89
170
  def self.list
90
- @@styles ||= {}
171
+ @styles ||= {}
91
172
  end
92
173
 
174
+ # @return [Hash] list of all cached Style codes
93
175
  def self.code_index
94
- @@code_index ||= {}
176
+ @code_index ||= {}
95
177
  end
96
178
 
179
+ # Remove any ANSI color escape sequence of the given String.
180
+ # @param string [String]
181
+ # @return [String]
97
182
  def self.uncolor(string)
98
- string.gsub(/\e\[\d+(;\d+)*m/, '')
183
+ string.gsub(/\e\[\d+(;\d+)*m/, "")
99
184
  end
100
185
 
101
- attr_reader :name, :list
102
- attr_accessor :rgb, :builtin
186
+ # Style name
187
+ # @return [Symbol] the name of the Style
188
+ attr_reader :name
189
+
190
+ # When a compound Style, returns a list of its components.
191
+ # @return [Array<Symbol>] components of a Style list
192
+ attr_reader :list
193
+
194
+ # @return [Array] the three numerical rgb codes. Ex: [10, 12, 127]
195
+ attr_accessor :rgb
196
+
197
+ # @return [Boolean] true if the Style is builtin or not.
198
+ attr_accessor :builtin
103
199
 
104
200
  # Single color/styles have :name, :code, :rgb (possibly), :builtin
105
201
  # Compound styles have :name, :list, :builtin
202
+ #
203
+ # @param defn [Hash] options for the Style to be created.
106
204
  def initialize(defn = {})
107
205
  @definition = defn
108
206
  @name = defn[:name]
@@ -112,65 +210,94 @@ class HighLine
112
210
  @builtin = defn[:builtin]
113
211
  if @rgb
114
212
  hex = self.class.rgb_hex(@rgb)
115
- @name ||= 'rgb_' + hex
213
+ @name ||= "rgb_" + hex
116
214
  elsif @list
117
215
  @name ||= @list
118
216
  end
119
217
  self.class.index self unless defn[:no_index]
120
218
  end
121
219
 
220
+ # Duplicate current Style using the same definition used to create it.
221
+ # @return [Style] duplicated Style
122
222
  def dup
123
223
  self.class.new(@definition)
124
224
  end
125
225
 
226
+ # @return [Hash] the definition used to create this Style
126
227
  def to_hash
127
228
  @definition
128
229
  end
129
230
 
231
+ # Uses the Style definition to add ANSI color escape codes
232
+ # to a a given String
233
+ # @param string [String] to be colored
234
+ # @return [String] an ANSI colored string
130
235
  def color(string)
131
236
  code + string + HighLine::CLEAR
132
237
  end
133
238
 
239
+ # @return [String] all codes of the Style list joined together
240
+ # (if a Style list)
241
+ # @return [String] the Style code
134
242
  def code
135
243
  if @list
136
- @list.map{|element| HighLine.Style(element).code}.join
244
+ @list.map { |element| HighLine.Style(element).code }.join
137
245
  else
138
246
  @code
139
247
  end
140
248
  end
141
249
 
250
+ # @return [Integer] the RED component of the rgb code
142
251
  def red
143
252
  @rgb && @rgb[0]
144
253
  end
145
254
 
255
+ # @return [Integer] the GREEN component of the rgb code
146
256
  def green
147
257
  @rgb && @rgb[1]
148
258
  end
149
259
 
260
+ # @return [Integer] the BLUE component of the rgb code
150
261
  def blue
151
262
  @rgb && @rgb[2]
152
263
  end
153
264
 
154
- def variant(new_name, options={})
265
+ # Duplicate Style with some minor changes
266
+ # @param new_name [Symbol]
267
+ # @param options [Hash] Style attributes to be changed
268
+ # @return [Style] new Style with changed attributes
269
+ def variant(new_name, options = {})
155
270
  raise "Cannot create a variant of a style list (#{inspect})" if @list
156
271
  new_code = options[:code] || code
157
272
  if options[:increment]
158
- raise "Unexpected code in #{inspect}" unless new_code =~ /^(.*?)(\d+)(.*)/
159
- new_code = $1 + ($2.to_i + options[:increment]).to_s + $3
273
+ raise "Unexpected code in #{inspect}" unless
274
+ new_code =~ /^(.*?)(\d+)(.*)/
275
+
276
+ new_code =
277
+ Regexp.last_match(1) +
278
+ (Regexp.last_match(2).to_i +
279
+ options[:increment]).to_s +
280
+ Regexp.last_match(3)
160
281
  end
161
282
  new_rgb = options[:rgb] || @rgb
162
- self.class.new(self.to_hash.merge(:name=>new_name, :code=>new_code, :rgb=>new_rgb))
283
+ self.class.new(to_hash.merge(name: new_name,
284
+ code: new_code,
285
+ rgb: new_rgb))
163
286
  end
164
287
 
288
+ # Uses the color as background and return a new style.
289
+ # @return [Style]
165
290
  def on
166
- new_name = ('on_'+@name.to_s).to_sym
167
- self.class.list[new_name] ||= variant(new_name, :increment=>10)
291
+ new_name = ("on_" + @name.to_s).to_sym
292
+ self.class.list[new_name] ||= variant(new_name, increment: 10)
168
293
  end
169
294
 
295
+ # @return [Style] a brighter version of this Style
170
296
  def bright
171
297
  create_bright_variant(:bright)
172
298
  end
173
299
 
300
+ # @return [Style] a lighter version of this Style
174
301
  def light
175
302
  create_bright_variant(:light)
176
303
  end
@@ -178,11 +305,17 @@ class HighLine
178
305
  private
179
306
 
180
307
  def create_bright_variant(variant_name)
181
- raise "Cannot create a #{name} variant of a style list (#{inspect})" if @list
182
- new_name = ("#{variant_name}_"+@name.to_s).to_sym
183
- new_rgb = @rgb == [0,0,0] ? [128, 128, 128] : @rgb.map {|color| color==0 ? 0 : [color+128,255].min }
308
+ raise "Cannot create a #{name} variant of a style list (#{inspect})" if
309
+ @list
310
+ new_name = ("#{variant_name}_" + @name.to_s).to_sym
311
+ new_rgb =
312
+ if @rgb == [0, 0, 0]
313
+ [128, 128, 128]
314
+ else
315
+ @rgb.map { |color| color.zero? ? 0 : [color + 128, 255].min }
316
+ end
184
317
 
185
- find_style(new_name) or variant(new_name, :increment=>60, :rgb=>new_rgb)
318
+ find_style(new_name) || variant(new_name, increment: 60, rgb: new_rgb)
186
319
  end
187
320
 
188
321
  def find_style(name)
@@ -0,0 +1,62 @@
1
+ # coding: utf-8
2
+
3
+ require "forwardable"
4
+
5
+ class HighLine
6
+ # Renders an erb template taking a {Question} and a {HighLine} instance
7
+ # as context.
8
+ class TemplateRenderer
9
+ extend Forwardable
10
+
11
+ def_delegators :@highline, :color, :list, :key
12
+ def_delegators :@source, :answer_type, :prompt, :header, :answer
13
+
14
+ # @return [ERB] ERB template being rendered
15
+ attr_reader :template
16
+
17
+ # @return [Question, Menu] Question instance used as context
18
+ attr_reader :source
19
+
20
+ # @return [HighLine] HighLine instance used as context
21
+ attr_reader :highline
22
+
23
+ # Initializes the TemplateRenderer object with its template and
24
+ # HighLine and Question contexts.
25
+ #
26
+ # @param template [ERB] ERB template.
27
+ # @param source [Question] question object.
28
+ # @param highline [HighLine] HighLine instance.
29
+
30
+ def initialize(template, source, highline)
31
+ @template = template
32
+ @source = source
33
+ @highline = highline
34
+ end
35
+
36
+ # @return [String] rendered template
37
+ def render
38
+ template.result(binding)
39
+ end
40
+
41
+ # Returns an error message when the called method
42
+ # is not available.
43
+ # @return [String] error message.
44
+ def method_missing(method, *args)
45
+ "Method #{method} with args #{args.inspect} " \
46
+ "is not available on #{inspect}. " \
47
+ "Try #{methods(false).sort.inspect}"
48
+ end
49
+
50
+ # @return [Question, Menu] {#source} attribute.
51
+ def menu
52
+ source
53
+ end
54
+
55
+ # If some constant is missing at this TemplateRenderer instance,
56
+ # get it from HighLine. Useful to get color and style contants.
57
+ # @param name [Symbol] automatically passed constant's name as Symbol
58
+ def self.const_missing(name)
59
+ HighLine.const_get(name)
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HighLine
4
+ class Terminal
5
+ # io/console option for HighLine::Terminal.
6
+ # It's the most used terminal.
7
+ # TODO: We're rescuing when not a terminal.
8
+ # We should make a more robust implementation.
9
+ class IOConsole < Terminal
10
+ # (see Terminal#terminal_size)
11
+ def terminal_size
12
+ output.winsize.reverse
13
+ rescue Errno::ENOTTY
14
+ end
15
+
16
+ # (see Terminal#raw_no_echo_mode)
17
+ def raw_no_echo_mode
18
+ input.echo = false
19
+ rescue Errno::ENOTTY
20
+ end
21
+
22
+ # (see Terminal#restore_mode)
23
+ def restore_mode
24
+ input.echo = true
25
+ rescue Errno::ENOTTY
26
+ end
27
+
28
+ # (see Terminal#get_character)
29
+ def get_character
30
+ input.getch # from ruby io/console
31
+ rescue Errno::ENOTTY
32
+ input.getc
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+
3
+ class HighLine
4
+ class Terminal
5
+ # NCurses HighLine::Terminal
6
+ # @note Code migrated +UNTESTED+ from the old code base to the new
7
+ # terminal api.
8
+ class NCurses < Terminal
9
+ require "ffi-ncurses"
10
+
11
+ # (see Terminal#raw_no_echo_mode)
12
+ def raw_no_echo_mode
13
+ FFI::NCurses.initscr
14
+ FFI::NCurses.cbreak
15
+ end
16
+
17
+ # (see Terminal#restore_mode)
18
+ def restore_mode
19
+ FFI::NCurses.endwin
20
+ end
21
+
22
+ #
23
+ # (see Terminal#terminal_size)
24
+ # A ncurses savvy method to fetch the console columns, and rows.
25
+ #
26
+ def terminal_size
27
+ size = [80, 40]
28
+ FFI::NCurses.initscr
29
+ begin
30
+ size = FFI::NCurses.getmaxyx(FFI::NCurses.stdscr).reverse
31
+ ensure
32
+ FFI::NCurses.endwin
33
+ end
34
+ size
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,51 @@
1
+ # coding: utf-8
2
+
3
+ class HighLine
4
+ class Terminal
5
+ # HighLine::Terminal option that uses external "stty" program
6
+ # to control terminal options.
7
+ class UnixStty < Terminal
8
+ # A Unix savvy method using stty to fetch the console columns, and rows.
9
+ # ... stty does not work in JRuby
10
+ # @return (see Terminal#terminal_size)
11
+ def terminal_size
12
+ begin
13
+ require "io/console"
14
+ winsize = begin
15
+ IO.console.winsize.reverse
16
+ rescue NoMethodError
17
+ nil
18
+ end
19
+ return winsize if winsize
20
+ rescue LoadError
21
+ end
22
+
23
+ if /solaris/ =~ RUBY_PLATFORM &&
24
+ `stty` =~ /\brows = (\d+).*\bcolumns = (\d+)/
25
+ [Regexp.last_match(2), Regexp.last_match(1)].map(&:to_i)
26
+ elsif `stty size` =~ /^(\d+)\s(\d+)$/
27
+ [Regexp.last_match(2).to_i, Regexp.last_match(1).to_i]
28
+ else
29
+ [80, 24]
30
+ end
31
+ end
32
+
33
+ # (see Terminal#raw_no_echo_mode)
34
+ def raw_no_echo_mode
35
+ @state = `stty -g`
36
+ system "stty raw -echo -icanon isig"
37
+ end
38
+
39
+ # (see Terminal#restore_mode)
40
+ def restore_mode
41
+ system "stty #{@state}"
42
+ print "\r"
43
+ end
44
+
45
+ # (see Terminal#get_character)
46
+ def get_character(input = STDIN)
47
+ input.getc
48
+ end
49
+ end
50
+ end
51
+ end