lib/cli/ui/spinner/spin_group.rb in cli-ui-2.1.0 vs lib/cli/ui/spinner/spin_group.rb in cli-ui-2.2.0
- old
+ new
@@ -2,10 +2,45 @@
module CLI
module UI
module Spinner
class SpinGroup
+ DEFAULT_FINAL_GLYPH = ->(success) { success ? CLI::UI::Glyph::CHECK.to_s : CLI::UI::Glyph::X.to_s }
+
+ class << self
+ extend T::Sig
+
+ sig { returns(Mutex) }
+ attr_reader :pause_mutex
+
+ sig { returns(T::Boolean) }
+ def paused?
+ @paused
+ end
+
+ sig do
+ type_parameters(:T)
+ .params(block: T.proc.returns(T.type_parameter(:T)))
+ .returns(T.type_parameter(:T))
+ end
+ def pause_spinners(&block)
+ previous_paused = T.let(nil, T.nilable(T::Boolean))
+ @pause_mutex.synchronize do
+ previous_paused = @paused
+ @paused = true
+ end
+ block.call
+ ensure
+ @pause_mutex.synchronize do
+ @paused = previous_paused
+ end
+ end
+ end
+
+ @pause_mutex = Mutex.new
+ @paused = false
+
extend T::Sig
# Initializes a new spin group
# This lets you add +Task+ objects to the group to multi-thread work
#
@@ -55,16 +90,27 @@
# ==== Attributes
#
# * +title+ - Title of the task
# * +block+ - Block for the task, will be provided with an instance of the spinner
#
- sig { params(title: String, block: T.proc.params(task: Task).returns(T.untyped)).void }
- def initialize(title, &block)
+ sig do
+ params(
+ title: String,
+ final_glyph: T.proc.params(success: T::Boolean).returns(String),
+ merged_output: T::Boolean,
+ duplicate_output_to: IO,
+ block: T.proc.params(task: Task).returns(T.untyped),
+ ).void
+ end
+ def initialize(title, final_glyph:, merged_output:, duplicate_output_to:, &block)
@title = title
+ @final_glyph = final_glyph
@always_full_render = title =~ Formatter::SCAN_WIDGET
@thread = Thread.new do
- cap = CLI::UI::StdoutRouter::Capture.new(with_frame_inset: false) { block.call(self) }
+ cap = CLI::UI::StdoutRouter::Capture.new(
+ merged_output: merged_output, duplicate_output_to: duplicate_output_to,
+ ) { block.call(self) }
begin
cap.run
ensure
@stdout = cap.stdout
@stderr = cap.stderr
@@ -171,11 +217,11 @@
end
sig { params(index: Integer).returns(String) }
def glyph(index)
if @done
- @success ? CLI::UI::Glyph::CHECK.to_s : CLI::UI::Glyph::X.to_s
+ @final_glyph.call(@success)
else
GLYPHS[index]
end
end
@@ -200,14 +246,34 @@
# ==== Example Usage:
# spin_group = CLI::UI::SpinGroup.new
# spin_group.add('Title') { |spinner| sleep 1.0 }
# spin_group.wait
#
- sig { params(title: String, block: T.proc.params(task: Task).void).void }
- def add(title, &block)
+ sig do
+ params(
+ title: String,
+ final_glyph: T.proc.params(success: T::Boolean).returns(String),
+ merged_output: T::Boolean,
+ duplicate_output_to: IO,
+ block: T.proc.params(task: Task).void,
+ ).void
+ end
+ def add(
+ title,
+ final_glyph: DEFAULT_FINAL_GLYPH,
+ merged_output: false,
+ duplicate_output_to: File.new(File::NULL, 'w'),
+ &block
+ )
@m.synchronize do
- @tasks << Task.new(title, &block)
+ @tasks << Task.new(
+ title,
+ final_glyph: final_glyph,
+ merged_output: merged_output,
+ duplicate_output_to: duplicate_output_to,
+ &block
+ )
end
end
# Tells the group you're done adding tasks and to wait for all of them to finish
#
@@ -219,35 +285,39 @@
sig { returns(T::Boolean) }
def wait
idx = 0
loop do
- all_done = T.let(true, T::Boolean)
+ done_count = 0
width = CLI::UI::Terminal.width
- @m.synchronize do
- CLI::UI.raw do
- @tasks.each.with_index do |task, int_index|
- nat_index = int_index + 1
- task_done = task.check
- all_done = false unless task_done
+ self.class.pause_mutex.synchronize do
+ next if self.class.paused?
- if nat_index > @consumed_lines
- print(task.render(idx, true, width: width) + "\n")
- @consumed_lines += 1
- else
- offset = @consumed_lines - int_index
- move_to = CLI::UI::ANSI.cursor_up(offset) + "\r"
- move_from = "\r" + CLI::UI::ANSI.cursor_down(offset)
+ @m.synchronize do
+ CLI::UI.raw do
+ @tasks.each.with_index do |task, int_index|
+ nat_index = int_index + 1
+ task_done = task.check
+ done_count += 1 if task_done
- print(move_to + task.render(idx, idx.zero?, width: width) + move_from)
+ if nat_index > @consumed_lines
+ print(task.render(idx, true, width: width) + "\n")
+ @consumed_lines += 1
+ else
+ offset = @consumed_lines - int_index
+ move_to = CLI::UI::ANSI.cursor_up(offset) + "\r"
+ move_from = "\r" + CLI::UI::ANSI.cursor_down(offset)
+
+ print(move_to + task.render(idx, idx.zero?, width: width) + move_from)
+ end
end
end
end
end
- break if all_done
+ break if done_count == @tasks.size
idx = (idx + 1) % GLYPHS.size
Spinner.index = idx
sleep(PERIOD)
end