From 0ff727b64d61e9491187bf2c4a3a4c7bfe1384e7 Mon Sep 17 00:00:00 2001 From: Piotr Murach Date: Sat, 17 Oct 2020 13:19:10 +0200 Subject: [PATCH] Add log method for printing multiline text above a spinner (ref #36 & #43) --- lib/tty/spinner.rb | 45 ++++++++++++++++++++++++-------- spec/unit/log_spec.rb | 60 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 spec/unit/log_spec.rb diff --git a/lib/tty/spinner.rb b/lib/tty/spinner.rb index 0dbbb0b..b2a136f 100644 --- a/lib/tty/spinner.rb +++ b/lib/tty/spinner.rb @@ -350,23 +350,31 @@ def kill # # @api public def spin + return if done? + synchronize do - return if @done emit(:spin) - - if @hide_cursor && !spinning? - write(TTY::Cursor.hide) - end - - data = message.gsub(MATCHER, @frames[@current]) - data = replace_tokens(data) - write(data, true) + render @current = (@current + 1) % @length @state = :spinning - data end end + # Render spinner to the output + # + # @api private + def render + return if done? + + if @hide_cursor && !spinning? + write(TTY::Cursor.hide) + end + + data = message.gsub(MATCHER, @frames[@current]) + data = replace_tokens(data) + write(data, true) + end + # Redraw the indent for this spinner, if it exists # # @api private @@ -474,6 +482,23 @@ def update(tokens) end end + # Log text above the current spinner + # + # @param [String] text + # the message to log out + # + # @api public + def log(text) + synchronize do + cleared_text = text.to_s.lines.map do |line| + TTY::Cursor.clear_line + line + end.join + + write("#{cleared_text}#{"\n" unless cleared_text.end_with?("\n")}", false) + render + end + end + # Check if IO is attached to a terminal # # return [Boolean] diff --git a/spec/unit/log_spec.rb b/spec/unit/log_spec.rb new file mode 100644 index 0000000..eaab083 --- /dev/null +++ b/spec/unit/log_spec.rb @@ -0,0 +1,60 @@ +RSpec.describe TTY::Spinner, "#log" do + let(:output) { StringIO.new('', 'w+') } + + it "logs a message above a spinner" do + spinner = TTY::Spinner.new(output: output) + + 2.times { + spinner.log "foo\nbar" + spinner.spin + } + output.rewind + + expect(output.read).to eq([ + "\e[2K\e[1Gfoo\n", + "\e[2K\e[1Gbar\n", + "\e[1G|", + "\e[1G|", + "\e[2K\e[1Gfoo\n", + "\e[2K\e[1Gbar\n", + "\e[1G/", + "\e[1G/", + ].join) + end + + it "logs a message ending with a newline above a spinner" do + spinner = TTY::Spinner.new(output: output) + + 2.times { + spinner.log "foo\n" + spinner.spin + } + output.rewind + + expect(output.read).to eq([ + "\e[2K\e[1Gfoo\n", + "\e[1G|", + "\e[1G|", + "\e[2K\e[1Gfoo\n", + "\e[1G/", + "\e[1G/", + ].join) + end + + it "logs message under a spinner when done" do + spinner = TTY::Spinner.new(output: output) + 2.times { spinner.spin } + spinner.stop + + spinner.log "foo\nbar" + + output.rewind + expect(output.read).to eq([ + "\e[1G|", + "\e[1G/", + "\e[0m\e[2K\e[1G/\n", + "\e[2K\e[1Gfoo\n", + "\e[2K\e[1Gbar\n" + ].join) + end +end