diff options
Diffstat (limited to 'lib')
26 files changed, 657 insertions, 587 deletions
diff --git a/lib/highline.rb b/lib/highline.rb index a94bbed..b51a1a0 100755 --- a/lib/highline.rb +++ b/lib/highline.rb @@ -10,6 +10,7 @@ # # This is Free Software. See LICENSE and COPYING for details. +require "English" require "erb" require "optparse" require "stringio" @@ -29,17 +30,17 @@ require "highline/builtin_styles" # # A HighLine object is a "high-level line oriented" shell over an input and an # output stream. HighLine simplifies common console interaction, effectively -# replacing {Kernel#puts} and {Kernel#gets}. User code can simply specify the question to ask -# and any details about user interaction, then leave the rest of the work to -# HighLine. When {HighLine#ask} returns, you'll have the answer you requested, -# even if HighLine had to ask many times, validate results, perform range -# checking, convert types, etc. +# replacing {Kernel#puts} and {Kernel#gets}. User code can simply specify the +# question to ask and any details about user interaction, then leave the rest +# of the work to HighLine. When {HighLine#ask} returns, you'll have the answer +# you requested, even if HighLine had to ask many times, validate results, +# perform range checking, convert types, etc. # # @example Basic usage # cli = HighLine.new # answer = cli.ask "What do you think?" # puts "You have answered: #{answer}" - +# class HighLine include BuiltinStyles include CustomErrors @@ -58,7 +59,7 @@ class HighLine # Returns +true+ if HighLine is currently using a color scheme. def using_color_scheme? - !!@color_scheme + true if @color_scheme end # Reset color scheme to default (+nil+) @@ -75,7 +76,8 @@ class HighLine end # For checking if the current version of HighLine supports RGB colors - # Usage: HighLine.supports_rgb_color? rescue false # rescue for compatibility with older versions + # Usage: HighLine.supports_rgb_color? rescue false + # using rescue for compatibility with older versions # Note: color usage also depends on HighLine.use_color being set # TODO: Discuss removing this method def supports_rgb_color? @@ -97,8 +99,9 @@ class HighLine # @param page_at [Integer] page size and paginating. # @param indent_size [Integer] indentation size in spaces. # @param indent_level [Integer] how deep is indentated. - def initialize( input = $stdin, output = $stdout, - wrap_at = nil, page_at = nil, indent_size=3, indent_level=0 ) + def initialize(input = $stdin, output = $stdout, + wrap_at = nil, page_at = nil, + indent_size = 3, indent_level = 0) @input = input @output = output @@ -123,7 +126,7 @@ class HighLine # Returns truethy if HighLine instance is currently using color escapes. def use_color? - !!use_color + use_color end # Resets the use of color. @@ -136,7 +139,7 @@ class HighLine # Returns true if HighLine is currently tracking EOF for input. def track_eof? - !!track_eof + true if track_eof end # @return [Integer] The current column setting for wrapping output. @@ -182,11 +185,13 @@ 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 + # @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| + def agree(yes_or_no_question, character = nil) + ask(yes_or_no_question, ->(yn) { yn.downcase[0] == "y" }) do |q| q.validate = /\A(?:y(?:es)?|no?)\Z/i q.responses[:not_valid] = 'Please enter "yes" or "no".' q.responses[:ask_on_error] = :question @@ -236,7 +241,7 @@ class HighLine # @param items [Array<String>] # @param details [Proc] to be passed to Menu.new # @return [String] answer - def choose( *items, &details ) + def choose(*items, &details) menu = Menu.new(&details) menu.choices(*items) unless items.empty? @@ -268,7 +273,7 @@ class HighLine # @return [lambda] lambda to be used in autocompletion operations def shell_style_lambda(menu) - lambda do |command| # shell-style selection + lambda do |command| # shell-style selection first_word = command.to_s.split.first || "" options = menu.options @@ -359,7 +364,7 @@ class HighLine statement = render_statement(statement) return if statement.empty? - statement = (indentation+statement) + statement = (indentation + statement) # Don't add a newline if statement ends with whitespace, OR # if statement ends with whitespace before a color escape code. @@ -384,7 +389,7 @@ class HighLine # set to <tt>:auto</tt>, HighLine will attempt to determine the columns # available for the <tt>@output</tt> or use a sensible default. # - def wrap_at=( setting ) + def wrap_at=(setting) @wrap_at = setting == :auto ? output_cols : setting end @@ -394,7 +399,7 @@ class HighLine # set to <tt>:auto</tt>, HighLine will attempt to determine the rows available # for the <tt>@output</tt> or use a sensible default. # - def page_at=( setting ) + def page_at=(setting) @page_at = setting == :auto ? output_rows - 2 : setting end @@ -402,7 +407,7 @@ class HighLine # Outputs indentation with current settings # def indentation - ' '*@indent_size*@indent_level + " " * @indent_size * @indent_level end # @@ -413,7 +418,7 @@ class HighLine # @param multiline [Boolean] # @return [void] # @see #multi_indent - def indent(increase=1, statement=nil, multiline=nil) + def indent(increase = 1, statement = nil, multiline = nil) @indent_level += increase multi = @multi_indent @multi_indent ||= multiline @@ -443,7 +448,7 @@ class HighLine def output_cols return 80 unless @output.tty? terminal.terminal_size.first - rescue + rescue NoMethodError return 80 end @@ -454,7 +459,7 @@ class HighLine def output_rows return 24 unless @output.tty? terminal.terminal_size.last - rescue + rescue NoMethodError return 24 end @@ -468,7 +473,8 @@ class HighLine # Creates a new HighLine instance with the same options # def new_scope - self.class.new(@input, @output, @wrap_at, @page_at, @indent_size, @indent_level) + self.class.new(@input, @output, @wrap_at, + @page_at, @indent_size, @indent_level) end private @@ -505,7 +511,7 @@ class HighLine # @param question [Question] # @return [String] response def get_response_line_mode(question) - if question.echo == true and !question.limit + if question.echo == true && !question.limit get_line(question) else get_line_raw_no_echo_mode(question) @@ -529,13 +535,15 @@ class HighLine line = "" terminal.raw_no_echo_mode_exec do - while character = terminal.get_character + loop do + character = terminal.get_character + break unless character break if ["\n", "\r"].include? character # honor backspace and delete if character == "\b" chopped = line.chop! - output_erase_char if chopped and question.echo + output_erase_char if chopped && question.echo else line << character say_last_char_or_echo_char(line, question) @@ -563,11 +571,11 @@ class HighLine def say_last_char_or_echo_char(line, question) @output.print(line[-1]) if question.echo == true - @output.print(question.echo) if question.echo and question.echo != true + @output.print(question.echo) if question.echo && question.echo != true end def line_overflow_for_question?(line, question) - question.limit and line.size == question.limit + question.limit && line.size == question.limit end def output_erase_char diff --git a/lib/highline/builtin_styles.rb b/lib/highline/builtin_styles.rb index c829f60..71c2429 100644 --- a/lib/highline/builtin_styles.rb +++ b/lib/highline/builtin_styles.rb @@ -1,4 +1,4 @@ -#coding: utf-8 +# coding: utf-8 class HighLine # Builtin Styles that are included at HighLine initialization. @@ -23,17 +23,19 @@ class HighLine blink: "\e[5m", reverse: "\e[7m", concealed: "\e[8m" - } + }.freeze STYLE_LIST.each do |style_name, code| - style = String(style_name).upcase + style = String(style_name).upcase - const_set style, code - const_set style + "_STYLE", Style.new(name: style_name, code: code, builtin: true) + const_set style, code + 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} + STYLES = %w[CLEAR RESET BOLD DARK UNDERLINE + UNDERSCORE BLINK REVERSE CONCEALED].freeze # A Hash with the basic colors an their ANSI escape codes. COLOR_LIST = { @@ -48,33 +50,35 @@ class HighLine gray: { code: "\e[37m", rgb: [192, 192, 192] }, grey: { code: "\e[37m", rgb: [192, 192, 192] }, none: { code: "\e[38m", rgb: [0, 0, 0] } - } + }.freeze COLOR_LIST.each do |color_name, attributes| - color = String(color_name).upcase + color = String(color_name).upcase - style = Style.new( - name: color_name, - code: attributes[:code], - rgb: attributes[:rgb], - builtin: true - ) + style = Style.new( + name: color_name, + code: attributes[:code], + rgb: attributes[:rgb], + builtin: true + ) - const_set color + "_STYLE", style + 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} + BASIC_COLORS = + %w[BLACK RED GREEN YELLOW BLUE + MAGENTA CYAN WHITE GRAY GREY NONE].freeze colors = BASIC_COLORS.dup BASIC_COLORS.each do |color| bright_color = "BRIGHT_#{color}" colors << bright_color - const_set bright_color + '_STYLE', const_get(color + '_STYLE').bright + const_set bright_color + "_STYLE", const_get(color + "_STYLE").bright light_color = "LIGHT_#{color}" colors << light_color - const_set light_color + '_STYLE', const_get(color + '_STYLE').light + const_set light_color + "_STYLE", const_get(color + "_STYLE").light end # The builtin styles' colors like LIGHT_RED and BRIGHT_BLUE. @@ -86,11 +90,10 @@ class HighLine const_set "ON_#{color}", const_get("ON_#{color}_STYLE").code end - ON_NONE_STYLE.rgb = [255,255,255] # Override; white background + ON_NONE_STYLE.rgb = [255, 255, 255] # Override; white background # BuiltinStyles class methods to be extended. module ClassMethods - # Regexp to match against RGB style constant names. RGB_COLOR_PATTERN = /^(ON_)?(RGB_)([A-F0-9]{6})(_STYLE)?$/ @@ -98,27 +101,28 @@ class HighLine # builtin constants (without explicitly defining them) # @param name [Symbol] missing constant name def const_missing(name) - if name.to_s =~ RGB_COLOR_PATTERN - on = $1 - suffix = $4 - - if suffix - code_name = $1.to_s + $2 + $3 - else - code_name = name.to_s - end - - style_name = code_name + '_STYLE' - style = Style.rgb($3) - style = style.on if on - - const_set(style_name, style) - const_set(code_name, style.code) - - suffix ? style : style.code - else - raise NameError, "Bad color or uninitialized constant #{name}" - end + raise NameError, "Bad color or uninitialized constant #{name}" unless + name.to_s =~ RGB_COLOR_PATTERN + + on = Regexp.last_match(1) + suffix = Regexp.last_match(4) + + code_name = if suffix + Regexp.last_match(1).to_s + + Regexp.last_match(2) + + Regexp.last_match(3) + else + name.to_s + end + + style_name = code_name + "_STYLE" + style = Style.rgb(Regexp.last_match(3)) + style = style.on if on + + const_set(style_name, style) + const_set(code_name, style.code) + + suffix ? style : style.code end end end diff --git a/lib/highline/color_scheme.rb b/lib/highline/color_scheme.rb index 11cd687..e1bec88 100644 --- a/lib/highline/color_scheme.rb +++ b/lib/highline/color_scheme.rb @@ -8,7 +8,6 @@ # # This is Free Software. See LICENSE and COPYING for details - class HighLine # # ColorScheme objects encapsulate a named set of colors to be used in the @@ -19,7 +18,8 @@ class HighLine # # 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 ] @@ -49,15 +49,15 @@ class HighLine # constants. # # @param h [Hash] - def initialize( h = nil ) - @scheme = Hash.new + def initialize(h = nil) + @scheme = {} load_from_hash(h) if h yield self if block_given? end # Load multiple colors from key/value pairs. # @param h [Hash] - def load_from_hash( h ) + def load_from_hash(h) h.each_pair do |color_tag, constants| self[color_tag] = constants end @@ -66,20 +66,20 @@ class HighLine # Does this color scheme include the given tag name? # @param color_tag [#to_sym] # @return [Boolean] - def include?( color_tag ) + 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 ) + 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 ) + def definition(color_tag) style = @scheme[to_symbol(color_tag)] style && style.list end @@ -93,29 +93,33 @@ class HighLine # 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) + 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 } + @scheme.each_with_object({}) do |pair, hsh| + key, value = pair + hsh[key] = value.list + end end - private # Return a normalized representation of a color name. - def to_symbol( t ) + def to_symbol(t) t.to_s.downcase end # Return a normalized representation of a color setting. - def to_constant( v ) + def to_constant(v) v = v.to_s if v.is_a?(Symbol) - if v.is_a?(::String) then + if v.is_a?(::String) HighLine.const_get(v.upcase) else v @@ -125,23 +129,23 @@ class HighLine # A sample ColorScheme. class SampleColorScheme < ColorScheme + SAMPLE_SCHEME = { + critical: [:yellow, :on_red], + error: [:bold, :red], + warning: [:bold, :yellow], + notice: [:bold, :magenta], + info: [:bold, :cyan], + debug: [:bold, :green], + row_even: [:cyan], + row_odd: [:magenta] + }.freeze # # Builds the sample scheme with settings for <tt>:critical</tt>, # <tt>:error</tt>, <tt>:warning</tt>, <tt>:notice</tt>, <tt>:info</tt>, # <tt>:debug</tt>, <tt>:row_even</tt>, and <tt>:row_odd</tt> colors. # - def initialize( h = nil ) - scheme = { - :critical => [ :yellow, :on_red ], - :error => [ :bold, :red ], - :warning => [ :bold, :yellow ], - :notice => [ :bold, :magenta ], - :info => [ :bold, :cyan ], - :debug => [ :bold, :green ], - :row_even => [ :cyan ], - :row_odd => [ :magenta ] - } - super(scheme) + def initialize(_h = nil) + super(SAMPLE_SCHEME) end end end diff --git a/lib/highline/compatibility.rb b/lib/highline/compatibility.rb index 33c889c..ee13fd6 100644 --- a/lib/highline/compatibility.rb +++ b/lib/highline/compatibility.rb @@ -4,13 +4,13 @@ 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 + alias 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 + alias getbyte getc end end @@ -18,6 +18,6 @@ unless "".respond_to? :each_line # 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 + alias each_line each end end diff --git a/lib/highline/custom_errors.rb b/lib/highline/custom_errors.rb index 2d45687..28e97df 100644 --- a/lib/highline/custom_errors.rb +++ b/lib/highline/custom_errors.rb @@ -1,5 +1,6 @@ -class HighLine +# encoding: utf-8 +class HighLine # Internal HighLine errors. module CustomErrors # An error that responds to :explanation_key diff --git a/lib/highline/import.rb b/lib/highline/import.rb index c2e6084..46cb3db 100644 --- a/lib/highline/import.rb +++ b/lib/highline/import.rb @@ -38,11 +38,11 @@ class Object # @param details [lambda] block to be called with the question # instance as argument. # @return [String] answer - def or_ask( *args, &details ) + def or_ask(*args, &details) ask(*args) do |question| question.first_answer = String(self) - details.call(question) if details + yield(question) if details end end end diff --git a/lib/highline/list.rb b/lib/highline/list.rb index 1f2af2c..6ca876f 100644 --- a/lib/highline/list.rb +++ b/lib/highline/list.rb @@ -1,10 +1,8 @@ # coding: utf-8 class HighLine - # List class with some convenience methods like {#col_down}. class List - # Original given *items* argument. # It's frozen at initialization time and # all later transformations will happen on {#list}. @@ -33,7 +31,6 @@ class HighLine attr_reader :transpose_mode - # Content are distributed first by column in col down mode. # @return [Boolean] # @@ -69,7 +66,8 @@ class HighLine build end - # Transpose the (already sliced by rows) list, turning its rows into columns. + # Transpose the (already sliced by rows) list, + # turning its rows into columns. # @return [self] def transpose first_row = @list[0] @@ -141,9 +139,7 @@ class HighLine # Set the {#row_join_string}. # @see #row_join_string - def row_join_string=(string) - @row_join_string = string - end + attr_writer :row_join_string # Returns the row join string size. # Useful for calculating the actual size of @@ -178,4 +174,4 @@ class HighLine row.compact.join(row_join_string) + "\n" end end -end
\ No newline at end of file +end diff --git a/lib/highline/list_renderer.rb b/lib/highline/list_renderer.rb index fde6ad4..cc53bf0 100644 --- a/lib/highline/list_renderer.rb +++ b/lib/highline/list_renderer.rb @@ -1,251 +1,257 @@ # coding: utf-8 -require 'highline/template_renderer' -require 'highline/wrapper' -require 'highline/list' - -# -# This class is a utility for quickly and easily laying out lists -# to be used by HighLine. -# -class HighLine::ListRenderer - # 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 - # @param mode [Symbol] controls how that list is formed - # @param option has different effects, depending on the _mode_. - # @param highline [HighLine] a HighLine instance to direct the output to. - # - # Recognized modes are: +require "highline/template_renderer" +require "highline/wrapper" +require "highline/list" + +class HighLine # - # <tt>:columns_across</tt>:: _items_ will be placed in columns, - # flowing from left to right. If given, - # _option_ is the number of columns to be - # used. When absent, columns will be - # determined based on _wrap_at_ or a - # default of 80 characters. - # <tt>:columns_down</tt>:: Identical to <tt>:columns_across</tt>, - # save flow goes down. - # <tt>:uneven_columns_across</tt>:: Like <tt>:columns_across</tt> but each - # column is sized independently. - # <tt>:uneven_columns_down</tt>:: Like <tt>:columns_down</tt> but each - # column is sized independently. - # <tt>:inline</tt>:: All _items_ are placed on a single line. - # The last two _items_ are separated by - # _option_ or a default of " or ". All - # other _items_ are separated by ", ". - # <tt>:rows</tt>:: The default mode. Each of the _items_ is - # placed on its own line. The _option_ - # parameter is ignored in this mode. + # This class is a utility for quickly and easily laying out lists + # to be used by HighLine. # - # Each member of the _items_ Array is passed through ERb and thus can contain - # their own expansions. Color escape expansions do not contribute to the - # final field width. - - def initialize(items, mode = :rows, option = nil, highline) - @highline = highline - @mode = mode - @option = option - @items = render_list_items(items) - end + class ListRenderer + # 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 + # @param mode [Symbol] controls how that list is formed + # @param option has different effects, depending on the _mode_. + # @param highline [HighLine] a HighLine instance to direct the output to. + # + # Recognized modes are: + # + # <tt>:columns_across</tt>:: _items_ will be placed in columns, + # flowing from left to right. If given, + # _option_ is the number of columns to be + # used. When absent, columns will be + # determined based on _wrap_at_ or a + # default of 80 characters. + # <tt>:columns_down</tt>:: Identical to <tt>:columns_across</tt>, + # save flow goes down. + # <tt>:uneven_columns_across</tt>:: Like <tt>:columns_across</tt> but each + # column is sized independently. + # <tt>:uneven_columns_down</tt>:: Like <tt>:columns_down</tt> but each + # column is sized independently. + # <tt>:inline</tt>:: All _items_ are placed on a single + # line. The last two _items_ are + # separated by _option_ or a default of + # " or ". All other _items_ are + # separated by ", ". + # <tt>:rows</tt>:: The default mode. Each of the _items_ + # is placed on its own line. The _option_ + # parameter is ignored in this mode. + # + # Each member of the _items_ Array is passed through ERb and thus can + # contain their own expansions. Color escape expansions do not contribute to + # the final field width. + + def initialize(items, mode = :rows, option = nil, highline) + @highline = highline + @mode = mode + @option = option + @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? - - case mode - when :inline - list_inline_mode - when :columns_across - list_columns_across_mode - when :columns_down - list_columns_down_mode - when :uneven_columns_across - list_uneven_columns_mode - when :uneven_columns_down - list_uneven_columns_down_mode - else - list_default_mode + # Render the list using the appropriate mode and options. + # @return [String] rendered list as String + def render + return "" if items.empty? + + case mode + when :inline + list_inline_mode + when :columns_across + list_columns_across_mode + when :columns_down + list_columns_down_mode + when :uneven_columns_across + list_uneven_columns_mode + when :uneven_columns_down + list_uneven_columns_down_mode + else + list_default_mode + end end - end - private + private - def render_list_items(items) - items.to_ary.map do |item| - item = String(item) - template = ERB.new(item, nil, "%") - template_renderer = HighLine::TemplateRenderer.new(template, self, highline) - template_renderer.render + def render_list_items(items) + items.to_ary.map do |item| + item = String(item) + template = ERB.new(item, nil, "%") + template_renderer = + HighLine::TemplateRenderer.new(template, self, highline) + template_renderer.render + end end - end - def list_default_mode - items.map { |i| "#{i}\n" }.join - end + def list_default_mode + items.map { |i| "#{i}\n" }.join + end - def list_inline_mode - end_separator = option || " or " + def list_inline_mode + end_separator = option || " or " - if items.size == 1 - items.first - else - items[0..-2].join(", ") + "#{end_separator}#{items.last}" + if items.size == 1 + items.first + else + items[0..-2].join(", ") + "#{end_separator}#{items.last}" + end end - end - def list_columns_across_mode - HighLine::List.new(right_padded_items, cols: col_count).to_s - end + def list_columns_across_mode + HighLine::List.new(right_padded_items, cols: col_count).to_s + end - def list_columns_down_mode - HighLine::List.new(right_padded_items, cols: col_count, col_down: true).to_s - end + def list_columns_down_mode + HighLine::List.new( + right_padded_items, + cols: col_count, + col_down: true + ).to_s + end - def list_uneven_columns_mode(list=nil) - list ||= HighLine::List.new(items) + def list_uneven_columns_mode(list = nil) + list ||= HighLine::List.new(items) - col_max = option || items.size - col_max.downto(1) do |column_count| - list.cols = column_count - widths = get_col_widths(list) + col_max = option || items.size + col_max.downto(1) do |column_count| + list.cols = column_count + widths = get_col_widths(list) - if column_count == 1 or # last guess - inside_line_size_limit?(widths) or # good guess - option # defined by user - return pad_uneven_rows(list, widths) + if column_count == 1 || # last guess + inside_line_size_limit?(widths) || # good guess + option # defined by user + return pad_uneven_rows(list, widths) + end end end - end - def list_uneven_columns_down_mode - list = HighLine::List.new(items, col_down: true) - list_uneven_columns_mode(list) - end + def list_uneven_columns_down_mode + list = HighLine::List.new(items, col_down: true) + list_uneven_columns_mode(list) + end - def pad_uneven_rows(list, widths) - right_padded_list = Array(list).map do |row| - right_pad_row(row.compact, widths) + def pad_uneven_rows(list, widths) + right_padded_list = Array(list).map do |row| + right_pad_row(row.compact, widths) + end + stringfy_list(right_padded_list) end - stringfy_list(right_padded_list) - end - def stringfy_list(list) - list.map { |row| row_to_s(row) }.join - end + def stringfy_list(list) + list.map { |row| row_to_s(row) }.join + end - def row_to_s(row) - row.compact.join(row_join_string) + "\n" - end + def row_to_s(row) + row.compact.join(row_join_string) + "\n" + end - def right_pad_row(row, widths) - row.zip(widths).map do |field, width| - right_pad_field(field, width) + def right_pad_row(row, widths) + row.zip(widths).map do |field, width| + right_pad_field(field, width) + end end - end - def right_pad_field(field, width) - field = String(field) # nil protection - pad_size = width - actual_length(field) - field + (pad_char * pad_size) - end + def right_pad_field(field, width) + field = String(field) # nil protection + pad_size = width - actual_length(field) + field + (pad_char * pad_size) + end - def get_col_widths(lines) - lines = transpose(lines) - get_segment_widths(lines) - end + def get_col_widths(lines) + lines = transpose(lines) + get_segment_widths(lines) + end - def get_row_widths(lines) - get_segment_widths(lines) - end + def get_row_widths(lines) + get_segment_widths(lines) + end - def get_segment_widths(lines) - lines.map do |col| - actual_lengths_for(col).max + def get_segment_widths(lines) + lines.map do |col| + actual_lengths_for(col).max + end end - end - def actual_lengths_for(line) - line.map do |item| - actual_length(item) + def actual_lengths_for(line) + line.map do |item| + actual_length(item) + end end - end - def transpose(lines) - lines = Array(lines) - first_line = lines.shift - first_line.zip(*lines) - end + def transpose(lines) + lines = Array(lines) + first_line = lines.shift + first_line.zip(*lines) + end - def inside_line_size_limit?(widths) - line_size = widths.inject(0) { |sum, n| sum + n + row_join_str_size } - line_size <= line_size_limit + row_join_str_size - end + def inside_line_size_limit?(widths) + line_size = widths.reduce(0) { |sum, n| sum + n + row_join_str_size } + line_size <= line_size_limit + row_join_str_size + end - def actual_length(text) - HighLine::Wrapper.actual_length text - end + def actual_length(text) + HighLine::Wrapper.actual_length text + end - def items_max_length - @items_max_length ||= max_length(items) - end + def items_max_length + @items_max_length ||= max_length(items) + end - def max_length(items) - items.map { |item| actual_length(item) }.max - end + def max_length(items) + items.map { |item| actual_length(item) }.max + end - def line_size_limit - @line_size_limit ||= ( highline.wrap_at || 80 ) - end + def line_size_limit + @line_size_limit ||= (highline.wrap_at || 80) + end - def row_join_string - @row_join_string ||= " " - end + def row_join_string + @row_join_string ||= " " + end - def row_join_string=(string) - @row_join_string = string - end + attr_writer :row_join_string - def row_join_str_size - row_join_string.size - end + def row_join_str_size + row_join_string.size + end - def get_col_count - (line_size_limit + row_join_str_size) / - (items_max_length + row_join_str_size) - end + def col_count_calculate + (line_size_limit + row_join_str_size) / + (items_max_length + row_join_str_size) + end - def col_count - option || get_col_count - end + def col_count + option || col_count_calculate + end - def right_padded_items - items.map do |item| - right_pad_field(item, items_max_length) + def right_padded_items + items.map do |item| + right_pad_field(item, items_max_length) + end end - end - def pad_char - " " - end + def pad_char + " " + end - def row_count - (items.count / col_count.to_f).ceil + def row_count + (items.count / col_count.to_f).ceil + end end -end
\ No newline at end of file +end diff --git a/lib/highline/menu.rb b/lib/highline/menu.rb index e1834ef..5f73cd7 100644 --- a/lib/highline/menu.rb +++ b/lib/highline/menu.rb @@ -13,7 +13,8 @@ require "highline/menu/item" class HighLine # - # Menu objects encapsulate all the details of a call to {HighLine#choose HighLine#choose}. + # Menu objects encapsulate all the details of a call to + # {HighLine#choose HighLine#choose}. # Using the accessors and {Menu#choice} and {Menu#choices}, the block passed # to {HighLine#choose} can detail all aspects of menu display and control. # @@ -21,16 +22,16 @@ class HighLine # Pass +false+ to _color_ to turn off HighLine::Menu's # index coloring. # Pass a color and the Menu's indices will be colored. - def self.index_color=(color = :rgb_77bbff) - @index_color = color + class << self + attr_writer :index_color end # Initialize it self.index_color = false # Returns color used for coloring Menu's indices - def self.index_color - @index_color + class << self + attr_reader :index_color end # @@ -53,10 +54,10 @@ class HighLine # Initialize Question objects with ignored values, we'll # adjust ours as needed. # - super("Ignored", [ ], &nil) # avoiding passing the block along + super("Ignored", [], &nil) # avoiding passing the block along - @items = [ ] - @hidden_items = [ ] + @items = [] + @hidden_items = [] @help = Hash.new("There's no help for that topic.") @index = :number @@ -75,13 +76,13 @@ class HighLine @index_color = self.class.index_color # Override Questions responses, we'll set our own. - @responses = { } + @responses = {} # Context for action code. @highline = nil yield self if block_given? - init_help if @shell and not @help.empty? + init_help if @shell && !@help.empty? end # @@ -179,26 +180,27 @@ class HighLine # cli.choose do |menu| # menu.shell = true # - # menu.choice(:load, text: 'Load a file', help: "Load a file using your favourite editor.") + # menu.choice(:load, text: 'Load a file', + # help: "Load a file using your favourite editor.") # menu.choice(:save, help: "Save data in file.") # menu.choice(:quit, help: "Exit program.") # # menu.help("rules", "The rules of this system are as follows...") # end - def choice( name, help = nil, text = nil, &action ) + def choice(name, help = nil, text = nil, &action) item = Menu::Item.new(name, text: text, help: help, action: action) @items << item @help.merge!(item.item_help) - update_responses # rebuild responses based on our settings + update_responses # rebuild responses based on our settings end # - # This method helps reduce the namespaces in the original call, which would look - # like this: HighLine::Menu::Item.new(...) + # This method helps reduce the namespaces in the original call, + # which would look like this: HighLine::Menu::Item.new(...) # With #build_item, it looks like this: menu.build_item(...) - # @param *args splat args, the same args you would pass to an initialization of - # HighLine::Menu::Item + # @param *args splat args, the same args you would pass to an + # initialization of HighLine::Menu::Item # @return [HighLine::Menu::Item] the menu item def build_item(*args) @@ -206,8 +208,8 @@ class HighLine end # - # Adds an item directly to the menu. If you want more configuraiton or options, - # use this method + # Adds an item directly to the menu. If you want more configuration + # or options, use this method # # @param item [Menu::Item] item containing choice fields and more # @return [void] @@ -223,7 +225,8 @@ class HighLine # 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 names [Array<#to_s>] menu item titles/headers/names to be + # displayed. # @param action (see #choice) # @return [void] # @example (see HighLine::Menu#initialize) @@ -231,7 +234,7 @@ class HighLine # choice has more options available to you, like longer text or help (and # of course, individual actions) # - def choices( *names, &action ) + def choices(*names, &action) names.each { |n| choice(n, &action) } end @@ -242,7 +245,7 @@ class HighLine # @param action (see #choice) # @return (see #choice) - def hidden( name, help = nil, &action ) + def hidden(name, help = nil, &action) item = Menu::Item.new(name, text: name, help: help, action: action) @hidden_items << item @help.merge!(item.item_help) @@ -263,31 +266,36 @@ class HighLine # _index_suffix_ to a single space and _select_by_ to <tt>:name</tt>. # Because of this, you should make a habit of setting the _index_ first. # - def index=( style ) + def index=(style) @index = style + return unless @index == :none || @index.is_a?(::String) + # Default settings. - if @index == :none or @index.is_a?(::String) - @index_suffix = " " - @select_by = :name - end + @index_suffix = " " + @select_by = :name end # # Initializes the help system by adding a <tt>:help</tt> choice, some # action code, and the default help listing. # - def init_help( ) + def init_help return if @items.include?(:help) topics = @help.keys.sort - help_help = @help.include?("help") ? @help["help"] : - "This command will display helpful messages about " + - "functionality, like this one. To see the help for " + - "a specific topic enter:\n\thelp [TOPIC]\nTry asking " + - "for help on any of the following:\n\n" + - "<%= list(#{topics.inspect}, :columns_across) %>" - choice(:help, help_help) do |command, topic| + help_help = + if @help.include?("help") + @help["help"] + else + "This command will display helpful messages about " \ + "functionality, like this one. To see the help for " \ + "a specific topic enter:\n\thelp [TOPIC]\nTry asking " \ + "for help on any of the following:\n\n" \ + "<%= list(#{topics.inspect}, :columns_across) %>" + end + + choice(:help, help_help) do |_command, topic| topic.strip! topic.downcase! if topic.empty? @@ -302,11 +310,11 @@ class HighLine # Used to set help for arbitrary topics. Use the topic <tt>"help"</tt> # 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 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 ) + def help(topic, help) @help[topic] = help end @@ -339,14 +347,14 @@ class HighLine # will default to <tt>:none</tt> and _flow_ will default to # <tt>:inline</tt>. # - def layout=( new_layout ) + def layout=(new_layout) @layout = new_layout # Default settings. case @layout when :one_line, :menu_only self.index = :none - @flow = :inline + @flow = :inline end end @@ -387,12 +395,14 @@ class HighLine # rules for this Menu object. If an action was provided for the # 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 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 ) + def select(highline_context, selection, details = nil) # add in any hidden menu commands items = all_items @@ -423,18 +433,20 @@ class HighLine def get_item_by_letter(items, selection) item = items.find { |i| i.name == selection } return item if item - l_index = "`" # character before the letter "a" - index = items.map { "#{l_index.succ!}" }.index(selection) + + # 97 is the "a" letter at ascii table + # Ex: For "a" it will return 0, and for "c" it will return 2 + index = selection.ord - 97 items[index] end def value_for_selected_item(item, details) if item.action - if @shell - result = item.action.call(item.name, details) - else - result = item.action.call(item.name) - end + result = if @shell + item.action.call(item.name, details) + else + item.action.call(item.name) + end @nil_on_handled ? nil : result else item.name @@ -451,7 +463,7 @@ class HighLine elsif selections.is_a?(Hash) value_for_hash_selections(items, selections, details) else - fail ArgumentError, 'selections must be either Array or Hash' + raise ArgumentError, "selections must be either Array or Hash" end end @@ -512,23 +524,23 @@ class HighLine # Allows Menu to behave as a String, just like Question. Returns the # _layout_ to be rendered, which is used by HighLine.say(). # - def to_s( ) + def to_s case @layout when :list %(<%= header ? "#{header}:\n" : '' %>) + - parse_list + - show_default_if_any + - "<%= prompt %>" + parse_list + + show_default_if_any + + "<%= prompt %>" when :one_line %(<%= header ? "#{header}: " : '' %>) + - "<%= prompt %>" + - "(" + parse_list + ")" + - show_default_if_any + - "<%= prompt[/\s*$/] %>" + "<%= prompt %>" \ + "(" + parse_list + ")" + + show_default_if_any + + "<%= prompt[/\s*$/] %>" when :menu_only parse_list + - show_default_if_any + - "<%= prompt %>" + show_default_if_any + + "<%= prompt %>" else @layout end @@ -536,11 +548,11 @@ class HighLine def parse_list "<%= list( menu, #{@flow.inspect}, - #{@list_option.inspect} ) %>" + #{@list_option.inspect} ) %>" end def show_default_if_any - return default.to_s.empty? ? "" : "(#{default}) " + default.to_s.empty? ? "" : "(#{default}) " end # diff --git a/lib/highline/menu/item.rb b/lib/highline/menu/item.rb index 76e1e56..78594cf 100644 --- a/lib/highline/menu/item.rb +++ b/lib/highline/menu/item.rb @@ -1,3 +1,5 @@ +# encoding: utf-8 + class HighLine class Menu < Question # Represents an Item of a HighLine::Menu. diff --git a/lib/highline/paginator.rb b/lib/highline/paginator.rb index 48792af..9da8735 100644 --- a/lib/highline/paginator.rb +++ b/lib/highline/paginator.rb @@ -3,7 +3,6 @@ class HighLine # Take the task of paginating some piece of text given a HighLine context class Paginator - # @return [HighLine] HighLine context attr_reader :highline @@ -36,18 +35,18 @@ class HighLine # Return last line if user wants to abort paging return "...\n#{lines.last}" unless continue_paging? end - return lines.join + lines.join end # - # Ask user if they wish to continue paging output. Allows them to type "q" to - # cancel the paging process. + # Ask user if they wish to continue paging output. Allows them to + # type "q" to cancel the paging process. # def continue_paging? command = highline.new_scope.ask( "-- press enter/return to continue or q to stop -- " ) { |q| q.character = true } - command !~ /\A[qQ]\Z/ # Only continue paging if Q was not hit. + command !~ /\A[qQ]\Z/ # Only continue paging if Q was not hit. end end -end
\ No newline at end of file +end diff --git a/lib/highline/question.rb b/lib/highline/question.rb index 4f9560c..f93d4d1 100755 --- a/lib/highline/question.rb +++ b/lib/highline/question.rb @@ -8,6 +8,7 @@ # # This is Free Software. See LICENSE and COPYING for details. +require "English" require "optparse" require "date" require "pathname" @@ -59,11 +60,11 @@ class HighLine @case = nil @in = nil @first_answer = nil - @directory = Pathname.new(File.expand_path(File.dirname($0))) @glob = "*" - @user_responses = Hash.new - @internal_responses = default_responses_hash @overwrite = false + @user_responses = {} + @internal_responses = default_responses_hash + @directory = Pathname.new(File.expand_path(File.dirname($PROGRAM_NAME))) # allow block to override settings yield self if block_given? @@ -148,11 +149,11 @@ class HighLine # # Asks a yes or no confirmation question, to ensure a user knows what # they have just agreed to. The confirm attribute can be set to : - # +true+ : In this case the question will be, "Are you sure?". - # Proc : The Proc is yielded the answer given. The Proc must - # output a string which is then used as the confirm - # question. - # String : The String must use ERB syntax. The String is + # +true+ : In this case the question will be, "Are you sure?". + # Proc : The Proc is yielded the answer given. The Proc must + # output a string which is then used as the confirm + # question. + # String : The String must use ERB syntax. The String is # evaluated with access to question and answer and # is then used as the confirm question. # When set to +false+ or +nil+ (the default), answers are not confirmed. @@ -261,8 +262,8 @@ class HighLine def default_responses_hash { - :ask_on_error => "? ", - :mismatch => "Your entries didn't match." + ask_on_error: "? ", + mismatch: "Your entries didn't match." } end @@ -270,15 +271,15 @@ class HighLine # @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) + '.', - :invalid_type => "You must enter a valid #{message_source}.", - :no_completion => "You must choose one of " + - choice_error_str(message_source) + '.', - :not_in_range => "Your answer isn't within the expected range " + - "(#{expected_range}).", - :not_valid => "Your answer isn't valid (must match " + - "#{validate.inspect})." } + { ambiguous_completion: "Ambiguous choice. Please choose one of " + + choice_error_str(message_source) + ".", + invalid_type: "You must enter a valid #{message_source}.", + no_completion: "You must choose one of " + + choice_error_str(message_source) + ".", + not_in_range: "Your answer isn't within the expected range " \ + "(#{expected_range}).", + not_valid: "Your answer isn't valid (must match " \ + "#{validate.inspect})." } end # This is the actual responses hash that gets used in determining output @@ -369,7 +370,7 @@ class HighLine # Returns an English explanation of the current range settings. def expected_range - expected = [ ] + expected = [] expected << "above #{above}" if above expected << "below #{below}" if below @@ -392,7 +393,7 @@ class HighLine # Returns true if _first_answer_ is set. def first_answer? - !!@first_answer + true if @first_answer end # @@ -402,9 +403,9 @@ class HighLine # are not checked. # def in_range? - (!above or answer > above) and - (!below or answer < below) and - (!@in or @in.include?(answer)) + (!above || answer > above) && + (!below || answer < below) && + (!@in || @in.include?(answer)) end # @@ -468,7 +469,7 @@ class HighLine File.basename(file) end else - [ ] + [] end end @@ -485,9 +486,9 @@ class HighLine # and case handling. # def valid_answer? - !validate or - (validate.is_a?(Regexp) and answer =~ validate) or - (validate.is_a?(Proc) and validate[answer]) + !validate || + (validate.is_a?(Regexp) && answer =~ validate) || + (validate.is_a?(Proc) && validate[answer]) end # @@ -534,11 +535,11 @@ class HighLine if confirm == true "Are you sure? " elsif confirm.is_a?(Proc) - confirm.call(self.answer) + confirm.call(answer) else # evaluate ERb under initial scope, so it will have # access to question and answer - template = ERB.new(confirm, nil, "%") + template = ERB.new(confirm, nil, "%") template_renderer = TemplateRenderer.new(template, self, highline) template_renderer.render end @@ -547,7 +548,8 @@ class HighLine # 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 + # @return [String] if :ask_on_error on responses Hash is set to + # something else def ask_on_error_msg if final_responses[:ask_on_error] == :question self @@ -564,7 +566,7 @@ class HighLine # @param highline [HighLine] context # @return [void] def show_question(highline) - highline.say(self) unless (readline && (echo == true && !limit)) + highline.say(self) unless readline && (echo == true && !limit) end # Returns an echo string that is adequate for this Question settings. @@ -577,7 +579,7 @@ class HighLine if echo == true response # any truethy value, probably a String - elsif !!echo + elsif echo echo # any falsy value, false or nil else @@ -594,11 +596,11 @@ class HighLine # def append_default if template =~ /([\t ]+)\Z/ - template << "|#{default}|#{$1}" + template << "|#{default}|#{Regexp.last_match(1)}" elsif template == "" template << "|#{default}| " elsif template[-1, 1] == "\n" - template[-2, 0] = " |#{default}|" + template[-2, 0] = " |#{default}|" else template << " |#{default}|" end @@ -606,7 +608,7 @@ class HighLine def choice_error_str(message_source) if message_source.is_a? Array - '[' + message_source.join(', ') + ']' + "[" + message_source.join(", ") + "]" else message_source.inspect end diff --git a/lib/highline/question/answer_converter.rb b/lib/highline/question/answer_converter.rb index d4067f1..76558ae 100644 --- a/lib/highline/question/answer_converter.rb +++ b/lib/highline/question/answer_converter.rb @@ -1,6 +1,6 @@ # coding: utf-8 -require 'forwardable' +require "forwardable" class HighLine class Question @@ -100,4 +100,4 @@ class HighLine end end end -end
\ No newline at end of file +end diff --git a/lib/highline/question_asker.rb b/lib/highline/question_asker.rb index 06e6647..cb8dfa8 100644 --- a/lib/highline/question_asker.rb +++ b/lib/highline/question_asker.rb @@ -1,3 +1,5 @@ +# encoding: utf-8 + class HighLine # Deals with the task of "asking" a question class QuestionAsker @@ -31,13 +33,12 @@ class HighLine question.convert if question.confirm - raise NoConfirmationQuestionError unless @highline.send(:confirm, question) + confirmation = @highline.send(:confirm, question) + raise NoConfirmationQuestionError unless confirmation end - rescue ExplainableError => e explain_error(e.explanation_key) retry - rescue ArgumentError => error case error.message when /ambiguous/ @@ -65,29 +66,27 @@ class HighLine # # @return [Array, Hash] answers def gather_answers - original_question_template = question.template verify_match = question.verify_match + answers = [] - begin # when verify_match is set this loop will repeat until unique_answers == 1 - question.template = original_question_template - + # when verify_match is set this loop will repeat until unique_answers == 1 + loop do answers = gather_answers_based_on_type - if verify_match && (@highline.send(:unique_answers, answers).size > 1) - explain_error(:mismatch) - else - verify_match = false - end - end while verify_match + break unless verify_match && + (@highline.send(:unique_answers, answers).size > 1) + + explain_error(:mismatch) + end - question.verify_match ? @highline.send(:last_answer, answers) : answers + 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 } + (question.gather - 1).times { answers << ask_once } end end @@ -112,7 +111,6 @@ class HighLine end end - private ## Delegate to Highline @@ -133,7 +131,7 @@ class HighLine def answer_matches_regex(answer) if question.gather.is_a?(::String) || question.gather.is_a?(Symbol) answer.to_s == question.gather.to_s - else question.gather.is_a?(Regexp) + elsif question.gather.is_a?(Regexp) answer.to_s =~ question.gather end end diff --git a/lib/highline/simulate.rb b/lib/highline/simulate.rb index c1ee6df..f92eaa3 100644 --- a/lib/highline/simulate.rb +++ b/lib/highline/simulate.rb @@ -10,12 +10,9 @@ # # adapted from https://gist.github.com/194554 - class HighLine - # Simulates Highline input for use in tests. 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. @@ -28,14 +25,15 @@ class HighLine @strings.shift end - # Simulate StringIO#getbyte by shifting a single character off of the next line of the script + # Simulate StringIO#getbyte by shifting a single character off of + # the next line of the script def getbyte line = gets - if line.length > 0 - char = line.slice! 0 - @strings.unshift line - char - end + return if line.empty? + + char = line.slice! 0 + @strings.unshift line + char end # The simulator handles its own EOF diff --git a/lib/highline/statement.rb b/lib/highline/statement.rb index 5e6e2f2..35d7961 100644 --- a/lib/highline/statement.rb +++ b/lib/highline/statement.rb @@ -1,8 +1,8 @@ # coding: utf-8 -require 'highline/wrapper' -require 'highline/paginator' -require 'highline/template_renderer' +require "highline/wrapper" +require "highline/paginator" +require "highline/template_renderer" class HighLine # This class handles proper formatting based @@ -45,6 +45,10 @@ class HighLine statement end + def self.const_missing(constant) + HighLine.const_get(constant) + end + private def stringfy(template_string) @@ -52,14 +56,16 @@ class HighLine end def format_statement - return template_string unless template_string.length > 0 + return template_string if template_string.empty? statement = render_template statement = HighLine::Wrapper.wrap(statement, highline.wrap_at) statement = HighLine::Paginator.new(highline).page_print(statement) - statement = statement.gsub(/\n(?!$)/,"\n#{highline.indentation}") if highline.multi_indent + statement = statement.gsub(/\n(?!$)/, "\n#{highline.indentation}") if + highline.multi_indent + statement end @@ -74,9 +80,5 @@ class HighLine def template @template ||= ERB.new(template_string, nil, "%") end - - def self.const_missing(constant) - HighLine.const_get(constant) - end end -end
\ No newline at end of file +end diff --git a/lib/highline/string.rb b/lib/highline/string.rb index 162cfeb..8d2e0a6 100644 --- a/lib/highline/string.rb +++ b/lib/highline/string.rb @@ -3,12 +3,13 @@ require "highline/string_extensions" class HighLine - # - # HighLine::String is a subclass of String with convenience methods added for colorization. + # HighLine::String is a subclass of String with convenience methods added + # for colorization. # # Available convenience methods include: - # * 'color' method e.g. highline_string.color(:bright_blue, :underline) + # * 'color' method e.g. highline_string.color(:bright_blue, + # :underline) # * colors e.g. highline_string.magenta # * RGB colors e.g. highline_string.rgb_ff6000 # or highline_string.rgb(255,96,0) @@ -17,19 +18,19 @@ class HighLine # or highline_string.on_rgb(255,96,0) # * styles e.g. highline_string.underline # - # Additionally, convenience methods can be chained, for instance the following are equivalent: + # Additionally, convenience methods can be chained, for instance the + # following are equivalent: # highline_string.bright_blue.blink.underline # highline_string.color(:bright_blue, :blink, :underline) # HighLine.color(highline_string, :bright_blue, :blink, :underline) # - # For those less squeamish about possible conflicts, the same convenience methods can be - # added to the built-in String class, as follows: + # For those less squeamish about possible conflicts, the same convenience + # methods can be added to the built-in String class, as follows: # # require 'highline' # Highline.colorize_strings # - class String < ::String include StringExtensions end -end
\ No newline at end of file +end diff --git a/lib/highline/string_extensions.rb b/lib/highline/string_extensions.rb index d2f2de4..f6c4c6b 100644 --- a/lib/highline/string_extensions.rb +++ b/lib/highline/string_extensions.rb @@ -1,16 +1,18 @@ # coding: utf-8 -class HighLine +class HighLine #:nodoc: # Returns a HighLine::String from any given String. # @param s [String] # @return [HighLine::String] from the given string. - def self.String(s) + def self.String(s) # rubocop:disable Naming/MethodName HighLine::String.new(s) end # HighLine extensions for String class. # Included by HighLine::String. module StringExtensions + STYLE_METHOD_NAME_PATTERN = /^(on_)?rgb_([0-9a-fA-F]{6})$/ + # Included hook. Actions to take when being included. # @param base [Class, Module] base class def self.included(base) @@ -35,7 +37,7 @@ class HighLine undef :on if method_defined? :on def on(arg) - color(('on_' + arg.to_s).to_sym) + color(("on_" + arg.to_s).to_sym) end undef :uncolor if method_defined? :uncolor @@ -57,19 +59,33 @@ class HighLine # @todo Chain existing method_missing? undef :method_missing if method_defined? :method_missing - def method_missing(method, *args, &blk) - if method.to_s =~ /^(on_)?rgb_([0-9a-fA-F]{6})$/ + def method_missing(method, *_args) + if method.to_s =~ STYLE_METHOD_NAME_PATTERN color(method) else - raise NoMethodError, "undefined method `#{method}' for #<#{self.class}:#{'%#x'%self.object_id}>" + super end end + undef :respond_to_missing if method_defined? :respond_to_missing + def respond_to_missing?(method_name, include_private = false) + method_name.to_s =~ STYLE_METHOD_NAME_PATTERN || super + end + private def setup_color_code(*colors) - color_code = colors.map{|color| color.is_a?(Numeric) ? '%02x'%color : color.to_s}.join - raise "Bad RGB color #{colors.inspect}" unless color_code =~ /^[a-fA-F0-9]{6}/ + color_code = colors.map do |color| + if color.is_a?(Numeric) + format("%02x", color) + else + color.to_s + end + end.join + + raise "Bad RGB color #{colors.inspect}" unless + color_code =~ /^[a-fA-F0-9]{6}/ + color_code end end @@ -80,28 +96,28 @@ class HighLine def self.define_builtin_style_methods(base) HighLine::COLORS.each do |color| color = color.downcase - base.class_eval <<-END + base.class_eval <<-METHOD_DEFINITION undef :#{color} if method_defined? :#{color} def #{color} color(:#{color}) end - END + METHOD_DEFINITION - base.class_eval <<-END + base.class_eval <<-METHOD_DEFINITION undef :on_#{color} if method_defined? :on_#{color} def on_#{color} on(:#{color}) end - END + METHOD_DEFINITION HighLine::STYLES.each do |style| style = style.downcase - base.class_eval <<-END + base.class_eval <<-METHOD_DEFINITION undef :#{style} if method_defined? :#{style} def #{style} color(:#{style}) end - END + METHOD_DEFINITION end end end diff --git a/lib/highline/style.rb b/lib/highline/style.rb index c53024a..773cba0 100755 --- a/lib/highline/style.rb +++ b/lib/highline/style.rb @@ -8,16 +8,14 @@ # # This is Free Software. See LICENSE and COPYING for details - -class HighLine - +class HighLine #:nodoc: # 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 + if args.size == 1 find_or_create_style(args.first) else find_or_create_style_list(*args) @@ -33,21 +31,22 @@ class HighLine if arg.is_a?(Style) Style.list[arg.name] || Style.index(arg) elsif arg.is_a?(::String) && arg =~ /^\e\[/ # arg is a code - if styles = Style.code_index[arg] + styles = Style.code_index[arg] + if styles styles.first else - Style.new(:code=>arg) + Style.new(code: arg) end - elsif style = Style.list[arg] - style + elsif Style.list[arg] + Style.list[arg] elsif HighLine.color_scheme && HighLine.color_scheme[arg] HighLine.color_scheme[arg] elsif arg.is_a?(Hash) Style.new(arg) elsif arg.to_s.downcase =~ /^rgb_([a-f0-9]{6})$/ - Style.rgb($1) + Style.rgb(Regexp.last_match(1)) elsif arg.to_s.downcase =~ /^on_rgb_([a-f0-9]{6})$/ - Style.rgb($1).on + Style.rgb(Regexp.last_match(1)).on else raise NameError, "#{arg.inspect} is not a defined Style" end @@ -62,12 +61,11 @@ class HighLine def self.find_or_create_style_list(*args) name = args - Style.list[name] || Style.new(:list=>args) + Style.list[name] || Style.new(list: args) end # ANSI styles to be used by HighLine. class Style - # Index the given style. # Uses @code_index (Hash) as repository. # @param style [Style] @@ -77,10 +75,12 @@ class HighLine @styles ||= {} @styles[style.name] = style end - if !style.list + unless style.list @code_index ||= {} @code_index[style.code] ||= [] - @code_index[style.code].reject!{|indexed_style| indexed_style.name == style.name} + @code_index[style.code].reject! do |indexed_style| + indexed_style.name == style.name + end @code_index[style.code] << style end style @@ -91,9 +91,9 @@ class HighLine # @return [void] def self.clear_index # reset to builtin only styles - @styles = list.select { |name, style| style.builtin } + @styles = list.select { |_name, style| style.builtin } @code_index = {} - @styles.each { |name, style| index(style) } + @styles.each_value { |style| index(style) } end # Converts all given color codes to hexadecimal and @@ -106,7 +106,7 @@ class HighLine # 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 + color.is_a?(Numeric) ? format("%02x", color) : color.to_s end.join end @@ -117,7 +117,7 @@ class HighLine # HighLine::Style.rgb_parts("090A0B") # => [9, 10, 11] def self.rgb_parts(hex) - hex.scan(/../).map{|part| part.to_i(16)} + hex.scan(/../).map { |part| part.to_i(16) } end # Search for or create a new Style from the colors provided. @@ -132,29 +132,37 @@ class HighLine # def self.rgb(*colors) hex = rgb_hex(*colors) - name = ('rgb_' + hex).to_sym - if style = list[name] - style - else - parts = rgb_parts(hex) - new(:name=>name, :code=>"\e[38;5;#{rgb_number(parts)}m", :rgb=>parts) - end + name = ("rgb_" + hex).to_sym + style = list[name] + return style if style + + parts = rgb_parts(hex) + new(name: name, code: "\e[38;5;#{rgb_number(parts)}m", rgb: parts) 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 + # @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} + 16 + parts.reduce(0) do |kode, part| + kode * 6 + (part / 256.0 * 6.0).floor + end 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} + raise "Invalid ANSI rgb code #{ansi_number}" unless + (16..231).cover?(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 @@ -170,9 +178,9 @@ class HighLine # Remove any ANSI color escape sequence of the given String. # @param string [String] - # @return [String] + # @return [String] def self.uncolor(string) - string.gsub(/\e\[\d+(;\d+)*m/, '') + string.gsub(/\e\[\d+(;\d+)*m/, "") end # Style name @@ -202,7 +210,7 @@ class HighLine @builtin = defn[:builtin] if @rgb hex = self.class.rgb_hex(@rgb) - @name ||= 'rgb_' + hex + @name ||= "rgb_" + hex elsif @list @name ||= @list end @@ -228,11 +236,12 @@ class HighLine code + string + HighLine::CLEAR end - # @return [String] all codes of the Style list joined together (if a Style list) + # @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 + @list.map { |element| HighLine.Style(element).code }.join else @code end @@ -257,22 +266,30 @@ class HighLine # @param new_name [Symbol] # @param options [Hash] Style attributes to be changed # @return [Style] new Style with changed attributes - def variant(new_name, options={}) + def variant(new_name, options = {}) raise "Cannot create a variant of a style list (#{inspect})" if @list new_code = options[:code] || code if options[:increment] - raise "Unexpected code in #{inspect}" unless new_code =~ /^(.*?)(\d+)(.*)/ - new_code = $1 + ($2.to_i + options[:increment]).to_s + $3 + raise "Unexpected code in #{inspect}" unless + new_code =~ /^(.*?)(\d+)(.*)/ + + new_code = + Regexp.last_match(1) + + (Regexp.last_match(2).to_i + + options[:increment]).to_s + + Regexp.last_match(3) end new_rgb = options[:rgb] || @rgb - self.class.new(self.to_hash.merge(:name=>new_name, :code=>new_code, :rgb=>new_rgb)) + self.class.new(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) + 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 @@ -288,11 +305,17 @@ class HighLine private def create_bright_variant(variant_name) - raise "Cannot create a #{name} variant of a style list (#{inspect})" if @list - new_name = ("#{variant_name}_"+@name.to_s).to_sym - new_rgb = @rgb == [0,0,0] ? [128, 128, 128] : @rgb.map {|color| color==0 ? 0 : [color+128,255].min } - - find_style(new_name) or variant(new_name, :increment=>60, :rgb=>new_rgb) + raise "Cannot create a #{name} variant of a style list (#{inspect})" if + @list + new_name = ("#{variant_name}_" + @name.to_s).to_sym + new_rgb = + if @rgb == [0, 0, 0] + [128, 128, 128] + else + @rgb.map { |color| color.zero? ? 0 : [color + 128, 255].min } + end + + find_style(new_name) || variant(new_name, increment: 60, rgb: new_rgb) end def find_style(name) diff --git a/lib/highline/template_renderer.rb b/lib/highline/template_renderer.rb index 9ac3970..e948c06 100644 --- a/lib/highline/template_renderer.rb +++ b/lib/highline/template_renderer.rb @@ -1,6 +1,6 @@ # coding: utf-8 -require 'forwardable' +require "forwardable" class HighLine # Renders an erb template taking a {Question} and a {HighLine} instance @@ -42,9 +42,9 @@ class HighLine # 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}" + "Method #{method} with args #{args.inspect} " \ + "is not available on #{inspect}. " \ + "Try #{methods(false).sort.inspect}" end # @return [Question, Menu] {#source} attribute. @@ -59,4 +59,4 @@ class HighLine HighLine.const_get(name) end end -end
\ No newline at end of file +end diff --git a/lib/highline/terminal.rb b/lib/highline/terminal.rb index a76a136..cd1d3dc 100755 --- a/lib/highline/terminal.rb +++ b/lib/highline/terminal.rb @@ -16,24 +16,17 @@ class HighLine # 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 - # First of all, probe for io/console begin require "io/console" require "highline/terminal/io_console" terminal = HighLine::Terminal::IOConsole.new(input, output) rescue LoadError - end - - # Fall back to UnixStty - unless terminal - require 'highline/terminal/unix_stty' + require "highline/terminal/unix_stty" terminal = HighLine::Terminal::UnixStty.new(input, output) end @@ -57,8 +50,7 @@ class HighLine # An initialization callback. # It is called by {.get_terminal}. - def initialize_system_extensions - end + def initialize_system_extensions; end # @return [Array<Integer, Integer>] two value terminal # size like [columns, lines] @@ -67,8 +59,7 @@ class HighLine end # Enter Raw No Echo mode. - def raw_no_echo_mode - end + def raw_no_echo_mode; end # Yieds a block to be executed in Raw No Echo mode and # then restore the terminal state. @@ -80,40 +71,38 @@ class HighLine end # Restore terminal to its default mode - def restore_mode - end + def restore_mode; end # Get one character from the terminal # @return [String] one character - def get_character - end + def get_character; end # rubocop:disable Naming/AccessorMethodName # 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={}) + def get_line(question, highline) raw_answer = - if question.readline - get_line_with_readline(question, highline, options={}) - else - get_line_default(highline) - end + if question.readline + get_line_with_readline(question, highline) + else + get_line_default(highline) + end 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 + def get_line_with_readline(question, highline) + require "readline" # load only if needed question_string = highline.render_statement(question) raw_answer = readline_read(question_string, question) - if !raw_answer and highline.track_eof? + if !raw_answer && highline.track_eof? raise EOFError, "The input stream is exhausted." end @@ -140,7 +129,7 @@ class HighLine Readline.readline(prompt, true) end - $VERBOSE = old_verbose + $VERBOSE = old_verbose raw_answer end @@ -148,8 +137,8 @@ class HighLine # 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? + raise EOFError, "The input stream is exhausted." if highline.track_eof? && + highline.input.eof? highline.input.gets end @@ -157,12 +146,12 @@ class HighLine # Running on JRuby? def jruby? - defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby' + defined?(RUBY_ENGINE) && RUBY_ENGINE == "jruby" end # Running on Rubinius? def rubinius? - defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx' + defined?(RUBY_ENGINE) && RUBY_ENGINE == "rbx" end # Running on Windows? @@ -190,7 +179,11 @@ class HighLine # Saves terminal state using shell stty command. def save_stty - @stty_save = `stty -g`.chomp rescue nil + @stty_save = begin + `stty -g`.chomp + rescue StandardError + nil + end end # Restores terminal state using shell stty command. diff --git a/lib/highline/terminal/io_console.rb b/lib/highline/terminal/io_console.rb index eee152f..e9ff925 100644 --- a/lib/highline/terminal/io_console.rb +++ b/lib/highline/terminal/io_console.rb @@ -21,9 +21,9 @@ class HighLine end # (see Terminal#get_character) - def get_character + def get_character # rubocop:disable Naming/AccessorMethodName input.getch # from ruby io/console end end end -end
\ No newline at end of file +end diff --git a/lib/highline/terminal/ncurses.rb b/lib/highline/terminal/ncurses.rb index 817cc23..72ffac8 100644 --- a/lib/highline/terminal/ncurses.rb +++ b/lib/highline/terminal/ncurses.rb @@ -3,9 +3,10 @@ class HighLine class Terminal # NCurses HighLine::Terminal - # @note Code migrated +UNTESTED+ from the old code base to the new terminal api. + # @note Code migrated +UNTESTED+ from the old code base to the new + # terminal api. class NCurses < Terminal - require 'ffi-ncurses' + require "ffi-ncurses" # (see Terminal#raw_no_echo_mode) def raw_no_echo_mode @@ -34,4 +35,4 @@ class HighLine end end end -end
\ No newline at end of file +end diff --git a/lib/highline/terminal/unix_stty.rb b/lib/highline/terminal/unix_stty.rb index 3b9668a..ad9c85d 100644 --- a/lib/highline/terminal/unix_stty.rb +++ b/lib/highline/terminal/unix_stty.rb @@ -5,25 +5,28 @@ class HighLine # 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" - winsize = IO.console.winsize.reverse rescue nil + winsize = begin + IO.console.winsize.reverse + rescue NoMethodError + nil + end return winsize if winsize rescue LoadError end - if /solaris/ =~ RUBY_PLATFORM and - `stty` =~ /\brows = (\d+).*\bcolumns = (\d+)/ - [$2, $1].map { |x| x.to_i } + if /solaris/ =~ RUBY_PLATFORM && + `stty` =~ /\brows = (\d+).*\bcolumns = (\d+)/ + [Regexp.last_match(2), Regexp.last_match(1)].map(&:to_i) elsif `stty size` =~ /^(\d+)\s(\d+)$/ - [$2.to_i, $1.to_i] + [Regexp.last_match(2).to_i, Regexp.last_match(1).to_i] else - [ 80, 24 ] + [80, 24] end end @@ -40,9 +43,9 @@ class HighLine end # (see Terminal#get_character) - def get_character( input = STDIN ) + def get_character(input = STDIN) input.getc end end end -end
\ No newline at end of file +end diff --git a/lib/highline/version.rb b/lib/highline/version.rb index 5b6b4f8..87be435 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.10".freeze + VERSION = "2.0.0-develop.11".freeze end diff --git a/lib/highline/wrapper.rb b/lib/highline/wrapper.rb index ae93db6..a2ab4ac 100644 --- a/lib/highline/wrapper.rb +++ b/lib/highline/wrapper.rb @@ -1,12 +1,12 @@ # coding: utf-8 -class HighLine +require "English" +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 - # # Wrap a sequence of _lines_ at _wrap_at_ characters per line. Existing # newlines will not be affected by this process, but additional newlines @@ -18,24 +18,25 @@ class HighLine return text unless wrap_at wrap_at = Integer(wrap_at) - wrapped = [ ] + wrapped = [] text.each_line do |line| # take into account color escape sequences when wrapping - wrap_at = wrap_at + (line.length - actual_length(line)) + wrap_at += (line.length - actual_length(line)) while line =~ /([^\n]{#{wrap_at + 1},})/ - search = $1.dup - replace = $1.dup - if index = replace.rindex(" ", wrap_at) + search = Regexp.last_match(1).dup + replace = Regexp.last_match(1).dup + index = replace.rindex(" ", wrap_at) + if index replace[index, 1] = "\n" replace.sub!(/\n[ \t]+/, "\n") line.sub!(search, replace) else - line[$~.begin(1) + wrap_at, 0] = "\n" + line[$LAST_MATCH_INFO.begin(1) + wrap_at, 0] = "\n" end end wrapped << line end - return wrapped.join + wrapped.join end # @@ -45,8 +46,8 @@ class HighLine # @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 ) + def self.actual_length(string_with_escapes) string_with_escapes.to_s.gsub(/\e\[\d{1,2}m/, "").length end end -end
\ No newline at end of file +end |