summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJames Edward Gray II <james@grayproductions.net>2015-12-14 17:08:36 -0600
committerJames Edward Gray II <james@grayproductions.net>2015-12-14 17:08:36 -0600
commit818a3befcc7ba75f60e119f8fc8ea07bb699de2b (patch)
tree0a115f176b1b3aea271e840a38f8ecf6ec013abf
parent7ba5aa219d9058a8d46d24f39efcc36bcf6a87ee (diff)
parent0cfd33e8d5d368ea572070f3af6313c0687e1345 (diff)
downloadhighline-818a3befcc7ba75f60e119f8fc8ea07bb699de2b.tar.gz
Merge pull request #179 from abinoam/feat_improve_docsv2.0.0.pre.develop.4
Improve documentation grade on inch
-rw-r--r--Changelog.md23
-rwxr-xr-xlib/highline.rb85
-rw-r--r--lib/highline/builtin_styles.rb20
-rw-r--r--lib/highline/color_scheme.rb19
-rw-r--r--lib/highline/compatibility.rb7
-rw-r--r--lib/highline/custom_errors.rb18
-rw-r--r--lib/highline/import.rb13
-rw-r--r--lib/highline/list.rb10
-rw-r--r--lib/highline/list_renderer.rb18
-rw-r--r--lib/highline/menu.rb49
-rw-r--r--lib/highline/paginator.rb10
-rwxr-xr-xlib/highline/question.rb59
-rw-r--r--lib/highline/question/answer_converter.rb19
-rw-r--r--lib/highline/question_asker.rb18
-rw-r--r--lib/highline/simulate.rb10
-rw-r--r--lib/highline/statement.rb100
-rw-r--r--lib/highline/string_extensions.rb14
-rwxr-xr-xlib/highline/style.rb100
-rw-r--r--lib/highline/template_renderer.rb26
-rwxr-xr-xlib/highline/terminal.rb60
-rw-r--r--lib/highline/terminal/io_console.rb12
-rw-r--r--lib/highline/terminal/ncurses.rb14
-rw-r--r--lib/highline/terminal/unix_stty.rb13
-rw-r--r--lib/highline/version.rb2
-rw-r--r--lib/highline/wrapper.rb9
-rwxr-xr-xtest/test_highline.rb2
26 files changed, 626 insertions, 104 deletions
diff --git a/Changelog.md b/Changelog.md
index 6e8e692..0252073 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -2,6 +2,29 @@
Below is a complete listing of changes for each revision of HighLine.
+### 2.0.0-develop.4 / 2015-12-14
+This versions makes the code documentation 100% 'A' grade on inch.
+We have used inch and http://inch-ci.org to guide the priorities
+on documentation production.
+
+The grade 'A' (on inch) number of objects on master branch was 44,22% (153/346).
+After this PR we have a 100% grade 'A' (344 objects).
+
+There's already a inch-ci.org badge on README.md. And now it's all green!
+
+We also bring some improvement on CodeClimate scores.
+
+#### CHANGES SUMMARY
+
+* PR #179 - Make inch happy. Grade "A" for the whole HighLine documentation. By Abinoam Jr. (@abinoam)
+* PR #178 - Improve score on Code Climate by applying some refactoring. By Abinoam Jr. (@abinoam)
+* PR #172 - Initial work on documentation by Abinoam Jr. (@abinoam)
+ * Use yard
+ * Use inch
+ * New Readme file
+* Fix #166 with PR #173 by (@matugm)
+
+
### 2.0.0-develop.3 / 2015-10-28
This version brings some improvements on documentation (switch to Yardoc).
diff --git a/lib/highline.rb b/lib/highline.rb
index 27b689a..ba409b6 100755
--- a/lib/highline.rb
+++ b/lib/highline.rb
@@ -77,6 +77,7 @@ class HighLine
@track_eof
end
+ # (see HighLine.track_eof?)
def track_eof?
self.class.track_eof?
end
@@ -106,6 +107,7 @@ class HighLine
reset_color_scheme
end
+ # Reset color scheme to default (+nil+)
def self.reset_color_scheme
self.color_scheme = nil
end
@@ -183,6 +185,9 @@ class HighLine
#
# 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
def agree( yes_or_no_question, character = nil )
ask(yes_or_no_question, lambda { |yn| yn.downcase[0] == ?y}) do |q|
q.validate = /\Ay(?:es)?|no?\Z/i
@@ -198,12 +203,14 @@ class HighLine
# 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 HighLine.say() for details on the format of _question_, and
- # HighLine::Question for more information about _answer_type_ and what's
+ # 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
def ask(template_or_question, answer_type = nil, &details)
question = Question.build(template_or_question, answer_type, &details)
@@ -229,6 +236,9 @@ class HighLine
#
# Raises EOFError if input is exhausted.
#
+ # @param items [Array<String>]
+ # @param details [Proc] to be passed to Menu.new
+ # @return [String] answer
def choose( *items, &details )
menu = Menu.new(&details)
menu.choices(*items) unless items.empty?
@@ -249,6 +259,10 @@ class HighLine
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
+
def shell_style_lambda(menu)
lambda do |command| # shell-style selection
first_word = command.to_s.split.first || ""
@@ -274,36 +288,65 @@ class HighLine
# This method returns the original _string_ unchanged if HighLine::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
+ # HighLine.color("Sustainable", :green, :bold)
+ # # => "\e[32m\e[1mSustainable\e[0m"
def self.color( string, *colors )
return string unless self.use_color?
Style(*colors).color(string)
end
- # In case you just want the color code, without the embedding and the CLEAR
+ # (see .color)
+ # Convenience instance method. It delegates to the class method.
+ def color(string, *colors)
+ self.class.color(string, *colors)
+ 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"
+
def self.color_code(*colors)
Style(*colors).code
end
- # Works as an instance method, same as the class method
+ # (see HighLine.color_code)
+ # Convenience instance method. It delegates to the class method.
def color_code(*colors)
self.class.color_code(*colors)
end
- # Works as an instance method, same as the class method
- def color(*args)
- self.class.color(*args)
- end
-
- # Remove color codes from a string
+ # Remove color codes from a string.
+ # @param string [String] to be decolorized
+ # @return [String] without the ANSI escape sequence (colors)
def self.uncolor(string)
Style.uncolor(string)
end
- # Works as an instance method, same as the class method
+ # (see .uncolor)
+ # Convenience instance method. It delegates to the class method.
+
def uncolor(string)
self.class.uncolor(string)
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
def list(items, mode = :rows, option = nil)
ListRenderer.new(items, mode, option, self).render
end
@@ -318,6 +361,7 @@ class 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
def say(statement)
statement = render_statement(statement)
return if statement.empty?
@@ -334,6 +378,9 @@ class HighLine
end
end
+ # Renders a statement using {HighLine::Statement}
+ # @param statement [String] any string
+ # @return [Statement] rendered statement
def render_statement(statement)
Statement.new(statement, self).to_s
end
@@ -368,6 +415,11 @@ class HighLine
#
# 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
def indent(increase=1, statement=nil, multiline=nil)
@indent_level += increase
multi = @multi_indent
@@ -413,6 +465,8 @@ class HighLine
return 24
end
+ # Call #puts on the HighLine's output stream
+ # @param args [String] same args for Kernel#puts
def puts(*args)
@output.puts(*args)
end
@@ -454,6 +508,9 @@ class HighLine
answers.respond_to?(:values) ? answers.values.last : answers.last
end
+ # Get response one line at time
+ # @param question [Question]
+ # @return [String] response
def get_response_line_mode(question)
if question.echo == true and !question.limit
get_line(question)
@@ -524,6 +581,9 @@ class HighLine
@output.print("\b#{HighLine.Style(:erase_char).code}")
end
+ # Get response using #getc
+ # @param question [Question]
+ # @return [String] response
def get_response_getc_mode(question)
terminal.raw_no_echo_mode_exec do
response = @input.getc
@@ -531,6 +591,9 @@ class HighLine
end
end
+ # Get response each character per turn
+ # @param question [Question]
+ # @return [String] response
def get_response_character_mode(question)
terminal.raw_no_echo_mode_exec do
response = terminal.get_character
diff --git a/lib/highline/builtin_styles.rb b/lib/highline/builtin_styles.rb
index 6321d1d..c829f60 100644
--- a/lib/highline/builtin_styles.rb
+++ b/lib/highline/builtin_styles.rb
@@ -1,11 +1,16 @@
#coding: utf-8
class HighLine
+ # Builtin Styles that are included at HighLine initialization.
+ # It has the basic styles like :bold and :underline.
module BuiltinStyles
+ # Included callback
+ # @param base [Class, Module] base class
def self.included(base)
base.extend ClassMethods
end
+ # Basic styles' ANSI escape codes like :bold => "\e[1m"
STYLE_LIST = {
erase_line: "\e[K",
erase_char: "\e[P",
@@ -27,8 +32,10 @@ class HighLine
const_set style + "_STYLE", Style.new(name: style_name, code: code, builtin: true)
end
+ # Basic Style names like CLEAR, BOLD, UNDERLINE
STYLES = %w{CLEAR RESET BOLD DARK UNDERLINE UNDERSCORE BLINK REVERSE CONCEALED}
+ # A Hash with the basic colors an their ANSI escape codes.
COLOR_LIST = {
black: { code: "\e[30m", rgb: [0, 0, 0] },
red: { code: "\e[31m", rgb: [128, 0, 0] },
@@ -56,6 +63,7 @@ class HighLine
const_set color + "_STYLE", style
end
+ # The builtin styles basic colors like black, red, green.
BASIC_COLORS = %w{BLACK RED GREEN YELLOW BLUE MAGENTA CYAN WHITE GRAY GREY NONE}
colors = BASIC_COLORS.dup
@@ -68,6 +76,8 @@ class HighLine
colors << light_color
const_set light_color + '_STYLE', const_get(color + '_STYLE').light
end
+
+ # The builtin styles' colors like LIGHT_RED and BRIGHT_BLUE.
COLORS = colors
colors.each do |color|
@@ -78,11 +88,17 @@ class HighLine
ON_NONE_STYLE.rgb = [255,255,255] # Override; white background
+ # BuiltinStyles class methods to be extended.
module ClassMethods
- RGB_COLOR = /^(ON_)?(RGB_)([A-F0-9]{6})(_STYLE)?$/
+ # Regexp to match against RGB style constant names.
+ RGB_COLOR_PATTERN = /^(ON_)?(RGB_)([A-F0-9]{6})(_STYLE)?$/
+
+ # const_missing callback for automatically respond to
+ # builtin constants (without explicitly defining them)
+ # @param name [Symbol] missing constant name
def const_missing(name)
- if name.to_s =~ RGB_COLOR
+ if name.to_s =~ RGB_COLOR_PATTERN
on = $1
suffix = $4
diff --git a/lib/highline/color_scheme.rb b/lib/highline/color_scheme.rb
index c947531..11cd687 100644
--- a/lib/highline/color_scheme.rb
+++ b/lib/highline/color_scheme.rb
@@ -12,16 +12,14 @@
class HighLine
#
# ColorScheme objects encapsulate a named set of colors to be used in the
- # HighLine.colors() method call. For example, by applying a ColorScheme that
+ # {HighLine.color} method call. For example, by applying a ColorScheme that
# has a <tt>:warning</tt> color then the following could be used:
#
- # colors("This is a warning", :warning)
+ # color("This is a warning", :warning)
#
# A ColorScheme contains named sets of HighLine color constants.
#
- # Example: Instantiating a color scheme, applying it to HighLine,
- # and using it:
- #
+ # @example Instantiating a color scheme, applying it to HighLine, and using it:
# ft = HighLine::ColorScheme.new do |cs|
# cs[:headline] = [ :bold, :yellow, :on_black ]
# cs[:horizontal_line] = [ :bold, :white ]
@@ -50,6 +48,7 @@ class HighLine
# converted to <tt>:symbols</tt> and values are converted to HighLine
# constants.
#
+ # @param h [Hash]
def initialize( h = nil )
@scheme = Hash.new
load_from_hash(h) if h
@@ -57,6 +56,7 @@ class HighLine
end
# Load multiple colors from key/value pairs.
+ # @param h [Hash]
def load_from_hash( h )
h.each_pair do |color_tag, constants|
self[color_tag] = constants
@@ -64,33 +64,42 @@ class HighLine
end
# Does this color scheme include the given tag name?
+ # @param color_tag [#to_sym]
+ # @return [Boolean]
def include?( color_tag )
@scheme.keys.include?(to_symbol(color_tag))
end
# Allow the scheme to be accessed like a Hash.
+ # @param color_tag [#to_sym]
+ # @return [Style]
def []( color_tag )
@scheme[to_symbol(color_tag)]
end
# Retrieve the original form of the scheme
+ # @param color_tag [#to_sym]
def definition( color_tag )
style = @scheme[to_symbol(color_tag)]
style && style.list
end
# Retrieve the keys in the scheme
+ # @return [Array] of keys
def keys
@scheme.keys
end
# Allow the scheme to be set like a Hash.
+ # @param color_tag [#to_sym]
+ # @param constants [Array<Symbol>] Array of Style symbols
def []=( color_tag, constants )
@scheme[to_symbol(color_tag)] = HighLine::Style.new(:name=>color_tag.to_s.downcase.to_sym,
:list=>constants, :no_index=>true)
end
# Retrieve the color scheme hash (in original definition format)
+ # @return [Hash] scheme as Hash. It may be reused in a new ColorScheme.
def to_hash
@scheme.inject({}) { |hsh, pair| key, value = pair; hsh[key] = value.list; hsh }
end
diff --git a/lib/highline/compatibility.rb b/lib/highline/compatibility.rb
index 1083686..33c889c 100644
--- a/lib/highline/compatibility.rb
+++ b/lib/highline/compatibility.rb
@@ -1,18 +1,23 @@
# coding: utf-8
unless STDIN.respond_to? :getbyte
+ # HighLine adds #getbyte alias to #getc when #getbyte is not available.
class IO
+ # alias to #getc when #getbyte is not available
alias_method :getbyte, :getc
end
+ # HighLine adds #getbyte alias to #getc when #getbyte is not available.
class StringIO
+ # alias to #getc when #getbyte is not available
alias_method :getbyte, :getc
end
end
unless "".respond_to? :each_line
- # Not a perfect translation, but sufficient for our needs.
+ # HighLine adds #each_line alias to #each when each_line is not available.
class String
+ # alias to #each when each_line is not available.
alias_method :each_line, :each
end
end
diff --git a/lib/highline/custom_errors.rb b/lib/highline/custom_errors.rb
index 860414f..2d45687 100644
--- a/lib/highline/custom_errors.rb
+++ b/lib/highline/custom_errors.rb
@@ -2,34 +2,52 @@ class HighLine
# Internal HighLine errors.
module CustomErrors
+ # An error that responds to :explanation_key
class ExplainableError < StandardError
+ # Explanation key as Symbol or nil. Used to
+ # select the proper error message to be displayed.
+ # @return [nil, Symbol] explanation key to get the
+ # proper error message.
+ def explanation_key
+ nil
+ end
end
+ # Bare Question error
class QuestionError < ExplainableError
+ # (see ExplainableError#explanation_key)
def explanation_key
nil
end
end
+ # Invalid Question error
class NotValidQuestionError < ExplainableError
+ # (see ExplainableError#explanation_key)
def explanation_key
:not_valid
end
end
+ # Out of Range Question error
class NotInRangeQuestionError < ExplainableError
+ # (see ExplainableError#explanation_key)
def explanation_key
:not_in_range
end
end
+ # Unconfirmed Question error
class NoConfirmationQuestionError < ExplainableError
+ # (see ExplainableError#explanation_key)
def explanation_key
nil
end
end
+ # Unavailable auto complete error
class NoAutoCompleteMatch < ExplainableError
+ # (see ExplainableError#explanation_key)
def explanation_key
:no_completion
end
diff --git a/lib/highline/import.rb b/lib/highline/import.rb
index ce6ec0e..6f31de8 100644
--- a/lib/highline/import.rb
+++ b/lib/highline/import.rb
@@ -14,11 +14,12 @@ $terminal = HighLine.new
#
# <tt>require "highline/import"</tt> adds shortcut methods to Kernel, making
-# agree(), ask(), choose() and say() globally available. This is handy for
+# {HighLine#agree}, {HighLine#ask}, {HighLine#choose} and {HighLine#say}
+# globally available. This is handy for
# quick and dirty input and output. These methods use the HighLine object in
-# the global variable <tt>$terminal</tt>, which is initialized to used
+# the global variable <tt>$terminal</tt>, which is initialized to use
# <tt>$stdin</tt> and <tt>$stdout</tt> (you are free to change this).
-# Otherwise, these methods are identical to their HighLine counterparts, see that
+# Otherwise, these methods are identical to their {HighLine} counterparts, see that
# class for detailed explanations.
#
module Kernel
@@ -26,6 +27,8 @@ module Kernel
def_delegators :$terminal, :agree, :ask, :choose, :say
end
+# When requiring 'highline/import' HighLine adds {#or_ask} to Object so
+# it is globally available.
class Object
#
# Tries this object as a _first_answer_ for a HighLine::Question. See that
@@ -33,6 +36,10 @@ class Object
#
# *Warning*: This Object will be passed to String() before set.
#
+ # @param args [Array<#to_s>]
+ # @param details [lambda] block to be called with the question
+ # instance as argument.
+ # @return [String] answer
def or_ask( *args, &details )
ask(*args) do |question|
question.first_answer = String(self)
diff --git a/lib/highline/list.rb b/lib/highline/list.rb
index 48f8780..1f2af2c 100644
--- a/lib/highline/list.rb
+++ b/lib/highline/list.rb
@@ -108,11 +108,6 @@ class HighLine
build
end
- # (see #list)
- def to_a
- list
- end
-
# Returns an Array representation of the list
# in its current state.
# @return [Array] @list.dup
@@ -120,6 +115,11 @@ class HighLine
@list.dup
end
+ # (see #list)
+ def to_a
+ list
+ end
+
# Stringfies the list in its current state.
# It joins each individual _cell_ with the current
# {#row_join_string} between them.
diff --git a/lib/highline/list_renderer.rb b/lib/highline/list_renderer.rb
index 0b1030f..fde6ad4 100644
--- a/lib/highline/list_renderer.rb
+++ b/lib/highline/list_renderer.rb
@@ -9,7 +9,21 @@ require 'highline/list'
# to be used by HighLine.
#
class HighLine::ListRenderer
- attr_reader :items, :mode, :option, :highline
+ # Items list
+ # @return [Array]
+ attr_reader :items
+
+ # @return [Symbol] the current mode the List is being rendered
+ # @see #initialize for more details see mode parameter of #initialize
+ attr_reader :mode
+
+ # Changes the behaviour of some modes. Example, in :inline mode
+ # the option is treated as the 'end separator' (defaults to " or ")
+ # @return option parameter that changes the behaviour of some modes.
+ attr_reader :option
+
+ # @return [HighLine] context
+ attr_reader :highline
# The only required parameters are _items_ and _highline_.
# @param items [Array] the Array of items to list
@@ -50,6 +64,8 @@ class HighLine::ListRenderer
@items = render_list_items(items)
end
+ # Render the list using the appropriate mode and options.
+ # @return [String] rendered list as String
def render
return "" if items.empty?
diff --git a/lib/highline/menu.rb b/lib/highline/menu.rb
index 1fb7533..3e8f5b0 100644
--- a/lib/highline/menu.rb
+++ b/lib/highline/menu.rb
@@ -144,6 +144,23 @@ class HighLine
# the current HighLine context before the action code is called and can
# thus be used for adding output and the like.
#
+ # @param name [#to_s] menu item title/header/name to be displayed.
+ # @param action [Proc] callback action to be run when the item is selected.
+ # @param help [String] help/hint string to be displayed.
+ # @return [void]
+ # @example (see HighLine::Menu#initialize)
+ # @example Use of help string on menu items
+ # cli = HighLine.new
+ # cli.choose do |menu|
+ # menu.shell = true
+ #
+ # menu.choice(:load, "Load a file.")
+ # menu.choice(:save, "Save data in file.")
+ # menu.choice(:quit, "Exit program.")
+ #
+ # menu.help("rules", "The rules of this system are as follows...")
+ # end
+
def choice( name, help = nil, &action )
@items << [name, action]
@@ -152,16 +169,25 @@ class HighLine
end
#
- # A shortcut for multiple calls to the sister method choice(). <b>Be
+ # A shortcut for multiple calls to the sister method {#choice}. <b>Be
# warned:</b> An _action_ set here will apply to *all* provided
# _names_. This is considered to be a feature, so you can easily
# hand-off interface processing to a different chunk of code.
- #
+ # @param names [Array<#to_s>] menu item titles/headers/names to be displayed.
+ # @param action (see #choice)
+ # @return [void]
+ # @example (see HighLine::Menu#initialize)
def choices( *names, &action )
names.each { |n| choice(n, &action) }
end
- # Identical to choice(), but the item will not be listed for the user.
+ # Identical to {#choice}, but the item will not be listed for the user.
+ # @see #choice
+ # @param name (see #choice)
+ # @param help (see #choice)
+ # @param action (see #choice)
+ # @return (see #choice)
+
def hidden( name, help = nil, &action )
@hidden_items << [name, action]
@@ -220,8 +246,12 @@ class HighLine
#
# Used to set help for arbitrary topics. Use the topic <tt>"help"</tt>
- # to override the default message.
+ # to override the default message. Mainly for internal use.
#
+ # @param topic [String] the menu item header/title/name to be associated with
+ # a help message.
+ # @param help [String] the help message to be associated with the menu
+ # item/title/name.
def help( topic, help )
@help[topic] = help
end
@@ -298,8 +328,13 @@ class HighLine
#
# This method processes the auto-completed user selection, based on the
# rules for this Menu object. If an action was provided for the
- # selection, it will be executed as described in Menu.choice().
+ # selection, it will be executed as described in {#choice}.
#
+ # @param highline_context [HighLine] a HighLine instance to be used as context.
+ # @param selection [String, Integer] index or title of the selected menu item.
+ # @param details additional parameter to be passed when in shell mode.
+ # @return [nil, Object] if @nil_on_handled is set it returns +nil+,
+ # else it returns the action return value.
def select( highline_context, selection, details = nil )
# add in any hidden menu commands
@items.concat(@hidden_items)
@@ -328,10 +363,14 @@ class HighLine
@items.slice!(@items.size - @hidden_items.size, @hidden_items.size)
end
+ # Returns the menu item referenced by its index
+ # @param selection [Integer] menu item's index.
def get_item_by_number(selection)
@items[selection.to_i - 1]
end
+ # Returns the menu item referenced by its title/header/name.
+ # @param selection [String] menu's title/header/name
def get_item_by_letter(selection)
l_index = "`" # character before the letter "a"
index = @items.map { "#{l_index.succ!}" }.index(selection)
diff --git a/lib/highline/paginator.rb b/lib/highline/paginator.rb
index 62a73a5..48792af 100644
--- a/lib/highline/paginator.rb
+++ b/lib/highline/paginator.rb
@@ -1,9 +1,17 @@
# coding: utf-8
class HighLine
+ # Take the task of paginating some piece of text given a HighLine context
class Paginator
+
+ # @return [HighLine] HighLine context
attr_reader :highline
+ # Returns a HighLine::Paginator instance where you can
+ # call {#page_print} on it.
+ # @param highline [HighLine] context
+ # @example
+ # HighLine::Paginator.new(highline).page_print(statement)
def initialize(highline)
@highline = highline
end
@@ -16,6 +24,8 @@ class HighLine
# Note that the final page of _output_ is *not* printed, but returned
# instead. This is to support any special handling for the final sequence.
#
+ # @param text [String] text to be paginated
+ # @return [String] last line if paging is aborted
def page_print(text)
return text unless highline.page_at
diff --git a/lib/highline/question.rb b/lib/highline/question.rb
index d9545df..769da39 100755
--- a/lib/highline/question.rb
+++ b/lib/highline/question.rb
@@ -27,6 +27,10 @@ class HighLine
# If _template_or_question_ is already a Question object just return it.
# If not, build it.
#
+ # @param template_or_question [String, Question] what to ask
+ # @param answer_type [Class] to what class to convert the answer
+ # @param details to be passed to Question.new
+ # @return [Question]
def self.build(template_or_question, answer_type = nil, &details)
if template_or_question.is_a? Question
template_or_question
@@ -42,6 +46,8 @@ class HighLine
# Question.convert(). If given, a block is yielded the new Question
# object to allow custom initialization.
#
+ # @param template [String] any String
+ # @param answer_type [Class] the type the answer will be converted to it.
def initialize(template, answer_type)
# initialize instance data
@template = String(template).dup
@@ -221,6 +227,8 @@ class HighLine
# Returns the provided _answer_string_ or the default answer for this
# Question if a default was set and the answer is empty.
#
+ # @param answer_string [String]
+ # @return [String] the answer itself or a default message.
def answer_or_default(answer_string)
return default if answer_string.empty? && default
answer_string
@@ -231,6 +239,11 @@ class HighLine
# responses based on the details of this Question object.
# Also used by Menu#update_responses.
#
+ # @return [Hash] responses Hash winner (new and old merge).
+ # @param message_source [Class] Array or String for example.
+ # Same as {#answer_type}.
+ # @param new_hash_wins [Boolean] merge precedence (new vs. old).
+
def build_responses(message_source = answer_type, new_hash_wins = false)
append_default if [::String, Symbol].include? default.class
@@ -241,6 +254,9 @@ class HighLine
@responses = new_hash_wins ? old_hash.merge(new_hash) : new_hash.merge(old_hash)
end
+ # When updating the responses hash, it generates the new one.
+ # @param message_source (see #build_responses)
+ # @return [Hash] responses hash
def build_responses_new_hash(message_source)
{ :ambiguous_completion => "Ambiguous choice. Please choose one of " +
choice_error_str(message_source) + '.',
@@ -270,6 +286,9 @@ class HighLine
#
# An unrecognized choice (like <tt>:none</tt>) is treated as +nil+.
#
+ # @param answer_string [String]
+ # @return [String] upcased, downcased, capitalized
+ # or unchanged answer String.
def change_case(answer_string)
if [:up, :upcase].include?(@case)
answer_string.upcase
@@ -315,10 +334,14 @@ class HighLine
AnswerConverter.new(self).convert
end
+ # Run {#in_range?} and raise an error if not succesful
def check_range
raise NotInRangeQuestionError unless in_range?
end
+ # Try to auto complete answer_string
+ # @param answer_string [String]
+ # @return [String]
def choices_complete(answer_string)
# cheating, using OptionParser's Completion module
choices = selection
@@ -387,6 +410,8 @@ class HighLine
#
# This process is skipped for single character input.
#
+ # @param answer_string [String]
+ # @return [String] answer string with whitespaces removed
def remove_whitespace(answer_string)
if !whitespace
answer_string
@@ -404,6 +429,10 @@ class HighLine
end
end
+ # Convert to String, remove whitespace and change case
+ # when necessary
+ # @param answer_string [String]
+ # @return [String] converted String
def format_answer(answer_string)
answer_string = String(answer_string)
answer_string = remove_whitespace(answer_string)
@@ -454,6 +483,9 @@ class HighLine
#
# Raises EOFError if input is exhausted.
#
+ # @param highline [HighLine] context
+ # @return [String] a character or line
+
def get_response(highline)
return first_answer if first_answer?
@@ -467,10 +499,21 @@ class HighLine
end
end
+ # Uses {#get_response} but returns a default answer
+ # using {#answer_or_default} in case no answers was
+ # returned.
+ #
+ # @param highline [HighLine] context
+ # @return [String]
+
def get_response_or_default(highline)
self.answer = answer_or_default(get_response(highline))
end
+ # Returns the String to be shown when asking for an answer confirmation.
+ # @param highline [HighLine] context
+ # @return [String] default "Are you sure?" if {#confirm} is +true+
+ # @return [String] {#confirm} rendered as a template if it is a String
def confirm_question(highline)
if confirm == true
"Are you sure? "
@@ -483,6 +526,10 @@ class HighLine
end
end
+ # Provides the String to be asked when at an error situation.
+ # It may be just the question itself (repeat on error).
+ # @return [self] if :ask_on_error on responses Hash is set to :question
+ # @return [String] if :ask_on_error on responses Hash is set to something else
def ask_on_error_msg
if responses[:ask_on_error] == :question
self
@@ -496,15 +543,25 @@ class HighLine
# the prompt will not be issued. And we have to account for that now.
# Also, JRuby-1.7's ConsoleReader.readLine() needs to be passed the prompt
# to handle line editing properly.
+ # @param highline [HighLine] context
+ # @return [void]
def show_question(highline)
highline.say(self) unless (readline && (echo == true && !limit))
end
+ # Returns an echo string that is adequate for this Question settings.
+ # @param response [String]
+ # @return [String] the response itself if {#echo} is +true+.
+ # @return [String] echo character if {#echo} is truethy. Mainly a String.
+ # @return [String] empty string if {#echo} is falsy.
def get_echo_for_response(response)
+ # actually true, not only truethy value
if echo == true
response
- elsif echo != false
+ # any truethy value, probably a String
+ elsif !!echo
echo
+ # any falsy value, false or nil
else
""
end
diff --git a/lib/highline/question/answer_converter.rb b/lib/highline/question/answer_converter.rb
index 17c169f..d4067f1 100644
--- a/lib/highline/question/answer_converter.rb
+++ b/lib/highline/question/answer_converter.rb
@@ -4,6 +4,7 @@ require 'forwardable'
class HighLine
class Question
+ # It provides all answer conversion flow.
class AnswerConverter
extend Forwardable
@@ -11,10 +12,19 @@ class HighLine
:answer, :answer=, :check_range,
:directory, :answer_type, :choices_complete
+ # It should be initialized with a Question object.
+ # The class will get the answer from {Question#answer}
+ # and then convert it to the proper {Question#answer_type}.
+ # It is mainly used by {Question#convert}
+ #
+ # @param question [Question]
def initialize(question)
@question = question
end
+ # Based on the given Question object's settings,
+ # it makes the conversion and returns the answer.
+ # @return [Object] the converted answer.
def convert
return unless answer_type
@@ -23,6 +33,7 @@ class HighLine
answer
end
+ # @return [HighLine::String] answer converted to a HighLine::String
def to_string
HighLine::String(answer)
end
@@ -33,37 +44,45 @@ class HighLine
HighLine::String(answer)
end
+ # @return [Integer] answer converted to an Integer
def to_integer
Kernel.send(:Integer, answer)
end
+ # @return [Float] answer converted to a Float
def to_float
Kernel.send(:Float, answer)
end
+ # @return [Symbol] answer converted to an Symbol
def to_symbol
answer.to_sym
end
+ # @return [Regexp] answer converted to a Regexp
def to_regexp
Regexp.new(answer)
end
+ # @return [File] answer converted to a File
def to_file
self.answer = choices_complete(answer)
File.open(File.join(directory.to_s, answer.last))
end
+ # @return [Pathname] answer converted to an Pathname
def to_pathname
self.answer = choices_complete(answer)
Pathname.new(File.join(directory.to_s, answer.last))
end
+ # @return [Array] answer converted to an Array
def to_array
self.answer = choices_complete(answer)
answer.last
end
+ # @return [Proc] answer converted to an Proc
def to_proc
answer_type.call(answer)
end
diff --git a/lib/highline/question_asker.rb b/lib/highline/question_asker.rb
index ab34706..b8c8ebc 100644
--- a/lib/highline/question_asker.rb
+++ b/lib/highline/question_asker.rb
@@ -1,9 +1,17 @@
class HighLine
+ # Deals with the task of "asking" a question
class QuestionAsker
+ # @return [Question] question to be asked
attr_reader :question
include CustomErrors
+ # To do its work QuestionAsker needs a Question
+ # to be asked and a HighLine context where to
+ # direct output.
+ #
+ # @param question [Question] question to be asked
+ # @param highline [HighLine] context
def initialize(question, highline)
@question = question
@highline = highline
@@ -12,6 +20,7 @@ class HighLine
#
# Gets just one answer, as opposed to #gather_answers
#
+ # @return [String] answer
def ask_once
question.show_question(@highline)
@@ -54,6 +63,7 @@ class HighLine
# Collects an Array/Hash full of answers as described in
# HighLine::Question.gather().
#
+ # @return [Array, Hash] answers
def gather_answers
original_question_template = question.template
verify_match = question.verify_match
@@ -73,12 +83,17 @@ class HighLine
question.verify_match ? @highline.send(:last_answer, answers) : answers
end
+ # Gather multiple integer values based on {Question#gather} count
+ # @return [Array] answers
def gather_integer
gather_with_array do |answers|
(question.gather-1).times { answers << ask_once }
end
end
+ # Gather multiple values until any of them matches the
+ # {Question#gather} Regexp.
+ # @return [Array] answers
def gather_regexp
gather_with_array do |answers|
answers << ask_once until answer_matches_regex(answers.last)
@@ -86,6 +101,9 @@ class HighLine
end
end
+ # Gather multiple values and store them on a Hash
+ # with keys provided by the Hash on {Question#gather}
+ # @return [Hash]
def gather_hash
answers = {}
diff --git a/lib/highline/simulate.rb b/lib/highline/simulate.rb
index 0f1049d..a39c1f1 100644
--- a/lib/highline/simulate.rb
+++ b/lib/highline/simulate.rb
@@ -17,6 +17,8 @@ class HighLine
class Simulate
# Creates a simulator with an array of Strings as a script
+ # @param strings [Array<String>] preloaded string to be used
+ # as input buffer when simulating.
def initialize(strings)
@strings = strings
end
@@ -41,7 +43,13 @@ class HighLine
false
end
- # A wrapper method that temporarily replaces the Highline instance in $terminal with an instance of this object for the duration of the block
+ # A wrapper method that temporarily replaces the Highline
+ # instance in $terminal with an instance of this object
+ # for the duration of the block
+ #
+ # @param strings [String] preloaded string buffer that
+ # will feed the input operations when simulating.
+
def self.with(*strings)
@input = $terminal.instance_variable_get :@input
$terminal.instance_variable_set :@input, new(strings)
diff --git a/lib/highline/statement.rb b/lib/highline/statement.rb
index 870495c..5e6e2f2 100644
--- a/lib/highline/statement.rb
+++ b/lib/highline/statement.rb
@@ -4,55 +4,79 @@ require 'highline/wrapper'
require 'highline/paginator'
require 'highline/template_renderer'
-class HighLine::Statement
- attr_reader :source, :highline
- attr_reader :template_string
-
- def initialize(source, highline)
- @highline = highline
- @source = source
- @template_string = stringfy(source)
- end
+class HighLine
+ # This class handles proper formatting based
+ # on a HighLine context, applying wrapping,
+ # pagination, indentation and color rendering
+ # when necessary. It's used by {HighLine#render_statement}
+ # @see HighLine#render_statement
+ class Statement
+ # The source object to be stringfied and formatted.
+ attr_reader :source
- def statement
- @statement ||= format_statement
- end
+ # The HighLine context
+ # @return [HighLine]
+ attr_reader :highline
- def to_s
- statement
- end
+ # The stringfied source object
+ # @return [String]
+ attr_reader :template_string
- private
+ # It needs the input String and the HighLine context
+ # @param source [#to_s]
+ # @param highline [HighLine] context
+ def initialize(source, highline)
+ @highline = highline
+ @source = source
+ @template_string = stringfy(source)
+ end
- def stringfy(template_string)
- String(template_string || "").dup
- end
+ # Returns the formated statement.
+ # Applies wrapping, pagination, indentation and color rendering
+ # based on HighLine instance settings.
+ # @return [String] formated statement
+ def statement
+ @statement ||= format_statement
+ end
- def format_statement
- return template_string unless template_string.length > 0
+ # (see #statement)
+ # Delegates to {#statement}
+ def to_s
+ statement
+ end
- statement = render_template
+ private
- statement = HighLine::Wrapper.wrap(statement, highline.wrap_at)
- statement = HighLine::Paginator.new(highline).page_print(statement)
+ def stringfy(template_string)
+ String(template_string || "").dup
+ end
- statement = statement.gsub(/\n(?!$)/,"\n#{highline.indentation}") if highline.multi_indent
- statement
- end
+ def format_statement
+ return template_string unless template_string.length > 0
- def render_template
- # Assigning to a local var so it may be
- # used inside instance eval block
+ statement = render_template
- template_renderer = TemplateRenderer.new(template, source, highline)
- template_renderer.render
- end
+ statement = HighLine::Wrapper.wrap(statement, highline.wrap_at)
+ statement = HighLine::Paginator.new(highline).page_print(statement)
- def template
- @template ||= ERB.new(template_string, nil, "%")
- end
+ statement = statement.gsub(/\n(?!$)/,"\n#{highline.indentation}") if highline.multi_indent
+ statement
+ end
+
+ def render_template
+ # Assigning to a local var so it may be
+ # used inside instance eval block
+
+ template_renderer = TemplateRenderer.new(template, source, highline)
+ template_renderer.render
+ end
+
+ def template
+ @template ||= ERB.new(template_string, nil, "%")
+ end
- def self.const_missing(constant)
- HighLine.const_get(constant)
+ def self.const_missing(constant)
+ HighLine.const_get(constant)
+ end
end
end \ No newline at end of file
diff --git a/lib/highline/string_extensions.rb b/lib/highline/string_extensions.rb
index ec51ced..d2f2de4 100644
--- a/lib/highline/string_extensions.rb
+++ b/lib/highline/string_extensions.rb
@@ -1,6 +1,9 @@
# coding: utf-8
class HighLine
+ # Returns a HighLine::String from any given String.
+ # @param s [String]
+ # @return [HighLine::String] from the given string.
def self.String(s)
HighLine::String.new(s)
end
@@ -8,11 +11,19 @@ class HighLine
# HighLine extensions for String class.
# Included by HighLine::String.
module StringExtensions
+ # Included hook. Actions to take when being included.
+ # @param base [Class, Module] base class
def self.included(base)
define_builtin_style_methods(base)
define_style_support_methods(base)
end
+ # At include time, it defines all basic style
+ # support method like #color, #on, #uncolor,
+ # #rgb, #on_rgb and the #method_missing callback
+ # @return [void]
+ # @param base [Class, Module] the base class/module
+ #
def self.define_style_support_methods(base)
base.class_eval do
undef :color if method_defined? :color
@@ -64,6 +75,8 @@ class HighLine
end
end
+ # At include time, it defines all basic builtin styles.
+ # @param base [Class, Module] base Class/Module
def self.define_builtin_style_methods(base)
HighLine::COLORS.each do |color|
color = color.downcase
@@ -94,6 +107,7 @@ class HighLine
end
end
+ # Adds color support to the base String class
def self.colorize_strings
::String.send(:include, StringExtensions)
end
diff --git a/lib/highline/style.rb b/lib/highline/style.rb
index 63085f0..c53024a 100755
--- a/lib/highline/style.rb
+++ b/lib/highline/style.rb
@@ -11,17 +11,25 @@
class HighLine
+ # Creates a style using {.find_or_create_style} or
+ # {.find_or_create_style_list}
+ # @param args [Array<Style, Hash, String>] style properties
+ # @return [Style]
def self.Style(*args)
args = args.compact.flatten
if args.size==1
- find_or_create_style(*args)
+ find_or_create_style(args.first)
else
find_or_create_style_list(*args)
end
end
- def self.find_or_create_style(*args)
- arg = args.first
+ # Search for a Style with the given properties and return it.
+ # If there's no matched Style, it creates one.
+ # You can pass a Style, String or a Hash.
+ # @param arg [Style, String, Hash] style properties
+ # @return [Style] found or creted Style
+ def self.find_or_create_style(arg)
if arg.is_a?(Style)
Style.list[arg.name] || Style.index(arg)
elsif arg.is_a?(::String) && arg =~ /^\e\[/ # arg is a code
@@ -45,6 +53,13 @@ class HighLine
end
end
+ # Find a Style list or create a new one.
+ # @param args [Array<Symbol>] an Array of Symbols of each style
+ # that will be on the style list.
+ # @return [Style] Style list
+ # @example Creating a Style list of the combined RED and BOLD styles.
+ # style_list = HighLine.find_or_create_style_list(:red, :bold)
+
def self.find_or_create_style_list(*args)
name = args
Style.list[name] || Style.new(:list=>args)
@@ -53,6 +68,10 @@ class HighLine
# ANSI styles to be used by HighLine.
class Style
+ # Index the given style.
+ # Uses @code_index (Hash) as repository.
+ # @param style [Style]
+ # @return [Style] the given style
def self.index(style)
if style.name
@styles ||= {}
@@ -67,6 +86,9 @@ class HighLine
style
end
+ # Clear all custom Styles, restoring the Style index to
+ # builtin styles only.
+ # @return [void]
def self.clear_index
# reset to builtin only styles
@styles = list.select { |name, style| style.builtin }
@@ -74,16 +96,40 @@ class HighLine
@styles.each { |name, style| index(style) }
end
+ # Converts all given color codes to hexadecimal and
+ # join them in a single string. If any given color code
+ # is already a String, doesn't perform any convertion.
+ #
+ # @param colors [Array<Numeric, String>] color codes
+ # @return [String] all color codes joined
+ # @example
+ # HighLine::Style.rgb_hex(9, 10, "11") # => "090a11"
def self.rgb_hex(*colors)
colors.map do |color|
color.is_a?(Numeric) ? '%02x'%color : color.to_s
end.join
end
+ # Split an rgb code string into its 3 numerical compounds.
+ # @param hex [String] rgb code string like "010F0F"
+ # @return [Array<Numeric>] numerical compounds like [1, 15, 15]
+ # @example
+ # HighLine::Style.rgb_parts("090A0B") # => [9, 10, 11]
+
def self.rgb_parts(hex)
hex.scan(/../).map{|part| part.to_i(16)}
end
+ # Search for or create a new Style from the colors provided.
+ # @param colors (see .rgb_hex)
+ # @return [Style] a Style with the rgb colors provided.
+ # @example Creating a new Style based on rgb code
+ # rgb_style = HighLine::Style.rgb(9, 10, 11)
+ #
+ # rgb_style.name # => :rgb_090a0b
+ # rgb_style.code # => "\e[38;5;16m"
+ # rgb_style.rgb # => [9, 10, 11]
+ #
def self.rgb(*colors)
hex = rgb_hex(*colors)
name = ('rgb_' + hex).to_sym
@@ -95,34 +141,58 @@ class HighLine
end
end
+ # Returns the rgb number to be used as escape code on ANSI terminals.
+ # @param parts [Array<Numeric>] three numerical codes for red, green and blue
+ # @return [Numeric] to be used as escape code on ANSI terminals
def self.rgb_number(*parts)
parts = parts.flatten
16 + parts.inject(0) {|kode, part| kode*6 + (part/256.0*6.0).floor}
end
+ # From an ANSI number (color escape code), craft an 'rgb_hex' code of it
+ # @param ansi_number [Integer] ANSI escape code
+ # @return [String] all color codes joined as {.rgb_hex}
def self.ansi_rgb_to_hex(ansi_number)
raise "Invalid ANSI rgb code #{ansi_number}" unless (16..231).include?(ansi_number)
parts = (ansi_number-16).to_s(6).rjust(3,'0').scan(/./).map{|d| (d.to_i*255.0/6.0).ceil}
rgb_hex(*parts)
end
+ # @return [Hash] list of all cached Styles
def self.list
@styles ||= {}
end
+ # @return [Hash] list of all cached Style codes
def self.code_index
@code_index ||= {}
end
+ # Remove any ANSI color escape sequence of the given String.
+ # @param string [String]
+ # @return [String]
def self.uncolor(string)
string.gsub(/\e\[\d+(;\d+)*m/, '')
end
- attr_reader :name, :list
- attr_accessor :rgb, :builtin
+ # Style name
+ # @return [Symbol] the name of the Style
+ attr_reader :name
+
+ # When a compound Style, returns a list of its components.
+ # @return [Array<Symbol>] components of a Style list
+ attr_reader :list
+
+ # @return [Array] the three numerical rgb codes. Ex: [10, 12, 127]
+ attr_accessor :rgb
+
+ # @return [Boolean] true if the Style is builtin or not.
+ attr_accessor :builtin
# Single color/styles have :name, :code, :rgb (possibly), :builtin
# Compound styles have :name, :list, :builtin
+ #
+ # @param defn [Hash] options for the Style to be created.
def initialize(defn = {})
@definition = defn
@name = defn[:name]
@@ -139,18 +209,27 @@ class HighLine
self.class.index self unless defn[:no_index]
end
+ # Duplicate current Style using the same definition used to create it.
+ # @return [Style] duplicated Style
def dup
self.class.new(@definition)
end
+ # @return [Hash] the definition used to create this Style
def to_hash
@definition
end
+ # Uses the Style definition to add ANSI color escape codes
+ # to a a given String
+ # @param string [String] to be colored
+ # @return [String] an ANSI colored string
def color(string)
code + string + HighLine::CLEAR
end
+ # @return [String] all codes of the Style list joined together (if a Style list)
+ # @return [String] the Style code
def code
if @list
@list.map{|element| HighLine.Style(element).code}.join
@@ -159,18 +238,25 @@ class HighLine
end
end
+ # @return [Integer] the RED component of the rgb code
def red
@rgb && @rgb[0]
end
+ # @return [Integer] the GREEN component of the rgb code
def green
@rgb && @rgb[1]
end
+ # @return [Integer] the BLUE component of the rgb code
def blue
@rgb && @rgb[2]
end
+ # Duplicate Style with some minor changes
+ # @param new_name [Symbol]
+ # @param options [Hash] Style attributes to be changed
+ # @return [Style] new Style with changed attributes
def variant(new_name, options={})
raise "Cannot create a variant of a style list (#{inspect})" if @list
new_code = options[:code] || code
@@ -182,15 +268,19 @@ class HighLine
self.class.new(self.to_hash.merge(:name=>new_name, :code=>new_code, :rgb=>new_rgb))
end
+ # Uses the color as background and return a new style.
+ # @return [Style]
def on
new_name = ('on_'+@name.to_s).to_sym
self.class.list[new_name] ||= variant(new_name, :increment=>10)
end
+ # @return [Style] a brighter version of this Style
def bright
create_bright_variant(:bright)
end
+ # @return [Style] a lighter version of this Style
def light
create_bright_variant(:light)
end
diff --git a/lib/highline/template_renderer.rb b/lib/highline/template_renderer.rb
index 5b495f2..9ac3970 100644
--- a/lib/highline/template_renderer.rb
+++ b/lib/highline/template_renderer.rb
@@ -3,13 +3,29 @@
require 'forwardable'
class HighLine
+ # Renders an erb template taking a {Question} and a {HighLine} instance
+ # as context.
class TemplateRenderer
extend Forwardable
def_delegators :@highline, :color, :list, :key
def_delegators :@source, :answer_type, :prompt, :header, :answer
- attr_reader :template, :source, :highline
+ # @return [ERB] ERB template being rendered
+ attr_reader :template
+
+ # @return [Question, Menu] Question instance used as context
+ attr_reader :source
+
+ # @return [HighLine] HighLine instance used as context
+ attr_reader :highline
+
+ # Initializes the TemplateRenderer object with its template and
+ # HighLine and Question contexts.
+ #
+ # @param template [ERB] ERB template.
+ # @param source [Question] question object.
+ # @param highline [HighLine] HighLine instance.
def initialize(template, source, highline)
@template = template
@@ -17,20 +33,28 @@ class HighLine
@highline = highline
end
+ # @return [String] rendered template
def render
template.result(binding)
end
+ # Returns an error message when the called method
+ # is not available.
+ # @return [String] error message.
def method_missing(method, *args)
"Method #{method} with args #{args.inspect} " +
"is not available on #{self.inspect}. " +
"Try #{methods(false).sort.inspect}"
end
+ # @return [Question, Menu] {#source} attribute.
def menu
source
end
+ # If some constant is missing at this TemplateRenderer instance,
+ # get it from HighLine. Useful to get color and style contants.
+ # @param name [Symbol] automatically passed constant's name as Symbol
def self.const_missing(name)
HighLine.const_get(name)
end
diff --git a/lib/highline/terminal.rb b/lib/highline/terminal.rb
index 7c93f22..a76a136 100755
--- a/lib/highline/terminal.rb
+++ b/lib/highline/terminal.rb
@@ -12,7 +12,14 @@
require "highline/compatibility"
class HighLine
+ # Basic Terminal class which HighLine will direct
+ # input and output to.
+ # The specialized Terminals all decend from this HighLine::Terminal class
class Terminal
+
+ # Probe for and return a suitable Terminal instance
+ # @param input [IO] input stream
+ # @param output [IO] output stream
def self.get_terminal(input, output)
terminal = nil
@@ -34,22 +41,37 @@ class HighLine
terminal
end
- attr_reader :input, :output
+ # @return [IO] input stream
+ attr_reader :input
+
+ # @return [IO] output stream
+ attr_reader :output
+ # Creates a terminal instance based on given input and output streams.
+ # @param input [IO] input stream
+ # @param output [IO] output stream
def initialize(input, output)
@input = input
@output = output
end
+ # An initialization callback.
+ # It is called by {.get_terminal}.
def initialize_system_extensions
end
+ # @return [Array<Integer, Integer>] two value terminal
+ # size like [columns, lines]
def terminal_size
+ [80, 24]
end
+ # Enter Raw No Echo mode.
def raw_no_echo_mode
end
+ # Yieds a block to be executed in Raw No Echo mode and
+ # then restore the terminal state.
def raw_no_echo_mode_exec
raw_no_echo_mode
yield
@@ -57,12 +79,20 @@ class HighLine
restore_mode
end
+ # Restore terminal to its default mode
def restore_mode
end
+ # Get one character from the terminal
+ # @return [String] one character
def get_character
end
+ # Get one line from the terminal and format accordling.
+ # Use readline if question has readline mode set.
+ # @param question [HighLine::Question]
+ # @param highline [HighLine]
+ # @param options [Hash]
def get_line(question, highline, options={})
raw_answer =
if question.readline
@@ -74,6 +104,8 @@ class HighLine
question.format_answer(raw_answer)
end
+ # Get one line using #readline_read
+ # @param (see #get_line)
def get_line_with_readline(question, highline, options={})
require "readline" # load only if needed
@@ -88,7 +120,11 @@ class HighLine
raw_answer || ""
end
- def readline_read(string, question)
+ # Use readline to read one line
+ # @param prompt [String] Readline prompt
+ # @param question [HighLine::Question] question from where to get
+ # autocomplete candidate strings
+ def readline_read(prompt, question)
# prep auto-completion
unless question.selection.empty?
Readline.completion_proc = lambda do |str|
@@ -101,7 +137,7 @@ class HighLine
$VERBOSE = nil
raw_answer = run_preserving_stty do
- Readline.readline(string, true)
+ Readline.readline(prompt, true)
end
$VERBOSE = old_verbose
@@ -109,26 +145,42 @@ class HighLine
raw_answer
end
+ # Get one line from terminal using default #gets method.
+ # @param highline (see #get_line)
def get_line_default(highline)
raise EOFError, "The input stream is exhausted." if highline.track_eof? and
highline.input.eof?
highline.input.gets
end
+ # @!group Enviroment queries
+
+ # Running on JRuby?
def jruby?
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
end
+ # Running on Rubinius?
def rubinius?
defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
end
+ # Running on Windows?
def windows?
defined?(RUBY_PLATFORM) && (RUBY_PLATFORM =~ /mswin|mingw|cygwin/)
end
+ # @!endgroup
+
+ # Returns the class name as String. Useful for debuggin.
+ # @return [String] class name. Ex: "HighLine::Terminal::IOConsole"
+ def character_mode
+ self.class.name
+ end
+
private
+ # Yield a block using stty shell commands to preserve the terminal state.
def run_preserving_stty
save_stty
yield
@@ -136,10 +188,12 @@ class HighLine
restore_stty
end
+ # Saves terminal state using shell stty command.
def save_stty
@stty_save = `stty -g`.chomp rescue nil
end
+ # Restores terminal state using shell stty command.
def restore_stty
system("stty", @stty_save) if @stty_save
end
diff --git a/lib/highline/terminal/io_console.rb b/lib/highline/terminal/io_console.rb
index c66f81e..eee152f 100644
--- a/lib/highline/terminal/io_console.rb
+++ b/lib/highline/terminal/io_console.rb
@@ -2,28 +2,28 @@
class HighLine
class Terminal
+ # io/console option for HighLine::Terminal.
+ # It's the most used terminal.
class IOConsole < Terminal
+ # (see Terminal#terminal_size)
def terminal_size
output.winsize.reverse
end
- CHARACTER_MODE = "io_console" # For Debugging purposes only.
-
+ # (see Terminal#raw_no_echo_mode)
def raw_no_echo_mode
input.echo = false
end
+ # (see Terminal#restore_mode)
def restore_mode
input.echo = true
end
+ # (see Terminal#get_character)
def get_character
input.getch # from ruby io/console
end
-
- def character_mode
- "io_console"
- end
end
end
end \ No newline at end of file
diff --git a/lib/highline/terminal/ncurses.rb b/lib/highline/terminal/ncurses.rb
index db9163c..817cc23 100644
--- a/lib/highline/terminal/ncurses.rb
+++ b/lib/highline/terminal/ncurses.rb
@@ -1,25 +1,25 @@
# coding: utf-8
class HighLine
- module SystemExtensions
-
- # @todo Code to be discussed.
- # Will we maintain an ncurses version of HighLine::Terminal?
- # If so, port it to the new api.
- module NCurses
+ class Terminal
+ # NCurses HighLine::Terminal
+ # @note Code migrated +UNTESTED+ from the old code base to the new terminal api.
+ class NCurses < Terminal
require 'ffi-ncurses'
- CHARACTER_MODE = "ncurses" # For Debugging purposes only.
+ # (see Terminal#raw_no_echo_mode)
def raw_no_echo_mode
FFI::NCurses.initscr
FFI::NCurses.cbreak
end
+ # (see Terminal#restore_mode)
def restore_mode
FFI::NCurses.endwin
end
#
+ # (see Terminal#terminal_size)
# A ncurses savvy method to fetch the console columns, and rows.
#
def terminal_size
diff --git a/lib/highline/terminal/unix_stty.rb b/lib/highline/terminal/unix_stty.rb
index 4f567a6..3b9668a 100644
--- a/lib/highline/terminal/unix_stty.rb
+++ b/lib/highline/terminal/unix_stty.rb
@@ -2,10 +2,13 @@
class HighLine
class Terminal
+ # HighLine::Terminal option that uses external "stty" program
+ # to control terminal options.
class UnixStty < Terminal
# A Unix savvy method using stty to fetch the console columns, and rows.
# ... stty does not work in JRuby
+ # @return (see Terminal#terminal_size)
def terminal_size
begin
require "io/console"
@@ -24,26 +27,22 @@ class HighLine
end
end
- # *WARNING*: This requires the external "stty" program!
- CHARACTER_MODE = "unix_stty" # For Debugging purposes only.
-
+ # (see Terminal#raw_no_echo_mode)
def raw_no_echo_mode
@state = `stty -g`
system "stty raw -echo -icanon isig"
end
+ # (see Terminal#restore_mode)
def restore_mode
system "stty #{@state}"
print "\r"
end
+ # (see Terminal#get_character)
def get_character( input = STDIN )
input.getc
end
-
- def character_mode
- "unix_stty"
- end
end
end
end \ No newline at end of file
diff --git a/lib/highline/version.rb b/lib/highline/version.rb
index 0ecac1c..9c8315c 100644
--- a/lib/highline/version.rb
+++ b/lib/highline/version.rb
@@ -2,5 +2,5 @@
class HighLine
# The version of the installed library.
- VERSION = "2.0.0-develop.3".freeze
+ VERSION = "2.0.0-develop.4".freeze
end
diff --git a/lib/highline/wrapper.rb b/lib/highline/wrapper.rb
index 98f713f..ae93db6 100644
--- a/lib/highline/wrapper.rb
+++ b/lib/highline/wrapper.rb
@@ -1,6 +1,10 @@
# coding: utf-8
class HighLine
+
+ # A simple Wrapper module that is aware of ANSI escape codes.
+ # It compensates for the ANSI escape codes so it works on the
+ # actual (visual) line length.
module Wrapper
#
@@ -8,6 +12,8 @@ class HighLine
# newlines will not be affected by this process, but additional newlines
# may be added.
#
+ # @param text [String] text to be wrapped
+ # @param wrap_at [#to_i] column count to wrap the text into
def self.wrap(text, wrap_at)
return text unless wrap_at
wrap_at = Integer(wrap_at)
@@ -36,6 +42,9 @@ class HighLine
# Returns the length of the passed +string_with_escapes+, minus and color
# sequence escapes.
#
+ # @param string_with_escapes [String] any ANSI colored String
+ # @return [Integer] length based on the visual size of the String
+ # (without the escape codes)
def self.actual_length( string_with_escapes )
string_with_escapes.to_s.gsub(/\e\[\d{1,2}m/, "").length
end
diff --git a/test/test_highline.rb b/test/test_highline.rb
index 19fd989..a5101c1 100755
--- a/test/test_highline.rb
+++ b/test/test_highline.rb
@@ -959,7 +959,7 @@ class TestHighLine < Minitest::Test
end
def test_mode
- assert(%w[io_console Win32API termios ncurses stty unix_stty jline].include?(@terminal.terminal.character_mode),
+ assert(%w[HighLine::Terminal::IOConsole HighLine::Terminal::NCurses HighLine::Terminal::UnixStty].include?(@terminal.terminal.character_mode),
"#{@terminal.terminal.character_mode} not in list")
end