class HighLine

A HighLine object is a “high-level line oriented” shell over an input and an output stream. HighLine simplifies common console interaction, effectively replacing {Kernel#puts} and {Kernel#gets}. User code can simply specify the question to ask and any details about user interaction, then leave the rest of the work to HighLine. When {HighLine#ask} returns, you'll have the answer you requested, even if HighLine had to ask many times, validate results, perform range checking, convert types, etc.

@example Basic usage

cli = HighLine.new
answer = cli.ask "What do you think?"
puts "You have answered: #{answer}"

Constants

VERSION

The version of the installed library.

Attributes

color_scheme[RW]

Pass ColorScheme to set a HighLine color scheme.

default_instance[RW]
indent_level[RW]

@return [Integer] The indentation level

indent_size[RW]

@return [Integer] The indentation size in characters

input[R]

@return [IO] the default input stream for a HighLine instance

key[RW]

When gathering a Hash with {QuestionAsker#gather_hash}, it tracks the current key being asked.

@todo We should probably move this into the HighLine::Question

object.
multi_indent[RW]

@return [Boolean] Indentation over multiple lines

output[R]

@return [IO] the default output stream for a HighLine instance

page_at[R]

@return [Integer] The current row setting for paging output.

terminal[R]

System specific that responds to initialize_system_extensions, terminal_size, raw_no_echo_mode, restore_mode, get_character. It polymorphically handles specific cases for different platforms. @return [HighLine::Terminal]

track_eof[RW]

Pass false to turn off HighLine's EOF tracking.

use_color[RW]

Set it to false to disable ANSI coloring

wrap_at[R]

@return [Integer] The current column setting for wrapping output.

Public Class Methods

new(input = $stdin, output = $stdout, wrap_at = nil, page_at = nil, indent_size = 3, indent_level = 0) click to toggle source

Create an instance of HighLine connected to the given input and output streams.

@param input [IO] the default input stream for HighLine. @param output [IO] the default output stream for HighLine. @param wrap_at [Integer] all statements outputed through

HighLine will be wrapped to this column size if set.

@param page_at [Integer] page size and paginating. @param indent_size [Integer] indentation size in spaces. @param indent_level [Integer] how deep is indentated.

# File lib/highline.rb, line 102
def initialize(input = $stdin, output = $stdout,
               wrap_at = nil, page_at = nil,
               indent_size = 3, indent_level = 0)
  @input   = input
  @output  = output

  @multi_indent = true
  @indent_size  = indent_size
  @indent_level = indent_level

  self.wrap_at = wrap_at
  self.page_at = page_at

  @header   = nil
  @prompt   = nil
  @key      = nil

  @use_color = default_use_color
  @track_eof = true # The setting used to disable EOF tracking.
  @terminal = HighLine::Terminal.get_terminal(input, output)
end
reset() click to toggle source

Reset HighLine to default. Clears Style index and resets color_scheme and use_color settings.

# File lib/highline.rb, line 72
def reset
  Style.clear_index
  reset_color_scheme
  reset_use_color
end
reset_color_scheme() click to toggle source

Reset color scheme to default (nil)

# File lib/highline.rb, line 66
def reset_color_scheme
  self.color_scheme = nil
end
supports_rgb_color?() click to toggle source

For checking if the current version of HighLine supports RGB colors Usage: HighLine.supports_rgb_color? rescue false

using rescue for compatibility with older versions

Note: color usage also depends on HighLine.use_color being set TODO: Discuss removing this method

# File lib/highline.rb, line 83
def supports_rgb_color?
  true
end
using_color_scheme?() click to toggle source

Returns true if HighLine is currently using a color scheme.

# File lib/highline.rb, line 61
def using_color_scheme?
  true if @color_scheme
end

Public Instance Methods

agree(yes_or_no_question, character = nil) { |q| ... } click to toggle source

A shortcut to HighLine.ask() a question that only accepts “yes” or “no” answers (“y” and “n” are allowed) and returns true or false (true for “yes”). If provided a true value, character will cause HighLine to fetch a single character response. A block can be provided to further configure the question as in HighLine.ask()

Raises EOFError if input is exhausted.

@param yes_or_no_question [String] a question that accepts yes and no as

answers

@param character [Boolean, :getc] character mode to be passed to

Question#character

@see Question#character

# File lib/highline.rb, line 193
def agree(yes_or_no_question, character = nil)
  ask(yes_or_no_question, ->(yn) { yn.downcase[0] == "y" }) do |q|
    q.validate                 = /\A(?:y(?:es)?|no?)\Z/i
    q.responses[:not_valid]    = 'Please enter "yes" or "no".'
    q.responses[:ask_on_error] = :question
    q.character                = character
    q.completion               = %w[yes no]

    yield q if block_given?
  end
end
ask(template_or_question, answer_type = nil, &details) click to toggle source

This method is the primary interface for user input. Just provide a question to ask the user, the answer_type you want returned, and optionally a code block setting up details of how you want the question handled. See {#say} for details on the format of question, and {Question} for more information about answer_type and what's valid in the code block.

Raises EOFError if input is exhausted.

@param (see Question.build) @return answer converted to the class in answer_type

# File lib/highline.rb, line 217
def ask(template_or_question, answer_type = nil, &details)
  question = Question.build(template_or_question, answer_type, &details)

  if question.gather
    QuestionAsker.new(question, self).gather_answers
  else
    QuestionAsker.new(question, self).ask_once
  end
end
choose(*items, &details) click to toggle source

This method is HighLine's menu handler. For simple usage, you can just pass all the menu items you wish to display. At that point, choose() will build and display a menu, walk the user through selection, and return their choice among the provided items. You might use this in a case statement for quick and dirty menus.

However, choose() is capable of much more. If provided, a block will be passed a HighLine::Menu object to configure. Using this method, you can customize all the details of menu handling from index display, to building a complete shell-like menuing system. See HighLine::Menu for all the methods it responds to.

Raises EOFError if input is exhausted.

@param items [Array<String>] @param details [Proc] to be passed to Menu.new @return [String] answer

# File lib/highline.rb, line 245
def choose(*items, &details)
  menu = Menu.new(&details)
  menu.choices(*items) unless items.empty?

  # Set auto-completion
  menu.completion = menu.options

  # Set _answer_type_ so we can double as the Question for ask().
  # menu.option = normal menu selection, by index or name
  menu.answer_type = menu.shell ? shell_style_lambda(menu) : menu.options

  selected = ask(menu)
  return unless selected

  if menu.shell
    if menu.gather
      selection = []
      details = []
      selected.each do |value|
        selection << value[0]
        details << value[1]
      end
    else
      selection, details = selected
    end
  else
    selection = selected
  end

  if menu.gather
    menu.gather_selected(self, selection, details)
  else
    menu.select(self, selection, details)
  end
end
color(string, *colors) click to toggle source

This method provides easy access to ANSI color sequences, without the user needing to remember to CLEAR at the end of each sequence. Just pass the string to color, followed by a list of colors you would like it to be affected by. The colors can be HighLine class constants, or symbols (:blue for BLUE, for example). A CLEAR will automatically be embedded to the end of the returned String.

This method returns the original string unchanged if use_color? is false.

@param string [String] string to be colored @param colors [Array<Symbol>] array of colors like [:red, :blue] @return [String] (ANSI escaped) colored string @example

cli = HighLine.new
cli.color("Sustainable", :green, :bold)
# => "\e[32m\e[1mSustainable\e[0m"

# As class method (delegating to HighLine.default_instance)
HighLine.color("Sustainable", :green, :bold)
# File lib/highline.rb, line 321
def color(string, *colors)
  return string unless use_color?
  HighLine.Style(*colors).color(string)
end
color_code(*colors) click to toggle source

In case you just want the color code, without the embedding and the CLEAR sequence.

@param colors [Array<Symbol>] @return [String] ANSI escape code for the given colors.

@example

s = HighLine.Style(:red, :blue)
s.code # => "\e[31m\e[34m"

HighLine.color_code(:red, :blue) # => "\e[31m\e[34m"

cli = HighLine.new
cli.color_code(:red, :blue) # => "\e[31m\e[34m"
# File lib/highline.rb, line 341
def color_code(*colors)
  HighLine.Style(*colors).code
end
get_response_character_mode(question) click to toggle source

Get response each character per turn @param question [Question] @return [String] response

# File lib/highline.rb, line 607
def get_response_character_mode(question)
  terminal.raw_no_echo_mode_exec do
    response = terminal.get_character
    if question.overwrite
      erase_current_line
    else
      echo = question.get_echo_for_response(response)
      say("#{echo}\n")
    end
    question.format_answer(response)
  end
end
get_response_getc_mode(question) click to toggle source

Get response using getc @param question [Question] @return [String] response

# File lib/highline.rb, line 597
def get_response_getc_mode(question)
  terminal.raw_no_echo_mode_exec do
    response = @input.getc
    question.format_answer(response)
  end
end
get_response_line_mode(question) click to toggle source

Get response one line at time @param question [Question] @return [String] response

# File lib/highline.rb, line 514
def get_response_line_mode(question)
  if question.echo == true && !question.limit
    get_line(question)
  else
    get_line_raw_no_echo_mode(question)
  end
end
indent(increase = 1, statement = nil, multiline = nil) { |self| ... } click to toggle source

Executes block or outputs statement with indentation

@param increase [Integer] how much to increase indentation @param statement [Statement, String] to be said @param multiline [Boolean] @return [void] @see multi_indent

# File lib/highline.rb, line 431
def indent(increase = 1, statement = nil, multiline = nil)
  @indent_level += increase
  multi = @multi_indent
  @multi_indent ||= multiline
  begin
    if block_given?
      yield self
    else
      say(statement)
    end
  ensure
    @multi_indent = multi
    @indent_level -= increase
  end
end
indentation() click to toggle source

Outputs indentation with current settings

# File lib/highline.rb, line 419
def indentation
  " " * @indent_size * @indent_level
end
list(items, mode = :rows, option = nil) click to toggle source

Renders a list of itens using a {ListRenderer} @param items [Array] @param mode [Symbol] @param option @return [String] @see ListRenderer#initialize ListRenderer#initialize for parameter details

# File lib/highline.rb, line 358
def list(items, mode = :rows, option = nil)
  ListRenderer.new(items, mode, option, self).render
end
new_scope() click to toggle source

Creates a new HighLine instance with the same options

# File lib/highline.rb, line 485
def new_scope
  self.class.new(@input, @output, @wrap_at,
                 @page_at, @indent_size, @indent_level)
end
newline() click to toggle source

Outputs newline

# File lib/highline.rb, line 450
def newline
  @output.puts
end
output_cols() click to toggle source

Returns the number of columns for the console, or a default it they cannot be determined.

# File lib/highline.rb, line 458
def output_cols
  return 80 unless @output.tty?
  terminal.terminal_size.first
rescue NoMethodError
  return 80
end
output_rows() click to toggle source

Returns the number of rows for the console, or a default if they cannot be determined.

# File lib/highline.rb, line 469
def output_rows
  return 24 unless @output.tty?
  terminal.terminal_size.last
rescue NoMethodError
  return 24
end
page_at=(setting) click to toggle source

Set to an integer value to cause HighLine to page output lines over the indicated line limit. When nil, the default, no paging occurs. If set to :auto, HighLine will attempt to determine the rows available for the @output or use a sensible default.

# File lib/highline.rb, line 412
def page_at=(setting)
  @page_at = setting == :auto ? output_rows - 2 : setting
end
puts(*args) click to toggle source

Call puts on the HighLine's output stream @param args [String] same args for Kernel#puts

# File lib/highline.rb, line 478
def puts(*args)
  @output.puts(*args)
end
render_statement(statement) click to toggle source

Renders a statement using {HighLine::Statement} @param statement [String] any string @return [Statement] rendered statement

# File lib/highline.rb, line 392
def render_statement(statement)
  Statement.new(statement, self).to_s
end
reset_use_color() click to toggle source

Resets the use of color.

# File lib/highline.rb, line 133
def reset_use_color
  @use_color = true
end
say(statement) click to toggle source

The basic output method for HighLine objects. If the provided statement ends with a space or tab character, a newline will not be appended (output will be flush()ed). All other cases are passed straight to Kernel.puts().

The statement argument is processed as an ERb template, supporting embedded Ruby code. The template is evaluated within a HighLine instance's binding for providing easy access to the ANSI color constants and the HighLine#color() method.

@param statement [Statement, String] what to be said

# File lib/highline.rb, line 373
def say(statement)
  statement = render_statement(statement)
  return if statement.empty?

  statement = (indentation + statement)

  # Don't add a newline if statement ends with whitespace, OR
  # if statement ends with whitespace before a color escape code.
  if /[ \t](\e\[\d+(;\d+)*m)?\Z/ =~ statement
    output.print(statement)
    output.flush
  else
    output.puts(statement)
  end
end
shell_style_lambda(menu) click to toggle source

Convenience method to craft a lambda suitable for beind used in autocompletion operations by {#choose} @return [lambda] lambda to be used in autocompletion operations

# File lib/highline.rb, line 285
def shell_style_lambda(menu)
  lambda do |command| # shell-style selection
    first_word = command.to_s.split.first || ""

    options = menu.options
    options.extend(OptionParser::Completion)
    answer = options.complete(first_word)

    raise Question::NoAutoCompleteMatch unless answer

    [answer.last, command.sub(/^\s*#{first_word}\s*/, "")]
  end
end
track_eof?() click to toggle source

Returns true if HighLine is currently tracking EOF for input.

# File lib/highline.rb, line 141
def track_eof?
  true if track_eof
end
uncolor(string) click to toggle source

Remove color codes from a string. @param string [String] to be decolorized @return [String] without the ANSI escape sequence (colors)

# File lib/highline.rb, line 348
def uncolor(string)
  Style.uncolor(string)
end
use_color?() click to toggle source

Returns truethy if HighLine instance is currently using color escapes.

# File lib/highline.rb, line 128
def use_color?
  use_color
end
wrap_at=(setting) click to toggle source

Set to an integer value to cause HighLine to wrap output lines at the indicated character limit. When nil, the default, no wrapping occurs. If set to :auto, HighLine will attempt to determine the columns available for the @output or use a sensible default.

# File lib/highline.rb, line 402
def wrap_at=(setting)
  @wrap_at = setting == :auto ? output_cols : setting
end

Private Instance Methods

actual_length(text) click to toggle source
# File lib/highline.rb, line 628
def actual_length(text)
  Wrapper.actual_length text
end
confirm(question) click to toggle source

Adds a layer of scope (new_scope) to ask a question inside a question, without destroying instance data

# File lib/highline.rb, line 494
def confirm(question)
  new_scope.agree(question.confirm_question(self))
end
default_use_color() click to toggle source

Check to see if there's already a HighLine.default_instance or if this is the first time the method is called (eg: at HighLine.default_instance initialization). If there's already one, copy use_color settings. This is here most to help migrate code from HighLine 1.7.x to 2.0.x

@return [Boolean]

# File lib/highline.rb, line 639
def default_use_color
  if HighLine.default_instance
    HighLine.default_instance.use_color
  else
    true
  end
end
erase_current_line() click to toggle source
# File lib/highline.rb, line 620
def erase_current_line
  @output.print("\r#{HighLine.Style(:erase_line).code}")
  @output.flush
end
get_line(question) click to toggle source

Read a line of input from the input stream and process whitespace as requested by the Question object.

If Question's readline property is set, that library will be used to fetch input. WARNING: This ignores the currently set input stream.

Raises EOFError if input is exhausted.

# File lib/highline.rb, line 531
def get_line(question)
  terminal.get_line(question, self)
end
get_line_raw_no_echo_mode(question) click to toggle source
# File lib/highline.rb, line 535
def get_line_raw_no_echo_mode(question)
  line = ""

  terminal.raw_no_echo_mode_exec do
    loop do
      character = terminal.get_character
      break unless character
      break if ["\n", "\r"].include? character

      # honor backspace and delete
      if character == "\b" || character == "\u007F"
        chopped = line.chop!
        output_erase_char if chopped && question.echo
      elsif character == "\e"
        ignore_arrow_key
      else
        line << character
        say_last_char_or_echo_char(line, question)
      end

      @output.flush

      break if line_overflow_for_question?(line, question)
    end
  end

  say_new_line_or_overwrite(question)

  question.format_answer(line)
end
ignore_arrow_key() click to toggle source
# File lib/highline.rb, line 575
def ignore_arrow_key
  2.times do
    terminal.get_character
  end
end
last_answer(answers) click to toggle source
# File lib/highline.rb, line 507
def last_answer(answers)
  answers.respond_to?(:values) ? answers.values.last : answers.last
end
line_overflow_for_question?(line, question) click to toggle source
# File lib/highline.rb, line 586
def line_overflow_for_question?(line, question)
  question.limit && line.size == question.limit
end
output_erase_char() click to toggle source
# File lib/highline.rb, line 590
def output_erase_char
  @output.print("\b#{HighLine.Style(:erase_char).code}")
end
say_last_char_or_echo_char(line, question) click to toggle source
# File lib/highline.rb, line 581
def say_last_char_or_echo_char(line, question)
  @output.print(line[-1]) if question.echo == true
  @output.print(question.echo) if question.echo && question.echo != true
end
say_new_line_or_overwrite(question) click to toggle source
# File lib/highline.rb, line 566
def say_new_line_or_overwrite(question)
  if question.overwrite
    @output.print("\r#{HighLine.Style(:erase_line).code}")
    @output.flush
  else
    say("\n")
  end
end
unique_answers(list) click to toggle source

A helper method used by HighLine::Question.verify_match for finding whether a list of answers match or differ from each other.

# File lib/highline.rb, line 503
def unique_answers(list)
  (list.respond_to?(:values) ? list.values : list).uniq
end