diff options
author | James Edward Gray II <james@grayproductions.net> | 2015-12-14 17:08:36 -0600 |
---|---|---|
committer | James Edward Gray II <james@grayproductions.net> | 2015-12-14 17:08:36 -0600 |
commit | 818a3befcc7ba75f60e119f8fc8ea07bb699de2b (patch) | |
tree | 0a115f176b1b3aea271e840a38f8ecf6ec013abf | |
parent | 7ba5aa219d9058a8d46d24f39efcc36bcf6a87ee (diff) | |
parent | 0cfd33e8d5d368ea572070f3af6313c0687e1345 (diff) | |
download | highline-818a3befcc7ba75f60e119f8fc8ea07bb699de2b.tar.gz |
Merge pull request #179 from abinoam/feat_improve_docsv2.0.0.pre.develop.4
Improve documentation grade on inch
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 |