tty-prompt 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -1
- data/CHANGELOG.md +15 -0
- data/Gemfile +2 -2
- data/README.md +185 -25
- data/examples/enum.rb +8 -0
- data/examples/enum_select.rb +7 -0
- data/examples/in.rb +3 -1
- data/examples/slider.rb +6 -0
- data/lib/tty-prompt.rb +8 -2
- data/lib/tty/prompt.rb +63 -13
- data/lib/tty/prompt/converters.rb +12 -6
- data/lib/tty/prompt/enum_list.rb +222 -0
- data/lib/tty/prompt/list.rb +48 -15
- data/lib/tty/prompt/multi_list.rb +11 -11
- data/lib/tty/prompt/question.rb +38 -14
- data/lib/tty/prompt/question/checks.rb +5 -3
- data/lib/tty/prompt/reader.rb +12 -18
- data/lib/tty/prompt/reader/codes.rb +15 -9
- data/lib/tty/prompt/reader/key_event.rb +51 -24
- data/lib/tty/prompt/slider.rb +170 -0
- data/lib/tty/prompt/symbols.rb +7 -1
- data/lib/tty/prompt/utils.rb +31 -3
- data/lib/tty/prompt/version.rb +1 -1
- data/spec/spec_helper.rb +1 -0
- data/spec/unit/converters/convert_bool_spec.rb +1 -1
- data/spec/unit/converters/convert_date_spec.rb +11 -2
- data/spec/unit/converters/convert_file_spec.rb +1 -1
- data/spec/unit/converters/convert_number_spec.rb +19 -2
- data/spec/unit/converters/convert_path_spec.rb +1 -1
- data/spec/unit/converters/convert_range_spec.rb +4 -3
- data/spec/unit/enum_select_spec.rb +93 -0
- data/spec/unit/multi_select_spec.rb +14 -12
- data/spec/unit/question/checks_spec.rb +97 -0
- data/spec/unit/reader/key_event_spec.rb +67 -0
- data/spec/unit/select_spec.rb +15 -16
- data/spec/unit/slider_spec.rb +54 -0
- data/tty-prompt.gemspec +2 -1
- metadata +31 -5
- data/.ruby-version +0 -1
@@ -9,8 +9,7 @@ module TTY
|
|
9
9
|
#
|
10
10
|
# @api private
|
11
11
|
class MultiList < List
|
12
|
-
|
13
|
-
HELP = '(Use arrow keys, press Space to select and Enter to finish)'.freeze
|
12
|
+
HELP = '(Use arrow%s keys, press Space to select and Enter to finish)'.freeze
|
14
13
|
|
15
14
|
# Create instance of TTY::Prompt::MultiList menu.
|
16
15
|
#
|
@@ -21,14 +20,14 @@ module TTY
|
|
21
20
|
def initialize(prompt, options)
|
22
21
|
super
|
23
22
|
@selected = []
|
24
|
-
@help
|
25
|
-
@default
|
23
|
+
@help = options[:help]
|
24
|
+
@default = options.fetch(:default) { [] }
|
26
25
|
end
|
27
26
|
|
28
27
|
# Callback fired when space key is pressed
|
29
28
|
#
|
30
29
|
# @api private
|
31
|
-
def keyspace(
|
30
|
+
def keyspace(*)
|
32
31
|
active_choice = @choices[@active - 1]
|
33
32
|
if @selected.include?(active_choice)
|
34
33
|
@selected.delete(active_choice)
|
@@ -57,9 +56,7 @@ module TTY
|
|
57
56
|
elsif @selected.size.nonzero?
|
58
57
|
@selected.map(&:name).join(', ')
|
59
58
|
elsif @first_render
|
60
|
-
@prompt.decorate(
|
61
|
-
else
|
62
|
-
''
|
59
|
+
@prompt.decorate(help, :bright_black)
|
63
60
|
end
|
64
61
|
end
|
65
62
|
|
@@ -76,18 +73,21 @@ module TTY
|
|
76
73
|
#
|
77
74
|
# @api private
|
78
75
|
def render_menu
|
76
|
+
output = ''
|
79
77
|
@choices.each_with_index do |choice, index|
|
78
|
+
num = enumerate? ? (index + 1).to_s + @enum + Symbols::SPACE : ''
|
80
79
|
indicator = (index + 1 == @active) ? @marker : Symbols::SPACE
|
81
80
|
indicator += Symbols::SPACE
|
82
81
|
message = if @selected.include?(choice)
|
83
82
|
selected = @prompt.decorate(Symbols::RADIO_CHECKED, :green)
|
84
|
-
selected + Symbols::SPACE + choice.name
|
83
|
+
selected + Symbols::SPACE + num + choice.name
|
85
84
|
else
|
86
|
-
Symbols::RADIO_UNCHECKED + Symbols::SPACE + choice.name
|
85
|
+
Symbols::RADIO_UNCHECKED + Symbols::SPACE + num + choice.name
|
87
86
|
end
|
88
87
|
newline = (index == @choices.length - 1) ? '' : "\n"
|
89
|
-
|
88
|
+
output << indicator + message + newline
|
90
89
|
end
|
90
|
+
output
|
91
91
|
end
|
92
92
|
end # MultiList
|
93
93
|
end # Prompt
|
data/lib/tty/prompt/question.rb
CHANGED
@@ -16,8 +16,6 @@ module TTY
|
|
16
16
|
include Checks
|
17
17
|
include Converters
|
18
18
|
|
19
|
-
BLANK_REGEX = /\A[[:space:]]*\z/o.freeze
|
20
|
-
|
21
19
|
UndefinedSetting = Module.new
|
22
20
|
|
23
21
|
# Store question message
|
@@ -44,6 +42,7 @@ module TTY
|
|
44
42
|
@read = options.fetch(:read) { UndefinedSetting }
|
45
43
|
@convert = options.fetch(:convert) { UndefinedSetting }
|
46
44
|
@color = options.fetch(:color) { :green }
|
45
|
+
@messages = Utils.deep_copy(options.fetch(:messages) { { } })
|
47
46
|
@done = false
|
48
47
|
@input = nil
|
49
48
|
|
@@ -56,6 +55,33 @@ module TTY
|
|
56
55
|
@evaluator << CheckModifier
|
57
56
|
end
|
58
57
|
|
58
|
+
# Stores all the error messages displayed to user
|
59
|
+
# The currently supported messages are:
|
60
|
+
# * :range?
|
61
|
+
# * :required?
|
62
|
+
# * :valid?
|
63
|
+
attr_reader :messages
|
64
|
+
|
65
|
+
# Retrieve message based on the key
|
66
|
+
#
|
67
|
+
# @param [Symbol] name
|
68
|
+
# the name of message key
|
69
|
+
#
|
70
|
+
# @param [Hash] tokens
|
71
|
+
# the tokens to evaluate
|
72
|
+
#
|
73
|
+
# @return [Array[String]]
|
74
|
+
#
|
75
|
+
# @api private
|
76
|
+
def message_for(name, tokens = nil)
|
77
|
+
template = @messages[name]
|
78
|
+
if template && !template.match(/\%\{/).nil?
|
79
|
+
[template % tokens]
|
80
|
+
else
|
81
|
+
[template || '']
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
59
85
|
# Call the question
|
60
86
|
#
|
61
87
|
# @param [String] message
|
@@ -64,7 +90,7 @@ module TTY
|
|
64
90
|
#
|
65
91
|
# @api public
|
66
92
|
def call(message, &block)
|
67
|
-
return if blank?(message)
|
93
|
+
return if Utils.blank?(message)
|
68
94
|
@message = message
|
69
95
|
block.call(self) if block
|
70
96
|
render
|
@@ -108,7 +134,7 @@ module TTY
|
|
108
134
|
# @api private
|
109
135
|
def process_input
|
110
136
|
@input = read_input
|
111
|
-
if blank?(@input)
|
137
|
+
if Utils.blank?(@input)
|
112
138
|
@input = default? ? default : nil
|
113
139
|
end
|
114
140
|
@evaluator.(@input)
|
@@ -168,7 +194,7 @@ module TTY
|
|
168
194
|
#
|
169
195
|
# @api private
|
170
196
|
def convert_result(value)
|
171
|
-
if convert? & !blank?(value)
|
197
|
+
if convert? & !Utils.blank?(value)
|
172
198
|
converter_registry.(@convert, value)
|
173
199
|
else
|
174
200
|
value
|
@@ -220,7 +246,8 @@ module TTY
|
|
220
246
|
# @return [Boolean]
|
221
247
|
#
|
222
248
|
# @api public
|
223
|
-
def required(value = (not_set = true))
|
249
|
+
def required(value = (not_set = true), message = nil)
|
250
|
+
messages[:required?] = message if message
|
224
251
|
return @required if not_set
|
225
252
|
@required = value
|
226
253
|
end
|
@@ -233,7 +260,8 @@ module TTY
|
|
233
260
|
# @return [Question]
|
234
261
|
#
|
235
262
|
# @api public
|
236
|
-
def validate(value = nil, &block)
|
263
|
+
def validate(value = nil, message = nil, &block)
|
264
|
+
messages[:valid?] = message if message
|
237
265
|
@validation = (value || block)
|
238
266
|
end
|
239
267
|
|
@@ -274,7 +302,8 @@ module TTY
|
|
274
302
|
# @param [String] value
|
275
303
|
#
|
276
304
|
# @api public
|
277
|
-
def in(value = (not_set = true))
|
305
|
+
def in(value = (not_set = true), message = nil)
|
306
|
+
messages[:range?] = message if message
|
278
307
|
if in? && !@in.is_a?(Range)
|
279
308
|
@in = converter_registry.(:range, @in)
|
280
309
|
end
|
@@ -291,12 +320,7 @@ module TTY
|
|
291
320
|
@in != UndefinedSetting
|
292
321
|
end
|
293
322
|
|
294
|
-
|
295
|
-
value.nil? ||
|
296
|
-
value.respond_to?(:empty?) && value.empty? ||
|
297
|
-
BLANK_REGEX === value
|
298
|
-
end
|
299
|
-
|
323
|
+
# @api public
|
300
324
|
def to_s
|
301
325
|
"#{message}"
|
302
326
|
end
|
@@ -40,7 +40,8 @@ module TTY
|
|
40
40
|
(question.in? && question.in.include?(cast(value)))
|
41
41
|
[value]
|
42
42
|
else
|
43
|
-
|
43
|
+
tokens = {value: value, in: question.in}
|
44
|
+
[value, question.message_for(:range?, tokens)]
|
44
45
|
end
|
45
46
|
end
|
46
47
|
end
|
@@ -53,7 +54,8 @@ module TTY
|
|
53
54
|
Validation.new(question.validation).call(value))
|
54
55
|
[value]
|
55
56
|
else
|
56
|
-
|
57
|
+
tokens = {valid: question.validation.inspect}
|
58
|
+
[value, question.message_for(:valid?, tokens)]
|
57
59
|
end
|
58
60
|
end
|
59
61
|
end
|
@@ -73,7 +75,7 @@ module TTY
|
|
73
75
|
class CheckRequired
|
74
76
|
def self.call(question, value)
|
75
77
|
if question.required? && !question.default? && value.nil?
|
76
|
-
[value,
|
78
|
+
[value, question.message_for(:required?)]
|
77
79
|
else
|
78
80
|
[value]
|
79
81
|
end
|
data/lib/tty/prompt/reader.rb
CHANGED
@@ -8,6 +8,10 @@ module TTY
|
|
8
8
|
# A class responsible for shell prompt interactions.
|
9
9
|
class Prompt
|
10
10
|
# A class responsible for reading character input from STDIN
|
11
|
+
#
|
12
|
+
# Used internally to provide key and line reading functionality
|
13
|
+
#
|
14
|
+
# @api private
|
11
15
|
class Reader
|
12
16
|
include Wisper::Publisher
|
13
17
|
|
@@ -56,6 +60,9 @@ module TTY
|
|
56
60
|
# Read a single keypress that may include
|
57
61
|
# 2 or 3 escape characters.
|
58
62
|
#
|
63
|
+
# @param [Boolean] echo
|
64
|
+
# whether to echo chars back or not, defaults to false
|
65
|
+
#
|
59
66
|
# @return [String]
|
60
67
|
#
|
61
68
|
# @api public
|
@@ -64,7 +71,7 @@ module TTY
|
|
64
71
|
mode.echo(echo) do
|
65
72
|
mode.raw(true) do
|
66
73
|
key = read_char
|
67
|
-
|
74
|
+
emit_key_event(key) if key
|
68
75
|
exit 130 if key == Codes::CTRL_C
|
69
76
|
key
|
70
77
|
end
|
@@ -107,7 +114,7 @@ module TTY
|
|
107
114
|
mode.echo(echo) do
|
108
115
|
while (char = input.getbyte) &&
|
109
116
|
!(char == CARRIAGE_RETURN || char == NEWLINE)
|
110
|
-
|
117
|
+
emit_key_event(convert_byte(char))
|
111
118
|
line = handle_char(line, char)
|
112
119
|
end
|
113
120
|
end
|
@@ -145,25 +152,12 @@ module TTY
|
|
145
152
|
# @return [nil]
|
146
153
|
#
|
147
154
|
# @api public
|
148
|
-
def
|
149
|
-
event = KeyEvent.from(
|
150
|
-
|
151
|
-
publish(event_name, event) unless event_name.nil?
|
155
|
+
def emit_key_event(key)
|
156
|
+
event = KeyEvent.from(key)
|
157
|
+
publish(:"key#{event.key.name}", event) if event.emit?
|
152
158
|
publish(:keypress, event)
|
153
159
|
end
|
154
160
|
|
155
|
-
# Interpret the key and provide event name
|
156
|
-
#
|
157
|
-
# @return [Symbol]
|
158
|
-
#
|
159
|
-
# @api public
|
160
|
-
def parse_key_event(event)
|
161
|
-
return if event.key.nil?
|
162
|
-
permitted_events = %w(up down left right space return enter num)
|
163
|
-
return unless permitted_events.include?("#{event.key.name}")
|
164
|
-
:"key#{event.key.name}"
|
165
|
-
end
|
166
|
-
|
167
161
|
private
|
168
162
|
|
169
163
|
trap('SIGINT') { exit 130 }
|
@@ -39,15 +39,21 @@ module TTY
|
|
39
39
|
CTRL_H = "\b"
|
40
40
|
CTRL_L = "\f"
|
41
41
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
42
|
+
F1_XTERM = "\eOP"
|
43
|
+
F2_XTERM = "\eOQ"
|
44
|
+
F3_XTERM = "\eOR"
|
45
|
+
F4_XTERM = "\eOS"
|
46
|
+
|
47
|
+
F1_GNOME = "\e[11~"
|
48
|
+
F2_GNOME = "\e[12~"
|
49
|
+
F3_GNOME = "\e[13~"
|
50
|
+
F4_GNOME = "\e[14~"
|
51
|
+
|
52
|
+
F1_WIN = "\e[[A"
|
53
|
+
F2_WIN = "\e[[B"
|
54
|
+
F3_WIN = "\e[[C"
|
55
|
+
F4_WIN = "\e[[D"
|
56
|
+
F5_WIN = "\e[[E"
|
51
57
|
|
52
58
|
F5 = "\e[15~"
|
53
59
|
F6 = "\e[17~"
|
@@ -21,46 +21,73 @@ module TTY
|
|
21
21
|
#
|
22
22
|
# @api public
|
23
23
|
class KeyEvent < Struct.new(:value, :key)
|
24
|
-
META_KEY_CODE_RE = /^(?:\
|
24
|
+
META_KEY_CODE_RE = /^(?:\e)(O|N|\[|\[\[)(?:(\d+)(?:;(\d+))?([~^$])|(?:1;)?(\d+)?([a-zA-Z]))/
|
25
25
|
|
26
26
|
def self.from(char)
|
27
27
|
key = Key.new
|
28
28
|
case char
|
29
|
-
when Codes::RETURN
|
30
|
-
|
31
|
-
when Codes::
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
when Codes::BACKSPACE
|
29
|
+
when Codes::RETURN then key.name = :return
|
30
|
+
when Codes::LINEFEED then key.name = :enter
|
31
|
+
when Codes::TAB then key.name = :tab
|
32
|
+
when Codes::BACKSPACE, Codes::CTRL_H,
|
33
|
+
"#{Codes::ESCAPE}#{Codes::BACKSPACE}",
|
34
|
+
"#{Codes::ESCAPE}#{Codes::CTRL_H}"
|
36
35
|
key.name = :backspace
|
37
|
-
when Codes::DELETE
|
38
|
-
|
39
|
-
when Codes::
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
key.ctrl = true
|
47
|
-
when /\d/
|
36
|
+
when Codes::DELETE then key.name = :delete
|
37
|
+
when Codes::SPACE then key.name = :space
|
38
|
+
when Codes::CTRL_C, Codes::ESCAPE then key.name = :escape
|
39
|
+
when proc { |c| c.length == 1 && c =~ /[a-z]/ }
|
40
|
+
key.name = char
|
41
|
+
when proc { |c| c.length == 1 && c =~ /[A-Z]/ }
|
42
|
+
key.name = char.downcase
|
43
|
+
key.shift = true
|
44
|
+
when /^\d+$/
|
48
45
|
key.name = :num
|
49
|
-
when META_KEY_CODE_RE
|
46
|
+
when META_KEY_CODE_RE # ansi escape
|
50
47
|
key.meta = true
|
48
|
+
|
51
49
|
case char
|
52
|
-
|
50
|
+
# f1 - f12
|
51
|
+
when Codes::F1_XTERM, Codes::F1_GNOME, Codes::F1_WIN then key.name = :f1
|
52
|
+
when Codes::F2_XTERM, Codes::F2_GNOME, Codes::F2_WIN then key.name = :f2
|
53
|
+
when Codes::F3_XTERM, Codes::F3_GNOME, Codes::F3_WIN then key.name = :f3
|
54
|
+
when Codes::F4_XTERM, Codes::F4_GNOME, Codes::F4_WIN then key.name = :f4
|
55
|
+
when Codes::F5 then key.name = :f5
|
56
|
+
when Codes::F6 then key.name = :f6
|
57
|
+
when Codes::F7 then key.name = :f7
|
58
|
+
when Codes::F8 then key.name = :f8
|
59
|
+
when Codes::F9 then key.name = :f9
|
60
|
+
when Codes::F10 then key.name = :f10
|
61
|
+
when Codes::F11 then key.name = :f11
|
62
|
+
when Codes::F12 then key.name = :f12
|
63
|
+
# navigation
|
64
|
+
when Codes::KEY_UP, Codes::KEY_UP_ALT, Codes::CTRL_K, Codes::CTRL_P
|
53
65
|
key.name = :up
|
54
|
-
when Codes::KEY_DOWN, Codes::CTRL_J, Codes::CTRL_N
|
66
|
+
when Codes::KEY_DOWN, Codes::KEY_DOWN_ALT, Codes::CTRL_J, Codes::CTRL_N
|
55
67
|
key.name = :down
|
56
|
-
when Codes::KEY_RIGHT, Codes::CTRL_L
|
68
|
+
when Codes::KEY_RIGHT, Codes::KEY_RIGHT_ALT, Codes::CTRL_L
|
57
69
|
key.name = :right
|
58
|
-
when Codes::KEY_LEFT, Codes::CTRL_H
|
70
|
+
when Codes::KEY_LEFT, Codes::KEY_LEFT_ALT, Codes::CTRL_H
|
59
71
|
key.name = :left
|
72
|
+
when Codes::KEY_CLEAR, Codes::KEY_CLEAR_ALT
|
73
|
+
key.name = :clear
|
74
|
+
when Codes::KEY_END, Codes::KEY_END_ALT
|
75
|
+
key.name = :end
|
76
|
+
when Codes::KEY_HOME, Codes::KEY_HOME_ALT
|
77
|
+
key.name = :home
|
60
78
|
end
|
61
79
|
end
|
62
80
|
new(char, key)
|
63
81
|
end
|
82
|
+
|
83
|
+
# Check if key event can be emitted
|
84
|
+
#
|
85
|
+
# @return [Boolean]
|
86
|
+
#
|
87
|
+
# @api public
|
88
|
+
def emit?
|
89
|
+
!key.nil? && !key.name.nil?
|
90
|
+
end
|
64
91
|
end # KeyEvent
|
65
92
|
end # Reader
|
66
93
|
end # Prompt
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module TTY
|
4
|
+
# A class responsible for shell prompt interactions.
|
5
|
+
class Prompt
|
6
|
+
# A class responsible for gathering numeric input from range
|
7
|
+
#
|
8
|
+
# @api public
|
9
|
+
class Slider
|
10
|
+
HELP = '(Use arrow keys, press Enter to select)'.freeze
|
11
|
+
|
12
|
+
# Initailize a Slider
|
13
|
+
#
|
14
|
+
# @api public
|
15
|
+
def initialize(prompt, options = {})
|
16
|
+
@prompt = prompt
|
17
|
+
@first_render = true
|
18
|
+
@done = false
|
19
|
+
@color = options.fetch(:color) { :green }
|
20
|
+
@min = options.fetch(:min) { 0 }
|
21
|
+
@max = options.fetch(:max) { 10 }
|
22
|
+
@step = options.fetch(:step) { 1 }
|
23
|
+
@default = options[:default]
|
24
|
+
|
25
|
+
@prompt.subscribe(self)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Setup initial active position
|
29
|
+
#
|
30
|
+
# @return [Integer]
|
31
|
+
#
|
32
|
+
# @api private
|
33
|
+
def initial
|
34
|
+
if @default.nil?
|
35
|
+
range.size / 2
|
36
|
+
else
|
37
|
+
range.index(@default)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Range of numbers to render
|
42
|
+
#
|
43
|
+
# @return [Array[Integer]]
|
44
|
+
#
|
45
|
+
# @apip private
|
46
|
+
def range
|
47
|
+
(@min..@max).step(@step).to_a
|
48
|
+
end
|
49
|
+
|
50
|
+
# @api public
|
51
|
+
def default(value)
|
52
|
+
@default = value
|
53
|
+
end
|
54
|
+
|
55
|
+
# @api public
|
56
|
+
def min(value)
|
57
|
+
@min = value
|
58
|
+
end
|
59
|
+
|
60
|
+
# @api public
|
61
|
+
def max(value)
|
62
|
+
@max = value
|
63
|
+
end
|
64
|
+
|
65
|
+
# @api public
|
66
|
+
def step(value)
|
67
|
+
@step = value
|
68
|
+
end
|
69
|
+
|
70
|
+
# Call the slider by passing question
|
71
|
+
#
|
72
|
+
# @param [String] question
|
73
|
+
# the question to ask
|
74
|
+
#
|
75
|
+
# @apu public
|
76
|
+
def call(question, &block)
|
77
|
+
@question = question
|
78
|
+
block.call(self) if block
|
79
|
+
@active = initial
|
80
|
+
render
|
81
|
+
end
|
82
|
+
|
83
|
+
def keyleft(*)
|
84
|
+
@active -= 1 if @active > 0
|
85
|
+
end
|
86
|
+
alias_method :keydown, :keyleft
|
87
|
+
|
88
|
+
def keyright(*)
|
89
|
+
@active += 1 if (@active + @step) <= range.size
|
90
|
+
end
|
91
|
+
alias_method :keyup, :keyright
|
92
|
+
|
93
|
+
def keyreturn(*)
|
94
|
+
@done = true
|
95
|
+
end
|
96
|
+
alias_method :keyspace, :keyreturn
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
# Render an interactive range slider.
|
101
|
+
#
|
102
|
+
# @api private
|
103
|
+
def render
|
104
|
+
@prompt.print(@prompt.hide)
|
105
|
+
until @done
|
106
|
+
render_question
|
107
|
+
@prompt.read_keypress
|
108
|
+
refresh
|
109
|
+
end
|
110
|
+
render_question
|
111
|
+
answer = render_answer
|
112
|
+
ensure
|
113
|
+
@prompt.print(@prompt.show)
|
114
|
+
answer
|
115
|
+
end
|
116
|
+
|
117
|
+
# Clear screen
|
118
|
+
#
|
119
|
+
# @api private
|
120
|
+
def refresh
|
121
|
+
lines = @question.scan("\n").length + 2
|
122
|
+
@prompt.print(@prompt.clear_lines(lines))
|
123
|
+
end
|
124
|
+
|
125
|
+
# @return [Integer]
|
126
|
+
#
|
127
|
+
# @api private
|
128
|
+
def render_answer
|
129
|
+
range[@active]
|
130
|
+
end
|
131
|
+
|
132
|
+
# Render question with the slider
|
133
|
+
#
|
134
|
+
# @api private
|
135
|
+
def render_question
|
136
|
+
header = "#{@prompt.prefix}#{@question} #{render_header}"
|
137
|
+
@prompt.puts(header)
|
138
|
+
@first_render = false
|
139
|
+
@prompt.print(render_slider) unless @done
|
140
|
+
end
|
141
|
+
|
142
|
+
# Render actual answer or help
|
143
|
+
#
|
144
|
+
# @api private
|
145
|
+
def render_header
|
146
|
+
if @done
|
147
|
+
@prompt.decorate(render_answer.to_s, @color)
|
148
|
+
elsif @first_render
|
149
|
+
@prompt.decorate(HELP, :bright_black)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
# Render slider representation
|
154
|
+
#
|
155
|
+
# @return [String]
|
156
|
+
#
|
157
|
+
# @api private
|
158
|
+
def render_slider
|
159
|
+
output = ''
|
160
|
+
output << Symbols::SLIDER_END
|
161
|
+
output << '-' * @active
|
162
|
+
output << @prompt.decorate(Symbols::SLIDER_HANDLE, @color)
|
163
|
+
output << '-' * (range.size - @active - 1)
|
164
|
+
output << Symbols::SLIDER_END
|
165
|
+
output << " #{range[@active]}"
|
166
|
+
output
|
167
|
+
end
|
168
|
+
end # Slider
|
169
|
+
end # Prompt
|
170
|
+
end # TTY
|