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 "ruby-macho"
|
||||||
gem "sorbet-runtime"
|
gem "sorbet-runtime"
|
||||||
gem "warning"
|
gem "warning"
|
||||||
gem "whirly"
|
|
||||||
|
|||||||
@ -80,10 +80,36 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
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 }
|
sig { override.void }
|
||||||
def run
|
def run
|
||||||
require "whirly"
|
|
||||||
|
|
||||||
Formulary.enable_factory_cache!
|
Formulary.enable_factory_cache!
|
||||||
|
|
||||||
bucket = if args.deps?
|
bucket = if args.deps?
|
||||||
@ -194,25 +220,84 @@ module Homebrew
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
downloads.each do |downloadable, promise|
|
if concurrency == 1
|
||||||
message = "#{downloadable.download_type.capitalize} #{downloadable.name}"
|
downloads.each_value do |promise|
|
||||||
if concurrency > 1
|
|
||||||
Whirly.start spinner: "arc", status: message
|
|
||||||
else
|
|
||||||
puts message
|
|
||||||
end
|
|
||||||
|
|
||||||
promise.wait!
|
promise.wait!
|
||||||
|
|
||||||
Whirly.configure stop: "#{Tty.green}✔︎#{Tty.reset}"
|
|
||||||
Whirly.stop if args.concurrency
|
|
||||||
rescue ChecksumMismatchError => e
|
rescue ChecksumMismatchError => e
|
||||||
Whirly.configure stop: "#{Tty.red}✘#{Tty.reset}"
|
|
||||||
Whirly.stop if args.concurrency
|
|
||||||
|
|
||||||
opoo "#{downloadable.download_type.capitalize} reports different checksum: #{e.expected}"
|
opoo "#{downloadable.download_type.capitalize} reports different checksum: #{e.expected}"
|
||||||
Homebrew.failed = true if downloadable.is_a?(Resource::Patch)
|
Homebrew.failed = true if downloadable.is_a?(Resource::Patch)
|
||||||
end
|
end
|
||||||
|
else
|
||||||
|
|
||||||
|
spinner = Spinner.new
|
||||||
|
|
||||||
|
remaining_downloads = downloads.dup
|
||||||
|
|
||||||
|
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
|
download_queue.shutdown
|
||||||
end
|
end
|
||||||
|
|||||||
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/, "")
|
string.gsub(/\033\[\d+(;\d+)*m/, "")
|
||||||
end
|
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) }
|
sig { returns(Integer) }
|
||||||
def width
|
def width
|
||||||
@width ||= begin
|
@width ||= begin
|
||||||
_, width = `/bin/stty size 2>/dev/null`.split
|
_, width = size
|
||||||
width, = `/usr/bin/tput cols 2>/dev/null`.split if width.to_i.zero?
|
width, = `/usr/bin/tput cols 2>/dev/null`.split if width.zero?
|
||||||
width ||= 80
|
width ||= 80
|
||||||
width.to_i
|
width.to_i
|
||||||
end
|
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