cli-ui 1.2.1 → 1.2.2
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/README.md +5 -2
- data/cli-ui.gemspec +1 -1
- data/lib/cli/ui.rb +2 -2
- data/lib/cli/ui/progress.rb +1 -1
- data/lib/cli/ui/prompt.rb +16 -6
- data/lib/cli/ui/prompt/interactive_options.rb +227 -37
- data/lib/cli/ui/spinner/spin_group.rb +7 -1
- data/lib/cli/ui/version.rb +1 -1
- metadata +3 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c658a2195a92d5e4ccf05fc1359eef09d25b5a01e0110c6ab84190d4a10c922
|
4
|
+
data.tar.gz: 4745f15513a91abef5cce3e3523d26de38d60336dbdf723601402fe8bd4b2635
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 609b7cab1cde15280659813f6b283c419c76b744a9089dcded708c3cf08113b7118150fd5e6642f8e41709f8b481ad0a51927d48ac294432ce108209faefccce
|
7
|
+
data.tar.gz: feb06db36c85af0d83c2407bf6ebb06ee1c65ea8ec38b7b80b09bbeaac0a25220a7fe2707993bee950916cd203e31d219b6db6adf132b08e9027be8e00df846a
|
data/README.md
CHANGED
@@ -34,7 +34,7 @@ To handle content flow (see example below)
|
|
34
34
|
CLI::UI::StdoutRouter.enable
|
35
35
|
CLI::UI::Frame.open('Frame 1') do
|
36
36
|
CLI::UI::Frame.open('Frame 2') { puts "inside frame 2" }
|
37
|
-
puts "inside frame 1"
|
37
|
+
puts "inside frame 1"
|
38
38
|
end
|
39
39
|
```
|
40
40
|
|
@@ -43,7 +43,10 @@ end
|
|
43
43
|
---
|
44
44
|
|
45
45
|
### Interactive Prompts
|
46
|
-
Prompt user with options and ask them to choose. Can answer using arrow keys,
|
46
|
+
Prompt user with options and ask them to choose. Can answer using arrow keys, vim bindings (`j`/`k`), or numbers (or y/n for yes/no questions).
|
47
|
+
|
48
|
+
For large numbers of options, using `e`, `:`, or `G` will toggle "line select" mode which allows numbers greater than 9 to be typed and
|
49
|
+
`f` or `/` will allow the user to filter options using a free-form text input.
|
47
50
|
|
48
51
|
```ruby
|
49
52
|
CLI::UI.ask('What language/framework do you use?', options: %w(rails go ruby python))
|
data/cli-ui.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |spec|
|
|
21
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
22
22
|
spec.require_paths = ["lib"]
|
23
23
|
|
24
|
-
spec.add_development_dependency "bundler", "~>
|
24
|
+
# spec.add_development_dependency "bundler", "~> 2.0"
|
25
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
26
26
|
spec.add_development_dependency "minitest", "~> 5.0"
|
27
27
|
end
|
data/lib/cli/ui.rb
CHANGED
@@ -48,8 +48,8 @@ module CLI
|
|
48
48
|
#
|
49
49
|
# * +question+ - question to confirm
|
50
50
|
#
|
51
|
-
def self.confirm(question)
|
52
|
-
CLI::UI::Prompt.confirm(question)
|
51
|
+
def self.confirm(question, **kwargs)
|
52
|
+
CLI::UI::Prompt.confirm(question, **kwargs)
|
53
53
|
end
|
54
54
|
|
55
55
|
# Conviencence Method for +CLI::UI::Prompt.ask+
|
data/lib/cli/ui/progress.rb
CHANGED
@@ -72,7 +72,7 @@ module CLI
|
|
72
72
|
# Format the progress bar to be printed to terminal
|
73
73
|
#
|
74
74
|
def to_s
|
75
|
-
suffix = " #{(@percent_done * 100).
|
75
|
+
suffix = " #{(@percent_done * 100).floor}%".ljust(5)
|
76
76
|
workable_width = @max_width - Frame.prefix_width - suffix.size
|
77
77
|
filled = [(@percent_done * workable_width.to_f).ceil, 0].max
|
78
78
|
unfilled = [workable_width - filled, 0].max
|
data/lib/cli/ui/prompt.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'cli/ui'
|
2
3
|
require 'readline'
|
3
4
|
|
@@ -30,11 +31,15 @@ module CLI
|
|
30
31
|
# * +:default+ - The default answer to the question (e.g. they just press enter and don't input anything)
|
31
32
|
# * +:is_file+ - Tells the input to use file auto-completion (tab completion)
|
32
33
|
# * +:allow_empty+ - Allows the answer to be empty
|
34
|
+
# * +:multiple+ - Allow multiple options to be selected
|
35
|
+
# * +:filter_ui+ - Enable option filtering (default: true)
|
36
|
+
# * +:select_ui+ - Enable long-form option selection (default: true)
|
33
37
|
#
|
34
38
|
# Note:
|
35
39
|
# * +:options+ or providing a +Block+ conflicts with +:default+ and +:is_file+, you cannot set options with either of these keywords
|
36
40
|
# * +:default+ conflicts with +:allow_empty:, you cannot set these together
|
37
41
|
# * +:options+ conflicts with providing a +Block+ , you may only set one
|
42
|
+
# * +:multiple+ can only be used with +:options+ or a +Block+; it is ignored, otherwise.
|
38
43
|
#
|
39
44
|
# ==== Block (optional)
|
40
45
|
#
|
@@ -71,13 +76,13 @@ module CLI
|
|
71
76
|
# handler.option('python') { |selection| selection }
|
72
77
|
# end
|
73
78
|
#
|
74
|
-
def ask(question, options: nil, default: nil, is_file: nil, allow_empty: true, multiple: false, &options_proc)
|
79
|
+
def ask(question, options: nil, default: nil, is_file: nil, allow_empty: true, multiple: false, filter_ui: true, select_ui: true, &options_proc)
|
75
80
|
if ((options || block_given?) && (default || is_file))
|
76
81
|
raise(ArgumentError, 'conflicting arguments: options provided with default or is_file')
|
77
82
|
end
|
78
83
|
|
79
84
|
if options || block_given?
|
80
|
-
ask_interactive(question, options, multiple: multiple, &options_proc)
|
85
|
+
ask_interactive(question, options, multiple: multiple, filter_ui: filter_ui, select_ui: select_ui, &options_proc)
|
81
86
|
else
|
82
87
|
ask_free_form(question, default, is_file, allow_empty)
|
83
88
|
end
|
@@ -91,8 +96,10 @@ module CLI
|
|
91
96
|
# Confirmation question
|
92
97
|
# CLI::UI::Prompt.confirm('Is the sky blue?')
|
93
98
|
#
|
94
|
-
|
95
|
-
|
99
|
+
# CLI::UI::Prompt.confirm('Do a dangerous thing?', default: false)
|
100
|
+
#
|
101
|
+
def confirm(question, default: true)
|
102
|
+
ask_interactive(question, default ? %w(yes no) : %w(no yes), filter_ui: false) == 'yes'
|
96
103
|
end
|
97
104
|
|
98
105
|
private
|
@@ -121,7 +128,7 @@ module CLI
|
|
121
128
|
end
|
122
129
|
end
|
123
130
|
|
124
|
-
def ask_interactive(question, options = nil, multiple: false)
|
131
|
+
def ask_interactive(question, options = nil, multiple: false, filter_ui: true, select_ui: true)
|
125
132
|
raise(ArgumentError, 'conflicting arguments: options and block given') if options && block_given?
|
126
133
|
|
127
134
|
options ||= if block_given?
|
@@ -132,6 +139,8 @@ module CLI
|
|
132
139
|
|
133
140
|
raise(ArgumentError, 'insufficient options') if options.nil? || options.size < 2
|
134
141
|
instructions = (multiple ? "Toggle options. " : "") + "Choose with ↑ ↓ ⏎"
|
142
|
+
instructions += ", filter with 'f'" if filter_ui
|
143
|
+
instructions += ", enter option with 'e'" if select_ui and options.size > 9
|
135
144
|
puts_question("#{question} {{yellow:(#{instructions})}}")
|
136
145
|
resp = interactive_prompt(options, multiple: multiple)
|
137
146
|
|
@@ -195,10 +204,11 @@ module CLI
|
|
195
204
|
# thread to manage output, but the current strategy feels like a
|
196
205
|
# better tradeoff.
|
197
206
|
prefix = CLI::UI.with_frame_color(:blue) { CLI::UI::Frame.prefix }
|
198
|
-
prompt = prefix + CLI::UI.fmt('{{blue:> }}
|
207
|
+
prompt = prefix + CLI::UI.fmt('{{blue:> }}') + CLI::UI::Color::YELLOW.code
|
199
208
|
|
200
209
|
begin
|
201
210
|
line = Readline.readline(prompt, true)
|
211
|
+
print CLI::UI::Color::RESET.code
|
202
212
|
line.to_s.chomp
|
203
213
|
rescue Interrupt
|
204
214
|
CLI::UI.raw { STDERR.puts('^C' + CLI::UI::Color::RESET.code) }
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# coding: utf-8
|
1
2
|
require 'io/console'
|
2
3
|
|
3
4
|
module CLI
|
@@ -10,6 +11,9 @@ module CLI
|
|
10
11
|
# Prompts the user with options
|
11
12
|
# Uses an interactive session to allow the user to pick an answer
|
12
13
|
# Can use arrows, y/n, numbers (1/2), and vim bindings to control
|
14
|
+
# For more than 9 options, hitting 'e', ':', or 'G' will enter select
|
15
|
+
# mode allowing the user to type in longer numbers
|
16
|
+
# Pressing 'f' or '/' will allow the user to filter the results
|
13
17
|
#
|
14
18
|
# https://user-images.githubusercontent.com/3074765/33797984-0ebb5e64-dcdf-11e7-9e7e-7204f279cece.gif
|
15
19
|
#
|
@@ -42,16 +46,22 @@ module CLI
|
|
42
46
|
@answer = nil
|
43
47
|
@state = :root
|
44
48
|
@multiple = multiple
|
49
|
+
# Indicate that an extra line (the "metadata" line) is present and
|
50
|
+
# the terminal output should be drawn over when processing user input
|
51
|
+
@displaying_metadata = false
|
52
|
+
@filter = ''
|
45
53
|
# 0-indexed array representing if selected
|
46
54
|
# @options[0] is selected if @chosen[0]
|
47
55
|
@chosen = Array.new(@options.size) { false } if multiple
|
48
56
|
@redraw = true
|
57
|
+
@presented_options = []
|
49
58
|
end
|
50
59
|
|
51
60
|
# Calls the +InteractiveOptions+ and asks the question
|
52
61
|
# Usually used from +self.call+
|
53
62
|
#
|
54
63
|
def call
|
64
|
+
calculate_option_line_lengths
|
55
65
|
CLI::UI.raw { print(ANSI.hide_cursor) }
|
56
66
|
while @answer.nil?
|
57
67
|
render_options
|
@@ -59,6 +69,7 @@ module CLI
|
|
59
69
|
reset_position
|
60
70
|
end
|
61
71
|
clear_output
|
72
|
+
|
62
73
|
@answer
|
63
74
|
ensure
|
64
75
|
CLI::UI.raw do
|
@@ -68,46 +79,96 @@ module CLI
|
|
68
79
|
|
69
80
|
private
|
70
81
|
|
71
|
-
def
|
82
|
+
def calculate_option_line_lengths
|
83
|
+
@terminal_width_at_calculation_time = CLI::UI::Terminal.width
|
84
|
+
# options will be an array of questions but each option can be multi-line
|
85
|
+
# so to get the # of lines, you need to join then split
|
86
|
+
|
87
|
+
# since lines may be longer than the terminal is wide, we need to
|
88
|
+
# determine how many extra lines would be taken up by them
|
89
|
+
max_width = (@terminal_width_at_calculation_time -
|
90
|
+
@options.count.to_s.size - # Width of the displayed number
|
91
|
+
5 - # Extra characters added during rendering
|
92
|
+
(@multiple ? 1 : 0) # Space for the checkbox, if rendered
|
93
|
+
).to_f
|
94
|
+
|
95
|
+
@option_lengths = @options.map do |text|
|
96
|
+
width = 1 if text.empty?
|
97
|
+
width ||= text
|
98
|
+
.split("\n")
|
99
|
+
.reject(&:empty?)
|
100
|
+
.map { |l| (l.length / max_width).ceil }
|
101
|
+
.reduce(&:+)
|
102
|
+
|
103
|
+
width
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def reset_position(number_of_lines=num_lines)
|
72
108
|
# This will put us back at the beginning of the options
|
73
109
|
# When we redraw the options, they will be overwritten
|
74
110
|
CLI::UI.raw do
|
75
|
-
|
111
|
+
number_of_lines.times { print(ANSI.previous_line) }
|
76
112
|
end
|
77
113
|
end
|
78
114
|
|
79
|
-
def clear_output
|
115
|
+
def clear_output(number_of_lines=num_lines)
|
80
116
|
CLI::UI.raw do
|
81
117
|
# Write over all lines with whitespace
|
82
|
-
|
118
|
+
number_of_lines.times { puts(' ' * CLI::UI::Terminal.width) }
|
83
119
|
end
|
84
|
-
reset_position
|
120
|
+
reset_position number_of_lines
|
121
|
+
|
122
|
+
# Update if metadata is being displayed
|
123
|
+
# This must be done _after_ the output is cleared or it won't draw over
|
124
|
+
# the entire output
|
125
|
+
@displaying_metadata = display_metadata?
|
126
|
+
end
|
127
|
+
|
128
|
+
# Don't use this in place of +@displaying_metadata+, this updates too
|
129
|
+
# quickly to be useful when drawing to the screen.
|
130
|
+
def display_metadata?
|
131
|
+
filtering? or selecting? or has_filter?
|
85
132
|
end
|
86
133
|
|
87
134
|
def num_lines
|
88
|
-
|
89
|
-
# @options will be an array of questions but each option can be multi-line
|
90
|
-
# so to get the # of lines, you need to join then split
|
135
|
+
calculate_option_line_lengths if terminal_width_changed?
|
91
136
|
|
92
|
-
|
93
|
-
|
137
|
+
option_length = presented_options.reduce(0) do |total_length, (_, option_number)|
|
138
|
+
# Handle continuation markers and "Done" option when multiple is true
|
139
|
+
next total_length + 1 if option_number.nil? or option_number.zero?
|
140
|
+
total_length + @option_lengths[option_number - 1]
|
141
|
+
end
|
142
|
+
|
143
|
+
option_length + (@displaying_metadata ? 1 : 0)
|
144
|
+
end
|
94
145
|
|
95
|
-
|
96
|
-
|
97
|
-
joined_options.split("\n").reject(&:empty?).size + empty_option_count
|
146
|
+
def terminal_width_changed?
|
147
|
+
@terminal_width_at_calculation_time != CLI::UI::Terminal.width
|
98
148
|
end
|
99
149
|
|
100
150
|
ESC = "\e"
|
151
|
+
BACKSPACE = "\u007F"
|
152
|
+
CTRL_C = "\u0003"
|
153
|
+
CTRL_D = "\u0004"
|
101
154
|
|
102
155
|
def up
|
103
|
-
|
104
|
-
|
156
|
+
active_index = @filtered_options.index { |_,num| num == @active } || 0
|
157
|
+
|
158
|
+
previous_visible = @filtered_options[active_index - 1]
|
159
|
+
previous_visible ||= @filtered_options.last
|
160
|
+
|
161
|
+
@active = previous_visible ? previous_visible.last : -1
|
105
162
|
@redraw = true
|
106
163
|
end
|
107
164
|
|
108
165
|
def down
|
109
|
-
|
110
|
-
|
166
|
+
active_index = @filtered_options.index { |_,num| num == @active } || 0
|
167
|
+
|
168
|
+
next_visible = @filtered_options[active_index + 1]
|
169
|
+
next_visible ||= @filtered_options.first
|
170
|
+
|
171
|
+
@active = next_visible ? next_visible.last : -1
|
111
172
|
@redraw = true
|
112
173
|
end
|
113
174
|
|
@@ -141,7 +202,36 @@ module CLI
|
|
141
202
|
@redraw = true
|
142
203
|
end
|
143
204
|
|
205
|
+
def build_selection(char)
|
206
|
+
@active = (@active.to_s + char).to_i
|
207
|
+
@redraw = true
|
208
|
+
end
|
209
|
+
|
210
|
+
def chop_selection
|
211
|
+
@active = @active.to_s.chop.to_i
|
212
|
+
@redraw = true
|
213
|
+
end
|
214
|
+
|
215
|
+
def update_search(char)
|
216
|
+
@redraw = true
|
217
|
+
|
218
|
+
# Control+D or Backspace on empty search closes search
|
219
|
+
if char == CTRL_D or (@filter.empty? and char == BACKSPACE)
|
220
|
+
@filter = ''
|
221
|
+
@state = :root
|
222
|
+
return
|
223
|
+
end
|
224
|
+
|
225
|
+
if char == BACKSPACE
|
226
|
+
@filter.chop!
|
227
|
+
else
|
228
|
+
@filter += char
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
144
232
|
def select_current
|
233
|
+
# Prevent selection of invisible options
|
234
|
+
return unless presented_options.any? { |_,num| num == @active }
|
145
235
|
select_n(@active)
|
146
236
|
end
|
147
237
|
|
@@ -153,29 +243,49 @@ module CLI
|
|
153
243
|
# rubocop:disable Style/WhenThen,Layout/SpaceBeforeSemicolon
|
154
244
|
def wait_for_user_input
|
155
245
|
char = read_char
|
246
|
+
@last_char = char
|
247
|
+
|
248
|
+
case char
|
249
|
+
when :timeout ; raise Interrupt # Timeout, use interrupt to simulate
|
250
|
+
when CTRL_C ; raise Interrupt
|
251
|
+
end
|
252
|
+
|
156
253
|
case @state
|
157
254
|
when :root
|
158
255
|
case char
|
159
|
-
when :timeout ; raise Interrupt # Timeout, use interrupt to simulate
|
160
256
|
when ESC ; @state = :esc
|
161
257
|
when 'k' ; up
|
162
258
|
when 'j' ; down
|
163
|
-
when '
|
164
|
-
when
|
259
|
+
when 'e', ':', 'G' ; start_line_select
|
260
|
+
when 'f', '/' ; start_filter
|
261
|
+
when ('0'..@options.size.to_s) ; select_n(char.to_i)
|
165
262
|
when 'y', 'n' ; select_bool(char)
|
166
263
|
when " ", "\r", "\n" ; select_current # <enter>
|
167
|
-
|
264
|
+
end
|
265
|
+
when :filter
|
266
|
+
case char
|
267
|
+
when ESC ; @state = :esc
|
268
|
+
when "\r", "\n" ; select_current
|
269
|
+
else ; update_search(char)
|
270
|
+
end
|
271
|
+
when :line_select
|
272
|
+
case char
|
273
|
+
when ESC ; @state = :esc
|
274
|
+
when 'k' ; up ; @state = :root
|
275
|
+
when 'j' ; down ; @state = :root
|
276
|
+
when 'e',':','G','q' ; stop_line_select
|
277
|
+
when '0'..'9' ; build_selection(char)
|
278
|
+
when BACKSPACE ; chop_selection # Pop last input on backspace
|
279
|
+
when ' ', "\r", "\n" ; select_current
|
168
280
|
end
|
169
281
|
when :esc
|
170
282
|
case char
|
171
|
-
when :timeout ; raise Interrupt # Timeout, use interrupt to simulate
|
172
283
|
when '[' ; @state = :esc_bracket
|
173
284
|
else ; raise Interrupt # unhandled escape sequence.
|
174
285
|
end
|
175
286
|
when :esc_bracket
|
176
|
-
@state = :root
|
287
|
+
@state = has_filter? ? :filter : :root
|
177
288
|
case char
|
178
|
-
when :timeout ; raise Interrupt # Timeout, use interrupt to simulate
|
179
289
|
when 'A' ; up
|
180
290
|
when 'B' ; down
|
181
291
|
else ; raise Interrupt # unhandled escape sequence.
|
@@ -184,6 +294,35 @@ module CLI
|
|
184
294
|
end
|
185
295
|
# rubocop:enable Style/WhenThen,Layout/SpaceBeforeSemicolon
|
186
296
|
|
297
|
+
def selecting?
|
298
|
+
@state == :line_select
|
299
|
+
end
|
300
|
+
|
301
|
+
def filtering?
|
302
|
+
@state == :filter
|
303
|
+
end
|
304
|
+
|
305
|
+
def has_filter?
|
306
|
+
!@filter.empty?
|
307
|
+
end
|
308
|
+
|
309
|
+
def start_filter
|
310
|
+
@state = :filter
|
311
|
+
@redraw = true
|
312
|
+
end
|
313
|
+
|
314
|
+
def start_line_select
|
315
|
+
@state = :line_select
|
316
|
+
@active = 0
|
317
|
+
@redraw = true
|
318
|
+
end
|
319
|
+
|
320
|
+
def stop_line_select
|
321
|
+
@state = :root
|
322
|
+
@active = 1 if @active.zero?
|
323
|
+
@redraw = true
|
324
|
+
end
|
325
|
+
|
187
326
|
def read_char
|
188
327
|
raw_tty! do
|
189
328
|
getc = $stdin.getc
|
@@ -205,9 +344,18 @@ module CLI
|
|
205
344
|
return @presented_options unless recalculate
|
206
345
|
|
207
346
|
@presented_options = @options.zip(1..Float::INFINITY)
|
347
|
+
if has_filter?
|
348
|
+
@presented_options.select! { |option,_| option.downcase.include?(@filter.downcase) }
|
349
|
+
end
|
350
|
+
|
351
|
+
# Used for selection purposes
|
352
|
+
@filtered_options = @presented_options.dup
|
353
|
+
|
208
354
|
@presented_options.unshift([DONE, 0]) if @multiple
|
209
355
|
|
210
|
-
|
356
|
+
ensure_visible_is_active if has_filter?
|
357
|
+
|
358
|
+
while num_lines > max_lines
|
211
359
|
# try to keep the selection centered in the window:
|
212
360
|
if distance_from_selection_to_end > distance_from_start_to_selection
|
213
361
|
# selection is closer to top than bottom, so trim a row from the bottom
|
@@ -223,14 +371,22 @@ module CLI
|
|
223
371
|
@presented_options
|
224
372
|
end
|
225
373
|
|
374
|
+
def ensure_visible_is_active
|
375
|
+
unless presented_options.any? { |_, num| num == @active }
|
376
|
+
@active = presented_options.first&.last.to_i
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
226
380
|
def distance_from_selection_to_end
|
227
|
-
|
228
|
-
last_visible_option_number - @active
|
381
|
+
@presented_options.count - index_of_active_option
|
229
382
|
end
|
230
383
|
|
231
384
|
def distance_from_start_to_selection
|
232
|
-
|
233
|
-
|
385
|
+
index_of_active_option
|
386
|
+
end
|
387
|
+
|
388
|
+
def index_of_active_option
|
389
|
+
@presented_options.index { |_,num| num == @active }.to_i
|
234
390
|
end
|
235
391
|
|
236
392
|
def ensure_last_item_is_continuation_marker
|
@@ -241,14 +397,38 @@ module CLI
|
|
241
397
|
@presented_options.unshift(["...", nil]) if @presented_options.first.last
|
242
398
|
end
|
243
399
|
|
244
|
-
def
|
245
|
-
|
400
|
+
def max_lines
|
401
|
+
CLI::UI::Terminal.height - (@displaying_metadata ? 3 : 2) # Keeps a one line question visible
|
246
402
|
end
|
247
403
|
|
248
404
|
def render_options
|
405
|
+
previously_displayed_lines = num_lines
|
406
|
+
|
407
|
+
@displaying_metadata = display_metadata?
|
408
|
+
|
409
|
+
options = presented_options(recalculate: true)
|
410
|
+
|
411
|
+
clear_output(previously_displayed_lines) if previously_displayed_lines > num_lines
|
412
|
+
|
249
413
|
max_num_length = (@options.size + 1).to_s.length
|
250
414
|
|
251
|
-
|
415
|
+
metadata_text = if selecting?
|
416
|
+
select_text = @active
|
417
|
+
select_text = '{{info:e, q, or up/down anytime to exit}}' if @active == 0
|
418
|
+
"Select: #{select_text}"
|
419
|
+
elsif filtering? or has_filter?
|
420
|
+
filter_text = @filter
|
421
|
+
filter_text = '{{info:Ctrl-D anytime or Backspace now to exit}}' if @filter.empty?
|
422
|
+
"Filter: #{filter_text}"
|
423
|
+
end
|
424
|
+
|
425
|
+
if metadata_text
|
426
|
+
CLI::UI.with_frame_color(:blue) do
|
427
|
+
puts CLI::UI.fmt(" {{green:#{metadata_text}}}#{ANSI.clear_to_end_of_line}")
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
options.each do |choice, num|
|
252
432
|
is_chosen = @multiple && num && @chosen[num - 1]
|
253
433
|
|
254
434
|
padding = ' ' * (max_num_length - num.to_s.length)
|
@@ -261,19 +441,29 @@ module CLI
|
|
261
441
|
format = " #{format}"
|
262
442
|
|
263
443
|
message += sprintf(format, CHECKBOX_ICON[is_chosen]) if @multiple && num && num > 0
|
264
|
-
message +=
|
444
|
+
message += format_choice(format, choice)
|
265
445
|
|
266
446
|
if num == @active
|
267
|
-
|
268
|
-
|
269
|
-
|
447
|
+
|
448
|
+
color = (filtering? or selecting?) ? 'green' : 'blue'
|
449
|
+
message = message.split("\n").map { |l| "{{#{color}:> #{l.strip}}}" }.join("\n")
|
270
450
|
end
|
271
451
|
|
272
452
|
CLI::UI.with_frame_color(:blue) do
|
273
|
-
puts CLI::UI.fmt(message)
|
453
|
+
puts CLI::UI.fmt(message)
|
274
454
|
end
|
275
455
|
end
|
276
456
|
end
|
457
|
+
|
458
|
+
def format_choice(format, choice)
|
459
|
+
eol = CLI::UI::ANSI.clear_to_end_of_line
|
460
|
+
lines = choice.split("\n")
|
461
|
+
|
462
|
+
return eol if lines.empty? # Handle blank options
|
463
|
+
|
464
|
+
lines.map! { |l| sprintf(format, l) + eol }
|
465
|
+
lines.join("\n")
|
466
|
+
end
|
277
467
|
end
|
278
468
|
end
|
279
469
|
end
|
@@ -197,7 +197,13 @@ module CLI
|
|
197
197
|
sleep(PERIOD)
|
198
198
|
end
|
199
199
|
|
200
|
-
|
200
|
+
if @auto_debrief
|
201
|
+
debrief
|
202
|
+
else
|
203
|
+
@m.synchronize do
|
204
|
+
@tasks.all?(&:success)
|
205
|
+
end
|
206
|
+
end
|
201
207
|
end
|
202
208
|
|
203
209
|
# Debriefs failed tasks is +auto_debrief+ is true
|
data/lib/cli/ui/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cli-ui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
@@ -10,22 +10,8 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2019-02-27 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
|
-
- !ruby/object:Gem::Dependency
|
16
|
-
name: bundler
|
17
|
-
requirement: !ruby/object:Gem::Requirement
|
18
|
-
requirements:
|
19
|
-
- - "~>"
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '1.15'
|
22
|
-
type: :development
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
requirements:
|
26
|
-
- - "~>"
|
27
|
-
- !ruby/object:Gem::Version
|
28
|
-
version: '1.15'
|
29
15
|
- !ruby/object:Gem::Dependency
|
30
16
|
name: rake
|
31
17
|
requirement: !ruby/object:Gem::Requirement
|
@@ -110,8 +96,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
110
96
|
- !ruby/object:Gem::Version
|
111
97
|
version: '0'
|
112
98
|
requirements: []
|
113
|
-
|
114
|
-
rubygems_version: 2.7.6
|
99
|
+
rubygems_version: 3.0.2
|
115
100
|
signing_key:
|
116
101
|
specification_version: 4
|
117
102
|
summary: Terminal UI framework
|