Skip to content

Commit

Permalink
Upgrade TTY::Cursor for #save and #restore
Browse files Browse the repository at this point in the history
  • Loading branch information
austb committed Aug 2, 2017
1 parent dcf138d commit ab8ffb8
Show file tree
Hide file tree
Showing 6 changed files with 130 additions and 10 deletions.
26 changes: 26 additions & 0 deletions examples/multi_with_inset.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# encoding: utf-8

require 'tty-spinner'

spinners = TTY::Spinner::Multi.new(message: "[:spinner] Top level spinner")

sp1 = spinners.register "[:spinner] one"
sp2 = spinners.register "[:spinner] two"
sp3 = spinners.register "[:spinner] three"

spinners.auto_spin
sp1.auto_spin
sp2.auto_spin
sp3.auto_spin

sleep(2)

sp1.success
sleep 1
sp2.success

sleep 1

sp3.error

spinners.error
10 changes: 7 additions & 3 deletions lib/tty/spinner.rb
Original file line number Diff line number Diff line change
Expand Up @@ -272,7 +272,7 @@ def stop(stop_message = '')
end

write(data, true)
write("\n", false) unless @clear
write("\n", false) unless @clear || @multispinner
ensure
@state = :stopped
@done = true
Expand Down Expand Up @@ -354,7 +354,7 @@ def execute_on_line
@first_run = false
else
output.print TTY::Cursor.save
output.print TTY::Cursor.up lines_up
output.print TTY::Cursor.up(lines_up)
yield if block_given?
output.print TTY::Cursor.restore
end
Expand All @@ -372,7 +372,11 @@ def execute_on_line
def write(data, clear_first = false)
execute_on_line do
output.print(ECMA_CSI + '1' + ECMA_CHA) if clear_first
output.print(data)

# If there's a top level spinner, print with inset
characters_in = @multispinner.nil? ? "" : @multispinner.line_inset(self)

output.print(characters_in + data)
output.flush
end
end
Expand Down
29 changes: 29 additions & 0 deletions lib/tty/spinner/multi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,16 @@ class Multi
def_delegators :@spinners, :each, :empty?, :length

def initialize(options = {})
message = options.delete(:message)
@options = options

@create_spinner_lock = Mutex.new

@spinners = []
unless message.nil?
@top_level_spinner = register(message)
end

@callbacks = {
success: [],
error: [],
Expand All @@ -47,6 +52,18 @@ def register(pattern, options = {})
spinner
end

def top_level_spinner
raise "No top level spinner" if @top_level_spinner.nil?

@top_level_spinner
end

def auto_spin
raise "No top level spinner" if @top_level_spinner.nil?

@top_level_spinner.auto_spin
end

# Find relative offset position to which to move the current cursor
#
# The position is found among the registered spinners given the current
Expand All @@ -69,6 +86,16 @@ def count_line_offset(index)
end
end

# Find the number of characters to move into the line before printing the
# spinner
#
# @api public
def line_inset(spinner)
return " " if @top_level_spinner && spinner != @top_level_spinner

""
end

# Check if all spinners are done
#
# @return [Boolean]
Expand Down Expand Up @@ -108,6 +135,7 @@ def stop
#
# @api public
def success
@top_level_spinner.success if @top_level_spinner
@spinners.dup.each(&:success)
emit :success
end
Expand All @@ -116,6 +144,7 @@ def success
#
# @api public
def error
@top_level_spinner.error if @top_level_spinner
@spinners.dup.each(&:error)
emit :error
end
Expand Down
57 changes: 57 additions & 0 deletions spec/unit/multi/top_level_spinner_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# coding: utf-8
#

RSpec.describe TTY::Spinner::Multi, '#line_inset' do
let(:output) { StringIO.new('', 'w+') }

it "returns the empty string when there's no top level spinner" do
spinners = TTY::Spinner::Multi.new(output: output)
allow_any_instance_of(TTY::Spinner).to receive(:add_multispinner)

spinner = spinners.register ""

expect(spinners.line_inset(spinner)).to eq('')
end

it "returns the empty string for the top level spinner" do
spinners = TTY::Spinner::Multi.new(output: output, message: "Top level spinner")
allow_any_instance_of(TTY::Spinner).to receive(:add_multispinner)

spinners.register ""

expect(spinners.line_inset(spinners.top_level_spinner)).to eq('')
end

it "returns four spaces when there is a top level spinner" do
spinners = TTY::Spinner::Multi.new(output: output, message: "Top level spinner")
allow_any_instance_of(TTY::Spinner).to receive(:add_multispinner)

spinner = spinners.register ""

expect(spinners.line_inset(spinner)).to eq(' ')
end
end

RSpec.describe TTY::Spinner::Multi, '#auto_spin' do
let(:output) { StringIO.new('', 'w+') }

it "raises and exception when #auto_spin is called without a top level spinner" do
spinners = TTY::Spinner::Multi.new(output: output)
allow_any_instance_of(TTY::Spinner).to receive(:add_multispinner)

spinners.register ""

expect { spinners.auto_spin }.to raise_exception
end

it "doesn't raise exception" do
spinners = TTY::Spinner::Multi.new(output: output, message: "Top level spinner")
allow_any_instance_of(TTY::Spinner).to receive(:add_multispinner)
allow_any_instance_of(TTY::Spinner).to receive(:auto_spin)

spinners.register ""

expect { spinners.auto_spin }.not_to raise_exception
end

end
16 changes: 10 additions & 6 deletions spec/unit/spin_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,17 @@
it "spins with newline when it has a MultiSpinner" do
multi_spinner = double("MultiSpinner")
allow(multi_spinner).to receive(:count_line_offset).and_return(1, 1, 2, 1)
allow(multi_spinner).to receive(:line_inset).and_return("")

spinner = TTY::Spinner.new(output: output, interval: 100)
spinner.add_multispinner(multi_spinner, 0)

spinner2 = TTY::Spinner.new(output: output, interval: 100)
spinner2.add_multispinner(multi_spinner, 1)

save = Gem.win_platform? ? "\e[s" : "\e7"
restore = Gem.win_platform? ? "\e[u" : "\e8"

spinner.spin
spinner2.spin
output.rewind
Expand All @@ -66,25 +70,25 @@
expect(output.read).to eq([
"\e[1G|\n",
"\e[1G|\n",
"\e[s", # save position
save,
"\e[2A", # up 2 lines
"\e[1G/",
"\e[u" # restore position
restore
].join)

spinner2.spin
output.rewind
expect(output.read).to eq([
"\e[1G|\n",
"\e[1G|\n",
"\e[s", # save position
save,
"\e[2A", # up 2 lines
"\e[1G/",
"\e[u", # restore position
"\e[s", # save position
restore,
save,
"\e[1A", # up 1 line
"\e[1G/",
"\e[u" # restore position
restore
].join)

end
Expand Down
2 changes: 1 addition & 1 deletion tty-spinner.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Gem::Specification.new do |spec|
spec.files = Dir['lib/**/*.rb', 'LICENSE.txt', 'README.md']
spec.require_paths = ['lib']

spec.add_runtime_dependency 'tty-cursor'
spec.add_runtime_dependency 'tty-cursor', '>= 0.5.0'
spec.add_development_dependency 'bundler', '>= 1.5.0', '< 2.0'
spec.add_development_dependency 'rake'
end

0 comments on commit ab8ffb8

Please sign in to comment.