cli-ui 1.5.0 → 2.0.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.
@@ -1,34 +1,48 @@
1
1
  # coding: utf-8
2
+
3
+ # typed: true
4
+
2
5
  require 'io/console'
3
6
 
4
7
  module CLI
5
8
  module UI
6
9
  module Prompt
7
10
  class InteractiveOptions
11
+ extend T::Sig
12
+
8
13
  DONE = 'Done'
9
14
  CHECKBOX_ICON = { false => '☐', true => '☑' }
10
15
 
11
- # Prompts the user with options
12
- # Uses an interactive session to allow the user to pick an answer
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
17
- #
18
- # https://user-images.githubusercontent.com/3074765/33797984-0ebb5e64-dcdf-11e7-9e7e-7204f279cece.gif
19
- #
20
- # ==== Example Usage:
21
- #
22
- # Ask an interactive question
23
- # CLI::UI::Prompt::InteractiveOptions.call(%w(rails go python))
24
- #
25
- def self.call(options, multiple: false, default: nil)
26
- list = new(options, multiple: multiple, default: default)
27
- selected = list.call
28
- if multiple
29
- selected.map { |s| options[s - 1] }
30
- else
31
- options[selected - 1]
16
+ class << self
17
+ extend T::Sig
18
+
19
+ # Prompts the user with options
20
+ # Uses an interactive session to allow the user to pick an answer
21
+ # Can use arrows, y/n, numbers (1/2), and vim bindings to control
22
+ # For more than 9 options, hitting 'e', ':', or 'G' will enter select
23
+ # mode allowing the user to type in longer numbers
24
+ # Pressing 'f' or '/' will allow the user to filter the results
25
+ #
26
+ # https://user-images.githubusercontent.com/3074765/33797984-0ebb5e64-dcdf-11e7-9e7e-7204f279cece.gif
27
+ #
28
+ # ==== Example Usage:
29
+ #
30
+ # Ask an interactive question
31
+ # CLI::UI::Prompt::InteractiveOptions.call(%w(rails go python))
32
+ #
33
+ sig do
34
+ params(options: T::Array[String], multiple: T::Boolean, default: T.nilable(T.any(String, T::Array[String])))
35
+ .returns(T.any(String, T::Array[String]))
36
+ end
37
+ def call(options, multiple: false, default: nil)
38
+ list = new(options, multiple: multiple, default: default)
39
+ selected = list.call
40
+ case selected
41
+ when Array
42
+ selected.map { |s| T.must(options[s - 1]) }
43
+ else
44
+ T.must(options[selected - 1])
45
+ end
32
46
  end
33
47
  end
34
48
 
@@ -39,6 +53,10 @@ module CLI
39
53
  #
40
54
  # CLI::UI::Prompt::InteractiveOptions.new(%w(rails go python))
41
55
  #
56
+ sig do
57
+ params(options: T::Array[String], multiple: T::Boolean, default: T.nilable(T.any(String, T::Array[String])))
58
+ .void
59
+ end
42
60
  def initialize(options, multiple: false, default: nil)
43
61
  @options = options
44
62
  @active = 1
@@ -60,12 +78,13 @@ module CLI
60
78
  end
61
79
  end
62
80
  @redraw = true
63
- @presented_options = []
81
+ @presented_options = T.let([], T::Array[[String, T.nilable(Integer)]])
64
82
  end
65
83
 
66
84
  # Calls the +InteractiveOptions+ and asks the question
67
85
  # Usually used from +self.call+
68
86
  #
87
+ sig { returns(T.any(Integer, T::Array[Integer])) }
69
88
  def call
70
89
  calculate_option_line_lengths
71
90
  CLI::UI.raw { print(ANSI.hide_cursor) }
@@ -85,6 +104,7 @@ module CLI
85
104
 
86
105
  private
87
106
 
107
+ sig { void }
88
108
  def calculate_option_line_lengths
89
109
  @terminal_width_at_calculation_time = CLI::UI::Terminal.width
90
110
  # options will be an array of questions but each option can be multi-line
@@ -103,13 +123,13 @@ module CLI
103
123
  width ||= text
104
124
  .split("\n")
105
125
  .reject(&:empty?)
106
- .map { |l| (CLI::UI.fmt(l, enable_color: false).length / max_width).ceil }
107
- .reduce(&:+)
126
+ .sum { |l| (CLI::UI.fmt(l, enable_color: false).length / max_width).ceil }
108
127
 
109
128
  width
110
129
  end
111
130
  end
112
131
 
132
+ sig { params(number_of_lines: Integer).void }
113
133
  def reset_position(number_of_lines = num_lines)
114
134
  # This will put us back at the beginning of the options
115
135
  # When we redraw the options, they will be overwritten
@@ -118,6 +138,7 @@ module CLI
118
138
  end
119
139
  end
120
140
 
141
+ sig { params(number_of_lines: Integer).void }
121
142
  def clear_output(number_of_lines = num_lines)
122
143
  CLI::UI.raw do
123
144
  # Write over all lines with whitespace
@@ -133,22 +154,26 @@ module CLI
133
154
 
134
155
  # Don't use this in place of +@displaying_metadata+, this updates too
135
156
  # quickly to be useful when drawing to the screen.
157
+ sig { returns(T::Boolean) }
136
158
  def display_metadata?
137
159
  filtering? || selecting? || has_filter?
138
160
  end
139
161
 
162
+ sig { returns(Integer) }
140
163
  def num_lines
141
164
  calculate_option_line_lengths if terminal_width_changed?
142
165
 
143
166
  option_length = presented_options.reduce(0) do |total_length, (_, option_number)|
144
167
  # Handle continuation markers and "Done" option when multiple is true
145
168
  next total_length + 1 if option_number.nil? || option_number.zero?
169
+
146
170
  total_length + @option_lengths[option_number - 1]
147
171
  end
148
172
 
149
173
  option_length + (@displaying_metadata ? 1 : 0)
150
174
  end
151
175
 
176
+ sig { returns(T::Boolean) }
152
177
  def terminal_width_changed?
153
178
  @terminal_width_at_calculation_time != CLI::UI::Terminal.width
154
179
  end
@@ -158,6 +183,7 @@ module CLI
158
183
  CTRL_C = "\u0003"
159
184
  CTRL_D = "\u0004"
160
185
 
186
+ sig { void }
161
187
  def up
162
188
  active_index = @filtered_options.index { |_, num| num == @active } || 0
163
189
 
@@ -168,6 +194,7 @@ module CLI
168
194
  @redraw = true
169
195
  end
170
196
 
197
+ sig { void }
171
198
  def down
172
199
  active_index = @filtered_options.index { |_, num| num == @active } || 0
173
200
 
@@ -180,6 +207,7 @@ module CLI
180
207
 
181
208
  # n is 1-indexed selection
182
209
  # n == 0 if "Done" was selected in @multiple mode
210
+ sig { params(n: Integer).void }
183
211
  def select_n(n)
184
212
  if @multiple
185
213
  if n == 0
@@ -200,24 +228,29 @@ module CLI
200
228
  @redraw = true
201
229
  end
202
230
 
231
+ sig { params(char: String).void }
203
232
  def select_bool(char)
204
- return unless (@options - %w(yes no)).empty?
205
- opt = @options.detect { |o| o.start_with?(char) }
206
- @active = @options.index(opt) + 1
207
- @answer = @options.index(opt) + 1
233
+ return unless (@options - ['yes', 'no']).empty?
234
+
235
+ index = T.must(@options.index { |o| o.start_with?(char) })
236
+ @active = index + 1
237
+ @answer = index + 1
208
238
  @redraw = true
209
239
  end
210
240
 
241
+ sig { params(char: String).void }
211
242
  def build_selection(char)
212
243
  @active = (@active.to_s + char).to_i
213
244
  @redraw = true
214
245
  end
215
246
 
247
+ sig { void }
216
248
  def chop_selection
217
249
  @active = @active.to_s.chop.to_i
218
250
  @redraw = true
219
251
  end
220
252
 
253
+ sig { params(char: String).void }
221
254
  def update_search(char)
222
255
  @redraw = true
223
256
 
@@ -235,25 +268,28 @@ module CLI
235
268
  end
236
269
  end
237
270
 
271
+ sig { void }
238
272
  def select_current
239
273
  # Prevent selection of invisible options
240
274
  return unless presented_options.any? { |_, num| num == @active }
275
+
241
276
  select_n(@active)
242
277
  end
243
278
 
279
+ sig { void }
244
280
  def process_input_until_redraw_required
245
281
  @redraw = false
246
282
  wait_for_user_input until @redraw
247
283
  end
248
284
 
249
285
  # rubocop:disable Style/WhenThen,Layout/SpaceBeforeSemicolon,Style/Semicolon
286
+ sig { void }
250
287
  def wait_for_user_input
251
288
  char = read_char
252
289
  @last_char = char
253
290
 
254
291
  case char
255
- when :timeout ; raise Interrupt # Timeout, use interrupt to simulate
256
- when CTRL_C ; raise Interrupt
292
+ when CTRL_C, nil ; raise Interrupt
257
293
  end
258
294
 
259
295
  max_digit = [@options.size, 9].min.to_s
@@ -302,51 +338,59 @@ module CLI
302
338
  end
303
339
  end
304
340
  end
305
- # rubocop:enable Style/WhenThen,Layout/SpaceBeforeSemicolon
341
+ # rubocop:enable Style/WhenThen,Layout/SpaceBeforeSemicolon,Style/Semicolon
306
342
 
343
+ sig { returns(T::Boolean) }
307
344
  def selecting?
308
345
  @state == :line_select
309
346
  end
310
347
 
348
+ sig { returns(T::Boolean) }
311
349
  def filtering?
312
350
  @state == :filter
313
351
  end
314
352
 
353
+ sig { returns(T::Boolean) }
315
354
  def has_filter?
316
355
  !@filter.empty?
317
356
  end
318
357
 
358
+ sig { void }
319
359
  def start_filter
320
360
  @state = :filter
321
361
  @redraw = true
322
362
  end
323
363
 
364
+ sig { void }
324
365
  def start_line_select
325
366
  @state = :line_select
326
367
  @active = 0
327
368
  @redraw = true
328
369
  end
329
370
 
371
+ sig { void }
330
372
  def stop_line_select
331
373
  @state = :root
332
374
  @active = 1 if @active.zero?
333
375
  @redraw = true
334
376
  end
335
377
 
378
+ sig { returns(T.nilable(String)) }
336
379
  def read_char
337
380
  if $stdin.tty? && !ENV['TEST']
338
381
  $stdin.getch # raw mode for tty
339
382
  else
340
- $stdin.getc
383
+ $stdin.getc # returns nil at end of input
341
384
  end
342
- rescue IOError
385
+ rescue Errno::EIO, Errno::EPIPE, IOError
343
386
  "\e"
344
387
  end
345
388
 
389
+ sig { params(recalculate: T::Boolean).returns(T::Array[[String, T.nilable(Integer)]]) }
346
390
  def presented_options(recalculate: false)
347
391
  return @presented_options unless recalculate
348
392
 
349
- @presented_options = @options.zip(1..Float::INFINITY)
393
+ @presented_options = @options.zip(1..)
350
394
  if has_filter?
351
395
  @presented_options.select! { |option, _| option.downcase.include?(@filter.downcase) }
352
396
  end
@@ -385,36 +429,44 @@ module CLI
385
429
  @presented_options
386
430
  end
387
431
 
432
+ sig { void }
388
433
  def ensure_visible_is_active
389
434
  unless presented_options.any? { |_, num| num == @active }
390
435
  @active = presented_options.first&.last.to_i
391
436
  end
392
437
  end
393
438
 
439
+ sig { returns(Integer) }
394
440
  def distance_from_selection_to_end
395
441
  @presented_options.count - index_of_active_option
396
442
  end
397
443
 
444
+ sig { returns(Integer) }
398
445
  def distance_from_start_to_selection
399
446
  index_of_active_option
400
447
  end
401
448
 
449
+ sig { returns(Integer) }
402
450
  def index_of_active_option
403
451
  @presented_options.index { |_, num| num == @active }.to_i
404
452
  end
405
453
 
454
+ sig { void }
406
455
  def ensure_last_item_is_continuation_marker
407
- @presented_options.push(['...', nil]) if @presented_options.last.last
456
+ @presented_options.push(['...', nil]) if @presented_options.last&.last
408
457
  end
409
458
 
459
+ sig { void }
410
460
  def ensure_first_item_is_continuation_marker
411
- @presented_options.unshift(['...', nil]) if @presented_options.first.last
461
+ @presented_options.unshift(['...', nil]) if @presented_options.first&.last
412
462
  end
413
463
 
464
+ sig { returns(Integer) }
414
465
  def max_lines
415
466
  CLI::UI::Terminal.height - (@displaying_metadata ? 3 : 2) # Keeps a one line question visible
416
467
  end
417
468
 
469
+ sig { void }
418
470
  def render_options
419
471
  previously_displayed_lines = num_lines
420
472
 
@@ -436,11 +488,7 @@ module CLI
436
488
  "Filter: #{filter_text}"
437
489
  end
438
490
 
439
- if metadata_text
440
- CLI::UI.with_frame_color(:blue) do
441
- puts CLI::UI.fmt(" {{green:#{metadata_text}}}#{ANSI.clear_to_end_of_line}")
442
- end
443
- end
491
+ puts CLI::UI.fmt(" {{green:#{metadata_text}}}#{ANSI.clear_to_end_of_line}") if metadata_text
444
492
 
445
493
  options.each do |choice, num|
446
494
  is_chosen = @multiple && num && @chosen[num - 1] && num != 0
@@ -463,12 +511,11 @@ module CLI
463
511
  message = message.split("\n").map { |l| "{{#{color}:> #{l.strip}}}" }.join("\n")
464
512
  end
465
513
 
466
- CLI::UI.with_frame_color(:blue) do
467
- puts CLI::UI.fmt(message)
468
- end
514
+ puts CLI::UI.fmt(message)
469
515
  end
470
516
  end
471
517
 
518
+ sig { params(format: String, choice: String).returns(String) }
472
519
  def format_choice(format, choice)
473
520
  eol = CLI::UI::ANSI.clear_to_end_of_line
474
521
  lines = choice.split("\n")
@@ -1,20 +1,28 @@
1
+ # typed: true
2
+
1
3
  module CLI
2
4
  module UI
3
5
  module Prompt
4
6
  # A class that handles the various options of an InteractivePrompt and their callbacks
5
7
  class OptionsHandler
8
+ extend T::Sig
9
+
10
+ sig { void }
6
11
  def initialize
7
12
  @options = {}
8
13
  end
9
14
 
15
+ sig { returns(T::Array[String]) }
10
16
  def options
11
17
  @options.keys
12
18
  end
13
19
 
20
+ sig { params(option: String, handler: T.proc.params(option: String).returns(String)).void }
14
21
  def option(option, &handler)
15
22
  @options[option] = handler
16
23
  end
17
24
 
25
+ sig { params(options: T.any(T::Array[String], String)).returns(String) }
18
26
  def call(options)
19
27
  case options
20
28
  when Array
data/lib/cli/ui/prompt.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  # coding: utf-8
2
+
3
+ # typed: true
4
+
2
5
  require 'cli/ui'
3
6
  require 'readline'
4
7
 
@@ -22,9 +25,10 @@ module CLI
22
25
  module Prompt
23
26
  autoload :InteractiveOptions, 'cli/ui/prompt/interactive_options'
24
27
  autoload :OptionsHandler, 'cli/ui/prompt/options_handler'
25
- private_constant :InteractiveOptions, :OptionsHandler
26
28
 
27
29
  class << self
30
+ extend T::Sig
31
+
28
32
  # Ask a user a question with either free form answer or a set of answers (multiple choice)
29
33
  # Can use arrows, y/n, numbers (1/2), and vim bindings to control multiple choice selection
30
34
  # Do not use this method for yes/no questions. Use +confirm+
@@ -92,26 +96,52 @@ module CLI
92
96
  # handler.option('python') { |selection| selection }
93
97
  # end
94
98
  #
99
+ sig do
100
+ params(
101
+ question: String,
102
+ options: T.nilable(T::Array[String]),
103
+ default: T.nilable(T.any(String, T::Array[String])),
104
+ is_file: T::Boolean,
105
+ allow_empty: T::Boolean,
106
+ multiple: T::Boolean,
107
+ filter_ui: T::Boolean,
108
+ select_ui: T::Boolean,
109
+ options_proc: T.nilable(T.proc.params(handler: OptionsHandler).void),
110
+ ).returns(T.any(String, T::Array[String]))
111
+ end
95
112
  def ask(
96
113
  question,
97
114
  options: nil,
98
115
  default: nil,
99
- is_file: nil,
116
+ is_file: false,
100
117
  allow_empty: true,
101
118
  multiple: false,
102
119
  filter_ui: true,
103
120
  select_ui: true,
104
121
  &options_proc
105
122
  )
106
- if (options || block_given?) && ((default && !multiple) || is_file)
107
- raise(ArgumentError, 'conflicting arguments: options provided with default or is_file')
123
+ has_options = !!(options || block_given?)
124
+ if has_options && default && !multiple
125
+ raise(ArgumentError, 'conflicting arguments: default may not be provided with options when not multiple')
126
+ end
127
+
128
+ if has_options && is_file
129
+ raise(ArgumentError, 'conflicting arguments: is_file is only useful when options are not provided')
108
130
  end
109
131
 
110
- if options && multiple && default && !(default - options).empty?
132
+ if options && multiple && default && !(Array(default) - options).empty?
111
133
  raise(ArgumentError, 'conflicting arguments: default should only include elements present in options')
112
134
  end
113
135
 
114
- if options || block_given?
136
+ if multiple && !has_options
137
+ raise(ArgumentError, 'conflicting arguments: options must be provided when multiple is true')
138
+ end
139
+
140
+ if !multiple && default.is_a?(Array)
141
+ raise(ArgumentError, 'conflicting arguments: multiple defaults may only be provided when multiple is true')
142
+ end
143
+
144
+ if has_options
115
145
  ask_interactive(
116
146
  question,
117
147
  options,
@@ -122,7 +152,7 @@ module CLI
122
152
  &options_proc
123
153
  )
124
154
  else
125
- ask_free_form(question, default, is_file, allow_empty)
155
+ ask_free_form(question, T.cast(default, T.nilable(String)), is_file, allow_empty)
126
156
  end
127
157
  end
128
158
 
@@ -133,24 +163,23 @@ module CLI
133
163
  #
134
164
  # The password, without a trailing newline.
135
165
  # If the user simply presses "Enter" without typing any password, this will return an empty string.
166
+ sig { params(question: String).returns(String) }
136
167
  def ask_password(question)
137
168
  require 'io/console'
138
169
 
139
- CLI::UI.with_frame_color(:blue) do
140
- STDOUT.print(CLI::UI.fmt('{{?}} ' + question)) # Do not use puts_question to avoid the new line.
170
+ STDOUT.print(CLI::UI.fmt('{{?}} ' + question)) # Do not use puts_question to avoid the new line.
141
171
 
142
- # noecho interacts poorly with Readline under system Ruby, so do a manual `gets` here.
143
- # No fancy Readline integration (like echoing back) is required for a password prompt anyway.
144
- password = STDIN.noecho do
145
- # Chomp will remove the one new line character added by `gets`, without touching potential extra spaces:
146
- # " 123 \n".chomp => " 123 "
147
- STDIN.gets.chomp
148
- end
172
+ # noecho interacts poorly with Readline under system Ruby, so do a manual `gets` here.
173
+ # No fancy Readline integration (like echoing back) is required for a password prompt anyway.
174
+ password = STDIN.noecho do
175
+ # Chomp will remove the one new line character added by `gets`, without touching potential extra spaces:
176
+ # " 123 \n".chomp => " 123 "
177
+ STDIN.gets.to_s.chomp
178
+ end
149
179
 
150
- STDOUT.puts # Complete the line
180
+ STDOUT.puts # Complete the line
151
181
 
152
- password
153
- end
182
+ password
154
183
  end
155
184
 
156
185
  # Asks the user a yes/no question.
@@ -163,12 +192,17 @@ module CLI
163
192
  #
164
193
  # CLI::UI::Prompt.confirm('Do a dangerous thing?', default: false)
165
194
  #
195
+ sig { params(question: String, default: T::Boolean).returns(T::Boolean) }
166
196
  def confirm(question, default: true)
167
- ask_interactive(question, default ? %w(yes no) : %w(no yes), filter_ui: false) == 'yes'
197
+ ask_interactive(question, default ? ['yes', 'no'] : ['no', 'yes'], filter_ui: false) == 'yes'
168
198
  end
169
199
 
170
200
  private
171
201
 
202
+ sig do
203
+ params(question: String, default: T.nilable(String), is_file: T::Boolean, allow_empty: T::Boolean)
204
+ .returns(String)
205
+ end
172
206
  def ask_free_form(question, default, is_file, allow_empty)
173
207
  if default && !allow_empty
174
208
  raise(ArgumentError, 'conflicting arguments: default enabled but allow_empty is false')
@@ -195,6 +229,16 @@ module CLI
195
229
  end
196
230
  end
197
231
 
232
+ sig do
233
+ params(
234
+ question: String,
235
+ options: T.nilable(T::Array[String]),
236
+ multiple: T::Boolean,
237
+ default: T.nilable(T.any(String, T::Array[String])),
238
+ filter_ui: T::Boolean,
239
+ select_ui: T::Boolean,
240
+ ).returns(T.any(String, T::Array[String]))
241
+ end
198
242
  def ask_interactive(question, options = nil, multiple: false, default: nil, filter_ui: true, select_ui: true)
199
243
  raise(ArgumentError, 'conflicting arguments: options and block given') if options && block_given?
200
244
 
@@ -205,7 +249,8 @@ module CLI
205
249
  end
206
250
 
207
251
  raise(ArgumentError, 'insufficient options') if options.nil? || options.empty?
208
- navigate_text = if CLI::UI::OS.current.supports_arrow_keys?
252
+
253
+ navigate_text = if CLI::UI::OS.current.suggest_arrow_keys?
209
254
  'Choose with ↑ ↓ ⏎'
210
255
  else
211
256
  "Navigate up with 'k' and down with 'j', press Enter to select"
@@ -223,9 +268,9 @@ module CLI
223
268
  print(ANSI.previous_line + "\n")
224
269
 
225
270
  # reset the question to include the answer
226
- resp_text = resp
227
- if multiple
228
- resp_text = case resp.size
271
+ resp_text = case resp
272
+ when Array
273
+ case resp.size
229
274
  when 0
230
275
  '<nothing>'
231
276
  when 1..2
@@ -233,18 +278,26 @@ module CLI
233
278
  else
234
279
  "#{resp.size} items"
235
280
  end
281
+ else
282
+ resp
236
283
  end
237
284
  puts_question("#{question} (You chose: {{italic:#{resp_text}}})")
238
285
 
239
- return handler.call(resp) if block_given?
286
+ return T.must(handler).call(resp) if block_given?
287
+
240
288
  resp
241
289
  end
242
290
 
243
291
  # Useful for stubbing in tests
292
+ sig do
293
+ params(options: T::Array[String], multiple: T::Boolean, default: T.nilable(T.any(T::Array[String], String)))
294
+ .returns(T.any(T::Array[String], String))
295
+ end
244
296
  def interactive_prompt(options, multiple: false, default: nil)
245
297
  InteractiveOptions.call(options, multiple: multiple, default: default)
246
298
  end
247
299
 
300
+ sig { params(default: String).void }
248
301
  def write_default_over_empty_input(default)
249
302
  CLI::UI.raw do
250
303
  STDERR.puts(
@@ -252,17 +305,17 @@ module CLI
252
305
  "\r" +
253
306
  CLI::UI::ANSI.cursor_forward(4) + # TODO: width
254
307
  default +
255
- CLI::UI::Color::RESET.code
308
+ CLI::UI::Color::RESET.code,
256
309
  )
257
310
  end
258
311
  end
259
312
 
313
+ sig { params(str: String).void }
260
314
  def puts_question(str)
261
- CLI::UI.with_frame_color(:blue) do
262
- STDOUT.puts(CLI::UI.fmt('{{?}} ' + str))
263
- end
315
+ STDOUT.puts(CLI::UI.fmt('{{?}} ' + str))
264
316
  end
265
317
 
318
+ sig { params(is_file: T::Boolean).returns(String) }
266
319
  def readline(is_file: false)
267
320
  if is_file
268
321
  Readline.completion_proc = Readline::FILENAME_COMPLETION_PROC
@@ -276,11 +329,11 @@ module CLI
276
329
  # work. We could work around this by having CLI::UI use a pipe and a
277
330
  # thread to manage output, but the current strategy feels like a
278
331
  # better tradeoff.
279
- prefix = CLI::UI.with_frame_color(:blue) { CLI::UI::Frame.prefix }
332
+ prefix = CLI::UI::Frame.prefix
280
333
  # If a prompt is interrupted on Windows it locks the colour of the terminal from that point on, so we should
281
334
  # not change the colour here.
282
335
  prompt = prefix + CLI::UI.fmt('{{blue:> }}')
283
- prompt += CLI::UI::Color::YELLOW.code if CLI::UI::OS.current.supports_color_prompt?
336
+ prompt += CLI::UI::Color::YELLOW.code if CLI::UI::OS.current.use_color_prompt?
284
337
 
285
338
  begin
286
339
  line = Readline.readline(prompt, true)