cli-ui 2.2.3 → 2.3.1
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 +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 +33 -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 +5 -3
@@ -0,0 +1,146 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module CLI
|
5
|
+
module UI
|
6
|
+
class WorkQueue
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
class Future
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { void }
|
13
|
+
def initialize
|
14
|
+
@mutex = T.let(Mutex.new, Mutex)
|
15
|
+
@condition = T.let(ConditionVariable.new, ConditionVariable)
|
16
|
+
@completed = T.let(false, T::Boolean)
|
17
|
+
@started = T.let(false, T::Boolean)
|
18
|
+
@result = T.let(nil, T.untyped)
|
19
|
+
@error = T.let(nil, T.nilable(Exception))
|
20
|
+
end
|
21
|
+
|
22
|
+
sig { params(result: T.untyped).void }
|
23
|
+
def complete(result)
|
24
|
+
@mutex.synchronize do
|
25
|
+
@completed = true
|
26
|
+
@result = result
|
27
|
+
@condition.broadcast
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
sig { params(error: Exception).void }
|
32
|
+
def fail(error)
|
33
|
+
@mutex.synchronize do
|
34
|
+
return if @completed
|
35
|
+
|
36
|
+
@completed = true
|
37
|
+
@error = error
|
38
|
+
@condition.broadcast
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
sig { returns(T.untyped) }
|
43
|
+
def value
|
44
|
+
@mutex.synchronize do
|
45
|
+
@condition.wait(@mutex) until @completed
|
46
|
+
raise @error if @error
|
47
|
+
|
48
|
+
@result
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
sig { returns(T::Boolean) }
|
53
|
+
def completed?
|
54
|
+
@mutex.synchronize { @completed }
|
55
|
+
end
|
56
|
+
|
57
|
+
sig { returns(T::Boolean) }
|
58
|
+
def started?
|
59
|
+
@mutex.synchronize { @started }
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { void }
|
63
|
+
def start
|
64
|
+
@mutex.synchronize do
|
65
|
+
@started = true
|
66
|
+
@condition.broadcast
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
sig { params(max_concurrent: Integer).void }
|
72
|
+
def initialize(max_concurrent)
|
73
|
+
@max_concurrent = max_concurrent
|
74
|
+
@queue = T.let(Queue.new, Queue)
|
75
|
+
@mutex = T.let(Mutex.new, Mutex)
|
76
|
+
@condition = T.let(ConditionVariable.new, ConditionVariable)
|
77
|
+
@workers = T.let([], T::Array[Thread])
|
78
|
+
end
|
79
|
+
|
80
|
+
sig { params(block: T.proc.returns(T.untyped)).returns(Future) }
|
81
|
+
def enqueue(&block)
|
82
|
+
future = Future.new
|
83
|
+
@mutex.synchronize do
|
84
|
+
start_worker if @workers.size < @max_concurrent
|
85
|
+
end
|
86
|
+
@queue.push([future, block])
|
87
|
+
future
|
88
|
+
end
|
89
|
+
|
90
|
+
sig { void }
|
91
|
+
def close
|
92
|
+
@queue.close
|
93
|
+
end
|
94
|
+
|
95
|
+
sig { void }
|
96
|
+
def wait
|
97
|
+
@queue.close
|
98
|
+
@workers.each(&:join)
|
99
|
+
end
|
100
|
+
|
101
|
+
sig { void }
|
102
|
+
def interrupt
|
103
|
+
@mutex.synchronize do
|
104
|
+
@queue.close
|
105
|
+
# Fail any remaining tasks in the queue
|
106
|
+
until @queue.empty?
|
107
|
+
future, _block = @queue.pop(true)
|
108
|
+
future&.fail(Interrupt.new)
|
109
|
+
end
|
110
|
+
# Interrupt all worker threads
|
111
|
+
@workers.each { |worker| worker.raise(Interrupt) if worker.alive? }
|
112
|
+
@workers.each(&:join)
|
113
|
+
@workers.clear
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
sig { void }
|
120
|
+
def start_worker
|
121
|
+
@workers << Thread.new do
|
122
|
+
loop do
|
123
|
+
work = @queue.pop
|
124
|
+
break if work.nil?
|
125
|
+
|
126
|
+
future, block = work
|
127
|
+
|
128
|
+
begin
|
129
|
+
future.start
|
130
|
+
result = block.call
|
131
|
+
future.complete(result)
|
132
|
+
rescue Interrupt => e
|
133
|
+
future.fail(e)
|
134
|
+
raise # Always re-raise interrupts to terminate the worker
|
135
|
+
rescue StandardError => e
|
136
|
+
future.fail(e)
|
137
|
+
# Don't re-raise standard errors - allow worker to continue
|
138
|
+
end
|
139
|
+
end
|
140
|
+
rescue Interrupt
|
141
|
+
# Clean exit on interrupt
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
data/lib/cli/ui/wrap.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
|
3
2
|
# typed: true
|
3
|
+
# frozen_string_literal: true
|
4
4
|
|
5
5
|
require 'cli/ui'
|
6
6
|
require 'cli/ui/frame/frame_stack'
|
@@ -16,9 +16,9 @@ module CLI
|
|
16
16
|
@input = input
|
17
17
|
end
|
18
18
|
|
19
|
-
sig { returns(String) }
|
20
|
-
def wrap
|
21
|
-
max_width =
|
19
|
+
sig { params(total_width: Integer).returns(String) }
|
20
|
+
def wrap(total_width = Terminal.width)
|
21
|
+
max_width = total_width - Frame.prefix_width
|
22
22
|
width = T.let(0, Integer)
|
23
23
|
final = []
|
24
24
|
# Create an alternation of format codes of parameter lengths 1-20, since + and {1,n} not allowed in lookbehind
|
data/lib/cli/ui.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# typed: true
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
unless defined?(T)
|
4
5
|
require('cli/ui/sorbet_runtime_stub')
|
@@ -16,6 +17,7 @@ module CLI
|
|
16
17
|
autoload :Printer, 'cli/ui/printer'
|
17
18
|
autoload :Progress, 'cli/ui/progress'
|
18
19
|
autoload :Prompt, 'cli/ui/prompt'
|
20
|
+
autoload :Table, 'cli/ui/table'
|
19
21
|
autoload :Terminal, 'cli/ui/terminal'
|
20
22
|
autoload :Truncater, 'cli/ui/truncater'
|
21
23
|
autoload :Formatter, 'cli/ui/formatter'
|
@@ -145,10 +147,11 @@ module CLI
|
|
145
147
|
#
|
146
148
|
# * +input+ - input to format
|
147
149
|
# * +truncate_to+ - number of characters to truncate the string to (or nil)
|
150
|
+
# * +enable_color+ - should color be used? default to true unless output is redirected.
|
148
151
|
#
|
149
|
-
sig { params(input: String, truncate_to: T.nilable(Integer)).returns(String) }
|
150
|
-
def resolve_text(input, truncate_to: nil)
|
151
|
-
formatted = CLI::UI::Formatter.new(input).format
|
152
|
+
sig { params(input: String, truncate_to: T.nilable(Integer), enable_color: T::Boolean).returns(String) }
|
153
|
+
def resolve_text(input, truncate_to: nil, enable_color: enable_color?)
|
154
|
+
formatted = CLI::UI::Formatter.new(input).format(enable_color: enable_color)
|
152
155
|
return formatted unless truncate_to
|
153
156
|
|
154
157
|
CLI::UI::Truncater.call(formatted, truncate_to)
|
@@ -231,6 +234,7 @@ module CLI
|
|
231
234
|
success_text: T.nilable(String),
|
232
235
|
timing: T.any(T::Boolean, Numeric),
|
233
236
|
frame_style: FrameStylable,
|
237
|
+
to: IOLike,
|
234
238
|
block: T.nilable(T.proc.returns(T.type_parameter(:T))),
|
235
239
|
).returns(T.nilable(T.type_parameter(:T)))
|
236
240
|
end
|
@@ -241,6 +245,7 @@ module CLI
|
|
241
245
|
success_text: nil,
|
242
246
|
timing: block_given?,
|
243
247
|
frame_style: Frame.frame_style,
|
248
|
+
to: $stdout,
|
244
249
|
&block
|
245
250
|
)
|
246
251
|
CLI::UI::Frame.open(
|
@@ -250,6 +255,7 @@ module CLI
|
|
250
255
|
success_text: success_text,
|
251
256
|
timing: timing,
|
252
257
|
frame_style: frame_style,
|
258
|
+
to: to,
|
253
259
|
&block
|
254
260
|
)
|
255
261
|
end
|
@@ -262,11 +268,15 @@ module CLI
|
|
262
268
|
# * +block+ - block for +Spinner.open+
|
263
269
|
#
|
264
270
|
sig do
|
265
|
-
params(
|
266
|
-
|
271
|
+
params(
|
272
|
+
title: String,
|
273
|
+
auto_debrief: T::Boolean,
|
274
|
+
to: IOLike,
|
275
|
+
block: T.proc.params(task: Spinner::SpinGroup::Task).void,
|
276
|
+
).returns(T::Boolean)
|
267
277
|
end
|
268
|
-
def spinner(title, auto_debrief: true, &block)
|
269
|
-
CLI::UI::Spinner.spin(title, auto_debrief: auto_debrief, &block)
|
278
|
+
def spinner(title, auto_debrief: true, to: $stdout, &block)
|
279
|
+
CLI::UI::Spinner.spin(title, auto_debrief: auto_debrief, to: to, &block)
|
270
280
|
end
|
271
281
|
|
272
282
|
# Convenience Method to override frame color using +CLI::UI::Frame.with_frame_color+
|
@@ -329,16 +339,16 @@ module CLI
|
|
329
339
|
Thread.current[:no_cliui_frame_inset] = prev
|
330
340
|
end
|
331
341
|
|
332
|
-
# Check whether colour is enabled in Formatter
|
333
|
-
# is enabled when STDOUT is a TTY; that is, when output
|
334
|
-
#
|
342
|
+
# Check whether colour is enabled in Formatter, Frame, and Spinner output.
|
343
|
+
# By default, colour is enabled when STDOUT is a TTY; that is, when output
|
344
|
+
# has not been directed to another program or to a file.
|
335
345
|
#
|
336
346
|
sig { returns(T::Boolean) }
|
337
347
|
def enable_color?
|
338
348
|
@enable_color
|
339
349
|
end
|
340
350
|
|
341
|
-
# Turn colour
|
351
|
+
# Turn colour in Formatter, Frame, and Spinner output on or off.
|
342
352
|
#
|
343
353
|
# ==== Attributes
|
344
354
|
#
|
@@ -349,6 +359,26 @@ module CLI
|
|
349
359
|
@enable_color = !!bool
|
350
360
|
end
|
351
361
|
|
362
|
+
# Check whether cursor control is enabled in Formatter, Frame, and Spinner output.
|
363
|
+
# By default, cursor control is enabled when STDOUT is a TTY; that is, when output
|
364
|
+
# has not been directed to another program or to a file.
|
365
|
+
#
|
366
|
+
sig { returns(T::Boolean) }
|
367
|
+
def enable_cursor?
|
368
|
+
@enable_cursor
|
369
|
+
end
|
370
|
+
|
371
|
+
# Turn cursor control in Formatter, Frame, and Spinner output on or off.
|
372
|
+
#
|
373
|
+
# ==== Attributes
|
374
|
+
#
|
375
|
+
# * +bool+ - true or false; enable or disable cursor control.
|
376
|
+
#
|
377
|
+
sig { params(bool: T::Boolean).void }
|
378
|
+
def enable_cursor=(bool)
|
379
|
+
@enable_cursor = !!bool
|
380
|
+
end
|
381
|
+
|
352
382
|
# Set the default frame style.
|
353
383
|
# Convenience method for setting the default frame style with +CLI::UI::Frame.frame_style=+
|
354
384
|
#
|
@@ -375,6 +405,9 @@ module CLI
|
|
375
405
|
end
|
376
406
|
|
377
407
|
self.enable_color = $stdout.tty?
|
408
|
+
|
409
|
+
# Shopify's CI system supports color, but not cursor control
|
410
|
+
self.enable_cursor = T.must($stdout.tty? && ENV['CI'].nil? && ENV['JOURNAL_STREAM'].nil?)
|
378
411
|
end
|
379
412
|
end
|
380
413
|
|
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: 2.
|
4
|
+
version: 2.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Burke Libbey
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: exe
|
12
12
|
cert_chain: []
|
13
|
-
date:
|
13
|
+
date: 2025-03-17 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: minitest
|
@@ -72,12 +72,14 @@ files:
|
|
72
72
|
- lib/cli/ui/spinner/async.rb
|
73
73
|
- lib/cli/ui/spinner/spin_group.rb
|
74
74
|
- lib/cli/ui/stdout_router.rb
|
75
|
+
- lib/cli/ui/table.rb
|
75
76
|
- lib/cli/ui/terminal.rb
|
76
77
|
- lib/cli/ui/truncater.rb
|
77
78
|
- lib/cli/ui/version.rb
|
78
79
|
- lib/cli/ui/widgets.rb
|
79
80
|
- lib/cli/ui/widgets/base.rb
|
80
81
|
- lib/cli/ui/widgets/status.rb
|
82
|
+
- lib/cli/ui/work_queue.rb
|
81
83
|
- lib/cli/ui/wrap.rb
|
82
84
|
- vendor/reentrant_mutex.rb
|
83
85
|
homepage: https://github.com/shopify/cli-ui
|
@@ -99,7 +101,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
101
|
- !ruby/object:Gem::Version
|
100
102
|
version: '0'
|
101
103
|
requirements: []
|
102
|
-
rubygems_version: 3.
|
104
|
+
rubygems_version: 3.5.9
|
103
105
|
signing_key:
|
104
106
|
specification_version: 4
|
105
107
|
summary: Terminal UI framework
|