tty-prompt 0.18.0 → 0.22.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 +92 -0
- data/README.md +549 -248
- data/lib/tty-prompt.rb +1 -2
- data/lib/tty/prompt.rb +187 -143
- data/lib/tty/prompt/answers_collector.rb +5 -5
- data/lib/tty/prompt/{enum_paginator.rb → block_paginator.rb} +20 -19
- data/lib/tty/prompt/choice.rb +5 -7
- data/lib/tty/prompt/choices.rb +29 -11
- data/lib/tty/prompt/confirm_question.rb +38 -16
- data/lib/tty/prompt/const.rb +17 -0
- data/lib/tty/prompt/converter_dsl.rb +6 -7
- data/lib/tty/prompt/converter_registry.rb +31 -26
- data/lib/tty/prompt/converters.rb +139 -32
- data/lib/tty/prompt/enum_list.rb +57 -27
- data/lib/tty/prompt/errors.rb +31 -0
- data/lib/tty/prompt/evaluator.rb +1 -1
- data/lib/tty/prompt/expander.rb +39 -13
- data/lib/tty/prompt/keypress.rb +31 -36
- data/lib/tty/prompt/list.rb +175 -65
- data/lib/tty/prompt/mask_question.rb +4 -5
- data/lib/tty/prompt/multi_list.rb +124 -33
- data/lib/tty/prompt/multiline.rb +7 -6
- data/lib/tty/prompt/paginator.rb +38 -26
- data/lib/tty/prompt/question.rb +83 -34
- data/lib/tty/prompt/question/checks.rb +18 -0
- data/lib/tty/prompt/question/validation.rb +3 -3
- data/lib/tty/prompt/selected_choices.rb +76 -0
- data/lib/tty/prompt/slider.rb +83 -16
- data/lib/tty/prompt/statement.rb +3 -3
- data/lib/tty/prompt/suggestion.rb +6 -6
- data/lib/tty/prompt/symbols.rb +58 -34
- data/lib/tty/prompt/test.rb +36 -0
- data/lib/tty/prompt/timer.rb +75 -0
- data/lib/tty/prompt/utils.rb +1 -3
- data/lib/tty/prompt/version.rb +1 -1
- metadata +29 -227
- data/Rakefile +0 -8
- data/examples/ask.rb +0 -7
- data/examples/ask_valid.rb +0 -12
- data/examples/collect.rb +0 -21
- data/examples/echo.rb +0 -11
- data/examples/enum_select.rb +0 -7
- data/examples/enum_select_disabled.rb +0 -16
- data/examples/enum_select_paged.rb +0 -9
- data/examples/enum_select_wrapped.rb +0 -15
- data/examples/expand.rb +0 -29
- data/examples/in.rb +0 -9
- data/examples/inputs.rb +0 -10
- data/examples/key_events.rb +0 -15
- data/examples/keypress.rb +0 -9
- data/examples/mask.rb +0 -13
- data/examples/multi_select.rb +0 -8
- data/examples/multi_select_disabled.rb +0 -17
- data/examples/multi_select_paged.rb +0 -9
- data/examples/multi_select_wrapped.rb +0 -15
- data/examples/multiline.rb +0 -9
- data/examples/pause.rb +0 -9
- data/examples/select.rb +0 -20
- data/examples/select_disabled.rb +0 -18
- data/examples/select_enum.rb +0 -8
- data/examples/select_filtered.rb +0 -11
- data/examples/select_paginated.rb +0 -11
- data/examples/select_wrapped.rb +0 -15
- data/examples/slider.rb +0 -6
- data/examples/validation.rb +0 -9
- data/examples/yes_no.rb +0 -7
- data/lib/tty/prompt/messages.rb +0 -49
- data/lib/tty/prompt/timeout.rb +0 -78
- data/lib/tty/test_prompt.rb +0 -20
- data/spec/spec_helper.rb +0 -45
- data/spec/unit/ask_spec.rb +0 -132
- data/spec/unit/choice/eql_spec.rb +0 -22
- data/spec/unit/choice/from_spec.rb +0 -96
- data/spec/unit/choices/add_spec.rb +0 -12
- data/spec/unit/choices/each_spec.rb +0 -13
- data/spec/unit/choices/find_by_spec.rb +0 -10
- data/spec/unit/choices/new_spec.rb +0 -10
- data/spec/unit/choices/pluck_spec.rb +0 -9
- data/spec/unit/collect_spec.rb +0 -96
- data/spec/unit/converters/convert_bool_spec.rb +0 -58
- data/spec/unit/converters/convert_char_spec.rb +0 -11
- data/spec/unit/converters/convert_custom_spec.rb +0 -14
- data/spec/unit/converters/convert_date_spec.rb +0 -34
- data/spec/unit/converters/convert_file_spec.rb +0 -18
- data/spec/unit/converters/convert_number_spec.rb +0 -39
- data/spec/unit/converters/convert_path_spec.rb +0 -15
- data/spec/unit/converters/convert_range_spec.rb +0 -22
- data/spec/unit/converters/convert_regex_spec.rb +0 -12
- data/spec/unit/converters/convert_string_spec.rb +0 -21
- data/spec/unit/converters/on_error_spec.rb +0 -9
- data/spec/unit/distance/distance_spec.rb +0 -73
- data/spec/unit/enum_paginator_spec.rb +0 -75
- data/spec/unit/enum_select_spec.rb +0 -446
- data/spec/unit/error_spec.rb +0 -20
- data/spec/unit/evaluator_spec.rb +0 -67
- data/spec/unit/expand_spec.rb +0 -198
- data/spec/unit/keypress_spec.rb +0 -72
- data/spec/unit/mask_spec.rb +0 -132
- data/spec/unit/multi_select_spec.rb +0 -495
- data/spec/unit/multiline_spec.rb +0 -77
- data/spec/unit/new_spec.rb +0 -20
- data/spec/unit/ok_spec.rb +0 -10
- data/spec/unit/paginator_spec.rb +0 -73
- data/spec/unit/question/checks_spec.rb +0 -97
- data/spec/unit/question/default_spec.rb +0 -31
- data/spec/unit/question/echo_spec.rb +0 -38
- data/spec/unit/question/in_spec.rb +0 -115
- data/spec/unit/question/initialize_spec.rb +0 -12
- data/spec/unit/question/modifier/apply_to_spec.rb +0 -24
- data/spec/unit/question/modifier/letter_case_spec.rb +0 -41
- data/spec/unit/question/modifier/whitespace_spec.rb +0 -51
- data/spec/unit/question/modify_spec.rb +0 -41
- data/spec/unit/question/required_spec.rb +0 -92
- data/spec/unit/question/validate_spec.rb +0 -115
- data/spec/unit/question/validation/call_spec.rb +0 -31
- data/spec/unit/question/validation/coerce_spec.rb +0 -30
- data/spec/unit/result_spec.rb +0 -40
- data/spec/unit/say_spec.rb +0 -67
- data/spec/unit/select_spec.rb +0 -643
- data/spec/unit/slider_spec.rb +0 -100
- data/spec/unit/statement/initialize_spec.rb +0 -15
- data/spec/unit/subscribe_spec.rb +0 -22
- data/spec/unit/suggest_spec.rb +0 -28
- data/spec/unit/warn_spec.rb +0 -21
- data/spec/unit/yes_no_spec.rb +0 -251
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
- data/tty-prompt.gemspec +0 -33
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative 'symbols'
|
3
|
+
require_relative "question"
|
5
4
|
|
6
5
|
module TTY
|
7
6
|
class Prompt
|
@@ -12,9 +11,9 @@ module TTY
|
|
12
11
|
# @option options [String] :mask
|
13
12
|
#
|
14
13
|
# @api public
|
15
|
-
def initialize(prompt, options
|
14
|
+
def initialize(prompt, **options)
|
16
15
|
super
|
17
|
-
@mask = options.fetch(:mask) {
|
16
|
+
@mask = options.fetch(:mask) { @prompt.symbols[:dot] }
|
18
17
|
@done_masked = false
|
19
18
|
@failure = false
|
20
19
|
end
|
@@ -76,7 +75,7 @@ module TTY
|
|
76
75
|
def read_input(question)
|
77
76
|
@done_masked = false
|
78
77
|
@failure = false
|
79
|
-
@input =
|
78
|
+
@input = ""
|
80
79
|
@prompt.print(question)
|
81
80
|
until @done_masked
|
82
81
|
@prompt.read_keypress
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
3
|
+
require_relative "list"
|
4
|
+
require_relative "selected_choices"
|
4
5
|
|
5
6
|
module TTY
|
6
7
|
class Prompt
|
@@ -9,34 +10,80 @@ module TTY
|
|
9
10
|
#
|
10
11
|
# @api private
|
11
12
|
class MultiList < List
|
12
|
-
HELP = '(Use arrow%s keys, press Space to select and Enter to finish%s)'.freeze
|
13
|
-
|
14
13
|
# Create instance of TTY::Prompt::MultiList menu.
|
15
14
|
#
|
16
15
|
# @param [Prompt] :prompt
|
17
16
|
# @param [Hash] options
|
18
17
|
#
|
19
18
|
# @api public
|
20
|
-
def initialize(prompt, options)
|
19
|
+
def initialize(prompt, **options)
|
21
20
|
super
|
22
|
-
@selected =
|
23
|
-
@help
|
24
|
-
@default = Array(options[:default])
|
21
|
+
@selected = SelectedChoices.new
|
22
|
+
@help = options[:help]
|
25
23
|
@echo = options.fetch(:echo, true)
|
24
|
+
@min = options[:min]
|
25
|
+
@max = options[:max]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set a minimum number of choices
|
29
|
+
#
|
30
|
+
# @api public
|
31
|
+
def min(value)
|
32
|
+
@min = value
|
26
33
|
end
|
27
34
|
|
35
|
+
# Set a maximum number of choices
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
def max(value)
|
39
|
+
@max = value
|
40
|
+
end
|
41
|
+
|
42
|
+
# Callback fired when enter/return key is pressed
|
43
|
+
#
|
44
|
+
# @api private
|
45
|
+
def keyenter(*)
|
46
|
+
valid = true
|
47
|
+
valid = @min <= @selected.size if @min
|
48
|
+
valid = @selected.size <= @max if @max
|
49
|
+
|
50
|
+
super if valid
|
51
|
+
end
|
52
|
+
alias keyreturn keyenter
|
53
|
+
|
28
54
|
# Callback fired when space key is pressed
|
29
55
|
#
|
30
56
|
# @api private
|
31
57
|
def keyspace(*)
|
32
58
|
active_choice = choices[@active - 1]
|
33
59
|
if @selected.include?(active_choice)
|
34
|
-
@selected.
|
60
|
+
@selected.delete_at(@active - 1)
|
35
61
|
else
|
36
|
-
@selected
|
62
|
+
return if @max && @selected.size >= @max
|
63
|
+
@selected.insert(@active - 1, active_choice)
|
37
64
|
end
|
38
65
|
end
|
39
66
|
|
67
|
+
# Selects all choices when Ctrl+A is pressed
|
68
|
+
#
|
69
|
+
# @api private
|
70
|
+
def keyctrl_a(*)
|
71
|
+
return if @max && @max < choices.size
|
72
|
+
@selected = SelectedChoices.new(choices.enabled, choices.enabled_indexes)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Revert currently selected choices when Ctrl+I is pressed
|
76
|
+
#
|
77
|
+
# @api private
|
78
|
+
def keyctrl_r(*)
|
79
|
+
return if @max && @max < choices.size
|
80
|
+
indexes = choices.each_with_index.reduce([]) do |acc, (choice, idx)|
|
81
|
+
acc << idx if !choice.disabled? && !@selected.include?(choice)
|
82
|
+
acc
|
83
|
+
end
|
84
|
+
@selected = SelectedChoices.new(choices.enabled - @selected.to_a, indexes)
|
85
|
+
end
|
86
|
+
|
40
87
|
private
|
41
88
|
|
42
89
|
# Setup default options and active selection
|
@@ -45,11 +92,14 @@ module TTY
|
|
45
92
|
def setup_defaults
|
46
93
|
validate_defaults
|
47
94
|
# At this stage, @choices matches all the visible choices.
|
48
|
-
|
49
|
-
@
|
50
|
-
|
51
|
-
|
52
|
-
|
95
|
+
default_indexes = @default.map { |d| d - 1 }
|
96
|
+
@selected = SelectedChoices.new(@choices.values_at(*default_indexes),
|
97
|
+
default_indexes)
|
98
|
+
|
99
|
+
if !@default.empty?
|
100
|
+
@active = @default.last
|
101
|
+
else
|
102
|
+
@active = @choices.index { |choice| !choice.disabled? } + 1
|
53
103
|
end
|
54
104
|
end
|
55
105
|
|
@@ -59,23 +109,63 @@ module TTY
|
|
59
109
|
#
|
60
110
|
# @api private
|
61
111
|
def selected_names
|
62
|
-
@selected.map(&:name).join(
|
112
|
+
@selected.map(&:name).join(", ")
|
113
|
+
end
|
114
|
+
|
115
|
+
# Header part showing the minimum/maximum number of choices
|
116
|
+
#
|
117
|
+
# @return [String]
|
118
|
+
#
|
119
|
+
# @api private
|
120
|
+
def minmax_help
|
121
|
+
help = []
|
122
|
+
help << "min. #{@min}" if @min
|
123
|
+
help << "max. #{@max}" if @max
|
124
|
+
"(%s) " % [help.join(", ")]
|
125
|
+
end
|
126
|
+
|
127
|
+
# Build a default help text
|
128
|
+
#
|
129
|
+
# @return [String]
|
130
|
+
#
|
131
|
+
# @api private
|
132
|
+
def default_help
|
133
|
+
str = []
|
134
|
+
str << "(Press "
|
135
|
+
str << "#{arrows_help} arrow"
|
136
|
+
str << " or 1-#{choices.size} number" if enumerate?
|
137
|
+
str << " to move, Space"
|
138
|
+
str << "/Ctrl+A|R" if @max.nil?
|
139
|
+
str << " to select"
|
140
|
+
str << " (all|rev)" if @max.nil?
|
141
|
+
str << (filterable? ? "," : " and")
|
142
|
+
str << " Enter to finish"
|
143
|
+
str << " and letters to filter" if filterable?
|
144
|
+
str << ")"
|
145
|
+
str.join
|
63
146
|
end
|
64
147
|
|
65
148
|
# Render initial help text and then currently selected choices
|
66
149
|
#
|
67
150
|
# @api private
|
68
151
|
def render_header
|
69
|
-
instructions = @prompt.decorate(help,
|
152
|
+
instructions = @prompt.decorate(help, @help_color)
|
153
|
+
minmax_suffix = @min || @max ? minmax_help : ""
|
154
|
+
print_selected = @selected.size.nonzero? && @echo
|
155
|
+
|
70
156
|
if @done && @echo
|
71
157
|
@prompt.decorate(selected_names, @active_color)
|
72
|
-
elsif @
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
158
|
+
elsif (@first_render && (help_start? || help_always?)) ||
|
159
|
+
(help_always? && !@filter.any? && !@done)
|
160
|
+
minmax_suffix +
|
161
|
+
(print_selected ? "#{selected_names} " : "") +
|
162
|
+
instructions
|
77
163
|
elsif filterable? && @filter.any?
|
78
|
-
|
164
|
+
minmax_suffix +
|
165
|
+
(print_selected ? "#{selected_names} " : "") +
|
166
|
+
@prompt.decorate(filter_help, @help_color)
|
167
|
+
else
|
168
|
+
minmax_suffix + (print_selected ? selected_names : "")
|
79
169
|
end
|
80
170
|
end
|
81
171
|
|
@@ -96,21 +186,22 @@ module TTY
|
|
96
186
|
def render_menu
|
97
187
|
output = []
|
98
188
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
indicator
|
189
|
+
sync_paginators if @paging_changed
|
190
|
+
paginator.paginate(choices, @active, @per_page) do |choice, index|
|
191
|
+
num = enumerate? ? (index + 1).to_s + @enum + " " : ""
|
192
|
+
indicator = (index + 1 == @active) ? @symbols[:marker] : " "
|
193
|
+
indicator += " "
|
103
194
|
message = if @selected.include?(choice) && !choice.disabled?
|
104
|
-
selected = @prompt.decorate(symbols[:radio_on], @active_color)
|
105
|
-
selected
|
195
|
+
selected = @prompt.decorate(@symbols[:radio_on], @active_color)
|
196
|
+
"#{selected} #{num}#{choice.name}"
|
106
197
|
elsif choice.disabled?
|
107
|
-
@prompt.decorate(symbols[:cross], :red) +
|
108
|
-
|
198
|
+
@prompt.decorate(@symbols[:cross], :red) +
|
199
|
+
" #{num}#{choice.name} #{choice.disabled}"
|
109
200
|
else
|
110
|
-
symbols[:radio_off]
|
201
|
+
"#{@symbols[:radio_off]} #{num}#{choice.name}"
|
111
202
|
end
|
112
|
-
|
113
|
-
newline = (index ==
|
203
|
+
end_index = paginated? ? paginator.end_index : choices.size - 1
|
204
|
+
newline = (index == end_index) ? "" : "\n"
|
114
205
|
output << indicator + message + newline
|
115
206
|
end
|
116
207
|
|
data/lib/tty/prompt/multiline.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
3
|
+
require_relative "question"
|
4
|
+
require_relative "symbols"
|
5
5
|
|
6
6
|
module TTY
|
7
7
|
class Prompt
|
@@ -9,9 +9,9 @@ module TTY
|
|
9
9
|
#
|
10
10
|
# @api private
|
11
11
|
class Multiline < Question
|
12
|
-
HELP =
|
12
|
+
HELP = "(Press Ctrl+D or Ctrl+Z to finish)".freeze
|
13
13
|
|
14
|
-
def initialize(prompt, options
|
14
|
+
def initialize(prompt, **options)
|
15
15
|
super
|
16
16
|
@help = options[:help] || self.class::HELP
|
17
17
|
@first_render = true
|
@@ -55,8 +55,9 @@ module TTY
|
|
55
55
|
@prompt.print(question)
|
56
56
|
@lines = read_input
|
57
57
|
@input = "#{@lines.first.strip} ..." unless @lines.first.to_s.empty?
|
58
|
-
if Utils.blank?(@input)
|
59
|
-
@input = default
|
58
|
+
if Utils.blank?(@input) && default?
|
59
|
+
@input = default
|
60
|
+
@lines = default
|
60
61
|
end
|
61
62
|
@evaluator.(@lines)
|
62
63
|
end
|
data/lib/tty/prompt/paginator.rb
CHANGED
@@ -5,23 +5,33 @@ module TTY
|
|
5
5
|
class Paginator
|
6
6
|
DEFAULT_PAGE_SIZE = 6
|
7
7
|
|
8
|
+
# The 0-based index of the first item on this page
|
9
|
+
attr_accessor :start_index
|
10
|
+
|
11
|
+
# The 0-based index of the last item on this page
|
12
|
+
attr_reader :end_index
|
13
|
+
|
14
|
+
# The 0-based index of the active item on this page
|
15
|
+
attr_reader :current_index
|
16
|
+
|
17
|
+
# The 0-based index of the previously active item on this page
|
18
|
+
attr_reader :last_index
|
19
|
+
|
8
20
|
# Create a Paginator
|
9
21
|
#
|
10
22
|
# @api private
|
11
|
-
def initialize(options
|
23
|
+
def initialize(**options)
|
12
24
|
@last_index = Array(options[:default]).flatten.first || 0
|
13
25
|
@per_page = options[:per_page]
|
14
|
-
@
|
26
|
+
@start_index = Array(options[:default]).flatten.first
|
15
27
|
end
|
16
28
|
|
17
|
-
#
|
29
|
+
# Reset current page indexes
|
18
30
|
#
|
19
|
-
# @
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
raise ArgumentError, 'no max index' unless @per_page
|
24
|
-
@lower_index + @per_page - 1
|
31
|
+
# @api private
|
32
|
+
def reset!
|
33
|
+
@start_index = nil
|
34
|
+
@end_index = nil
|
25
35
|
end
|
26
36
|
|
27
37
|
# Check if page size is valid
|
@@ -30,7 +40,7 @@ module TTY
|
|
30
40
|
#
|
31
41
|
# @api private
|
32
42
|
def check_page_size!
|
33
|
-
raise InvalidArgument,
|
43
|
+
raise InvalidArgument, "per_page must be > 0" if @per_page < 1
|
34
44
|
end
|
35
45
|
|
36
46
|
# Paginate collection given an active index
|
@@ -43,21 +53,21 @@ module TTY
|
|
43
53
|
# number of choice items per page
|
44
54
|
#
|
45
55
|
# @return [Enumerable]
|
56
|
+
# the list between start and end index
|
46
57
|
#
|
47
58
|
# @api public
|
48
59
|
def paginate(list, active, per_page = nil, &block)
|
49
60
|
current_index = active - 1
|
50
61
|
default_size = (list.size <= DEFAULT_PAGE_SIZE ? list.size : DEFAULT_PAGE_SIZE)
|
51
62
|
@per_page = @per_page || per_page || default_size
|
52
|
-
@lower_index ||= current_index
|
53
|
-
@upper_index ||= max_index
|
54
|
-
|
55
63
|
check_page_size!
|
64
|
+
@start_index ||= (current_index / @per_page) * @per_page
|
65
|
+
@end_index ||= @start_index + @per_page - 1
|
56
66
|
|
57
67
|
# Don't paginate short lists
|
58
68
|
if list.size <= @per_page
|
59
|
-
@
|
60
|
-
@
|
69
|
+
@start_index = 0
|
70
|
+
@end_index = list.size - 1
|
61
71
|
if block
|
62
72
|
return list.each_with_index(&block)
|
63
73
|
else
|
@@ -65,33 +75,35 @@ module TTY
|
|
65
75
|
end
|
66
76
|
end
|
67
77
|
|
78
|
+
step = (current_index - @last_index).abs
|
68
79
|
if current_index > @last_index # going up
|
69
|
-
if current_index
|
70
|
-
|
80
|
+
if current_index >= @end_index && current_index < list.size - 1
|
81
|
+
last_page = list.size - @per_page
|
82
|
+
@start_index = [@start_index + step, last_page].min
|
71
83
|
end
|
72
84
|
elsif current_index < @last_index # going down
|
73
|
-
if current_index
|
74
|
-
@
|
85
|
+
if current_index <= @start_index && current_index > 0
|
86
|
+
@start_index = [@start_index - step, 0].max
|
75
87
|
end
|
76
88
|
end
|
77
89
|
|
78
90
|
# Cycle list
|
79
91
|
if current_index.zero?
|
80
|
-
@
|
92
|
+
@start_index = 0
|
81
93
|
elsif current_index == list.size - 1
|
82
|
-
@
|
94
|
+
@start_index = list.size - 1 - (@per_page - 1)
|
83
95
|
end
|
84
96
|
|
85
|
-
@
|
97
|
+
@end_index = @start_index + (@per_page - 1)
|
86
98
|
@last_index = current_index
|
87
99
|
|
88
|
-
sliced_list = list[@
|
89
|
-
|
100
|
+
sliced_list = list[@start_index..@end_index]
|
101
|
+
page_range = (@start_index..@end_index)
|
90
102
|
|
91
|
-
return sliced_list.zip(
|
103
|
+
return sliced_list.zip(page_range).to_enum unless block_given?
|
92
104
|
|
93
105
|
sliced_list.each_with_index do |item, index|
|
94
|
-
block[item, @
|
106
|
+
block[item, @start_index + index]
|
95
107
|
end
|
96
108
|
end
|
97
109
|
end # Paginator
|
data/lib/tty/prompt/question.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
3
|
+
require_relative "converters"
|
4
|
+
require_relative "evaluator"
|
5
|
+
require_relative "question/modifier"
|
6
|
+
require_relative "question/validation"
|
7
|
+
require_relative "question/checks"
|
8
|
+
require_relative "utils"
|
9
9
|
|
10
10
|
module TTY
|
11
11
|
# A class responsible for shell prompt interactions.
|
@@ -34,22 +34,31 @@ module TTY
|
|
34
34
|
# Initialize a Question
|
35
35
|
#
|
36
36
|
# @api public
|
37
|
-
def initialize(prompt, options
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
@
|
45
|
-
@
|
46
|
-
@
|
37
|
+
def initialize(prompt, **options)
|
38
|
+
# Option deprecation
|
39
|
+
if options[:validation]
|
40
|
+
warn "[DEPRECATION] The `:validation` option is deprecated. Use `:validate` instead."
|
41
|
+
options[:validate] = options[:validation]
|
42
|
+
end
|
43
|
+
|
44
|
+
@prompt = prompt
|
45
|
+
@prefix = options.fetch(:prefix) { @prompt.prefix }
|
46
|
+
@default = options.fetch(:default) { UndefinedSetting }
|
47
|
+
@required = options.fetch(:required) { false }
|
48
|
+
@echo = options.fetch(:echo) { true }
|
49
|
+
@in = options.fetch(:in) { UndefinedSetting }
|
50
|
+
@modifier = options.fetch(:modifier) { [] }
|
51
|
+
@validation = options.fetch(:validate) { UndefinedSetting }
|
52
|
+
@convert = options.fetch(:convert) { UndefinedSetting }
|
47
53
|
@active_color = options.fetch(:active_color) { @prompt.active_color }
|
48
|
-
@help_color
|
49
|
-
@error_color
|
50
|
-
@
|
51
|
-
@
|
52
|
-
@
|
54
|
+
@help_color = options.fetch(:help_color) { @prompt.help_color }
|
55
|
+
@error_color = options.fetch(:error_color) { :red }
|
56
|
+
@value = options.fetch(:value) { UndefinedSetting }
|
57
|
+
@quiet = options.fetch(:quiet) { @prompt.quiet }
|
58
|
+
@messages = Utils.deep_copy(options.fetch(:messages) { { } })
|
59
|
+
@done = false
|
60
|
+
@first_render = true
|
61
|
+
@input = nil
|
53
62
|
|
54
63
|
@evaluator = Evaluator.new(self)
|
55
64
|
|
@@ -58,6 +67,7 @@ module TTY
|
|
58
67
|
@evaluator << CheckRange
|
59
68
|
@evaluator << CheckValidation
|
60
69
|
@evaluator << CheckModifier
|
70
|
+
@evaluator << CheckConversion
|
61
71
|
end
|
62
72
|
|
63
73
|
# Stores all the error messages displayed to user
|
@@ -83,7 +93,7 @@ module TTY
|
|
83
93
|
if template && !template.match(/\%\{/).nil?
|
84
94
|
[template % tokens]
|
85
95
|
else
|
86
|
-
[template ||
|
96
|
+
[template || ""]
|
87
97
|
end
|
88
98
|
end
|
89
99
|
|
@@ -94,8 +104,7 @@ module TTY
|
|
94
104
|
# @return [self]
|
95
105
|
#
|
96
106
|
# @api public
|
97
|
-
def call(message, &block)
|
98
|
-
return if Utils.blank?(message)
|
107
|
+
def call(message = "", &block)
|
99
108
|
@message = message
|
100
109
|
block.call(self) if block
|
101
110
|
@prompt.subscribe(self) do
|
@@ -121,8 +130,8 @@ module TTY
|
|
121
130
|
total_lines = @prompt.count_screen_lines(input_line)
|
122
131
|
@prompt.print(refresh(question.lines.count, total_lines))
|
123
132
|
end
|
124
|
-
@prompt.print(render_question)
|
125
|
-
|
133
|
+
@prompt.print(render_question) unless @quiet
|
134
|
+
result.value
|
126
135
|
end
|
127
136
|
|
128
137
|
# Render question
|
@@ -131,13 +140,16 @@ module TTY
|
|
131
140
|
#
|
132
141
|
# @api private
|
133
142
|
def render_question
|
134
|
-
header = [
|
143
|
+
header = []
|
144
|
+
if !Utils.blank?(@prefix) || !Utils.blank?(message)
|
145
|
+
header << "#{@prefix}#{message} "
|
146
|
+
end
|
135
147
|
if !echo?
|
136
148
|
header
|
137
149
|
elsif @done
|
138
150
|
header << @prompt.decorate(@input.to_s, @active_color)
|
139
151
|
elsif default? && !Utils.blank?(@default)
|
140
|
-
header << @prompt.decorate("(#{default})", @help_color) +
|
152
|
+
header << @prompt.decorate("(#{default})", @help_color) + " "
|
141
153
|
end
|
142
154
|
header << "\n" if @done
|
143
155
|
header.join
|
@@ -158,7 +170,12 @@ module TTY
|
|
158
170
|
#
|
159
171
|
# @api private
|
160
172
|
def read_input(question)
|
161
|
-
|
173
|
+
options = {echo: echo}
|
174
|
+
if value? && @first_render
|
175
|
+
options[:value] = @value
|
176
|
+
@first_render = false
|
177
|
+
end
|
178
|
+
@prompt.read_line(question, **options).chomp
|
162
179
|
end
|
163
180
|
|
164
181
|
# Handle error condition
|
@@ -168,7 +185,7 @@ module TTY
|
|
168
185
|
# @api private
|
169
186
|
def render_error(errors)
|
170
187
|
errors.reduce([]) do |acc, err|
|
171
|
-
acc << @prompt.decorate(
|
188
|
+
acc << @prompt.decorate(">>", :red) + " " + err
|
172
189
|
acc
|
173
190
|
end.join("\n")
|
174
191
|
end
|
@@ -202,8 +219,13 @@ module TTY
|
|
202
219
|
#
|
203
220
|
# @api private
|
204
221
|
def convert_result(value)
|
205
|
-
if convert?
|
206
|
-
|
222
|
+
if convert? && !Utils.blank?(value)
|
223
|
+
case @convert
|
224
|
+
when Proc
|
225
|
+
@convert.call(value)
|
226
|
+
else
|
227
|
+
Converters.convert(@convert, value)
|
228
|
+
end
|
207
229
|
else
|
208
230
|
value
|
209
231
|
end
|
@@ -212,8 +234,13 @@ module TTY
|
|
212
234
|
# Specify answer conversion
|
213
235
|
#
|
214
236
|
# @api public
|
215
|
-
def convert(value)
|
216
|
-
|
237
|
+
def convert(value = (not_set = true), message = nil)
|
238
|
+
messages[:convert?] = message if message
|
239
|
+
if not_set
|
240
|
+
@convert
|
241
|
+
else
|
242
|
+
@convert = value
|
243
|
+
end
|
217
244
|
end
|
218
245
|
|
219
246
|
# Check if conversion is set
|
@@ -266,6 +293,21 @@ module TTY
|
|
266
293
|
@validation = (value || block)
|
267
294
|
end
|
268
295
|
|
296
|
+
# Prepopulate input with custom content
|
297
|
+
#
|
298
|
+
# @api public
|
299
|
+
def value(val)
|
300
|
+
return @value if val.nil?
|
301
|
+
@value = val
|
302
|
+
end
|
303
|
+
|
304
|
+
# Check if custom value is present
|
305
|
+
#
|
306
|
+
# @api private
|
307
|
+
def value?
|
308
|
+
@value != UndefinedSetting
|
309
|
+
end
|
310
|
+
|
269
311
|
def validation?
|
270
312
|
@validation != UndefinedSetting
|
271
313
|
end
|
@@ -321,6 +363,13 @@ module TTY
|
|
321
363
|
@in != UndefinedSetting
|
322
364
|
end
|
323
365
|
|
366
|
+
# Set quiet mode.
|
367
|
+
#
|
368
|
+
# @api public
|
369
|
+
def quiet(value)
|
370
|
+
@quiet = value
|
371
|
+
end
|
372
|
+
|
324
373
|
# @api public
|
325
374
|
def to_s
|
326
375
|
message.to_s
|