Skip to content

Commit

Permalink
Add SpinGroup.puts_above
Browse files Browse the repository at this point in the history
This is useful for things like printing out a stream of logs while you
have one or more spinners reporting on the current status. We could also
add Spinner.puts_above, but when you have multiple Spinners, "above" is
kind of ambiguous. We can add it later if the need becomes obvious.
  • Loading branch information
joshheinrichs-shopify committed Dec 2, 2024
1 parent 4d3fcf8 commit 093815f
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 5 deletions.
10 changes: 10 additions & 0 deletions lib/cli/ui/ansi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,16 @@ def previous_lines(n = 1)
def clear_to_end_of_line
control('', 'K')
end

sig { returns(String) }
def insert_line
insert_lines(1)
end

sig { params(n: Integer).returns(String) }
def insert_lines(n = 1)
control(n.to_s, 'L')
end
end
end
end
Expand Down
36 changes: 31 additions & 5 deletions lib/cli/ui/spinner/spin_group.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ def pause_spinners(&block)
def initialize(auto_debrief: true, interrupt_debrief: false, max_concurrent: 0, work_queue: nil)
@m = Mutex.new
@tasks = []
@puts_above = []
@auto_debrief = auto_debrief
@interrupt_debrief = interrupt_debrief
@start = Time.new
Expand Down Expand Up @@ -377,6 +378,24 @@ def wait

@m.synchronize do
CLI::UI.raw do
force_full_render = false

unless @puts_above.empty?
print(CLI::UI::ANSI.cursor_up(consumed_lines)) if CLI::UI.enable_cursor?
while (message = @puts_above.shift)
print(CLI::UI::ANSI.insert_lines(message.lines.count)) if CLI::UI.enable_cursor?
message.lines.each do |line|
print(CLI::UI::Frame.prefix + CLI::UI.fmt(line))
end
print("\n")
end
# we descend with newlines rather than ANSI.cursor_down as the inserted lines may've
# pushed the spinner off the front of the buffer, so we can't move back down below it
print("\n" * consumed_lines) if CLI::UI.enable_cursor?

force_full_render = true
end

@tasks.each.with_index do |task, int_index|
nat_index = int_index + 1
task_done = task.check
Expand All @@ -391,7 +410,7 @@ def wait
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)
print(move_to + task.render(idx, idx.zero? || force_full_render, width: width) + move_from)
end
elsif !tasks_seen[int_index] || (task_done && !tasks_seen_done[int_index])
print(task.render(idx, true, width: width) + "\n")
Expand Down Expand Up @@ -422,6 +441,13 @@ def wait
stopped? ? false : raise
end

sig { params(message: String).void }
def puts_above(message)
@m.synchronize do
@puts_above << message
end
end

# Provide an alternative debriefing for failed tasks
sig do
params(
Expand Down Expand Up @@ -471,17 +497,17 @@ def debrief

CLI::UI::Frame.open('Task Failed: ' + title, color: :red, timing: Time.new - @start) do
if e
puts "#{e.class}: #{e.message}"
puts "\tfrom #{e.backtrace.join("\n\tfrom ")}"
$stdout.puts("#{e.class}: #{e.message}")
$stdout.puts("\tfrom #{e.backtrace.join("\n\tfrom ")}")
end

CLI::UI::Frame.divider('STDOUT')
out = '(empty)' if out.nil? || out.strip.empty?
puts out
$stdout.puts(out)

CLI::UI::Frame.divider('STDERR')
err = '(empty)' if err.nil? || err.strip.empty?
puts err
$stdout.puts(err)
end
end
@tasks.all?(&:success)
Expand Down

0 comments on commit 093815f

Please sign in to comment.