class Toys::Utils::Terminal


* An array of ANSI codes as integers.
* An ANSI code string in ‘e[XXm` form.
* An rgb string in hex “rgb” or “rrggbb” form.
a defined style.
* A symbol indicating the name of a well-known style, or the name of
Styles may be specified in any of the following forms:
This class supports ANSI styled output where supported.
## Styles
A simple terminal class.
#

def self.remove_style_escapes(str)

Returns:
  • (String) - String with styles removed

Parameters:
  • str (String) -- Input string
def self.remove_style_escapes(str)
  str.gsub(/\e\[\d+(;\d+)*m/, "")
end

def <<(str)

Returns:
  • (self) -

Parameters:
  • str (String) -- The line to write
def <<(str)
  puts(str)
end

def apply_styles(str, *styles)

Returns:
  • (String) - The styled string

Parameters:
  • styles (Symbol, String, Array...) -- Styles to apply
  • str (String) -- String to style
def apply_styles(str, *styles)
  if styled
    prefix = escape_styles(*styles)
    suffix = prefix.empty? || str.end_with?(CLEAR_CODE) ? "" : CLEAR_CODE
    "#{prefix}#{str}#{suffix}"
  else
    Terminal.remove_style_escapes(str)
  end
end

def ask(prompt, *styles, default: nil, trailing_text: :default)

Returns:
  • (String) -

Parameters:
  • trailing_text (:default, String, nil) -- Trailing text appended to
  • default (String, nil) -- Default value, or `nil` for no default.
  • styles (Symbol, String, Array...) -- Styles to apply to the
  • prompt (String) -- Required prompt string.
def ask(prompt, *styles, default: nil, trailing_text: :default)
  if trailing_text == :default
    trailing_text = default.nil? ? nil : "[#{default}]"
  end
  if trailing_text
    ptext, pspaces, = prompt.partition(/\s+$/)
    prompt = "#{ptext} #{trailing_text}#{pspaces}"
  end
  write(prompt, *styles)
  resp = readline.to_s.chomp
  resp.empty? ? default.to_s : resp
end

def close


a log device target, but it does not actually close anything.
This method is defined so that `::Logger` will recognize a terminal as
#
def close
  nil
end

def confirm(prompt = "Proceed? ", *styles, default: nil)

Returns:
  • (Boolean) -

Parameters:
  • default (Boolean, nil) -- Default value, or `nil` for no default.
  • styles (Symbol, String, Array...) -- Styles to apply to the
  • prompt (String) -- Prompt string. Defaults to `"Proceed?"`.
def confirm(prompt = "Proceed? ", *styles, default: nil)
  default_val, trailing_text =
    case default
    when true
      ["y", "(Y/n)"]
    when false
      ["n", "(y/N)"]
    else
      [nil, "(y/n)"]
    end
  resp = ask(prompt, *styles, default: default_val, trailing_text: trailing_text)
  return true if resp =~ /^y/i
  return false if resp =~ /^n/i
  if resp.nil? && default.nil?
    raise TerminalError, "Cannot confirm because the input stream is at eof."
  end
  if !resp.strip.empty? || default.nil?
    if input.nil?
      raise TerminalError, "Cannot confirm because there is no input stream."
    end
    confirm('Please answer "y" or "n"', default: default)
  else
    default
  end
end

def define_style(name, *styles)

Returns:
  • (self) -

Parameters:
  • styles (Symbol, String, Array...) --
  • name (Symbol) -- The name for the style
def define_style(name, *styles)
  @named_styles[name] = resolve_styles(*styles)
  self
end

def escape_styles(*styles)


Resolve a style to an ANSI style escape sequence.
#
def escape_styles(*styles)
  codes = resolve_styles(*styles)
  codes.empty? ? "" : "\e[#{codes.join(';')}m"
end

def height

Returns:
  • (Integer) -
def height
  size[1]
end

def initialize(input: $stdin, output: $stdout, styled: nil)

Parameters:
  • styled (Boolean, nil) -- Whether to output ansi styles. If `nil`, the
  • output (IO, Logger, nil) -- Output stream or logger.
  • input (IO, nil) -- Input stream.
def initialize(input: $stdin, output: $stdout, styled: nil)
  @input = input
  @output = output
  @styled =
    if styled.nil?
      output.respond_to?(:tty?) && output.tty?
    else
      styled ? true : false
    end
  @named_styles = BUILTIN_STYLE_NAMES.dup
  @output_mutex = ::Monitor.new
  @input_mutex = ::Monitor.new
end

def interpret_style_string(style)


Transform various style string formats into a list of style codes.
#
def interpret_style_string(style)
  case style
  when /^[0-9a-fA-F]{6}$/
    rgb = style.to_i(16)
    [38, 2, rgb >> 16, (rgb & 0xff00) >> 8, rgb & 0xff]
  when /^[0-9a-fA-F]{3}$/
    rgb = style.to_i(16)
    [38, 2, (rgb >> 8) * 0x11, ((rgb & 0xf0) >> 4) * 0x11, (rgb & 0xf) * 0x11]
  when /^\e\[([\d;]+)m$/
    ::Regexp.last_match(1).split(";").map(&:to_i)
  end
end

def newline

Returns:
  • (self) -
def newline
  puts
end

def puts(str = "", *styles)

Returns:
  • (self) -

Parameters:
  • styles (Symbol, String, Array...) -- Styles to apply to the
  • str (String) -- The line to write
def puts(str = "", *styles)
  str = "#{str}\n" unless str.end_with?("\n")
  write(str, *styles)
end

def readline

Returns:
  • (nil) - if the input is closed or at eof, or there is no input
  • (String) - the entire string including the temrinating newline
def readline
  @input_mutex.synchronize do
    begin
      input&.gets
    rescue ::IOError
      nil
    end
  end
end

def resolve_styles(*styles)


Resolve a style to an array of ANSI style codes (integers).
#
def resolve_styles(*styles)
  result = []
  styles.each do |style|
    codes =
      case style
      when ::Array
        style
      when ::String
        interpret_style_string(style)
      when ::Symbol
        @named_styles[style]
      end
    raise ::ArgumentError, "Unknown style code: #{s.inspect}" unless codes
    result.concat(codes)
  end
  result
end

def size

Returns:
  • (Array(Integer,Integer)) -
def size
  if output.respond_to?(:tty?) && output.tty? && output.respond_to?(:winsize)
    output.winsize.reverse
  else
    [80, 25]
  end
end

def spinner(leading_text: "", final_text: "",

Returns:
  • (Object) - The return value of the block.

Parameters:
  • final_text (String) -- Optional final string to display when the
  • style (Symbol, Array) -- A terminal style or array of styles
  • frames (Array) -- An array of frames. Defaults to
  • frame_length (Float) -- Length of a single frame, in seconds.
  • leading_text (String) -- Optional leading string to display to the
def spinner(leading_text: "", final_text: "",
            frame_length: nil, frames: nil, style: nil)
  return nil unless block_given?
  frame_length ||= DEFAULT_SPINNER_FRAME_LENGTH
  frames ||= DEFAULT_SPINNER_FRAMES
  write(leading_text) unless leading_text.empty?
  spin = SpinDriver.new(self, frames, Array(style), frame_length)
  begin
    yield
  ensure
    spin.stop
    write(final_text) unless final_text.empty?
  end
end

def width

Returns:
  • (Integer) -
def width
  size[0]
end

def write(str = "", *styles)

Returns:
  • (self) -

Parameters:
  • styles (Symbol, String, Array...) -- Styles to apply to the
  • str (String) -- The line to write
def write(str = "", *styles)
  @output_mutex.synchronize do
    begin
      output&.write(apply_styles(str, *styles))
      output&.flush
    rescue ::IOError
      nil
    end
  end
  self
end