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.
- checksums.yaml +5 -5
- data/.gitignore +5 -0
- data/.rubocop.yml +84 -0
- data/.simplecov +5 -0
- data/.travis.yml +35 -9
- data/Changelog.md +214 -15
- data/Gemfile +16 -5
- data/README.md +202 -0
- data/Rakefile +8 -20
- data/appveyor.yml +37 -0
- data/examples/ansi_colors.rb +6 -11
- data/examples/asking_for_arrays.rb +6 -2
- data/examples/basic_usage.rb +31 -21
- data/examples/color_scheme.rb +11 -10
- data/examples/get_character.rb +8 -4
- data/examples/limit.rb +4 -0
- data/examples/menus.rb +16 -10
- data/examples/overwrite.rb +9 -5
- data/examples/page_and_wrap.rb +5 -4
- data/examples/password.rb +4 -0
- data/examples/repeat_entry.rb +10 -5
- data/examples/trapping_eof.rb +2 -1
- data/examples/using_readline.rb +2 -1
- data/highline.gemspec +25 -27
- data/lib/highline/builtin_styles.rb +129 -0
- data/lib/highline/color_scheme.rb +49 -32
- data/lib/highline/compatibility.rb +11 -4
- data/lib/highline/custom_errors.rb +57 -0
- data/lib/highline/import.rb +19 -12
- data/lib/highline/io_console_compatible.rb +37 -0
- data/lib/highline/list.rb +177 -0
- data/lib/highline/list_renderer.rb +261 -0
- data/lib/highline/menu/item.rb +32 -0
- data/lib/highline/menu.rb +306 -111
- data/lib/highline/paginator.rb +52 -0
- data/lib/highline/question/answer_converter.rb +103 -0
- data/lib/highline/question.rb +281 -131
- data/lib/highline/question_asker.rb +150 -0
- data/lib/highline/simulate.rb +24 -13
- data/lib/highline/statement.rb +88 -0
- data/lib/highline/string.rb +36 -0
- data/lib/highline/string_extensions.rb +83 -64
- data/lib/highline/style.rb +196 -63
- data/lib/highline/template_renderer.rb +62 -0
- data/lib/highline/terminal/io_console.rb +36 -0
- data/lib/highline/terminal/ncurses.rb +38 -0
- data/lib/highline/terminal/unix_stty.rb +51 -0
- data/lib/highline/terminal.rb +190 -0
- data/lib/highline/version.rb +3 -1
- data/lib/highline/wrapper.rb +53 -0
- data/lib/highline.rb +390 -788
- metadata +56 -35
- data/INSTALL +0 -59
- data/README.rdoc +0 -74
- data/lib/highline/system_extensions.rb +0 -254
- data/setup.rb +0 -1360
- data/test/string_methods.rb +0 -32
- data/test/tc_color_scheme.rb +0 -96
- data/test/tc_highline.rb +0 -1402
- data/test/tc_import.rb +0 -52
- data/test/tc_menu.rb +0 -439
- data/test/tc_simulator.rb +0 -33
- data/test/tc_string_extension.rb +0 -33
- data/test/tc_string_highline.rb +0 -38
- data/test/tc_style.rb +0 -578
data/lib/highline/style.rb
CHANGED
@@ -1,108 +1,206 @@
|
|
1
|
-
#
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
46
|
-
|
75
|
+
@styles ||= {}
|
76
|
+
@styles[style.name] = style
|
47
77
|
end
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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) ?
|
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 = (
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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.
|
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
|
85
|
-
|
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
|
-
|
171
|
+
@styles ||= {}
|
91
172
|
end
|
92
173
|
|
174
|
+
# @return [Hash] list of all cached Style codes
|
93
175
|
def self.code_index
|
94
|
-
|
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
|
-
|
102
|
-
|
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 ||=
|
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
|
-
|
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
|
159
|
-
|
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(
|
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 = (
|
167
|
-
self.class.list[new_name] ||= variant(new_name, :
|
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
|
182
|
-
|
183
|
-
|
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)
|
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
|