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: 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 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 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 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 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 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 620 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 610 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 523 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 440 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 428 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 494 def new_scope self.class.new(@input, @output, @wrap_at, @page_at, @indent_size, @indent_level) end
Outputs newline
# File lib/highline.rb, line 459 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 467 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 478 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 421 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 487 def puts(*args) @output.puts(*args) end
Renders and indents a statement.
Note: extracted here to be used by readline to render its prompt.
@param statement [String] The statement to be rendered and indented. @return [String] The rendered and indented statement.
# File lib/highline.rb, line 392 def render_and_ident_statement(statement) statement = render_statement(statement) statement = (indentation + statement) unless statement.empty? statement end
Renders a statement using {HighLine::Statement} @param statement [String] any string @return [Statement] rendered statement
# File lib/highline.rb, line 401 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 HighLine#color()
method.
@param statement [Statement, String] what to be said
# File lib/highline.rb, line 372 def say(statement) statement = render_and_ident_statement(statement) return statement if statement.empty? # 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 411 def wrap_at=(setting) @wrap_at = setting == :auto ? output_cols : setting end
Private Instance Methods
# File lib/highline.rb, line 641 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 503 def confirm(question) new_scope.agree(question.confirm_question(self)) end
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 652 def default_use_color if HighLine.default_instance HighLine.default_instance.use_color else true end end
# File lib/highline.rb, line 633 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 540 def get_line(question) terminal.get_line(question, self) end
# File lib/highline.rb, line 544 def get_line_raw_no_echo_mode(question) line = "" terminal.raw_no_echo_mode_exec do loop do character = terminal.get_character raise Interrupt if character == "\u0003" 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 == "\cU" line.size.times { output_erase_char } if question.echo line = "" 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 588 def ignore_arrow_key 2.times do terminal.get_character end end
# File lib/highline.rb, line 516 def last_answer(answers) answers.respond_to?(:values) ? answers.values.last : answers.last end
# File lib/highline.rb, line 599 def line_overflow_for_question?(line, question) question.limit && line.size == question.limit end
# File lib/highline.rb, line 603 def output_erase_char @output.print("\b#{HighLine.Style(:erase_char).code}") end
# File lib/highline.rb, line 594 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 579 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 512 def unique_answers(list) (list.respond_to?(:values) ? list.values : list).uniq end