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
Pass ColorScheme to set a HighLine color scheme.
@return [Integer] The indentation level
@return [Integer] The indentation size in characters
@return [IO] the default input stream for a HighLine instance
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.
@return [Boolean] Indentation over multiple lines
@return [IO] the default output stream for a HighLine instance
@return [Integer] The current row setting for paging output.
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]
Pass false
to turn off HighLine's EOF tracking.
Set it to false to disable ANSI coloring
@return [Integer] The current column setting for wrapping output.
Public Class Methods
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 101 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 HighLine to default. Clears Style index and resets ::color_scheme and #use_color settings.
# File lib/highline.rb, line 71 def reset Style.clear_index reset_color_scheme reset_use_color end
Reset color scheme to default (nil
)
# File lib/highline.rb, line 65 def reset_color_scheme self.color_scheme = nil end
For checking if the current version of HighLine supports RGB colors Usage: ::supports_rgb_color? rescue false
using rescue for compatibility with older versions
Note: color usage also depends on #use_color being set TODO: Discuss removing this method
# File lib/highline.rb, line 82 def supports_rgb_color? true end
Returns true
if HighLine is
currently using a color scheme.
# File lib/highline.rb, line 60 def using_color_scheme? true if @color_scheme end
Public Instance Methods
A shortcut to #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 #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 HighLine::Question#character
# File lib/highline.rb, line 192 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
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 HighLine::Question.build) @return answer converted to the class in answer_type
# File lib/highline.rb, line 216 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
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 HighLine::Menu.new @return [String] answer
# File lib/highline.rb, line 244 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
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 320 def color(string, *colors) return string unless use_color? HighLine.Style(*colors).color(string) end
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 340 def color_code(*colors) HighLine.Style(*colors).code end
Get response each character per turn @param question [Question] @return [String] response
# File lib/highline.rb, line 606 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 using getc @param question [Question] @return [String] response
# File lib/highline.rb, line 596 def get_response_getc_mode(question) terminal.raw_no_echo_mode_exec do response = @input.getc question.format_answer(response) end end
Get response one line at time @param question [Question] @return [String] response
# File lib/highline.rb, line 513 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
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 430 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
Outputs indentation with current settings
# File lib/highline.rb, line 418 def indentation " " * @indent_size * @indent_level end
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 357 def list(items, mode = :rows, option = nil) ListRenderer.new(items, mode, option, self).render end
Creates a new HighLine instance with the same options
# File lib/highline.rb, line 484 def new_scope self.class.new(@input, @output, @wrap_at, @page_at, @indent_size, @indent_level) end
Outputs newline
# File lib/highline.rb, line 449 def newline @output.puts end
Returns the number of columns for the console, or a default it they cannot be determined.
# File lib/highline.rb, line 457 def output_cols return 80 unless @output.tty? terminal.terminal_size.first rescue NoMethodError return 80 end
Returns the number of rows for the console, or a default if they cannot be determined.
# File lib/highline.rb, line 468 def output_rows return 24 unless @output.tty? terminal.terminal_size.last rescue NoMethodError return 24 end
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 411 def page_at=(setting) @page_at = setting == :auto ? output_rows - 2 : setting end
Call puts on the HighLine's output stream @param args [String] same args for Kernel#puts
# File lib/highline.rb, line 477 def puts(*args) @output.puts(*args) end
Renders a statement using {HighLine::Statement} @param statement [String] any string @return [Statement] rendered statement
# File lib/highline.rb, line 391 def render_statement(statement) Statement.new(statement, self).to_s end
Resets the use of color.
# File lib/highline.rb, line 132 def reset_use_color @use_color = true end
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 #color method.
@param statement [Statement, String] what to be said
# File lib/highline.rb, line 372 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
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 284 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
Returns true if HighLine is currently tracking EOF for input.
# File lib/highline.rb, line 140 def track_eof? true if track_eof end
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 347 def uncolor(string) Style.uncolor(string) end
Returns truethy if HighLine instance is currently using color escapes.
# File lib/highline.rb, line 127 def use_color? use_color end
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 401 def wrap_at=(setting) @wrap_at = setting == :auto ? output_cols : setting end
Private Instance Methods
# File lib/highline.rb, line 627 def actual_length(text) Wrapper.actual_length text end
Adds a layer of scope (new_scope) to ask a question inside a question, without destroying instance data
# File lib/highline.rb, line 493 def confirm(question) new_scope.agree(question.confirm_question(self)) end
Check to see if there's already a ::default_instance or if this is the first time the method is called (eg: at ::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 638 def default_use_color if HighLine.default_instance HighLine.default_instance.use_color else true end end
# File lib/highline.rb, line 619 def erase_current_line @output.print("\r#{HighLine.Style(:erase_line).code}") @output.flush end
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 530 def get_line(question) terminal.get_line(question, self) end
# File lib/highline.rb, line 534 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
# File lib/highline.rb, line 574 def ignore_arrow_key 2.times do terminal.get_character end end
# File lib/highline.rb, line 506 def last_answer(answers) answers.respond_to?(:values) ? answers.values.last : answers.last end
# File lib/highline.rb, line 585 def line_overflow_for_question?(line, question) question.limit && line.size == question.limit end
# File lib/highline.rb, line 589 def output_erase_char @output.print("\b#{HighLine.Style(:erase_char).code}") end
# File lib/highline.rb, line 580 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
# File lib/highline.rb, line 565 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
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 502 def unique_answers(list) (list.respond_to?(:values) ? list.values : list).uniq end