mirror of
https://github.com/Homebrew/brew.git
synced 2025-07-14 16:09:03 +08:00
Bump some utils/
files to Sorbet typed: strict
This commit is contained in:
parent
55475cb11a
commit
975a707b3c
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "context"
|
||||
@ -60,7 +60,7 @@ module Utils
|
||||
puts Utils.popen_read(curl, *args, url)
|
||||
else
|
||||
pid = spawn curl, *args, url
|
||||
Process.detach T.must(pid)
|
||||
Process.detach(pid)
|
||||
end
|
||||
end
|
||||
|
||||
@ -161,52 +161,62 @@ module Utils
|
||||
report_influx(:test_bot_test, tags, fields)
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def influx_message_displayed?
|
||||
config_true?(:influxanalyticsmessage)
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def messages_displayed?
|
||||
config_true?(:analyticsmessage) &&
|
||||
!!(config_true?(:analyticsmessage) &&
|
||||
config_true?(:caskanalyticsmessage) &&
|
||||
influx_message_displayed?
|
||||
influx_message_displayed?)
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def disabled?
|
||||
return true if Homebrew::EnvConfig.no_analytics?
|
||||
|
||||
config_true?(:analyticsdisabled)
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def not_this_run?
|
||||
ENV["HOMEBREW_NO_ANALYTICS_THIS_RUN"].present?
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
def no_message_output?
|
||||
# Used by Homebrew/install
|
||||
ENV["HOMEBREW_NO_ANALYTICS_MESSAGE_OUTPUT"].present?
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def messages_displayed!
|
||||
Homebrew::Settings.write :analyticsmessage, true
|
||||
Homebrew::Settings.write :caskanalyticsmessage, true
|
||||
Homebrew::Settings.write :influxanalyticsmessage, true
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def enable!
|
||||
Homebrew::Settings.write :analyticsdisabled, false
|
||||
delete_uuid!
|
||||
messages_displayed!
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def disable!
|
||||
Homebrew::Settings.write :analyticsdisabled, true
|
||||
delete_uuid!
|
||||
end
|
||||
|
||||
sig { void }
|
||||
def delete_uuid!
|
||||
Homebrew::Settings.delete :analyticsuuid
|
||||
end
|
||||
|
||||
sig { params(args: Homebrew::Cmd::Info::Args, filter: T.nilable(String)).void }
|
||||
def output(args:, filter: nil)
|
||||
require "api"
|
||||
|
||||
@ -244,6 +254,7 @@ module Utils
|
||||
table_output(category, days, results, os_version:, cask_install:)
|
||||
end
|
||||
|
||||
sig { params(json: T::Hash[String, T.untyped], args: Homebrew::Cmd::Info::Args).void }
|
||||
def output_analytics(json, args:)
|
||||
full_analytics = args.analytics? || verbose?
|
||||
|
||||
@ -273,6 +284,7 @@ module Utils
|
||||
# It relies on screen scraping some GitHub HTML that's not available as an API.
|
||||
# This seems very likely to break in the future.
|
||||
# That said, it's the only way to get the data we want right now.
|
||||
sig { params(formula: Formula, args: Homebrew::Cmd::Info::Args).void }
|
||||
def output_github_packages_downloads(formula, args:)
|
||||
return unless args.github_packages_downloads?
|
||||
return unless formula.core_formula?
|
||||
@ -316,6 +328,7 @@ module Utils
|
||||
puts "#{number_readable(thirty_day_download_count)} (30 days)"
|
||||
end
|
||||
|
||||
sig { params(formula: Formula, args: Homebrew::Cmd::Info::Args).void }
|
||||
def formula_output(formula, args:)
|
||||
return if Homebrew::EnvConfig.no_analytics? || Homebrew::EnvConfig.no_github_api?
|
||||
|
||||
@ -331,6 +344,7 @@ module Utils
|
||||
nil
|
||||
end
|
||||
|
||||
sig { params(cask: Cask::Cask, args: Homebrew::Cmd::Info::Args).void }
|
||||
def cask_output(cask, args:)
|
||||
return if Homebrew::EnvConfig.no_analytics? || Homebrew::EnvConfig.no_github_api?
|
||||
|
||||
@ -388,6 +402,12 @@ module Utils
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
category: String, days: String, results: T::Hash[String, Integer], os_version: T::Boolean,
|
||||
cask_install: T::Boolean
|
||||
).void
|
||||
}
|
||||
def table_output(category, days, results, os_version: false, cask_install: false)
|
||||
oh1 "#{category} (#{days} days)"
|
||||
total_count = results.values.inject("+")
|
||||
@ -475,14 +495,17 @@ module Utils
|
||||
"#{formatted_total_count_footer} | #{formatted_total_percent_footer}%"
|
||||
end
|
||||
|
||||
sig { params(key: Symbol).returns(T::Boolean) }
|
||||
def config_true?(key)
|
||||
Homebrew::Settings.read(key) == "true"
|
||||
end
|
||||
|
||||
sig { params(count: Integer).returns(String) }
|
||||
def format_count(count)
|
||||
count.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\\1,').reverse
|
||||
end
|
||||
|
||||
sig { params(percent: T.any(Integer, Float)).returns(String) }
|
||||
def format_percent(percent)
|
||||
format("%<percent>.2f", percent:)
|
||||
end
|
||||
|
@ -1,10 +1,11 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "fcntl"
|
||||
require "utils/socket"
|
||||
|
||||
module Utils
|
||||
sig { params(child_error: T::Hash[String, T.untyped]).returns(Exception) }
|
||||
def self.rewrite_child_error(child_error)
|
||||
inner_class = Object.const_get(child_error["json_class"])
|
||||
error = if child_error["cmd"] && inner_class == ErrorDuringExecution
|
||||
@ -33,7 +34,11 @@ module Utils
|
||||
# When using this function, remember to call `exec` as soon as reasonably possible.
|
||||
# This function does not protect against the pitfalls of what you can do pre-exec in a fork.
|
||||
# See `man fork` for more information.
|
||||
def self.safe_fork(directory: nil, yield_parent: false)
|
||||
sig {
|
||||
params(directory: T.nilable(String), yield_parent: T::Boolean,
|
||||
_blk: T.proc.params(arg0: T.nilable(String)).void).void
|
||||
}
|
||||
def self.safe_fork(directory: nil, yield_parent: false, &_blk)
|
||||
require "json/add/exception"
|
||||
|
||||
block = proc do |tmpdir|
|
||||
@ -80,8 +85,6 @@ module Utils
|
||||
exit!(true)
|
||||
end
|
||||
|
||||
pid = T.must(pid)
|
||||
|
||||
begin
|
||||
yield(nil) if yield_parent
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "utils/inreplace"
|
||||
@ -14,20 +14,20 @@ module PyPI
|
||||
class Package
|
||||
sig { params(package_string: String, is_url: T::Boolean, python_name: String).void }
|
||||
def initialize(package_string, is_url: false, python_name: "python")
|
||||
@pypi_info = nil
|
||||
@pypi_info = T.let(nil, T.nilable(T::Array[String]))
|
||||
@package_string = package_string
|
||||
@is_url = is_url
|
||||
@is_pypi_url = package_string.start_with? PYTHONHOSTED_URL_PREFIX
|
||||
@is_pypi_url = T.let(package_string.start_with?(PYTHONHOSTED_URL_PREFIX), T::Boolean)
|
||||
@python_name = python_name
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
sig { returns(T.nilable(String)) }
|
||||
def name
|
||||
basic_metadata if @name.blank?
|
||||
@name
|
||||
end
|
||||
|
||||
sig { returns(T::Array[T.nilable(String)]) }
|
||||
sig { returns(T.nilable(T::Array[String])) }
|
||||
def extras
|
||||
basic_metadata if @extras.blank?
|
||||
@extras
|
||||
@ -43,7 +43,7 @@ module PyPI
|
||||
def version=(new_version)
|
||||
raise ArgumentError, "can't update version for non-PyPI packages" unless valid_pypi_package?
|
||||
|
||||
@version = new_version
|
||||
@version = T.let(new_version, T.nilable(String))
|
||||
end
|
||||
|
||||
sig { returns(T::Boolean) }
|
||||
@ -97,8 +97,8 @@ module PyPI
|
||||
sig { returns(String) }
|
||||
def to_s
|
||||
if valid_pypi_package?
|
||||
out = name
|
||||
out += "[#{extras.join(",")}]" if extras.present?
|
||||
out = T.must(name)
|
||||
out += "[#{extras&.join(",")}]" if extras.present?
|
||||
out += "==#{version}" if version.present?
|
||||
out
|
||||
else
|
||||
@ -132,14 +132,15 @@ module PyPI
|
||||
private
|
||||
|
||||
# Returns [name, [extras], version] for this package.
|
||||
sig { returns(T.nilable(T.any(String, T::Array[String]))) }
|
||||
def basic_metadata
|
||||
if @is_pypi_url
|
||||
match = File.basename(@package_string).match(/^(.+)-([a-z\d.]+?)(?:.tar.gz|.zip)$/)
|
||||
raise ArgumentError, "Package should be a valid PyPI URL" if match.blank?
|
||||
|
||||
@name ||= PyPI.normalize_python_package match[1]
|
||||
@extras ||= []
|
||||
@version ||= match[2]
|
||||
@name ||= T.let(PyPI.normalize_python_package(T.must(match[1])), T.nilable(String))
|
||||
@extras ||= T.let([], T.nilable(T::Array[String]))
|
||||
@version ||= T.let(match[2], T.nilable(String))
|
||||
elsif @is_url
|
||||
ensure_formula_installed!(@python_name)
|
||||
|
||||
@ -162,9 +163,9 @@ module PyPI
|
||||
|
||||
metadata = JSON.parse(pip_output)["install"].first["metadata"]
|
||||
|
||||
@name ||= PyPI.normalize_python_package metadata["name"]
|
||||
@extras ||= []
|
||||
@version ||= metadata["version"]
|
||||
@name ||= T.let(PyPI.normalize_python_package(metadata["name"]), T.nilable(String))
|
||||
@extras ||= T.let([], T.nilable(T::Array[String]))
|
||||
@version ||= T.let(metadata["version"], T.nilable(String))
|
||||
else
|
||||
if @package_string.include? "=="
|
||||
name, version = @package_string.split("==")
|
||||
@ -180,7 +181,7 @@ module PyPI
|
||||
extras = []
|
||||
end
|
||||
|
||||
@name ||= PyPI.normalize_python_package name
|
||||
@name ||= T.let(PyPI.normalize_python_package(T.must(name)), T.nilable(String))
|
||||
@extras ||= extras
|
||||
@version ||= version
|
||||
end
|
||||
@ -248,7 +249,7 @@ module PyPI
|
||||
missing_msg = "formulae required to update \"#{formula.name}\" resources: #{missing_dependencies.join(", ")}"
|
||||
odie "Missing #{missing_msg}" unless install_dependencies
|
||||
ohai "Installing #{missing_msg}"
|
||||
missing_dependencies.each(&method(:ensure_formula_installed!))
|
||||
missing_dependencies.each(&:ensure_formula_installed!)
|
||||
end
|
||||
|
||||
python_deps = formula.deps
|
||||
@ -327,12 +328,12 @@ module PyPI
|
||||
# Resolve the dependency tree of all input packages
|
||||
show_info = !print_only && !silent
|
||||
ohai "Retrieving PyPI dependencies for \"#{input_packages.join(" ")}\"..." if show_info
|
||||
found_packages = pip_report(input_packages, python_name:, print_stderr: verbose && show_info)
|
||||
found_packages = pip_report(input_packages, python_name:, print_stderr: !!(verbose && show_info))
|
||||
# Resolve the dependency tree of excluded packages to prune the above
|
||||
exclude_packages.delete_if { |package| found_packages.exclude? package }
|
||||
ohai "Retrieving PyPI dependencies for excluded \"#{exclude_packages.join(" ")}\"..." if show_info
|
||||
exclude_packages = pip_report(exclude_packages, python_name:, print_stderr: verbose && show_info)
|
||||
exclude_packages += [Package.new(main_package.name)] unless main_package.nil?
|
||||
exclude_packages = pip_report(exclude_packages, python_name:, print_stderr: !!(verbose && show_info))
|
||||
exclude_packages += [Package.new(T.must(main_package.name))] unless main_package.nil?
|
||||
|
||||
new_resource_blocks = ""
|
||||
found_packages.sort.each do |package|
|
||||
@ -404,12 +405,18 @@ module PyPI
|
||||
true
|
||||
end
|
||||
|
||||
sig { params(name: String).returns(String) }
|
||||
def self.normalize_python_package(name)
|
||||
# This normalization is defined in the PyPA packaging specifications;
|
||||
# https://packaging.python.org/en/latest/specifications/name-normalization/#name-normalization
|
||||
name.gsub(/[-_.]+/, "-").downcase
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
packages: T::Array[Package], python_name: String, print_stderr: T::Boolean,
|
||||
).returns(T::Array[Package])
|
||||
}
|
||||
def self.pip_report(packages, python_name: "python", print_stderr: false)
|
||||
return [] if packages.blank?
|
||||
|
||||
@ -430,6 +437,7 @@ module PyPI
|
||||
pip_report_to_packages(JSON.parse(pip_output)).uniq
|
||||
end
|
||||
|
||||
sig { params(report: T::Hash[String, T.untyped]).returns(T::Array[Package]) }
|
||||
def self.pip_report_to_packages(report)
|
||||
return [] if report.blank?
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Utils
|
||||
@ -98,17 +98,20 @@ module Utils
|
||||
end
|
||||
end
|
||||
|
||||
SHELL_PROFILE_MAP = {
|
||||
bash: "~/.profile",
|
||||
csh: "~/.cshrc",
|
||||
fish: "~/.config/fish/config.fish",
|
||||
ksh: "~/.kshrc",
|
||||
mksh: "~/.kshrc",
|
||||
rc: "~/.rcrc",
|
||||
sh: "~/.profile",
|
||||
tcsh: "~/.tcshrc",
|
||||
zsh: "~/.zshrc",
|
||||
}.freeze
|
||||
SHELL_PROFILE_MAP = T.let(
|
||||
{
|
||||
bash: "~/.profile",
|
||||
csh: "~/.cshrc",
|
||||
fish: "~/.config/fish/config.fish",
|
||||
ksh: "~/.kshrc",
|
||||
mksh: "~/.kshrc",
|
||||
rc: "~/.rcrc",
|
||||
sh: "~/.profile",
|
||||
tcsh: "~/.tcshrc",
|
||||
zsh: "~/.zshrc",
|
||||
}.freeze,
|
||||
T::Hash[T.nilable(Symbol), String],
|
||||
)
|
||||
|
||||
UNSAFE_SHELL_CHAR = %r{([^A-Za-z0-9_\-.,:/@~+\n])}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "utils/curl"
|
||||
@ -8,7 +8,7 @@ require "utils/github"
|
||||
module SPDX
|
||||
module_function
|
||||
|
||||
DATA_PATH = (HOMEBREW_DATA_PATH/"spdx").freeze
|
||||
DATA_PATH = T.let((HOMEBREW_DATA_PATH/"spdx").freeze, Pathname)
|
||||
API_URL = "https://api.github.com/repos/spdx/license-list-data/releases/latest"
|
||||
LICENSEREF_PREFIX = "LicenseRef-Homebrew-"
|
||||
ALLOWED_LICENSE_SYMBOLS = [
|
||||
@ -16,24 +16,43 @@ module SPDX
|
||||
:cannot_represent,
|
||||
].freeze
|
||||
|
||||
sig { returns(T::Hash[String, T.untyped]) }
|
||||
def license_data
|
||||
@license_data ||= JSON.parse (DATA_PATH/"spdx_licenses.json").read
|
||||
@license_data ||= T.let(JSON.parse((DATA_PATH/"spdx_licenses.json").read), T.nilable(T::Hash[String, T.untyped]))
|
||||
end
|
||||
|
||||
sig { returns(T::Hash[String, T.untyped]) }
|
||||
def exception_data
|
||||
@exception_data ||= JSON.parse (DATA_PATH/"spdx_exceptions.json").read
|
||||
@exception_data ||= T.let(JSON.parse((DATA_PATH/"spdx_exceptions.json").read),
|
||||
T.nilable(T::Hash[String, T.untyped]))
|
||||
end
|
||||
|
||||
sig { returns(String) }
|
||||
def latest_tag
|
||||
@latest_tag ||= GitHub::API.open_rest(API_URL)["tag_name"]
|
||||
@latest_tag ||= T.let(GitHub::API.open_rest(API_URL)["tag_name"], T.nilable(String))
|
||||
end
|
||||
|
||||
sig { params(to: Pathname).void }
|
||||
def download_latest_license_data!(to: DATA_PATH)
|
||||
data_url = "https://raw.githubusercontent.com/spdx/license-list-data/#{latest_tag}/json/"
|
||||
Utils::Curl.curl_download("#{data_url}licenses.json", to: to/"spdx_licenses.json")
|
||||
Utils::Curl.curl_download("#{data_url}exceptions.json", to: to/"spdx_exceptions.json")
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
license_expression: T.any(
|
||||
String,
|
||||
Symbol,
|
||||
T::Hash[T.any(Symbol, String), T.untyped],
|
||||
T::Array[String],
|
||||
),
|
||||
).returns(
|
||||
[
|
||||
T::Array[T.any(String, Symbol)], T::Array[String]
|
||||
],
|
||||
)
|
||||
}
|
||||
def parse_license_expression(license_expression)
|
||||
licenses = T.let([], T::Array[T.any(String, Symbol)])
|
||||
exceptions = T.let([], T::Array[String])
|
||||
@ -63,6 +82,7 @@ module SPDX
|
||||
[licenses, exceptions]
|
||||
end
|
||||
|
||||
sig { params(license: T.any(String, Symbol)).returns(T::Boolean) }
|
||||
def valid_license?(license)
|
||||
return ALLOWED_LICENSE_SYMBOLS.include? license if license.is_a? Symbol
|
||||
|
||||
@ -70,22 +90,31 @@ module SPDX
|
||||
license_data["licenses"].any? { |spdx_license| spdx_license["licenseId"] == license }
|
||||
end
|
||||
|
||||
sig { params(license: T.any(String, Symbol)).returns(T::Boolean) }
|
||||
def deprecated_license?(license)
|
||||
return false if ALLOWED_LICENSE_SYMBOLS.include? license
|
||||
return false unless valid_license?(license)
|
||||
|
||||
license = license.delete_suffix "+"
|
||||
license = license.to_s.delete_suffix "+"
|
||||
license_data["licenses"].none? do |spdx_license|
|
||||
spdx_license["licenseId"] == license && !spdx_license["isDeprecatedLicenseId"]
|
||||
end
|
||||
end
|
||||
|
||||
sig { params(exception: String).returns(T::Boolean) }
|
||||
def valid_license_exception?(exception)
|
||||
exception_data["exceptions"].any? do |spdx_exception|
|
||||
spdx_exception["licenseExceptionId"] == exception && !spdx_exception["isDeprecatedLicenseId"]
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
license_expression: T.any(String, Symbol, T::Hash[T.nilable(T.any(Symbol, String)), T.untyped]),
|
||||
bracket: T::Boolean,
|
||||
hash_type: T.nilable(T.any(String, Symbol)),
|
||||
).returns(T.nilable(String))
|
||||
}
|
||||
def license_expression_to_string(license_expression, bracket: false, hash_type: nil)
|
||||
case license_expression
|
||||
when String
|
||||
@ -125,6 +154,19 @@ module SPDX
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
string: T.nilable(String),
|
||||
).returns(
|
||||
T.nilable(
|
||||
T.any(
|
||||
String,
|
||||
Symbol,
|
||||
T::Hash[T.any(String, Symbol), T.untyped],
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
def string_to_license_expression(string)
|
||||
return if string.blank?
|
||||
|
||||
@ -162,13 +204,23 @@ module SPDX
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
license: T.any(String, Symbol),
|
||||
).returns(
|
||||
T.any(
|
||||
[T.any(String, Symbol)],
|
||||
[String, T.nilable(String), T::Boolean],
|
||||
),
|
||||
)
|
||||
}
|
||||
def license_version_info(license)
|
||||
return [license] if ALLOWED_LICENSE_SYMBOLS.include? license
|
||||
|
||||
match = license.match(/-(?<version>[0-9.]+)(?:-.*?)??(?<or_later>\+|-only|-or-later)?$/)
|
||||
return [license] if match.blank?
|
||||
|
||||
license_name = license.split(match[0]).first
|
||||
license_name = license.to_s.split(match[0].to_s).first
|
||||
or_later = match["or_later"].present? && %w[+ -or-later].include?(match["or_later"])
|
||||
|
||||
# [name, version, later versions allowed?]
|
||||
@ -176,12 +228,18 @@ module SPDX
|
||||
[license_name, match["version"], or_later]
|
||||
end
|
||||
|
||||
sig {
|
||||
params(license_expression: T.any(String, Symbol, T::Hash[Symbol, T.untyped]),
|
||||
forbidden_licenses: T::Hash[Symbol, T.untyped]).returns(T::Boolean)
|
||||
}
|
||||
def licenses_forbid_installation?(license_expression, forbidden_licenses)
|
||||
case license_expression
|
||||
when String, Symbol
|
||||
forbidden_licenses_include? license_expression.to_s, forbidden_licenses
|
||||
when Hash
|
||||
key = license_expression.keys.first
|
||||
return false if key.nil?
|
||||
|
||||
case key
|
||||
when :any_of
|
||||
license_expression[key].all? { |license| licenses_forbid_installation? license, forbidden_licenses }
|
||||
@ -193,6 +251,12 @@ module SPDX
|
||||
end
|
||||
end
|
||||
|
||||
sig {
|
||||
params(
|
||||
license: T.any(Symbol, String),
|
||||
forbidden_licenses: T::Hash[T.any(Symbol, String), T.untyped],
|
||||
).returns(T::Boolean)
|
||||
}
|
||||
def forbidden_licenses_include?(license, forbidden_licenses)
|
||||
return true if forbidden_licenses.key? license
|
||||
|
||||
|
@ -1,49 +1,58 @@
|
||||
# typed: true # rubocop:todo Sorbet/StrictSigil
|
||||
# typed: strict
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Various helper functions for interacting with TTYs.
|
||||
module Tty
|
||||
@stream = $stdout
|
||||
@stream = T.let($stdout, T.nilable(T.any(IO, StringIO)))
|
||||
|
||||
COLOR_CODES = {
|
||||
red: 31,
|
||||
green: 32,
|
||||
yellow: 33,
|
||||
blue: 34,
|
||||
magenta: 35,
|
||||
cyan: 36,
|
||||
default: 39,
|
||||
}.freeze
|
||||
COLOR_CODES = T.let(
|
||||
{
|
||||
red: 31,
|
||||
green: 32,
|
||||
yellow: 33,
|
||||
blue: 34,
|
||||
magenta: 35,
|
||||
cyan: 36,
|
||||
default: 39,
|
||||
}.freeze,
|
||||
T::Hash[Symbol, Integer],
|
||||
)
|
||||
|
||||
STYLE_CODES = {
|
||||
reset: 0,
|
||||
bold: 1,
|
||||
italic: 3,
|
||||
underline: 4,
|
||||
strikethrough: 9,
|
||||
no_underline: 24,
|
||||
}.freeze
|
||||
STYLE_CODES = T.let(
|
||||
{
|
||||
reset: 0,
|
||||
bold: 1,
|
||||
italic: 3,
|
||||
underline: 4,
|
||||
strikethrough: 9,
|
||||
no_underline: 24,
|
||||
}.freeze,
|
||||
T::Hash[Symbol, Integer],
|
||||
)
|
||||
|
||||
SPECIAL_CODES = {
|
||||
up: "1A",
|
||||
down: "1B",
|
||||
right: "1C",
|
||||
left: "1D",
|
||||
erase_line: "K",
|
||||
erase_char: "P",
|
||||
}.freeze
|
||||
SPECIAL_CODES = T.let(
|
||||
{
|
||||
up: "1A",
|
||||
down: "1B",
|
||||
right: "1C",
|
||||
left: "1D",
|
||||
erase_line: "K",
|
||||
erase_char: "P",
|
||||
}.freeze,
|
||||
T::Hash[Symbol, String],
|
||||
)
|
||||
|
||||
CODES = COLOR_CODES.merge(STYLE_CODES).freeze
|
||||
CODES = T.let(COLOR_CODES.merge(STYLE_CODES).freeze, T::Hash[Symbol, Integer])
|
||||
|
||||
class << self
|
||||
sig { params(stream: T.any(IO, StringIO), _block: T.proc.params(arg0: T.any(IO, StringIO)).void).void }
|
||||
def with(stream, &_block)
|
||||
previous_stream = @stream
|
||||
@stream = stream
|
||||
@stream = T.let(stream, T.nilable(T.any(IO, StringIO)))
|
||||
|
||||
yield stream
|
||||
ensure
|
||||
@stream = previous_stream
|
||||
@stream = T.let(previous_stream, T.nilable(T.any(IO, StringIO)))
|
||||
end
|
||||
|
||||
sig { params(string: String).returns(String) }
|
||||
@ -88,17 +97,17 @@ module Tty
|
||||
height, width = `/bin/stty size 2>/dev/null`.presence&.split&.map(&:to_i)
|
||||
return if height.nil? || width.nil?
|
||||
|
||||
@size = [height, width]
|
||||
@size = T.let([height, width], T.nilable([Integer, Integer]))
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def height
|
||||
@height ||= size&.first || `/usr/bin/tput lines 2>/dev/null`.presence&.to_i || 40
|
||||
@height ||= T.let(size&.first || `/usr/bin/tput lines 2>/dev/null`.presence&.to_i || 40, T.nilable(Integer))
|
||||
end
|
||||
|
||||
sig { returns(Integer) }
|
||||
def width
|
||||
@width ||= size&.second || `/usr/bin/tput cols 2>/dev/null`.presence&.to_i || 80
|
||||
@width ||= T.let(size&.second || `/usr/bin/tput cols 2>/dev/null`.presence&.to_i || 80, T.nilable(Integer))
|
||||
end
|
||||
|
||||
sig { params(string: String).returns(String) }
|
||||
@ -115,12 +124,12 @@ module Tty
|
||||
|
||||
sig { void }
|
||||
def reset_escape_sequence!
|
||||
@escape_sequence = nil
|
||||
@escape_sequence = T.let(nil, T.nilable(T::Array[Integer]))
|
||||
end
|
||||
|
||||
CODES.each do |name, code|
|
||||
define_method(name) do
|
||||
@escape_sequence ||= []
|
||||
@escape_sequence ||= T.let([], T.nilable(T::Array[Integer]))
|
||||
@escape_sequence << code
|
||||
self
|
||||
end
|
||||
@ -128,7 +137,8 @@ module Tty
|
||||
|
||||
SPECIAL_CODES.each do |name, code|
|
||||
define_method(name) do
|
||||
if @stream.tty?
|
||||
@stream = T.let($stdout, T.nilable(T.any(IO, StringIO)))
|
||||
if @stream&.tty?
|
||||
"\033[#{code}"
|
||||
else
|
||||
""
|
||||
@ -152,7 +162,7 @@ module Tty
|
||||
return false if Homebrew::EnvConfig.no_color?
|
||||
return true if Homebrew::EnvConfig.color?
|
||||
|
||||
@stream.tty?
|
||||
!!@stream&.tty?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user