tty-prompt 0.11.0 → 0.12.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +66 -7
- data/examples/key_events.rb +11 -0
- data/examples/keypress.rb +3 -5
- data/examples/multiline.rb +9 -0
- data/examples/pause.rb +7 -0
- data/lib/tty/prompt.rb +82 -44
- data/lib/tty/prompt/confirm_question.rb +20 -36
- data/lib/tty/prompt/enum_list.rb +32 -23
- data/lib/tty/prompt/expander.rb +35 -31
- data/lib/tty/prompt/keypress.rb +91 -0
- data/lib/tty/prompt/list.rb +38 -23
- data/lib/tty/prompt/mask_question.rb +4 -7
- data/lib/tty/prompt/multi_list.rb +3 -1
- data/lib/tty/prompt/multiline.rb +71 -0
- data/lib/tty/prompt/question.rb +33 -35
- data/lib/tty/prompt/reader.rb +154 -38
- data/lib/tty/prompt/reader/codes.rb +4 -4
- data/lib/tty/prompt/reader/console.rb +1 -1
- data/lib/tty/prompt/reader/history.rb +145 -0
- data/lib/tty/prompt/reader/key_event.rb +4 -0
- data/lib/tty/prompt/reader/line.rb +162 -0
- data/lib/tty/prompt/reader/mode.rb +2 -2
- data/lib/tty/prompt/reader/win_console.rb +5 -1
- data/lib/tty/prompt/slider.rb +18 -12
- data/lib/tty/prompt/timeout.rb +48 -0
- data/lib/tty/prompt/version.rb +1 -1
- data/spec/unit/ask_spec.rb +15 -0
- data/spec/unit/converters/convert_bool_spec.rb +1 -0
- data/spec/unit/keypress_spec.rb +35 -6
- data/spec/unit/multi_select_spec.rb +18 -0
- data/spec/unit/multiline_spec.rb +67 -9
- data/spec/unit/question/default_spec.rb +1 -0
- data/spec/unit/question/echo_spec.rb +8 -0
- data/spec/unit/question/in_spec.rb +13 -0
- data/spec/unit/question/required_spec.rb +31 -2
- data/spec/unit/question/validate_spec.rb +39 -9
- data/spec/unit/reader/history_spec.rb +172 -0
- data/spec/unit/reader/key_event_spec.rb +12 -8
- data/spec/unit/reader/line_spec.rb +110 -0
- data/spec/unit/reader/publish_keypress_event_spec.rb +11 -0
- data/spec/unit/reader/read_line_spec.rb +32 -2
- data/spec/unit/reader/read_multiline_spec.rb +21 -7
- data/spec/unit/select_spec.rb +40 -1
- data/spec/unit/yes_no_spec.rb +48 -4
- metadata +14 -3
- data/lib/tty/prompt/history.rb +0 -16
data/lib/tty/prompt/enum_list.rb
CHANGED
@@ -197,12 +197,17 @@ module TTY
|
|
197
197
|
def render
|
198
198
|
@input = ''
|
199
199
|
until @done
|
200
|
-
|
200
|
+
question = render_question
|
201
|
+
@prompt.print(question)
|
202
|
+
@prompt.print(render_error) if @failure
|
203
|
+
if paginated? && !@done
|
204
|
+
@prompt.print(render_page_help)
|
205
|
+
end
|
201
206
|
@prompt.read_keypress
|
202
|
-
refresh(lines)
|
207
|
+
@prompt.print(refresh(question.lines.count))
|
203
208
|
end
|
204
|
-
render_question
|
205
|
-
|
209
|
+
@prompt.print(render_question)
|
210
|
+
answer
|
206
211
|
end
|
207
212
|
|
208
213
|
# Find value for the choice selected
|
@@ -210,7 +215,7 @@ module TTY
|
|
210
215
|
# @return [nil, Object]
|
211
216
|
#
|
212
217
|
# @api private
|
213
|
-
def
|
218
|
+
def answer
|
214
219
|
@choices[@active - 1].value
|
215
220
|
end
|
216
221
|
|
@@ -219,27 +224,26 @@ module TTY
|
|
219
224
|
# @param [Integer] lines
|
220
225
|
# the lines to clear
|
221
226
|
#
|
227
|
+
# @return [String]
|
228
|
+
#
|
222
229
|
# @api private
|
223
230
|
def refresh(lines)
|
224
|
-
@prompt.
|
225
|
-
|
231
|
+
@prompt.clear_lines(lines) +
|
232
|
+
@prompt.cursor.clear_screen_down
|
226
233
|
end
|
227
234
|
|
228
235
|
# Render question with the menu options
|
229
236
|
#
|
237
|
+
# @return [String]
|
238
|
+
#
|
230
239
|
# @api private
|
231
240
|
def render_question
|
232
|
-
header = "#{@prefix}#{@question} #{render_header}"
|
233
|
-
@prompt.puts(header)
|
234
|
-
lines = header.lines.count
|
241
|
+
header = "#{@prefix}#{@question} #{render_header}\n"
|
235
242
|
unless @done
|
236
|
-
|
237
|
-
|
238
|
-
@prompt.print(menu)
|
243
|
+
header << render_menu
|
244
|
+
header << render_footer
|
239
245
|
end
|
240
|
-
|
241
|
-
render_page_help if paginated? && !@done
|
242
|
-
lines
|
246
|
+
header
|
243
247
|
end
|
244
248
|
|
245
249
|
# Error message when incorrect index chosen
|
@@ -252,13 +256,16 @@ module TTY
|
|
252
256
|
|
253
257
|
# Render error message and return cursor to position of input
|
254
258
|
#
|
259
|
+
# @return [String]
|
260
|
+
#
|
255
261
|
# @api private
|
256
262
|
def render_error
|
257
|
-
|
263
|
+
error = error_message.dup
|
258
264
|
if !paginated?
|
259
|
-
@prompt.
|
260
|
-
@prompt.
|
265
|
+
error << @prompt.cursor.prev_line
|
266
|
+
error << @prompt.cursor.forward(render_footer.size)
|
261
267
|
end
|
268
|
+
error
|
262
269
|
end
|
263
270
|
|
264
271
|
# Render chosen option
|
@@ -294,14 +301,16 @@ module TTY
|
|
294
301
|
|
295
302
|
# Render page help
|
296
303
|
#
|
304
|
+
# @return [String]
|
305
|
+
#
|
297
306
|
# @api private
|
298
307
|
def render_page_help
|
299
|
-
|
308
|
+
help = page_help_message.dup
|
300
309
|
if @failure
|
301
|
-
@prompt.
|
310
|
+
help << @prompt.cursor.prev_line
|
302
311
|
end
|
303
|
-
@prompt.
|
304
|
-
@prompt.
|
312
|
+
help << @prompt.cursor.prev_line
|
313
|
+
help << @prompt.cursor.forward(render_footer.size)
|
305
314
|
end
|
306
315
|
|
307
316
|
# Render menu with indexed choices to select from
|
data/lib/tty/prompt/expander.rb
CHANGED
@@ -158,19 +158,25 @@ module TTY
|
|
158
158
|
def render
|
159
159
|
@input = ''
|
160
160
|
until @done
|
161
|
-
render_question
|
161
|
+
question = render_question
|
162
|
+
@prompt.print(question)
|
162
163
|
read_input
|
163
|
-
refresh
|
164
|
+
@prompt.print(refresh(question.lines.count))
|
164
165
|
end
|
165
|
-
render_question
|
166
|
-
|
166
|
+
@prompt.print(render_question)
|
167
|
+
answer
|
167
168
|
end
|
168
169
|
|
169
170
|
# @api private
|
170
|
-
def
|
171
|
+
def answer
|
171
172
|
@selected.value
|
172
173
|
end
|
173
174
|
|
175
|
+
# Render message with options
|
176
|
+
#
|
177
|
+
# @return [String]
|
178
|
+
#
|
179
|
+
# @api private
|
174
180
|
def render_header
|
175
181
|
header = "#{@prefix}#{@message} "
|
176
182
|
if @done
|
@@ -184,27 +190,34 @@ module TTY
|
|
184
190
|
header
|
185
191
|
end
|
186
192
|
|
193
|
+
# Show hint for selected option key
|
194
|
+
#
|
195
|
+
# return [String]
|
196
|
+
#
|
187
197
|
# @api private
|
188
198
|
def render_hint
|
189
199
|
hint = "\n"
|
190
200
|
hint << @prompt.decorate('>> ', @active_color)
|
191
201
|
hint << @hint
|
192
|
-
@prompt.
|
193
|
-
@prompt.
|
194
|
-
@prompt.print(@prompt.cursor.forward(@prompt.strip(render_header).size))
|
202
|
+
hint << @prompt.cursor.prev_line
|
203
|
+
hint << @prompt.cursor.forward(@prompt.strip(render_header).size)
|
195
204
|
end
|
196
205
|
|
206
|
+
# Render question with menu
|
207
|
+
#
|
208
|
+
# @return [String]
|
209
|
+
#
|
197
210
|
# @api private
|
198
211
|
def render_question
|
199
212
|
header = render_header
|
200
|
-
@
|
201
|
-
|
202
|
-
@prompt.print("\n") if @done
|
213
|
+
header << render_hint if @hint
|
214
|
+
header << "\n" if @done
|
203
215
|
|
204
216
|
if !@done && expanded?
|
205
|
-
|
206
|
-
|
217
|
+
header << render_menu
|
218
|
+
header << render_footer
|
207
219
|
end
|
220
|
+
header
|
208
221
|
end
|
209
222
|
|
210
223
|
def render_footer
|
@@ -215,31 +228,22 @@ module TTY
|
|
215
228
|
@prompt.read_keypress
|
216
229
|
end
|
217
230
|
|
218
|
-
# @api private
|
219
|
-
def count_lines
|
220
|
-
lines = render_header.scan("\n").length + 1
|
221
|
-
if @hint
|
222
|
-
lines += @hint.scan("\n").length + 1
|
223
|
-
elsif expanded?
|
224
|
-
lines += @choices.length
|
225
|
-
lines += render_footer.scan("\n").length + 1
|
226
|
-
end
|
227
|
-
lines
|
228
|
-
end
|
229
|
-
|
230
231
|
# Refresh the current input
|
231
232
|
#
|
233
|
+
# @param [Integer] lines
|
234
|
+
#
|
235
|
+
# @return [String]
|
236
|
+
#
|
232
237
|
# @api private
|
233
|
-
def refresh
|
234
|
-
lines = count_lines
|
238
|
+
def refresh(lines)
|
235
239
|
if @hint && (!@selected || @done)
|
236
240
|
@hint = nil
|
237
|
-
@prompt.
|
238
|
-
|
241
|
+
@prompt.clear_lines(lines, :down) +
|
242
|
+
@prompt.cursor.prev_line
|
239
243
|
elsif expanded?
|
240
|
-
@prompt.
|
244
|
+
@prompt.clear_lines(lines)
|
241
245
|
else
|
242
|
-
@prompt.
|
246
|
+
@prompt.clear_line
|
243
247
|
end
|
244
248
|
end
|
245
249
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require_relative 'question'
|
4
|
+
require_relative 'symbols'
|
5
|
+
require_relative 'timeout'
|
6
|
+
|
7
|
+
module TTY
|
8
|
+
class Prompt
|
9
|
+
class Keypress < Question
|
10
|
+
# Create keypress question
|
11
|
+
#
|
12
|
+
# @param [Prompt] prompt
|
13
|
+
# @param [Hash] options
|
14
|
+
#
|
15
|
+
# @api public
|
16
|
+
def initialize(prompt, options = {})
|
17
|
+
super
|
18
|
+
@echo = options.fetch(:echo) { false }
|
19
|
+
@keys = options.fetch(:keys) { UndefinedSetting }
|
20
|
+
@timeout = options.fetch(:timeout) { UndefinedSetting }
|
21
|
+
@interval = options.fetch(:interval) { 1 }
|
22
|
+
@pause = true
|
23
|
+
@countdown = @timeout
|
24
|
+
@interval_handler = proc { |time|
|
25
|
+
question = render_question
|
26
|
+
@prompt.print(refresh(question.lines.count))
|
27
|
+
countdown(time)
|
28
|
+
@prompt.print(render_question)
|
29
|
+
}
|
30
|
+
|
31
|
+
@prompt.subscribe(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def countdown(value = (not_set = true))
|
35
|
+
return @countdown if not_set
|
36
|
+
@countdown = value
|
37
|
+
end
|
38
|
+
|
39
|
+
# Check if any specific keys are set
|
40
|
+
def any_key?
|
41
|
+
@keys == UndefinedSetting
|
42
|
+
end
|
43
|
+
|
44
|
+
# Check if timeout is set
|
45
|
+
def timeout?
|
46
|
+
@timeout != UndefinedSetting
|
47
|
+
end
|
48
|
+
|
49
|
+
def keypress(event)
|
50
|
+
if any_key?
|
51
|
+
@pause = false
|
52
|
+
elsif @keys.is_a?(Array) && @keys.include?(event.key.name)
|
53
|
+
@pause = false
|
54
|
+
else
|
55
|
+
@pause = true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def render_question
|
60
|
+
header = super
|
61
|
+
header.gsub!(/:countdown/, countdown.to_s)
|
62
|
+
header
|
63
|
+
end
|
64
|
+
|
65
|
+
def process_input(question)
|
66
|
+
time do
|
67
|
+
while @pause
|
68
|
+
@input = @prompt.read_keypress
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@evaluator.(@input)
|
72
|
+
end
|
73
|
+
|
74
|
+
def refresh(lines)
|
75
|
+
@prompt.clear_lines(lines)
|
76
|
+
end
|
77
|
+
|
78
|
+
def time(&block)
|
79
|
+
if timeout?
|
80
|
+
secs = Integer(@timeout)
|
81
|
+
interval = Integer(@interval)
|
82
|
+
scheduler = Timeout.new(interval_handler: @interval_handler)
|
83
|
+
scheduler.timeout(secs, interval, &block)
|
84
|
+
else
|
85
|
+
block.()
|
86
|
+
end
|
87
|
+
rescue Timeout::Error
|
88
|
+
end
|
89
|
+
end # Keypress
|
90
|
+
end # Prompt
|
91
|
+
end # TTY
|
data/lib/tty/prompt/list.rb
CHANGED
@@ -92,6 +92,27 @@ module TTY
|
|
92
92
|
@page_help = text
|
93
93
|
end
|
94
94
|
|
95
|
+
# Provide help information
|
96
|
+
#
|
97
|
+
# @param [String] value
|
98
|
+
# the new help text
|
99
|
+
#
|
100
|
+
# @return [String]
|
101
|
+
#
|
102
|
+
# @api public
|
103
|
+
def help(value = (not_set = true))
|
104
|
+
return @help if !@help.nil? && not_set
|
105
|
+
|
106
|
+
@help = (@help.nil? && !not_set) ? value : default_help
|
107
|
+
end
|
108
|
+
|
109
|
+
# Default help text
|
110
|
+
#
|
111
|
+
# @api public
|
112
|
+
def default_help
|
113
|
+
self.class::HELP % [enumerate? ? " or number (1-#{@choices.size})" : '']
|
114
|
+
end
|
115
|
+
|
95
116
|
# Set selecting active index using number pad
|
96
117
|
#
|
97
118
|
# @api public
|
@@ -117,7 +138,7 @@ module TTY
|
|
117
138
|
#
|
118
139
|
# @api public
|
119
140
|
def choices(values)
|
120
|
-
values.each { |val| choice(*val) }
|
141
|
+
Array(values).each { |val| choice(*val) }
|
121
142
|
end
|
122
143
|
|
123
144
|
# Call the list menu by passing question and choices
|
@@ -202,12 +223,13 @@ module TTY
|
|
202
223
|
def render
|
203
224
|
@prompt.print(@prompt.hide)
|
204
225
|
until @done
|
205
|
-
|
226
|
+
question = render_question
|
227
|
+
@prompt.print(question)
|
206
228
|
@prompt.read_keypress
|
207
|
-
refresh(lines)
|
229
|
+
@prompt.print(refresh(question.lines.count))
|
208
230
|
end
|
209
|
-
render_question
|
210
|
-
|
231
|
+
@prompt.print(render_question)
|
232
|
+
answer
|
211
233
|
ensure
|
212
234
|
@prompt.print(@prompt.show)
|
213
235
|
end
|
@@ -217,42 +239,31 @@ module TTY
|
|
217
239
|
# @return [nil, Object]
|
218
240
|
#
|
219
241
|
# @api private
|
220
|
-
def
|
242
|
+
def answer
|
221
243
|
@choices[@active - 1].value
|
222
244
|
end
|
223
245
|
|
224
246
|
# Clear screen lines
|
225
247
|
#
|
226
|
-
# @param [
|
227
|
-
# the lines to clear
|
248
|
+
# @param [String]
|
228
249
|
#
|
229
250
|
# @api private
|
230
251
|
def refresh(lines)
|
231
|
-
@prompt.
|
252
|
+
@prompt.clear_lines(lines)
|
232
253
|
end
|
233
254
|
|
234
255
|
# Render question with instructions and menu
|
235
256
|
#
|
236
|
-
# @return [
|
257
|
+
# @return [String]
|
237
258
|
#
|
238
259
|
# @api private
|
239
260
|
def render_question
|
240
|
-
header = "#{@prefix}#{@question} #{render_header}"
|
241
|
-
@prompt.puts(header)
|
261
|
+
header = "#{@prefix}#{@question} #{render_header}\n"
|
242
262
|
@first_render = false
|
243
263
|
rendered_menu = render_menu
|
244
264
|
rendered_menu << render_footer
|
245
|
-
|
246
|
-
|
247
|
-
header.lines.count + rendered_menu.lines.count
|
248
|
-
end
|
249
|
-
|
250
|
-
# Provide help information
|
251
|
-
#
|
252
|
-
# @return [String]
|
253
|
-
def help
|
254
|
-
return @help unless @help.nil?
|
255
|
-
self.class::HELP % [enumerate? ? " or number (1-#{@choices.size})" : '']
|
265
|
+
header << rendered_menu unless @done
|
266
|
+
header
|
256
267
|
end
|
257
268
|
|
258
269
|
# Render initial help and selected choice
|
@@ -271,6 +282,8 @@ module TTY
|
|
271
282
|
|
272
283
|
# Render menu with choices to select from
|
273
284
|
#
|
285
|
+
# @return [String]
|
286
|
+
#
|
274
287
|
# @api private
|
275
288
|
def render_menu
|
276
289
|
output = ''
|
@@ -291,6 +304,8 @@ module TTY
|
|
291
304
|
|
292
305
|
# Render page info footer
|
293
306
|
#
|
307
|
+
# @return [String]
|
308
|
+
#
|
294
309
|
# @api private
|
295
310
|
def render_footer
|
296
311
|
return '' unless paginated?
|
@@ -62,10 +62,8 @@ module TTY
|
|
62
62
|
end
|
63
63
|
header += masked
|
64
64
|
end
|
65
|
-
@
|
66
|
-
|
67
|
-
|
68
|
-
header.lines.count + (@done ? 1 : 0)
|
65
|
+
header << "\n" if @done
|
66
|
+
header
|
69
67
|
end
|
70
68
|
|
71
69
|
def render_error(errors)
|
@@ -76,15 +74,14 @@ module TTY
|
|
76
74
|
# Read input from user masked by character
|
77
75
|
#
|
78
76
|
# @private
|
79
|
-
def read_input
|
77
|
+
def read_input(question)
|
80
78
|
@done_masked = false
|
81
79
|
@failure = false
|
82
80
|
@input = ''
|
83
|
-
|
84
81
|
until @done_masked
|
85
82
|
@prompt.read_keypress
|
86
83
|
@prompt.print(@prompt.clear_line)
|
87
|
-
render_question
|
84
|
+
@prompt.print(render_question)
|
88
85
|
end
|
89
86
|
@prompt.puts
|
90
87
|
@input
|