Remove Whirly and show concurrent downloads.
This commit is contained in:
parent
3a51f55572
commit
6434533ff5
@ -84,4 +84,3 @@ gem "plist"
|
||||
gem "ruby-macho"
|
||||
gem "sorbet-runtime"
|
||||
gem "warning"
|
||||
gem "whirly"
|
||||
|
||||
@ -80,10 +80,36 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
class Spinner
|
||||
FRAMES = [
|
||||
"◜",
|
||||
"◠",
|
||||
"◝",
|
||||
"◞",
|
||||
"◡",
|
||||
"◟",
|
||||
].freeze
|
||||
|
||||
sig { void }
|
||||
def initialize
|
||||
@start = Time.now
|
||||
@i = 0
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def to_s
|
||||
now = Time.now
|
||||
if @start + 0.1 < now
|
||||
@start = now
|
||||
@i = (@i + 1) % FRAMES.count
|
||||
end
|
||||
|
||||
FRAMES.fetch(@i)
|
||||
end
|
||||
end
|
||||
|
||||
sig { override.void }
|
||||
def run
|
||||
require "whirly"
|
||||
|
||||
Formulary.enable_factory_cache!
|
||||
|
||||
bucket = if args.deps?
|
||||
@ -194,24 +220,83 @@ module Homebrew
|
||||
end
|
||||
end
|
||||
|
||||
downloads.each do |downloadable, promise|
|
||||
message = "#{downloadable.download_type.capitalize} #{downloadable.name}"
|
||||
if concurrency > 1
|
||||
Whirly.start spinner: "arc", status: message
|
||||
else
|
||||
puts message
|
||||
if concurrency == 1
|
||||
downloads.each_value do |promise|
|
||||
promise.wait!
|
||||
rescue ChecksumMismatchError => e
|
||||
opoo "#{downloadable.download_type.capitalize} reports different checksum: #{e.expected}"
|
||||
Homebrew.failed = true if downloadable.is_a?(Resource::Patch)
|
||||
end
|
||||
else
|
||||
|
||||
promise.wait!
|
||||
spinner = Spinner.new
|
||||
|
||||
Whirly.configure stop: "#{Tty.green}✔︎#{Tty.reset}"
|
||||
Whirly.stop if args.concurrency
|
||||
rescue ChecksumMismatchError => e
|
||||
Whirly.configure stop: "#{Tty.red}✘#{Tty.reset}"
|
||||
Whirly.stop if args.concurrency
|
||||
remaining_downloads = downloads.dup
|
||||
|
||||
opoo "#{downloadable.download_type.capitalize} reports different checksum: #{e.expected}"
|
||||
Homebrew.failed = true if downloadable.is_a?(Resource::Patch)
|
||||
previous_pending_line_count = 0
|
||||
|
||||
begin
|
||||
print Tty.hide_cursor
|
||||
|
||||
output_message = lambda do |downloadable, promise|
|
||||
status = case promise.state
|
||||
when :fulfilled
|
||||
"#{Tty.green}✔︎#{Tty.reset}"
|
||||
when :rejected
|
||||
"#{Tty.red}✘#{Tty.reset}"
|
||||
when :pending
|
||||
spinner
|
||||
else
|
||||
raise promise.state
|
||||
end
|
||||
|
||||
message = "#{downloadable.download_type.capitalize} #{downloadable.name}"
|
||||
puts "#{status} #{message}"
|
||||
|
||||
if promise.rejected? && (e = promise.reason).is_a?(ChecksumMismatchError)
|
||||
opoo "#{downloadable.download_type.capitalize} reports different checksum: #{e.expected}"
|
||||
Homebrew.failed = true if downloadable.is_a?(Resource::Patch)
|
||||
next 2
|
||||
end
|
||||
|
||||
1
|
||||
end
|
||||
|
||||
until remaining_downloads.empty?
|
||||
begin
|
||||
finished_downloads = {}
|
||||
|
||||
finished_states = [:fulfilled, :rejected]
|
||||
remaining_downloads.each do |downloadable, promise|
|
||||
break unless finished_states.include?(promise.state)
|
||||
|
||||
finished_downloads[downloadable] = remaining_downloads.delete(downloadable)
|
||||
end
|
||||
|
||||
finished_downloads.each do |downloadable, promise|
|
||||
previous_pending_line_count -= 1
|
||||
output_message.call(downloadable, promise)
|
||||
end
|
||||
|
||||
previous_pending_line_count = 0
|
||||
remaining_downloads.each do |downloadable, promise|
|
||||
break if previous_pending_line_count >= (Tty.height - 1)
|
||||
|
||||
previous_pending_line_count += output_message.call(downloadable, promise)
|
||||
end
|
||||
|
||||
if previous_pending_line_count.positive?
|
||||
$stdout.print "\033[#{previous_pending_line_count}A"
|
||||
$stdout.flush
|
||||
end
|
||||
rescue Interrupt
|
||||
print "\n" * previous_pending_line_count
|
||||
raise
|
||||
end
|
||||
end
|
||||
ensure
|
||||
print Tty.show_cursor
|
||||
end
|
||||
end
|
||||
|
||||
download_queue.shutdown
|
||||
|
||||
103
Library/Homebrew/sorbet/rbi/gems/whirly@0.3.0.rbi
generated
103
Library/Homebrew/sorbet/rbi/gems/whirly@0.3.0.rbi
generated
@ -1,103 +0,0 @@
|
||||
# typed: true
|
||||
|
||||
# DO NOT EDIT MANUALLY
|
||||
# This is an autogenerated file for types exported from the `whirly` gem.
|
||||
# Please instead update this file by running `bin/tapioca gem whirly`.
|
||||
|
||||
|
||||
# source://whirly//lib/whirly/version.rb#3
|
||||
module Whirly
|
||||
class << self
|
||||
# save options and preprocess, set defaults if value is still unknown
|
||||
#
|
||||
# source://whirly//lib/whirly.rb#114
|
||||
def configure(**options); end
|
||||
|
||||
# frames can be generated from enumerables or procs
|
||||
#
|
||||
# source://whirly//lib/whirly.rb#85
|
||||
def configure_frames(spinner); end
|
||||
|
||||
# set spinner directly or lookup
|
||||
#
|
||||
# source://whirly//lib/whirly.rb#55
|
||||
def configure_spinner(spinner_option); end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://whirly//lib/whirly.rb#49
|
||||
def configured?; end
|
||||
|
||||
# @return [Boolean]
|
||||
#
|
||||
# source://whirly//lib/whirly.rb#45
|
||||
def enabled?; end
|
||||
|
||||
# source://whirly//lib/whirly.rb#240
|
||||
def initialize_color; end
|
||||
|
||||
# source://whirly//lib/whirly.rb#249
|
||||
def next_color; end
|
||||
|
||||
# Returns the value of attribute options.
|
||||
#
|
||||
# source://whirly//lib/whirly.rb#43
|
||||
def options; end
|
||||
|
||||
# source://whirly//lib/whirly.rb#215
|
||||
def render(next_frame = T.unsafe(nil)); end
|
||||
|
||||
# source://whirly//lib/whirly.rb#225
|
||||
def render_prefix; end
|
||||
|
||||
# source://whirly//lib/whirly.rb#233
|
||||
def render_suffix; end
|
||||
|
||||
# source://whirly//lib/whirly.rb#194
|
||||
def reset; end
|
||||
|
||||
# source://whirly//lib/whirly.rb#132
|
||||
def start(**options); end
|
||||
|
||||
# Returns the value of attribute status.
|
||||
#
|
||||
# source://whirly//lib/whirly.rb#42
|
||||
def status; end
|
||||
|
||||
# Sets the attribute status
|
||||
#
|
||||
# @param value the value to set the attribute status to.
|
||||
#
|
||||
# source://whirly//lib/whirly.rb#42
|
||||
def status=(_arg0); end
|
||||
|
||||
# source://whirly//lib/whirly.rb#180
|
||||
def stop(stop_frame = T.unsafe(nil)); end
|
||||
|
||||
# - - -
|
||||
#
|
||||
# source://whirly//lib/whirly.rb#203
|
||||
def unrender; end
|
||||
end
|
||||
end
|
||||
|
||||
# source://whirly//lib/whirly.rb#14
|
||||
Whirly::CLI_COMMANDS = T.let(T.unsafe(nil), Hash)
|
||||
|
||||
# source://whirly//lib/whirly.rb#19
|
||||
Whirly::DEFAULT_OPTIONS = T.let(T.unsafe(nil), Hash)
|
||||
|
||||
# source://whirly//lib/whirly.rb#35
|
||||
Whirly::SOFT_DEFAULT_OPTIONS = T.let(T.unsafe(nil), Hash)
|
||||
|
||||
# source://whirly//lib/whirly/spinners.rb#2
|
||||
module Whirly::Spinners; end
|
||||
|
||||
# source://whirly//lib/whirly/spinners/cli.rb#5
|
||||
Whirly::Spinners::CLI = T.let(T.unsafe(nil), Hash)
|
||||
|
||||
# source://whirly//lib/whirly/spinners/whirly.rb#5
|
||||
Whirly::Spinners::WHIRLY = T.let(T.unsafe(nil), Hash)
|
||||
|
||||
# source://whirly//lib/whirly/version.rb#4
|
||||
Whirly::VERSION = T.let(T.unsafe(nil), String)
|
||||
@ -51,11 +51,36 @@ module Tty
|
||||
string.gsub(/\033\[\d+(;\d+)*m/, "")
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def hide_cursor
|
||||
"\033[?25l"
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def show_cursor
|
||||
"\033[?25h"
|
||||
end
|
||||
|
||||
sig { returns(T.nilable([Integer, Integer])) }
|
||||
def size
|
||||
`/bin/stty size 2>/dev/null`.split&.map(&:to_i)
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def height
|
||||
@height ||= begin
|
||||
height, = size
|
||||
height, = `/usr/bin/tput lines 2>/dev/null`.split if height.zero?
|
||||
height ||= 40
|
||||
height.to_i
|
||||
end
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def width
|
||||
@width ||= begin
|
||||
_, width = `/bin/stty size 2>/dev/null`.split
|
||||
width, = `/usr/bin/tput cols 2>/dev/null`.split if width.to_i.zero?
|
||||
_, width = size
|
||||
width, = `/usr/bin/tput cols 2>/dev/null`.split if width.zero?
|
||||
width ||= 80
|
||||
width.to_i
|
||||
end
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
Copyright (c) 2016 Jan Lelis, https://janlelis.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,274 +0,0 @@
|
||||
{
|
||||
"roman_numerals": {
|
||||
"interval": 90,
|
||||
"mode": "swing",
|
||||
"frames": [
|
||||
"Ⅰ",
|
||||
"Ⅱ",
|
||||
"Ⅲ",
|
||||
"Ⅳ",
|
||||
"Ⅴ",
|
||||
"Ⅵ",
|
||||
"Ⅶ",
|
||||
"Ⅷ",
|
||||
"Ⅸ",
|
||||
"Ⅹ"
|
||||
]
|
||||
},
|
||||
"double_mark": {
|
||||
"interval": 120,
|
||||
"mode": "random",
|
||||
"frames": [
|
||||
"⁇",
|
||||
"⁈",
|
||||
"⁉",
|
||||
"‼"
|
||||
]
|
||||
},
|
||||
"heart_exclamation": {
|
||||
"interval": 45,
|
||||
"frames": [
|
||||
"❢",
|
||||
"❣"
|
||||
]
|
||||
},
|
||||
"pencil": {
|
||||
"interval": 200,
|
||||
"frames": [
|
||||
"✏",
|
||||
"✎"
|
||||
]
|
||||
},
|
||||
"bars": {
|
||||
"interval": 80,
|
||||
"mode": "swing",
|
||||
"frames": [
|
||||
"𝍠",
|
||||
"𝍡",
|
||||
"𝍢",
|
||||
"𝍣",
|
||||
"𝍤"
|
||||
]
|
||||
},
|
||||
"dice": {
|
||||
"interval": 100,
|
||||
"mode": "random",
|
||||
"frames": [
|
||||
"⚀",
|
||||
"⚁",
|
||||
"⚂",
|
||||
"⚃",
|
||||
"⚄",
|
||||
"⚅"
|
||||
]
|
||||
},
|
||||
"hanoi": {
|
||||
"interval": 150,
|
||||
"mode": "swing",
|
||||
"frames": [
|
||||
"𝍥",
|
||||
"𝍦",
|
||||
"𝍧",
|
||||
"𝍨"
|
||||
]
|
||||
},
|
||||
"vertical_bars": {
|
||||
"interval": 80,
|
||||
"mode": "swing",
|
||||
"frames": [
|
||||
"𝍩",
|
||||
"𝍪",
|
||||
"𝍫",
|
||||
"𝍬",
|
||||
"𝍭"
|
||||
]
|
||||
},
|
||||
"whirly": {
|
||||
"interval": 200,
|
||||
"mode": "random",
|
||||
"frames": [
|
||||
"😀",
|
||||
"😁",
|
||||
"😂",
|
||||
"😃",
|
||||
"😄",
|
||||
"😅",
|
||||
"😆",
|
||||
"😇",
|
||||
"😈",
|
||||
"😉",
|
||||
"😊",
|
||||
"😋",
|
||||
"😌",
|
||||
"😍",
|
||||
"😎",
|
||||
"😏",
|
||||
"😐",
|
||||
"😑",
|
||||
"😒",
|
||||
"😓",
|
||||
"😔",
|
||||
"😕",
|
||||
"😖",
|
||||
"😗",
|
||||
"😘",
|
||||
"😙",
|
||||
"😚",
|
||||
"😛",
|
||||
"😜",
|
||||
"😝",
|
||||
"😞",
|
||||
"😟",
|
||||
"😠",
|
||||
"😡",
|
||||
"😢",
|
||||
"😣",
|
||||
"😤",
|
||||
"😥",
|
||||
"😦",
|
||||
"😧",
|
||||
"😨",
|
||||
"😩",
|
||||
"😪",
|
||||
"😫",
|
||||
"😬",
|
||||
"😭",
|
||||
"😮",
|
||||
"😯",
|
||||
"😰",
|
||||
"😱",
|
||||
"😲",
|
||||
"😳",
|
||||
"😴",
|
||||
"😵",
|
||||
"😶",
|
||||
"🙁",
|
||||
"🙂",
|
||||
"🙃",
|
||||
"🙄",
|
||||
"😷",
|
||||
"🤐",
|
||||
"🤑",
|
||||
"🤒",
|
||||
"🤓",
|
||||
"🤔",
|
||||
"🤕",
|
||||
"🤖",
|
||||
"🤗"
|
||||
]
|
||||
},
|
||||
"cat": {
|
||||
"interval": 200,
|
||||
"mode": "random",
|
||||
"frames": [
|
||||
"😸",
|
||||
"😹",
|
||||
"😺",
|
||||
"😻",
|
||||
"😼",
|
||||
"😽",
|
||||
"😾",
|
||||
"😿",
|
||||
"🙀"
|
||||
]
|
||||
},
|
||||
"card": {
|
||||
"interval": 90,
|
||||
"stop": "🂠",
|
||||
"frames": [
|
||||
"🃁", "🃂", "🃃", "🃄", "🃅", "🃆", "🃇", "🃈", "🃉", "🃊", "🃋", "🃌", "🃍", "🃎",
|
||||
"🂱", "🂲", "🂳", "🂴", "🂵", "🂶", "🂷", "🂸", "🂹", "🂺", "🂻", "🂼", "🂽", "🂾",
|
||||
"🂡", "🂢", "🂣", "🂤", "🂥", "🂦", "🂧", "🂨", "🂩", "🂪", "🂫", "🂬", "🂭", "🂮",
|
||||
"🃑", "🃒", "🃓", "🃔", "🃕", "🃖", "🃗", "🃘", "🃙", "🃚", "🃛", "🃜", "🃝", "🃞"
|
||||
]
|
||||
},
|
||||
"cloud": {
|
||||
"interval": 140,
|
||||
"frames": [
|
||||
"🌥",
|
||||
"🌦",
|
||||
"🌧",
|
||||
"🌨",
|
||||
"🌩",
|
||||
"🌪"
|
||||
]
|
||||
},
|
||||
"photo": {
|
||||
"interval": 200,
|
||||
"frames": [
|
||||
"📷",
|
||||
"📸"
|
||||
]
|
||||
},
|
||||
"banknote": {
|
||||
"interval": 100,
|
||||
"frames": [
|
||||
"💴",
|
||||
"💵",
|
||||
"💶",
|
||||
"💷"
|
||||
]
|
||||
},
|
||||
"white_square": {
|
||||
"interval": 100,
|
||||
"mode": "swing",
|
||||
"frames": [
|
||||
"🞓",
|
||||
"🞒",
|
||||
"🞑",
|
||||
"🞐",
|
||||
"🞏",
|
||||
"🞎",
|
||||
"🞔"
|
||||
]
|
||||
},
|
||||
"xberg": {
|
||||
"interval": 150,
|
||||
"mode": "random",
|
||||
"frames": [
|
||||
"⨯",
|
||||
"⛰",
|
||||
"⛰",
|
||||
"⛰",
|
||||
"⛰",
|
||||
"⛰",
|
||||
"⛰"
|
||||
]
|
||||
},
|
||||
"circled_letter": {
|
||||
"interval": 120,
|
||||
"mode": "random",
|
||||
"frames": [
|
||||
"Ⓐ", "Ⓑ", "Ⓒ", "Ⓓ", "Ⓔ", "Ⓕ", "Ⓖ", "Ⓗ", "Ⓘ",
|
||||
"Ⓙ", "Ⓚ", "Ⓛ", "Ⓜ", "Ⓝ", "Ⓞ", "Ⓟ", "Ⓠ", "Ⓡ",
|
||||
"Ⓢ", "Ⓣ", "Ⓤ", "Ⓥ", "Ⓦ", "Ⓧ", "Ⓨ", "Ⓩ"
|
||||
]
|
||||
},
|
||||
"circled_number": {
|
||||
"interval": 120,
|
||||
"mode": "random",
|
||||
"frames": [
|
||||
"①", "②", "③", "④", "⑤", "⑥", "⑦", "⑧", "⑨"
|
||||
]
|
||||
},
|
||||
"letter_with_parens": {
|
||||
"interval": 150,
|
||||
"mode": "random",
|
||||
"frames": [
|
||||
"🄐", "🄑", "🄒", "🄓", "🄔", "🄕", "🄖", "🄗", "🄘",
|
||||
"🄙", "🄚", "🄛", "🄜", "🄝", "🄞", "🄟", "🄠", "🄡",
|
||||
"🄢", "🄣", "🄤", "🄥", "🄦", "🄧", "🄨", "🄩"
|
||||
]
|
||||
},
|
||||
"starlike": {
|
||||
"interval": 120,
|
||||
"mode": "random",
|
||||
"frames": [
|
||||
"✩", "✪", "✫", "✬", "✭", "✮", "✯", "✰",
|
||||
"✱", "✲", "✳", "✴", "✵", "✶", "✷", "✸",
|
||||
"✹", "✺", "✻", "✼", "✽", "✾", "✿", "❀",
|
||||
"❁", "❂", "❃", "❄", "❅", "❆", "❇", "❈",
|
||||
"❉", "❊"
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -1,263 +0,0 @@
|
||||
require_relative "whirly/version"
|
||||
require_relative "whirly/spinners"
|
||||
|
||||
require "unicode/display_width"
|
||||
|
||||
begin
|
||||
require "paint"
|
||||
rescue LoadError
|
||||
end
|
||||
|
||||
module Whirly
|
||||
@configured = false
|
||||
|
||||
CLI_COMMANDS = {
|
||||
hide_cursor: "\x1b[?25l",
|
||||
show_cursor: "\x1b[?25h",
|
||||
}.freeze
|
||||
|
||||
DEFAULT_OPTIONS = {
|
||||
ambiguous_character_width: 1,
|
||||
ansi_escape_mode: "restore",
|
||||
append_newline: true,
|
||||
color: !!defined?(Paint),
|
||||
color_change_rate: 30,
|
||||
hide_cursor: true,
|
||||
non_tty: false,
|
||||
position: "normal",
|
||||
remove_after_stop: false,
|
||||
spinner: "whirly",
|
||||
spinner_packs: [:whirly, :cli],
|
||||
status: nil,
|
||||
stream: $stdout,
|
||||
}.freeze
|
||||
|
||||
SOFT_DEFAULT_OPTIONS = {
|
||||
interval: 100,
|
||||
mode: "linear",
|
||||
stop: nil,
|
||||
}.freeze
|
||||
|
||||
class << self
|
||||
attr_accessor :status
|
||||
attr_reader :options
|
||||
|
||||
def enabled?
|
||||
!!(defined?(@enabled) && @enabled)
|
||||
end
|
||||
|
||||
def configured?
|
||||
!!(@configured)
|
||||
end
|
||||
end
|
||||
|
||||
# set spinner directly or lookup
|
||||
def self.configure_spinner(spinner_option)
|
||||
case spinner_option
|
||||
when Hash
|
||||
spinner = spinner_option.dup
|
||||
when Enumerable
|
||||
spinner = { "frames" => spinner_option.dup }
|
||||
when Proc
|
||||
spinner = { "proc" => spinner_option.dup }
|
||||
else
|
||||
spinner = nil
|
||||
catch(:found){
|
||||
@options[:spinner_packs].each{ |spinner_pack|
|
||||
spinners = Whirly::Spinners.const_get(spinner_pack.to_s.upcase)
|
||||
if spinners[spinner_option]
|
||||
spinner = spinners[spinner_option].dup
|
||||
throw(:found)
|
||||
end
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
# validate spinner
|
||||
if !spinner || (!spinner["frames"] && !spinner["proc"])
|
||||
raise(ArgumentError, "Whirly: Invalid spinner given")
|
||||
end
|
||||
|
||||
spinner
|
||||
end
|
||||
|
||||
# frames can be generated from enumerables or procs
|
||||
def self.configure_frames(spinner)
|
||||
if spinner["frames"]
|
||||
case spinner["mode"]
|
||||
when "swing"
|
||||
frames = (spinner["frames"].to_a + spinner["frames"].to_a[1..-2].reverse).cycle
|
||||
when "random"
|
||||
frame_pool = spinner["frames"].to_a
|
||||
frames = ->(){ frame_pool.sample }
|
||||
when "reverse"
|
||||
frames = spinner["frames"].to_a.reverse.cycle
|
||||
else
|
||||
frames = spinner["frames"].cycle
|
||||
end
|
||||
elsif spinner["proc"]
|
||||
frames = spinner["proc"].dup
|
||||
else
|
||||
raise(ArgumentError, "Whirly: Invalid spinner given")
|
||||
end
|
||||
|
||||
if frames.is_a? Proc
|
||||
class << frames
|
||||
alias next call
|
||||
end
|
||||
end
|
||||
|
||||
frames
|
||||
end
|
||||
|
||||
# save options and preprocess, set defaults if value is still unknown
|
||||
def self.configure(**options)
|
||||
if !defined?(@configured) || !@configured || !defined?(@options) || !@options
|
||||
@options = DEFAULT_OPTIONS.dup
|
||||
@configured = true
|
||||
end
|
||||
|
||||
@options.merge!(options)
|
||||
|
||||
spinner = configure_spinner(@options[:spinner])
|
||||
spinner_overwrites = {}
|
||||
spinner_overwrites["mode"] = @options[:mode] if @options.key?(:mode)
|
||||
@frames = configure_frames(spinner.merge(spinner_overwrites))
|
||||
|
||||
@interval = (@options[:interval] || spinner["interval"] || SOFT_DEFAULT_OPTIONS[:interval]) * 0.001
|
||||
@stop = @options[:stop] || spinner["stop"]
|
||||
@status = @options[:status]
|
||||
end
|
||||
|
||||
def self.start(**options)
|
||||
# optionally overwrite configuration on start
|
||||
configure(**options)
|
||||
|
||||
# only enable once
|
||||
return false if defined?(@enabled) && @enabled
|
||||
|
||||
# set status to enabled
|
||||
@enabled = true
|
||||
|
||||
# only do something if we are on a real terminal (or forced)
|
||||
return false unless @options[:stream].tty? || @options[:non_tty]
|
||||
|
||||
# ensure cursor is visible after exit the program (only register for the very first time)
|
||||
if (!defined?(@at_exit_handler_registered) || !@at_exit_handler_registered) && @options[:hide_cursor]
|
||||
@at_exit_handler_registered = true
|
||||
stream = @options[:stream]
|
||||
at_exit{ stream.print CLI_COMMANDS[:show_cursor] }
|
||||
end
|
||||
|
||||
# init color
|
||||
initialize_color if @options[:color]
|
||||
|
||||
# hide cursor
|
||||
@options[:stream].print CLI_COMMANDS[:hide_cursor] if @options[:hide_cursor]
|
||||
|
||||
# start spinner loop
|
||||
@thread = Thread.new do
|
||||
@current_frame = nil
|
||||
while true # it's just a spinner, no exact timing here
|
||||
next_color if @color
|
||||
render
|
||||
sleep(@interval)
|
||||
end
|
||||
end
|
||||
|
||||
# idiomatic block syntax support
|
||||
if block_given?
|
||||
begin
|
||||
yield
|
||||
ensure
|
||||
Whirly.stop
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def self.stop(stop_frame = nil)
|
||||
return false unless @enabled
|
||||
@enabled = false
|
||||
return false unless @options[:stream].tty? || @options[:non_tty]
|
||||
|
||||
@thread.terminate if @thread
|
||||
render(stop_frame || @stop) if stop_frame || @stop
|
||||
unrender if @options[:remove_after_stop]
|
||||
@options[:stream].puts if @options[:append_newline]
|
||||
@options[:stream].print CLI_COMMANDS[:show_cursor] if @options[:hide_cursor]
|
||||
|
||||
true
|
||||
end
|
||||
|
||||
def self.reset
|
||||
at_exit_handler_registered = defined?(@at_exit_handler_registered) && @at_exit_handler_registered
|
||||
instance_variables.each{ |iv| remove_instance_variable(iv) }
|
||||
@at_exit_handler_registered = at_exit_handler_registered
|
||||
@configured = false
|
||||
end
|
||||
|
||||
# - - -
|
||||
|
||||
def self.unrender
|
||||
return unless @current_frame
|
||||
case @options[:ansi_escape_mode]
|
||||
when "restore"
|
||||
@options[:stream].print(render_prefix + (
|
||||
' ' * (Unicode::DisplayWidth.of(@current_frame, @options[:ambiguous_character_width]) + 1)
|
||||
) + render_suffix)
|
||||
when "line"
|
||||
@options[:stream].print "\e[1K"
|
||||
end
|
||||
end
|
||||
|
||||
def self.render(next_frame = nil)
|
||||
unrender
|
||||
|
||||
@current_frame = next_frame || @frames.next
|
||||
@current_frame = Paint[@current_frame, @color] if @options[:color]
|
||||
@current_frame += " #{@status}" if @status
|
||||
|
||||
@options[:stream].print(render_prefix + @current_frame.to_s + render_suffix)
|
||||
end
|
||||
|
||||
def self.render_prefix
|
||||
res = ""
|
||||
res << "\n" if @options[:position] == "below"
|
||||
res << "\e7" if @options[:ansi_escape_mode] == "restore"
|
||||
res << "\e[G" if @options[:ansi_escape_mode] == "line"
|
||||
res
|
||||
end
|
||||
|
||||
def self.render_suffix
|
||||
res = ""
|
||||
res << "\e8" if @options[:ansi_escape_mode] == "restore"
|
||||
res << "\e[1A" if @options[:position] == "below"
|
||||
res
|
||||
end
|
||||
|
||||
def self.initialize_color
|
||||
if !defined?(Paint)
|
||||
warn "Whirly warning: Using colors requires the paint gem"
|
||||
else
|
||||
@color = "%.6x" % rand(16777216)
|
||||
@color_directions = (0..2).map{ |e| rand(3) - 1 }
|
||||
end
|
||||
end
|
||||
|
||||
def self.next_color
|
||||
@color = @color.scan(/../).map.with_index{ |c, i|
|
||||
color_change = rand(@options[:color_change_rate]) * @color_directions[i]
|
||||
nc = c.to_i(16) + color_change
|
||||
if nc <= 0
|
||||
nc = 0
|
||||
@color_directions[i] = rand(3) - 1
|
||||
elsif nc >= 255
|
||||
nc = 255
|
||||
@color_directions[i] = rand(3) - 1
|
||||
end
|
||||
"%.2x" % nc
|
||||
}.join
|
||||
end
|
||||
end
|
||||
@ -1,7 +0,0 @@
|
||||
module Whirly
|
||||
module Spinners
|
||||
end
|
||||
end
|
||||
|
||||
require_relative "spinners/whirly"
|
||||
require_relative "spinners/cli"
|
||||
@ -1,7 +0,0 @@
|
||||
require "json"
|
||||
|
||||
module Whirly
|
||||
module Spinners
|
||||
CLI = JSON.load(File.read(File.dirname(__FILE__) + "/../../../data/cli-spinners.json")).freeze
|
||||
end
|
||||
end
|
||||
@ -1,15 +0,0 @@
|
||||
require "json"
|
||||
|
||||
module Whirly
|
||||
module Spinners
|
||||
WHIRLY = {
|
||||
"random_dots" => { "proc" => ->(){ [ 0x2800 + rand(256)].pack("U") }, "interval" => 100 },
|
||||
"mahjong" => { "proc" => ->(){ [0x1F000 + rand(44)].pack("U") }, "interval" => 200 },
|
||||
"domino" => { "proc" => ->(){ [0x1F030 + rand(50)].pack("U") }, "interval" => 200 },
|
||||
"vertical_domino" => { "proc" => ->(){ [0x1F062 + rand(50)].pack("U") }, "interval" => 200 }
|
||||
}
|
||||
WHIRLY.merge! JSON.load(File.read(File.dirname(__FILE__) + "/../../../data/whirly-static-spinners.json"))
|
||||
|
||||
WHIRLY.freeze
|
||||
end
|
||||
end
|
||||
@ -1,5 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Whirly
|
||||
VERSION = "0.3.0"
|
||||
end
|
||||
Loading…
x
Reference in New Issue
Block a user