cli-ui 1.5.1 → 2.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +28 -17
- data/lib/cli/ui/ansi.rb +172 -129
- data/lib/cli/ui/color.rb +39 -20
- data/lib/cli/ui/formatter.rb +46 -21
- data/lib/cli/ui/frame/frame_stack.rb +30 -50
- data/lib/cli/ui/frame/frame_style/box.rb +16 -5
- data/lib/cli/ui/frame/frame_style/bracket.rb +19 -8
- data/lib/cli/ui/frame/frame_style.rb +84 -87
- data/lib/cli/ui/frame.rb +79 -32
- data/lib/cli/ui/glyph.rb +44 -31
- data/lib/cli/ui/os.rb +44 -48
- data/lib/cli/ui/printer.rb +65 -47
- data/lib/cli/ui/progress.rb +50 -33
- data/lib/cli/ui/prompt/interactive_options.rb +114 -68
- data/lib/cli/ui/prompt/options_handler.rb +8 -0
- data/lib/cli/ui/prompt.rb +168 -58
- data/lib/cli/ui/sorbet_runtime_stub.rb +169 -0
- data/lib/cli/ui/spinner/async.rb +15 -4
- data/lib/cli/ui/spinner/spin_group.rb +174 -36
- data/lib/cli/ui/spinner.rb +48 -28
- data/lib/cli/ui/stdout_router.rb +229 -47
- data/lib/cli/ui/terminal.rb +37 -25
- data/lib/cli/ui/truncater.rb +7 -2
- data/lib/cli/ui/version.rb +3 -1
- data/lib/cli/ui/widgets/base.rb +23 -4
- data/lib/cli/ui/widgets/status.rb +19 -1
- data/lib/cli/ui/widgets.rb +42 -23
- data/lib/cli/ui/wrap.rb +8 -1
- data/lib/cli/ui.rb +336 -188
- data/vendor/reentrant_mutex.rb +78 -0
- metadata +11 -9
data/lib/cli/ui/frame.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
|
3
|
+
# typed: true
|
4
|
+
|
2
5
|
require 'cli/ui'
|
3
6
|
require 'cli/ui/frame/frame_stack'
|
4
7
|
require 'cli/ui/frame/frame_style'
|
@@ -7,9 +10,12 @@ module CLI
|
|
7
10
|
module UI
|
8
11
|
module Frame
|
9
12
|
class UnnestedFrameException < StandardError; end
|
13
|
+
DEFAULT_FRAME_COLOR = CLI::UI.resolve_color(:cyan)
|
14
|
+
|
10
15
|
class << self
|
11
|
-
|
16
|
+
extend T::Sig
|
12
17
|
|
18
|
+
sig { returns(FrameStyle) }
|
13
19
|
def frame_style
|
14
20
|
@frame_style ||= FrameStyle::Box
|
15
21
|
end
|
@@ -22,6 +28,7 @@ module CLI
|
|
22
28
|
#
|
23
29
|
# * +symbol+ or +FrameStyle+ - the default frame style to use for frames
|
24
30
|
#
|
31
|
+
sig { params(frame_style: FrameStylable).void }
|
25
32
|
def frame_style=(frame_style)
|
26
33
|
@frame_style = CLI::UI.resolve_style(frame_style)
|
27
34
|
end
|
@@ -47,6 +54,8 @@ module CLI
|
|
47
54
|
# * +:success_text+ - If the block succeeds, what do we output? Defaults to nil
|
48
55
|
# * +:timing+ - How long did the frame content take? Invalid for blockless. Defaults to true for the block form
|
49
56
|
# * +frame_style+ - The frame style to use for this frame
|
57
|
+
# * +:to+ - Target stream, like $stdout or $stderr. Can be anything with print and puts methods,
|
58
|
+
# or under Sorbet, IO or StringIO. Defaults to $stdout.
|
50
59
|
#
|
51
60
|
# ==== Example
|
52
61
|
#
|
@@ -67,13 +76,27 @@ module CLI
|
|
67
76
|
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
68
77
|
#
|
69
78
|
#
|
79
|
+
sig do
|
80
|
+
type_parameters(:T).params(
|
81
|
+
text: String,
|
82
|
+
color: Colorable,
|
83
|
+
failure_text: T.nilable(String),
|
84
|
+
success_text: T.nilable(String),
|
85
|
+
timing: T.any(T::Boolean, Numeric),
|
86
|
+
frame_style: FrameStylable,
|
87
|
+
to: IOLike,
|
88
|
+
block: T.nilable(T.proc.returns(T.type_parameter(:T))),
|
89
|
+
).returns(T.nilable(T.type_parameter(:T)))
|
90
|
+
end
|
70
91
|
def open(
|
71
92
|
text,
|
72
93
|
color: DEFAULT_FRAME_COLOR,
|
73
94
|
failure_text: nil,
|
74
95
|
success_text: nil,
|
75
|
-
timing:
|
76
|
-
frame_style:
|
96
|
+
timing: block_given?,
|
97
|
+
frame_style: self.frame_style,
|
98
|
+
to: $stdout,
|
99
|
+
&block
|
77
100
|
)
|
78
101
|
frame_style = CLI::UI.resolve_style(frame_style)
|
79
102
|
color = CLI::UI.resolve_color(color)
|
@@ -90,8 +113,8 @@ module CLI
|
|
90
113
|
|
91
114
|
t_start = Time.now
|
92
115
|
CLI::UI.raw do
|
93
|
-
print(prefix.chop)
|
94
|
-
puts
|
116
|
+
to.print(prefix.chop)
|
117
|
+
to.puts(frame_style.start(text, color: color))
|
95
118
|
end
|
96
119
|
FrameStack.push(color: color, style: frame_style)
|
97
120
|
|
@@ -103,18 +126,18 @@ module CLI
|
|
103
126
|
success = yield
|
104
127
|
rescue
|
105
128
|
closed = true
|
106
|
-
t_diff =
|
107
|
-
close(failure_text, color: :red, elapsed: t_diff)
|
129
|
+
t_diff = elapsed(t_start, timing)
|
130
|
+
close(failure_text, color: :red, elapsed: t_diff, to: to)
|
108
131
|
raise
|
109
132
|
else
|
110
133
|
success
|
111
134
|
ensure
|
112
135
|
unless closed
|
113
|
-
t_diff =
|
114
|
-
if success != false
|
115
|
-
close(success_text, color: color, elapsed: t_diff)
|
136
|
+
t_diff = elapsed(t_start, timing)
|
137
|
+
if T.unsafe(success) != false
|
138
|
+
close(success_text, color: color, elapsed: t_diff, to: to)
|
116
139
|
else
|
117
|
-
close(failure_text, color: :red, elapsed: t_diff)
|
140
|
+
close(failure_text, color: :red, elapsed: t_diff, to: to)
|
118
141
|
end
|
119
142
|
end
|
120
143
|
end
|
@@ -131,6 +154,8 @@ module CLI
|
|
131
154
|
#
|
132
155
|
# * +:color+ - The color of the frame. Defaults to +DEFAULT_FRAME_COLOR+
|
133
156
|
# * +frame_style+ - The frame style to use for this frame
|
157
|
+
# * +:to+ - Target stream, like $stdout or $stderr. Can be anything with print and puts methods,
|
158
|
+
# or under Sorbet, IO or StringIO. Defaults to $stdout.
|
134
159
|
#
|
135
160
|
# ==== Example
|
136
161
|
#
|
@@ -145,16 +170,24 @@ module CLI
|
|
145
170
|
#
|
146
171
|
# MUST be inside an open frame or it raises a +UnnestedFrameException+
|
147
172
|
#
|
148
|
-
|
173
|
+
sig do
|
174
|
+
params(
|
175
|
+
text: T.nilable(String),
|
176
|
+
color: T.nilable(Colorable),
|
177
|
+
frame_style: T.nilable(FrameStylable),
|
178
|
+
to: IOLike,
|
179
|
+
).void
|
180
|
+
end
|
181
|
+
def divider(text, color: nil, frame_style: nil, to: $stdout)
|
149
182
|
fs_item = FrameStack.pop
|
150
183
|
raise UnnestedFrameException, 'No frame nesting to unnest' unless fs_item
|
151
184
|
|
152
|
-
|
153
|
-
frame_style = CLI::UI.resolve_style(frame_style
|
185
|
+
divider_color = CLI::UI.resolve_color(color || fs_item.color)
|
186
|
+
frame_style = CLI::UI.resolve_style(frame_style || fs_item.frame_style)
|
154
187
|
|
155
188
|
CLI::UI.raw do
|
156
|
-
print(prefix.chop)
|
157
|
-
puts
|
189
|
+
to.print(prefix.chop)
|
190
|
+
to.puts(frame_style.divider(text.to_s, color: divider_color))
|
158
191
|
end
|
159
192
|
|
160
193
|
FrameStack.push(fs_item)
|
@@ -172,6 +205,8 @@ module CLI
|
|
172
205
|
# * +:color+ - The color of the frame. Defaults to nil
|
173
206
|
# * +:elapsed+ - How long did the frame take? Defaults to nil
|
174
207
|
# * +frame_style+ - The frame style to use for this frame. Defaults to nil
|
208
|
+
# * +:to+ - Target stream, like $stdout or $stderr. Can be anything with print and puts methods,
|
209
|
+
# or under Sorbet, IO or StringIO. Defaults to $stdout.
|
175
210
|
#
|
176
211
|
# ==== Example
|
177
212
|
#
|
@@ -184,21 +219,26 @@ module CLI
|
|
184
219
|
#
|
185
220
|
# MUST be inside an open frame or it raises a +UnnestedFrameException+
|
186
221
|
#
|
187
|
-
|
222
|
+
sig do
|
223
|
+
params(
|
224
|
+
text: T.nilable(String),
|
225
|
+
color: T.nilable(Colorable),
|
226
|
+
elapsed: T.nilable(Numeric),
|
227
|
+
frame_style: T.nilable(FrameStylable),
|
228
|
+
to: IOLike,
|
229
|
+
).void
|
230
|
+
end
|
231
|
+
def close(text, color: nil, elapsed: nil, frame_style: nil, to: $stdout)
|
188
232
|
fs_item = FrameStack.pop
|
189
233
|
raise UnnestedFrameException, 'No frame nesting to unnest' unless fs_item
|
190
234
|
|
191
|
-
|
192
|
-
frame_style = CLI::UI.resolve_style(frame_style
|
193
|
-
|
194
|
-
kwargs = {}
|
195
|
-
if elapsed
|
196
|
-
kwargs[:right_text] = "(#{elapsed.round(2)}s)"
|
197
|
-
end
|
235
|
+
close_color = CLI::UI.resolve_color(color || fs_item.color)
|
236
|
+
frame_style = CLI::UI.resolve_style(frame_style || fs_item.frame_style)
|
237
|
+
elapsed_string = elapsed ? "(#{elapsed.round(2)}s)" : nil
|
198
238
|
|
199
239
|
CLI::UI.raw do
|
200
|
-
print(prefix.chop)
|
201
|
-
puts
|
240
|
+
to.print(prefix.chop)
|
241
|
+
to.puts(frame_style.close(text.to_s, color: close_color, right_text: elapsed_string))
|
202
242
|
end
|
203
243
|
end
|
204
244
|
|
@@ -208,11 +248,12 @@ module CLI
|
|
208
248
|
#
|
209
249
|
# * +:color+ - The color of the prefix. Defaults to +Thread.current[:cliui_frame_color_override]+
|
210
250
|
#
|
251
|
+
sig { params(color: T.nilable(Colorable)).returns(String) }
|
211
252
|
def prefix(color: Thread.current[:cliui_frame_color_override])
|
212
253
|
+''.tap do |output|
|
213
254
|
items = FrameStack.items
|
214
255
|
|
215
|
-
items[0..-2].each do |item|
|
256
|
+
items[0..-2].to_a.each do |item|
|
216
257
|
output << item.color.code << item.frame_style.prefix
|
217
258
|
end
|
218
259
|
|
@@ -227,6 +268,7 @@ module CLI
|
|
227
268
|
end
|
228
269
|
|
229
270
|
# The width of a prefix given the number of Frames in the stack
|
271
|
+
sig { returns(Integer) }
|
230
272
|
def prefix_width
|
231
273
|
w = FrameStack.items.reduce(0) do |width, item|
|
232
274
|
width + item.frame_style.prefix_width
|
@@ -241,7 +283,12 @@ module CLI
|
|
241
283
|
#
|
242
284
|
# * +color+ - The color to override to
|
243
285
|
#
|
244
|
-
|
286
|
+
sig do
|
287
|
+
type_parameters(:T)
|
288
|
+
.params(color: Colorable, block: T.proc.returns(T.type_parameter(:T)))
|
289
|
+
.returns(T.type_parameter(:T))
|
290
|
+
end
|
291
|
+
def with_frame_color_override(color, &block)
|
245
292
|
prev = Thread.current[:cliui_frame_color_override]
|
246
293
|
Thread.current[:cliui_frame_color_override] = color
|
247
294
|
yield
|
@@ -254,13 +301,13 @@ module CLI
|
|
254
301
|
# If timing is:
|
255
302
|
# Numeric: return it
|
256
303
|
# false: return nil
|
257
|
-
# true
|
258
|
-
|
259
|
-
def
|
304
|
+
# true: defaults to Time.new
|
305
|
+
sig { params(start: Time, timing: T.any(Numeric, T::Boolean)).returns(T.nilable(Numeric)) }
|
306
|
+
def elapsed(start, timing)
|
260
307
|
return timing if timing.is_a?(Numeric)
|
261
308
|
return if timing.is_a?(FalseClass)
|
262
309
|
|
263
|
-
timing = Time.new
|
310
|
+
timing = Time.new
|
264
311
|
timing - start
|
265
312
|
end
|
266
313
|
end
|
data/lib/cli/ui/glyph.rb
CHANGED
@@ -1,14 +1,22 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
require 'cli/ui'
|
2
4
|
|
3
5
|
module CLI
|
4
6
|
module UI
|
5
7
|
class Glyph
|
8
|
+
extend T::Sig
|
9
|
+
|
6
10
|
class InvalidGlyphHandle < ArgumentError
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
sig { params(handle: String).void }
|
7
14
|
def initialize(handle)
|
8
15
|
super
|
9
16
|
@handle = handle
|
10
17
|
end
|
11
18
|
|
19
|
+
sig { returns(String) }
|
12
20
|
def message
|
13
21
|
keys = Glyph.available.join(',')
|
14
22
|
"invalid glyph handle: #{@handle} " \
|
@@ -16,7 +24,14 @@ module CLI
|
|
16
24
|
end
|
17
25
|
end
|
18
26
|
|
19
|
-
|
27
|
+
sig { returns(String) }
|
28
|
+
attr_reader :handle, :to_s, :fmt, :char
|
29
|
+
|
30
|
+
sig { returns(T.any(Integer, T::Array[Integer])) }
|
31
|
+
attr_reader :codepoint
|
32
|
+
|
33
|
+
sig { returns(Color) }
|
34
|
+
attr_reader :color
|
20
35
|
|
21
36
|
# Creates a new glyph
|
22
37
|
#
|
@@ -27,26 +42,18 @@ module CLI
|
|
27
42
|
# * +plain+ - A fallback plain string to be used in case glyphs are disabled
|
28
43
|
# * +color+ - What color to output the glyph. Check +CLI::UI::Color+ for options.
|
29
44
|
#
|
45
|
+
sig { params(handle: String, codepoint: T.any(Integer, T::Array[Integer]), plain: String, color: Color).void }
|
30
46
|
def initialize(handle, codepoint, plain, color)
|
31
47
|
@handle = handle
|
32
48
|
@codepoint = codepoint
|
33
49
|
@color = color
|
34
|
-
@
|
35
|
-
@
|
36
|
-
@
|
37
|
-
@fmt = "{{#{color.name}:#{char}}}"
|
50
|
+
@char = CLI::UI::OS.current.use_emoji? ? Array(codepoint).pack('U*') : plain
|
51
|
+
@to_s = color.code + @char + Color::RESET.code
|
52
|
+
@fmt = "{{#{color.name}:#{@char}}}"
|
38
53
|
|
39
54
|
MAP[handle] = self
|
40
55
|
end
|
41
56
|
|
42
|
-
# Fetches the actual character(s) to be displayed for a glyph, based on the current OS support
|
43
|
-
#
|
44
|
-
# ==== Returns
|
45
|
-
# Returns the glyph string
|
46
|
-
def char
|
47
|
-
CLI::UI::OS.current.supports_emoji? ? @char : @plain
|
48
|
-
end
|
49
|
-
|
50
57
|
# Mapping of glyphs to terminal output
|
51
58
|
MAP = {}
|
52
59
|
STAR = new('*', 0x2b51, '*', Color::YELLOW) # YELLOW SMALL STAR (⭑)
|
@@ -59,25 +66,31 @@ module CLI
|
|
59
66
|
HOURGLASS = new('H', [0x231b, 0xfe0e], 'H', Color::BLUE) # HOURGLASS + VARIATION SELECTOR 15 (⌛︎)
|
60
67
|
WARNING = new('!', [0x26a0, 0xfe0f], '!', Color::YELLOW) # WARNING SIGN + VARIATION SELECTOR 16 (⚠️ )
|
61
68
|
|
62
|
-
|
63
|
-
|
64
|
-
# ==== Raises
|
65
|
-
# Raises a InvalidGlyphHandle if the glyph is not available
|
66
|
-
# You likely need to create it with +.new+ or you made a typo
|
67
|
-
#
|
68
|
-
# ==== Returns
|
69
|
-
# Returns a terminal output-capable string
|
70
|
-
#
|
71
|
-
def self.lookup(name)
|
72
|
-
MAP.fetch(name.to_s)
|
73
|
-
rescue KeyError
|
74
|
-
raise InvalidGlyphHandle, name
|
75
|
-
end
|
69
|
+
class << self
|
70
|
+
extend T::Sig
|
76
71
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
72
|
+
# Looks up a glyph by name
|
73
|
+
#
|
74
|
+
# ==== Raises
|
75
|
+
# Raises a InvalidGlyphHandle if the glyph is not available
|
76
|
+
# You likely need to create it with +.new+ or you made a typo
|
77
|
+
#
|
78
|
+
# ==== Returns
|
79
|
+
# Returns a terminal output-capable string
|
80
|
+
#
|
81
|
+
sig { params(name: String).returns(Glyph) }
|
82
|
+
def lookup(name)
|
83
|
+
MAP.fetch(name.to_s)
|
84
|
+
rescue KeyError
|
85
|
+
raise InvalidGlyphHandle, name
|
86
|
+
end
|
87
|
+
|
88
|
+
# All available glyphs by name
|
89
|
+
#
|
90
|
+
sig { returns(T::Array[String]) }
|
91
|
+
def available
|
92
|
+
MAP.keys
|
93
|
+
end
|
81
94
|
end
|
82
95
|
end
|
83
96
|
end
|
data/lib/cli/ui/os.rb
CHANGED
@@ -1,67 +1,63 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
require 'rbconfig'
|
2
4
|
|
3
5
|
module CLI
|
4
6
|
module UI
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
else
|
15
|
-
if RUBY_PLATFORM !~ /cygwin/ && ENV['OS'] == 'Windows_NT'
|
16
|
-
Windows
|
17
|
-
else
|
18
|
-
raise "Could not determine OS from host_os #{RbConfig::CONFIG["host_os"]}"
|
19
|
-
end
|
20
|
-
end
|
7
|
+
class OS
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { params(emoji: T::Boolean, color_prompt: T::Boolean, arrow_keys: T::Boolean, shift_cursor: T::Boolean).void }
|
11
|
+
def initialize(emoji: true, color_prompt: true, arrow_keys: true, shift_cursor: false)
|
12
|
+
@emoji = emoji
|
13
|
+
@color_prompt = color_prompt
|
14
|
+
@arrow_keys = arrow_keys
|
15
|
+
@shift_cursor = shift_cursor
|
21
16
|
end
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
true
|
27
|
-
end
|
28
|
-
|
29
|
-
def supports_color_prompt?
|
30
|
-
true
|
31
|
-
end
|
32
|
-
|
33
|
-
def supports_arrow_keys?
|
34
|
-
true
|
35
|
-
end
|
36
|
-
|
37
|
-
def shift_cursor_on_line_reset?
|
38
|
-
false
|
39
|
-
end
|
40
|
-
end
|
18
|
+
sig { returns(T::Boolean) }
|
19
|
+
def use_emoji?
|
20
|
+
@emoji
|
41
21
|
end
|
42
22
|
|
43
|
-
|
23
|
+
sig { returns(T::Boolean) }
|
24
|
+
def use_color_prompt?
|
25
|
+
@color_prompt
|
44
26
|
end
|
45
27
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
end
|
28
|
+
sig { returns(T::Boolean) }
|
29
|
+
def suggest_arrow_keys?
|
30
|
+
@arrow_keys
|
31
|
+
end
|
51
32
|
|
52
|
-
|
53
|
-
|
54
|
-
|
33
|
+
sig { returns(T::Boolean) }
|
34
|
+
def shift_cursor_back_on_horizontal_absolute?
|
35
|
+
@shift_cursor
|
36
|
+
end
|
55
37
|
|
56
|
-
|
57
|
-
|
58
|
-
end
|
38
|
+
class << self
|
39
|
+
extend T::Sig
|
59
40
|
|
60
|
-
|
61
|
-
|
41
|
+
sig { returns(OS) }
|
42
|
+
def current
|
43
|
+
@current_os ||= case RbConfig::CONFIG['host_os']
|
44
|
+
when /darwin/
|
45
|
+
MAC
|
46
|
+
when /linux/
|
47
|
+
LINUX
|
48
|
+
else
|
49
|
+
if RUBY_PLATFORM !~ /cygwin/ && ENV['OS'] == 'Windows_NT'
|
50
|
+
WINDOWS
|
51
|
+
else
|
52
|
+
raise "Could not determine OS from host_os #{RbConfig::CONFIG["host_os"]}"
|
53
|
+
end
|
62
54
|
end
|
63
55
|
end
|
64
56
|
end
|
57
|
+
|
58
|
+
MAC = OS.new
|
59
|
+
LINUX = OS.new
|
60
|
+
WINDOWS = OS.new(emoji: false, color_prompt: false, arrow_keys: false, shift_cursor: true)
|
65
61
|
end
|
66
62
|
end
|
67
63
|
end
|
data/lib/cli/ui/printer.rb
CHANGED
@@ -1,58 +1,76 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
require 'cli/ui'
|
2
4
|
|
3
5
|
module CLI
|
4
6
|
module UI
|
5
7
|
class Printer
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
# ==== Attributes
|
11
|
-
#
|
12
|
-
# * +msg+ - (required) the string to output. Can be frozen.
|
13
|
-
#
|
14
|
-
# ==== Options
|
15
|
-
#
|
16
|
-
# * +:frame_color+ - Override the frame color. Defaults to nil.
|
17
|
-
# * +:to+ - Target stream, like $stdout or $stderr. Can be anything with a puts method. Defaults to $stdout.
|
18
|
-
# * +:encoding+ - Force the output to be in a certain encoding. Defaults to UTF-8.
|
19
|
-
# * +:format+ - Whether to format the string using CLI::UI.fmt. Defaults to true.
|
20
|
-
# * +:graceful+ - Whether to gracefully ignore common I/O errors. Defaults to true.
|
21
|
-
# * +:wrap+ - Whether to wrap text at word boundaries to terminal width. Defaults to true.
|
22
|
-
#
|
23
|
-
# ==== Returns
|
24
|
-
# Returns whether the message was successfully printed,
|
25
|
-
# which can be useful if +:graceful+ is set to true.
|
26
|
-
#
|
27
|
-
# ==== Example
|
28
|
-
#
|
29
|
-
# CLI::UI::Printer.puts('{{x}} Ouch', to: $stderr)
|
30
|
-
#
|
31
|
-
def self.puts(
|
32
|
-
msg,
|
33
|
-
frame_color:
|
34
|
-
nil,
|
35
|
-
to:
|
36
|
-
$stdout,
|
37
|
-
encoding: Encoding::UTF_8,
|
38
|
-
format: true,
|
39
|
-
graceful: true,
|
40
|
-
wrap: true
|
41
|
-
)
|
42
|
-
msg = (+msg).force_encoding(encoding) if encoding
|
43
|
-
msg = CLI::UI.fmt(msg) if format
|
44
|
-
msg = CLI::UI.wrap(msg) if wrap
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
class << self
|
11
|
+
extend T::Sig
|
45
12
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
13
|
+
# Print a message to a stream with common utilities.
|
14
|
+
# Allows overriding the color, encoding, and target stream.
|
15
|
+
# By default, it formats the string using CLI:UI and rescues common stream errors.
|
16
|
+
#
|
17
|
+
# ==== Attributes
|
18
|
+
#
|
19
|
+
# * +msg+ - (required) the string to output. Can be frozen.
|
20
|
+
#
|
21
|
+
# ==== Options
|
22
|
+
#
|
23
|
+
# * +:frame_color+ - Override the frame color. Defaults to nil.
|
24
|
+
# * +:to+ - Target stream, like $stdout or $stderr. Can be anything with a puts method. Defaults to $stdout.
|
25
|
+
# * +:encoding+ - Force the output to be in a certain encoding. Defaults to UTF-8.
|
26
|
+
# * +:format+ - Whether to format the string using CLI::UI.fmt. Defaults to true.
|
27
|
+
# * +:graceful+ - Whether to gracefully ignore common I/O errors. Defaults to true.
|
28
|
+
# * +:wrap+ - Whether to wrap text at word boundaries to terminal width. Defaults to true.
|
29
|
+
#
|
30
|
+
# ==== Returns
|
31
|
+
# Returns whether the message was successfully printed,
|
32
|
+
# which can be useful if +:graceful+ is set to true.
|
33
|
+
#
|
34
|
+
# ==== Example
|
35
|
+
#
|
36
|
+
# CLI::UI::Printer.puts('{{x}} Ouch', to: $stderr)
|
37
|
+
#
|
38
|
+
sig do
|
39
|
+
params(
|
40
|
+
msg: String,
|
41
|
+
frame_color: T.nilable(Colorable),
|
42
|
+
to: IOLike,
|
43
|
+
encoding: T.nilable(Encoding),
|
44
|
+
format: T::Boolean,
|
45
|
+
graceful: T::Boolean,
|
46
|
+
wrap: T::Boolean,
|
47
|
+
).returns(T::Boolean)
|
50
48
|
end
|
49
|
+
def puts(
|
50
|
+
msg,
|
51
|
+
frame_color: nil,
|
52
|
+
to: $stdout,
|
53
|
+
encoding: Encoding::UTF_8,
|
54
|
+
format: true,
|
55
|
+
graceful: true,
|
56
|
+
wrap: true
|
57
|
+
)
|
58
|
+
msg = (+msg).force_encoding(encoding) if encoding
|
59
|
+
msg = CLI::UI.fmt(msg) if format
|
60
|
+
msg = CLI::UI.wrap(msg) if wrap
|
61
|
+
|
62
|
+
if frame_color
|
63
|
+
CLI::UI::Frame.with_frame_color_override(frame_color) { to.puts(msg) }
|
64
|
+
else
|
65
|
+
to.puts(msg)
|
66
|
+
end
|
51
67
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
68
|
+
true
|
69
|
+
rescue Errno::EIO, Errno::EPIPE, IOError => e
|
70
|
+
raise(e) unless graceful
|
71
|
+
|
72
|
+
false
|
73
|
+
end
|
56
74
|
end
|
57
75
|
end
|
58
76
|
end
|