# typed: true # frozen-string-literal: true require 'cli/ui' module CLI module UI module Spinner extend T::Sig autoload :Async, 'cli/ui/spinner/async' autoload :SpinGroup, 'cli/ui/spinner/spin_group' PERIOD = 0.1 # seconds TASK_FAILED = :task_failed RUNES = if CLI::UI::OS.current.use_emoji? ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'].freeze else ['\\', '|', '/', '-', '\\', '|', '/', '-'].freeze end colors = [CLI::UI::Color::CYAN.code] * (RUNES.size / 2).ceil + [CLI::UI::Color::MAGENTA.code] * (RUNES.size / 2).to_i GLYPHS = colors.zip(RUNES).map(&:join) class << self extend T::Sig sig { returns(T.nilable(Integer)) } attr_accessor(:index) # We use this from CLI::UI::Widgets::Status to render an additional # spinner next to the "working" element. While this global state looks # a bit repulsive at first, it's worth realizing that: # # * It's managed by the SpinGroup#wait method, not individual tasks; and # * It would be complete insanity to run two separate but concurrent SpinGroups. # # While it would be possible to stitch through some connection between # the SpinGroup and the Widgets included in its title, this is simpler # in practice and seems unlikely to cause issues in practice. sig { returns(String) } def current_rune RUNES[index || 0] end end # Adds a single spinner # Uses an interactive session to allow the user to pick an answer # Can use arrows, y/n, numbers (1/2), and vim bindings to control # # https://user-images.githubusercontent.com/3074765/33798295-d94fd822-dce3-11e7-819b-43e5502d490e.gif # # ==== Attributes # # * +title+ - Title of the spinner to use # # ==== Options # # * +:auto_debrief+ - Automatically debrief exceptions? Default to true # # ==== Block # # * *spinner+ - Instance of the spinner. Can call +update_title+ to update the user of changes # # ==== Example Usage: # # CLI::UI::Spinner.spin('Title') { sleep 1.0 } # sig do params(title: String, auto_debrief: T::Boolean, block: T.proc.params(task: SpinGroup::Task).void) .returns(T::Boolean) end def self.spin(title, auto_debrief: true, &block) sg = SpinGroup.new(auto_debrief: auto_debrief) sg.add(title, &block) sg.wait end end end end