cli-ui 2.2.3 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/cli/ui/ansi.rb +25 -3
- data/lib/cli/ui/color.rb +3 -0
- data/lib/cli/ui/formatter.rb +1 -0
- data/lib/cli/ui/frame/frame_stack.rb +1 -0
- data/lib/cli/ui/frame/frame_style/box.rb +15 -14
- data/lib/cli/ui/frame/frame_style/bracket.rb +14 -13
- data/lib/cli/ui/frame/frame_style.rb +3 -2
- data/lib/cli/ui/frame.rb +9 -8
- data/lib/cli/ui/glyph.rb +2 -1
- data/lib/cli/ui/os.rb +1 -0
- data/lib/cli/ui/printer.rb +1 -0
- data/lib/cli/ui/progress.rb +36 -8
- data/lib/cli/ui/prompt/interactive_options.rb +13 -12
- data/lib/cli/ui/prompt/options_handler.rb +1 -0
- data/lib/cli/ui/prompt.rb +27 -30
- data/lib/cli/ui/spinner/async.rb +1 -0
- data/lib/cli/ui/spinner/spin_group.rb +182 -51
- data/lib/cli/ui/spinner.rb +11 -5
- data/lib/cli/ui/stdout_router.rb +6 -4
- data/lib/cli/ui/table.rb +87 -0
- data/lib/cli/ui/terminal.rb +1 -0
- data/lib/cli/ui/version.rb +2 -1
- data/lib/cli/ui/widgets/base.rb +1 -0
- data/lib/cli/ui/widgets.rb +2 -1
- data/lib/cli/ui/work_queue.rb +146 -0
- data/lib/cli/ui/wrap.rb +4 -4
- data/lib/cli/ui.rb +44 -11
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9933dc93d8bd52dd2cd910ecc126476fabe857f44d2ceb411d75ba1db5b6eec6
|
4
|
+
data.tar.gz: 4eba28b0b33f775b0e941109529946b1c14b5bebeb09084d6ee69ea0663635c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50b7afe928e8d18e2159864cd646a0d5be3df82f456506682b061027e5bb047246e97dc22f04610cc2dc06091cb437e0b3bff713b4b3f4244f4b106df0cc6194
|
7
|
+
data.tar.gz: 8f4d5c54bdb5dff79d6d588f9082455913a2c6081d9b6ebb8e575839375c05ecb0560d15a3b6b305cda5cdd10e22d0a2fe37772c4a7c67ac507fc0162509f756
|
data/README.md
CHANGED
@@ -162,7 +162,7 @@ CLI::UI.frame_style = :box
|
|
162
162
|
To style an individual frame:
|
163
163
|
|
164
164
|
```ruby
|
165
|
-
CLI::UI.frame('New Style!', frame_style: :bracket) { puts
|
165
|
+
CLI::UI.frame('New Style!', frame_style: :bracket) { puts "It's pretty cool!" }
|
166
166
|
```
|
167
167
|
|
168
168
|
The default style - `:box` - is what has been used up until now. The other style - `:bracket` - looks like this:
|
data/lib/cli/ui/ansi.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'cli/ui'
|
4
5
|
|
@@ -45,7 +46,7 @@ module CLI
|
|
45
46
|
#
|
46
47
|
sig { params(str: String).returns(String) }
|
47
48
|
def strip_codes(str)
|
48
|
-
str.gsub(/\x1b\[[\d;]+[A-z]|\r/, '')
|
49
|
+
str.gsub(/\x1b\[[\d;]+[A-Za-z]|\x1b\][\d;]+.*?\x1b\\|\r/, '')
|
49
50
|
end
|
50
51
|
|
51
52
|
# Returns an ANSI control sequence
|
@@ -145,7 +146,7 @@ module CLI
|
|
145
146
|
|
146
147
|
sig { returns(Regexp) }
|
147
148
|
def match_alternate_screen
|
148
|
-
/#{Regexp.escape(control(
|
149
|
+
/#{Regexp.escape(control("?1049", ""))}[hl]/
|
149
150
|
end
|
150
151
|
|
151
152
|
# Show the cursor
|
@@ -187,13 +188,34 @@ module CLI
|
|
187
188
|
#
|
188
189
|
sig { returns(String) }
|
189
190
|
def previous_line
|
190
|
-
|
191
|
+
previous_lines(1)
|
192
|
+
end
|
193
|
+
|
194
|
+
# Move to the previous n lines
|
195
|
+
#
|
196
|
+
# ==== Attributes
|
197
|
+
#
|
198
|
+
# * +n+ - number of lines by which to move the cursor up
|
199
|
+
#
|
200
|
+
sig { params(n: Integer).returns(String) }
|
201
|
+
def previous_lines(n = 1)
|
202
|
+
cursor_up(n) + cursor_horizontal_absolute
|
191
203
|
end
|
192
204
|
|
193
205
|
sig { returns(String) }
|
194
206
|
def clear_to_end_of_line
|
195
207
|
control('', 'K')
|
196
208
|
end
|
209
|
+
|
210
|
+
sig { returns(String) }
|
211
|
+
def insert_line
|
212
|
+
insert_lines(1)
|
213
|
+
end
|
214
|
+
|
215
|
+
sig { params(n: Integer).returns(String) }
|
216
|
+
def insert_lines(n = 1)
|
217
|
+
control(n.to_s, 'L')
|
218
|
+
end
|
197
219
|
end
|
198
220
|
end
|
199
221
|
end
|
data/lib/cli/ui/color.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'cli/ui'
|
4
5
|
|
@@ -42,6 +43,8 @@ module CLI
|
|
42
43
|
|
43
44
|
# 240 is very dark gray; 255 is very light gray. 244 is somewhat dark.
|
44
45
|
GRAY = new('38;5;244', :gray)
|
46
|
+
# Using color 214 from the 256-color palette for a more distinct orange
|
47
|
+
ORANGE = new('38;5;214', :orange)
|
45
48
|
|
46
49
|
MAP = {
|
47
50
|
red: RED,
|
data/lib/cli/ui/formatter.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module CLI
|
4
5
|
module UI
|
@@ -94,7 +95,8 @@ module CLI
|
|
94
95
|
|
95
96
|
preamble = +''
|
96
97
|
|
97
|
-
preamble << color.code
|
98
|
+
preamble << color.code if CLI::UI.enable_color?
|
99
|
+
preamble << first << (HORIZONTAL * 2)
|
98
100
|
|
99
101
|
unless text.empty?
|
100
102
|
preamble << ' ' << CLI::UI.resolve_text("{{#{color.name}:#{text}}}") << ' '
|
@@ -128,18 +130,17 @@ module CLI
|
|
128
130
|
|
129
131
|
o = +''
|
130
132
|
|
131
|
-
|
132
|
-
# the fancier features that we normally use to draw frames
|
133
|
-
# extra-reliably, so we fall back to a less foolproof strategy. This
|
134
|
-
# is probably better in general for cases with impoverished terminal
|
135
|
-
# emulators and no active user.
|
136
|
-
unless [0, '', nil].include?(ENV['CI'])
|
133
|
+
unless CLI::UI.enable_cursor?
|
137
134
|
linewidth = [0, termwidth - (preamble_end + suffix_width + 1)].max
|
138
135
|
|
139
|
-
o << color.code
|
140
|
-
o <<
|
141
|
-
o << color.code
|
142
|
-
o <<
|
136
|
+
o << color.code if CLI::UI.enable_color?
|
137
|
+
o << preamble
|
138
|
+
o << color.code if CLI::UI.enable_color?
|
139
|
+
o << (HORIZONTAL * linewidth)
|
140
|
+
o << color.code if CLI::UI.enable_color?
|
141
|
+
o << suffix
|
142
|
+
o << CLI::UI::Color::RESET.code if CLI::UI.enable_color?
|
143
|
+
o << "\n"
|
143
144
|
return o
|
144
145
|
end
|
145
146
|
|
@@ -158,12 +159,12 @@ module CLI
|
|
158
159
|
# | | | | |
|
159
160
|
# V V V V V
|
160
161
|
# --- Preamble text --------------------- suffix text --
|
161
|
-
o << color.code
|
162
|
+
o << color.code if CLI::UI.enable_color?
|
162
163
|
o << print_at_x(preamble_start, HORIZONTAL * (termwidth - preamble_start)) # draw a full line
|
163
164
|
o << print_at_x(preamble_start, preamble)
|
164
|
-
o << color.code
|
165
|
+
o << color.code if CLI::UI.enable_color?
|
165
166
|
o << print_at_x(suffix_start, suffix)
|
166
|
-
o << CLI::UI::Color::RESET.code
|
167
|
+
o << CLI::UI::Color::RESET.code if CLI::UI.enable_color?
|
167
168
|
o << CLI::UI::ANSI.show_cursor
|
168
169
|
o << "\n"
|
169
170
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
module CLI
|
4
5
|
module UI
|
@@ -94,7 +95,8 @@ module CLI
|
|
94
95
|
|
95
96
|
preamble = +''
|
96
97
|
|
97
|
-
preamble << color.code
|
98
|
+
preamble << color.code if CLI::UI.enable_color?
|
99
|
+
preamble << first << (HORIZONTAL * 2)
|
98
100
|
|
99
101
|
unless text.empty?
|
100
102
|
preamble << ' ' << CLI::UI.resolve_text("{{#{color.name}:#{text}}}") << ' '
|
@@ -108,15 +110,12 @@ module CLI
|
|
108
110
|
|
109
111
|
o = +''
|
110
112
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
o << color.code << preamble
|
118
|
-
o << color.code << suffix
|
119
|
-
o << CLI::UI::Color::RESET.code
|
113
|
+
unless CLI::UI.enable_cursor?
|
114
|
+
o << color.code if CLI::UI.enable_color?
|
115
|
+
o << preamble
|
116
|
+
o << color.code if CLI::UI.enable_color?
|
117
|
+
o << suffix
|
118
|
+
o << CLI::UI::Color::RESET.code if CLI::UI.enable_color?
|
120
119
|
o << "\n"
|
121
120
|
|
122
121
|
return o
|
@@ -134,9 +133,11 @@ module CLI
|
|
134
133
|
# reset to column 1 so that things like ^C don't ruin formatting
|
135
134
|
o << "\r"
|
136
135
|
|
137
|
-
o << color.code
|
138
|
-
o << print_at_x(preamble_start, preamble
|
139
|
-
o << CLI::UI
|
136
|
+
o << color.code if CLI::UI.enable_color?
|
137
|
+
o << print_at_x(preamble_start, preamble)
|
138
|
+
o << color.code if CLI::UI.enable_color?
|
139
|
+
o << suffix
|
140
|
+
o << CLI::UI::Color::RESET.code if CLI::UI.enable_color?
|
140
141
|
o << CLI::UI::ANSI.show_cursor
|
141
142
|
o << "\n"
|
142
143
|
|
@@ -1,4 +1,5 @@
|
|
1
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'cli/ui/frame'
|
4
5
|
|
@@ -106,8 +107,8 @@ module CLI
|
|
106
107
|
sig { returns(String) }
|
107
108
|
def message
|
108
109
|
keys = FrameStyle::MAP.keys.map(&:inspect).join(', ')
|
109
|
-
"invalid frame style: #{@name.inspect}" \
|
110
|
-
'
|
110
|
+
"invalid frame style: #{@name.inspect} " \
|
111
|
+
'-- must be one of CLI::UI::Frame::FrameStyle::MAP ' \
|
111
112
|
"(#{keys})"
|
112
113
|
end
|
113
114
|
end
|
data/lib/cli/ui/frame.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
4
3
|
|
5
4
|
require 'cli/ui'
|
6
5
|
require 'cli/ui/frame/frame_stack'
|
@@ -250,19 +249,21 @@ module CLI
|
|
250
249
|
#
|
251
250
|
sig { params(color: T.nilable(Colorable)).returns(String) }
|
252
251
|
def prefix(color: Thread.current[:cliui_frame_color_override])
|
253
|
-
+''.tap do |output|
|
252
|
+
(+'').tap do |output|
|
254
253
|
items = FrameStack.items
|
255
254
|
|
256
255
|
items[0..-2].to_a.each do |item|
|
257
|
-
output << item.color.code
|
256
|
+
output << item.color.code if CLI::UI.enable_color?
|
257
|
+
output << item.frame_style.prefix
|
258
|
+
output << CLI::UI::Color::RESET.code if CLI::UI.enable_color?
|
258
259
|
end
|
259
260
|
|
260
261
|
if (item = items.last)
|
261
262
|
final_color = color || item.color
|
262
|
-
output << CLI::UI.resolve_color(final_color).code
|
263
|
-
|
264
|
-
|
265
|
-
|
263
|
+
output << CLI::UI.resolve_color(final_color).code if CLI::UI.enable_color?
|
264
|
+
output << item.frame_style.prefix
|
265
|
+
output << CLI::UI::Color::RESET.code if CLI::UI.enable_color?
|
266
|
+
output << ' '
|
266
267
|
end
|
267
268
|
end
|
268
269
|
end
|
data/lib/cli/ui/glyph.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'cli/ui'
|
4
5
|
|
@@ -63,7 +64,7 @@ module CLI
|
|
63
64
|
X = new('x', 0x2717, 'X', Color::RED) # RED BALLOT X (✗)
|
64
65
|
BUG = new('b', 0x1f41b, '!', Color::WHITE) # Bug emoji (🐛)
|
65
66
|
CHEVRON = new('>', 0xbb, '»', Color::YELLOW) # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK (»)
|
66
|
-
HOURGLASS = new('H',
|
67
|
+
HOURGLASS = new('H', 0x29d6, 'H', Color::ORANGE) # HOURGLASS (⧖)
|
67
68
|
WARNING = new('!', [0x26a0, 0xfe0f], '!', Color::YELLOW) # WARNING SIGN + VARIATION SELECTOR 16 (⚠️ )
|
68
69
|
|
69
70
|
class << self
|
data/lib/cli/ui/os.rb
CHANGED
data/lib/cli/ui/printer.rb
CHANGED
data/lib/cli/ui/progress.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'cli/ui'
|
4
5
|
|
@@ -35,13 +36,23 @@ module CLI
|
|
35
36
|
# CLI::UI::Progress.progress do |bar|
|
36
37
|
# bar.tick(percent: 0.05)
|
37
38
|
# end
|
39
|
+
#
|
40
|
+
# Update the title
|
41
|
+
# CLI::UI::Progress.progress('Title') do |bar|
|
42
|
+
# bar.tick(percent: 0.05)
|
43
|
+
# bar.update_title('New title')
|
44
|
+
# end
|
38
45
|
sig do
|
39
46
|
type_parameters(:T)
|
40
|
-
.params(
|
47
|
+
.params(
|
48
|
+
title: T.nilable(String),
|
49
|
+
width: Integer,
|
50
|
+
block: T.proc.params(bar: Progress).returns(T.type_parameter(:T)),
|
51
|
+
)
|
41
52
|
.returns(T.type_parameter(:T))
|
42
53
|
end
|
43
|
-
def progress(width: Terminal.width, &block)
|
44
|
-
bar = Progress.new(width: width)
|
54
|
+
def progress(title = nil, width: Terminal.width, &block)
|
55
|
+
bar = Progress.new(title, width: width)
|
45
56
|
print(CLI::UI::ANSI.hide_cursor)
|
46
57
|
yield(bar)
|
47
58
|
ensure
|
@@ -55,13 +66,14 @@ module CLI
|
|
55
66
|
# Initialize a progress bar. Typically used in a +Progress.progress+ block
|
56
67
|
#
|
57
68
|
# ==== Options
|
58
|
-
# One of the follow can be used, but not both together
|
59
69
|
#
|
70
|
+
# * +:title+ - The title of the progress bar
|
60
71
|
# * +:width+ - The width of the terminal
|
61
72
|
#
|
62
|
-
sig { params(width: Integer).void }
|
63
|
-
def initialize(width: Terminal.width)
|
73
|
+
sig { params(title: T.nilable(String), width: Integer).void }
|
74
|
+
def initialize(title = nil, width: Terminal.width)
|
64
75
|
@percent_done = T.let(0, Numeric)
|
76
|
+
@title = title
|
65
77
|
@max_width = width
|
66
78
|
end
|
67
79
|
|
@@ -84,7 +96,20 @@ module CLI
|
|
84
96
|
@percent_done = [@percent_done, 1.0].min # Make sure we can't go above 1.0
|
85
97
|
|
86
98
|
print(self)
|
87
|
-
|
99
|
+
|
100
|
+
printed_lines = @title ? 2 : 1
|
101
|
+
print(CLI::UI::ANSI.previous_lines(printed_lines) + "\n")
|
102
|
+
end
|
103
|
+
|
104
|
+
# Update the progress bar title
|
105
|
+
#
|
106
|
+
# ==== Attributes
|
107
|
+
#
|
108
|
+
# * +new_title+ - title to change the progress bar to
|
109
|
+
#
|
110
|
+
sig { params(new_title: String).void }
|
111
|
+
def update_title(new_title)
|
112
|
+
@title = new_title
|
88
113
|
end
|
89
114
|
|
90
115
|
# Format the progress bar to be printed to terminal
|
@@ -96,11 +121,14 @@ module CLI
|
|
96
121
|
filled = [(@percent_done * workable_width.to_f).ceil, 0].max
|
97
122
|
unfilled = [workable_width - filled, 0].max
|
98
123
|
|
99
|
-
CLI::UI.resolve_text(
|
124
|
+
title = CLI::UI.resolve_text(@title, truncate_to: @max_width - Frame.prefix_width) if @title
|
125
|
+
bar = CLI::UI.resolve_text([
|
100
126
|
FILLED_BAR + ' ' * filled,
|
101
127
|
UNFILLED_BAR + ' ' * unfilled,
|
102
128
|
CLI::UI::Color::RESET.code + suffix,
|
103
129
|
].join)
|
130
|
+
|
131
|
+
[title, bar].compact.join("\n")
|
104
132
|
end
|
105
133
|
end
|
106
134
|
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
|
3
2
|
# typed: true
|
3
|
+
# frozen_string_literal: true
|
4
4
|
|
5
5
|
require 'io/console'
|
6
6
|
|
@@ -59,7 +59,11 @@ module CLI
|
|
59
59
|
end
|
60
60
|
def initialize(options, multiple: false, default: nil)
|
61
61
|
@options = options
|
62
|
-
@active =
|
62
|
+
@active = if default && (i = options.index(default))
|
63
|
+
i + 1
|
64
|
+
else
|
65
|
+
1
|
66
|
+
end
|
63
67
|
@marker = '>'
|
64
68
|
@answer = nil
|
65
69
|
@state = :root
|
@@ -114,23 +118,20 @@ module CLI
|
|
114
118
|
# determine how many extra lines would be taken up by them.
|
115
119
|
#
|
116
120
|
# To accomplish this we split the string by new lines and add the
|
117
|
-
#
|
118
|
-
#
|
119
|
-
#
|
120
|
-
#
|
121
|
-
|
122
|
-
# options.count.to_s.size gets us the max size of the number we will display
|
123
|
-
extra_chars = @marker.length + 3 + @options.count.to_s.size + (@multiple ? 1 : 0)
|
121
|
+
# prefix to the first line. We use the options count as the number since
|
122
|
+
# it will be the widest number we will display, and we pad the others to
|
123
|
+
# align with it. Then we calculate how many lines would be needed to
|
124
|
+
# render the string based on the terminal width.
|
125
|
+
prefix = "#{@marker} #{@options.count}. #{@multiple ? "☐ " : ""}"
|
124
126
|
|
125
127
|
@option_lengths = @options.map do |text|
|
126
128
|
next 1 if text.empty?
|
127
129
|
|
128
130
|
# Find the length of all the lines in this string
|
129
|
-
non_empty_line_lengths = text.split("\n").reject(&:empty?).map do |line|
|
131
|
+
non_empty_line_lengths = "#{prefix}#{text}".split("\n").reject(&:empty?).map do |line|
|
130
132
|
CLI::UI.fmt(line, enable_color: false).length
|
131
133
|
end
|
132
|
-
|
133
|
-
non_empty_line_lengths[0] += extra_chars
|
134
|
+
|
134
135
|
# Finally, we need to calculate how many lines each one will take. We can do that by dividing each one
|
135
136
|
# by the width of the terminal, rounding up to the nearest natural number
|
136
137
|
non_empty_line_lengths.sum { |length| (length.to_f / @terminal_width_at_calculation_time).ceil }
|
data/lib/cli/ui/prompt.rb
CHANGED
@@ -1,23 +1,13 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
|
3
2
|
# typed: true
|
3
|
+
# frozen_string_literal: true
|
4
4
|
|
5
5
|
require 'cli/ui'
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
directory = input[-1] == '/' ? input : File.dirname(input)
|
12
|
-
filename = input[-1] == '/' ? '' : File.basename(input)
|
13
|
-
|
14
|
-
(Dir.entries(directory).select do |fp|
|
15
|
-
fp.start_with?(filename)
|
16
|
-
end - (input[-1] == '.' ? [] : ['.', '..'])).map do |fp|
|
17
|
-
File.join(directory, fp).gsub(/\A\.\//, '')
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
6
|
+
begin
|
7
|
+
require 'reline' # For 2.7+
|
8
|
+
rescue LoadError
|
9
|
+
require 'readline' # For 2.6
|
10
|
+
Object.const_set(:Reline, Readline)
|
21
11
|
end
|
22
12
|
|
23
13
|
module CLI
|
@@ -137,10 +127,6 @@ module CLI
|
|
137
127
|
&options_proc
|
138
128
|
)
|
139
129
|
has_options = !!(options || block_given?)
|
140
|
-
if has_options && default && !multiple
|
141
|
-
raise(ArgumentError, 'conflicting arguments: default may not be provided with options when not multiple')
|
142
|
-
end
|
143
|
-
|
144
130
|
if has_options && is_file
|
145
131
|
raise(ArgumentError, 'conflicting arguments: is_file is only useful when options are not provided')
|
146
132
|
end
|
@@ -310,6 +296,8 @@ module CLI
|
|
310
296
|
instructions += ", filter with 'f'" if filter_ui
|
311
297
|
instructions += ", enter option with 'e'" if select_ui && (options.size > 9)
|
312
298
|
|
299
|
+
resp = T.let([], T.any(String, T::Array[String]))
|
300
|
+
|
313
301
|
CLI::UI::StdoutRouter::Capture.in_alternate_screen do
|
314
302
|
puts_question("#{question} " + instructions_color.code + "(#{instructions})" + Color::RESET.code)
|
315
303
|
resp = interactive_prompt(options, multiple: multiple, default: default)
|
@@ -334,12 +322,12 @@ module CLI
|
|
334
322
|
resp
|
335
323
|
end
|
336
324
|
puts_question("#{question} (You chose: {{italic:#{resp_text}}})")
|
325
|
+
end
|
337
326
|
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
end
|
327
|
+
if block_given?
|
328
|
+
T.must(handler).call(resp)
|
329
|
+
else
|
330
|
+
resp
|
343
331
|
end
|
344
332
|
end
|
345
333
|
|
@@ -375,11 +363,20 @@ module CLI
|
|
375
363
|
sig { params(is_file: T::Boolean).returns(String) }
|
376
364
|
def readline(is_file: false)
|
377
365
|
if is_file
|
378
|
-
|
379
|
-
|
366
|
+
Reline.completion_proc = proc do |input|
|
367
|
+
directory = input[-1] == '/' ? input : File.dirname(input)
|
368
|
+
filename = input[-1] == '/' ? '' : File.basename(input)
|
369
|
+
|
370
|
+
(Dir.entries(directory).select do |fp|
|
371
|
+
fp.start_with?(filename)
|
372
|
+
end - (input[-1] == '.' ? [] : ['.', '..'])).map do |fp|
|
373
|
+
File.join(directory, fp).gsub(/\A\.\//, '')
|
374
|
+
end
|
375
|
+
end
|
376
|
+
Reline.completion_append_character = ''
|
380
377
|
else
|
381
|
-
|
382
|
-
|
378
|
+
Reline.completion_proc = proc {}
|
379
|
+
Reline.completion_append_character = ' '
|
383
380
|
end
|
384
381
|
|
385
382
|
# because Readline is a C library, CLI::UI's hooks into $stdout don't
|
@@ -393,7 +390,7 @@ module CLI
|
|
393
390
|
prompt += CLI::UI::Color::YELLOW.code if CLI::UI::OS.current.use_color_prompt?
|
394
391
|
|
395
392
|
begin
|
396
|
-
line =
|
393
|
+
line = Reline.readline(prompt, true)
|
397
394
|
print(CLI::UI::Color::RESET.code)
|
398
395
|
line.to_s.chomp
|
399
396
|
rescue Interrupt
|