tty-prompt 0.19.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +81 -0
- data/LICENSE.txt +1 -1
- data/README.md +485 -233
- data/lib/tty-prompt.rb +1 -2
- data/lib/tty/prompt.rb +159 -147
- data/lib/tty/prompt/answers_collector.rb +5 -5
- data/lib/tty/prompt/block_paginator.rb +1 -1
- data/lib/tty/prompt/choice.rb +31 -13
- data/lib/tty/prompt/choices.rb +30 -12
- data/lib/tty/prompt/confirm_question.rb +42 -16
- data/lib/tty/prompt/const.rb +17 -0
- data/lib/tty/prompt/converter_dsl.rb +6 -7
- data/lib/tty/prompt/converter_registry.rb +31 -26
- data/lib/tty/prompt/converters.rb +139 -32
- data/lib/tty/prompt/enum_list.rb +58 -25
- data/lib/tty/prompt/errors.rb +31 -0
- data/lib/tty/prompt/evaluator.rb +2 -2
- data/lib/tty/prompt/expander.rb +27 -15
- data/lib/tty/prompt/keypress.rb +5 -3
- data/lib/tty/prompt/list.rb +101 -38
- data/lib/tty/prompt/mask_question.rb +9 -5
- data/lib/tty/prompt/multi_list.rb +108 -29
- data/lib/tty/prompt/multiline.rb +9 -7
- data/lib/tty/prompt/paginator.rb +1 -1
- data/lib/tty/prompt/question.rb +67 -36
- data/lib/tty/prompt/question/checks.rb +20 -2
- data/lib/tty/prompt/question/modifier.rb +4 -2
- data/lib/tty/prompt/question/validation.rb +3 -3
- data/lib/tty/prompt/selected_choices.rb +77 -0
- data/lib/tty/prompt/slider.rb +110 -23
- data/lib/tty/prompt/statement.rb +3 -3
- data/lib/tty/prompt/suggestion.rb +7 -6
- data/lib/tty/prompt/symbols.rb +58 -58
- data/lib/tty/prompt/test.rb +36 -0
- data/lib/tty/prompt/utils.rb +1 -3
- data/lib/tty/prompt/version.rb +1 -1
- metadata +27 -196
- data/Rakefile +0 -8
- data/examples/ask.rb +0 -7
- data/examples/ask_blank.rb +0 -9
- data/examples/ask_valid.rb +0 -12
- data/examples/collect.rb +0 -21
- data/examples/echo.rb +0 -11
- data/examples/enum_select.rb +0 -7
- data/examples/enum_select_disabled.rb +0 -16
- data/examples/enum_select_paged.rb +0 -9
- data/examples/enum_select_wrapped.rb +0 -15
- data/examples/expand.rb +0 -29
- data/examples/expand_auto.rb +0 -29
- data/examples/in.rb +0 -9
- data/examples/inputs.rb +0 -10
- data/examples/key_events.rb +0 -15
- data/examples/keypress.rb +0 -9
- data/examples/mask.rb +0 -13
- data/examples/multi_select.rb +0 -8
- data/examples/multi_select_disabled.rb +0 -17
- data/examples/multi_select_disabled_paged.rb +0 -22
- data/examples/multi_select_paged.rb +0 -9
- data/examples/multi_select_wrapped.rb +0 -15
- data/examples/multiline.rb +0 -9
- data/examples/pause.rb +0 -9
- data/examples/select.rb +0 -24
- data/examples/select_disabled.rb +0 -18
- data/examples/select_disabled_paged.rb +0 -22
- data/examples/select_enum.rb +0 -8
- data/examples/select_filtered.rb +0 -11
- data/examples/select_paginated.rb +0 -11
- data/examples/select_wrapped.rb +0 -15
- data/examples/slider.rb +0 -6
- data/examples/validation.rb +0 -9
- data/examples/yes_no.rb +0 -7
- data/lib/tty/prompt/messages.rb +0 -49
- data/lib/tty/test_prompt.rb +0 -20
- data/spec/spec_helper.rb +0 -61
- data/spec/unit/ask_spec.rb +0 -173
- data/spec/unit/block_paginator_spec.rb +0 -84
- data/spec/unit/choice/eql_spec.rb +0 -22
- data/spec/unit/choice/from_spec.rb +0 -112
- data/spec/unit/choices/add_spec.rb +0 -12
- data/spec/unit/choices/each_spec.rb +0 -13
- data/spec/unit/choices/find_by_spec.rb +0 -10
- data/spec/unit/choices/new_spec.rb +0 -10
- data/spec/unit/choices/pluck_spec.rb +0 -9
- data/spec/unit/collect_spec.rb +0 -96
- data/spec/unit/converters/convert_bool_spec.rb +0 -58
- data/spec/unit/converters/convert_char_spec.rb +0 -11
- data/spec/unit/converters/convert_custom_spec.rb +0 -14
- data/spec/unit/converters/convert_date_spec.rb +0 -34
- data/spec/unit/converters/convert_file_spec.rb +0 -18
- data/spec/unit/converters/convert_number_spec.rb +0 -39
- data/spec/unit/converters/convert_path_spec.rb +0 -15
- data/spec/unit/converters/convert_range_spec.rb +0 -22
- data/spec/unit/converters/convert_regex_spec.rb +0 -12
- data/spec/unit/converters/convert_string_spec.rb +0 -21
- data/spec/unit/converters/on_error_spec.rb +0 -9
- data/spec/unit/distance/distance_spec.rb +0 -73
- data/spec/unit/enum_select_spec.rb +0 -518
- data/spec/unit/error_spec.rb +0 -20
- data/spec/unit/evaluator_spec.rb +0 -67
- data/spec/unit/expand_spec.rb +0 -290
- data/spec/unit/keypress_spec.rb +0 -66
- data/spec/unit/mask_spec.rb +0 -140
- data/spec/unit/multi_select_spec.rb +0 -741
- data/spec/unit/multiline_spec.rb +0 -77
- data/spec/unit/new_spec.rb +0 -20
- data/spec/unit/ok_spec.rb +0 -10
- data/spec/unit/paginator_spec.rb +0 -92
- data/spec/unit/question/checks_spec.rb +0 -97
- data/spec/unit/question/default_spec.rb +0 -31
- data/spec/unit/question/echo_spec.rb +0 -38
- data/spec/unit/question/in_spec.rb +0 -115
- data/spec/unit/question/initialize_spec.rb +0 -12
- data/spec/unit/question/modifier/apply_to_spec.rb +0 -24
- data/spec/unit/question/modifier/letter_case_spec.rb +0 -41
- data/spec/unit/question/modifier/whitespace_spec.rb +0 -51
- data/spec/unit/question/modify_spec.rb +0 -41
- data/spec/unit/question/required_spec.rb +0 -92
- data/spec/unit/question/validate_spec.rb +0 -115
- data/spec/unit/question/validation/call_spec.rb +0 -31
- data/spec/unit/question/validation/coerce_spec.rb +0 -30
- data/spec/unit/result_spec.rb +0 -40
- data/spec/unit/say_spec.rb +0 -67
- data/spec/unit/select_spec.rb +0 -942
- data/spec/unit/slider_spec.rb +0 -142
- data/spec/unit/statement/initialize_spec.rb +0 -15
- data/spec/unit/subscribe_spec.rb +0 -22
- data/spec/unit/suggest_spec.rb +0 -28
- data/spec/unit/timer_spec.rb +0 -29
- data/spec/unit/warn_spec.rb +0 -21
- data/spec/unit/yes_no_spec.rb +0 -251
- data/tasks/console.rake +0 -11
- data/tasks/coverage.rake +0 -11
- data/tasks/spec.rake +0 -29
- data/tty-prompt.gemspec +0 -31
| @@ -1,10 +1,13 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative  | 
| 3 | 
            +
            require_relative "question"
         | 
| 4 4 |  | 
| 5 5 | 
             
            module TTY
         | 
| 6 6 | 
             
              class Prompt
         | 
| 7 7 | 
             
                class MaskQuestion < Question
         | 
| 8 | 
            +
                  # Names for delete keys
         | 
| 9 | 
            +
                  DELETE_KEYS = %i[backspace delete].freeze
         | 
| 10 | 
            +
             | 
| 8 11 | 
             
                  # Create masked question
         | 
| 9 12 | 
             
                  #
         | 
| 10 13 | 
             
                  # @param [Hash] options
         | 
| @@ -27,19 +30,20 @@ module TTY | |
| 27 30 | 
             
                  # @api public
         | 
| 28 31 | 
             
                  def mask(char = (not_set = true))
         | 
| 29 32 | 
             
                    return @mask if not_set
         | 
| 33 | 
            +
             | 
| 30 34 | 
             
                    @mask = char
         | 
| 31 35 | 
             
                  end
         | 
| 32 36 |  | 
| 33 | 
            -
                  def keyreturn( | 
| 37 | 
            +
                  def keyreturn(_event)
         | 
| 34 38 | 
             
                    @done_masked = true
         | 
| 35 39 | 
             
                  end
         | 
| 36 40 |  | 
| 37 | 
            -
                  def keyenter( | 
| 41 | 
            +
                  def keyenter(_event)
         | 
| 38 42 | 
             
                    @done_masked = true
         | 
| 39 43 | 
             
                  end
         | 
| 40 44 |  | 
| 41 45 | 
             
                  def keypress(event)
         | 
| 42 | 
            -
                    if  | 
| 46 | 
            +
                    if DELETE_KEYS.include?(event.key.name)
         | 
| 43 47 | 
             
                      @input.chop! unless @input.empty?
         | 
| 44 48 | 
             
                    elsif event.value =~ /^[^\e\n\r]/
         | 
| 45 49 | 
             
                      @input += event.value
         | 
| @@ -75,7 +79,7 @@ module TTY | |
| 75 79 | 
             
                  def read_input(question)
         | 
| 76 80 | 
             
                    @done_masked = false
         | 
| 77 81 | 
             
                    @failure = false
         | 
| 78 | 
            -
                    @input =  | 
| 82 | 
            +
                    @input = ""
         | 
| 79 83 | 
             
                    @prompt.print(question)
         | 
| 80 84 | 
             
                    until @done_masked
         | 
| 81 85 | 
             
                      @prompt.read_keypress
         | 
| @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative  | 
| 3 | 
            +
            require_relative "list"
         | 
| 4 | 
            +
            require_relative "selected_choices"
         | 
| 4 5 |  | 
| 5 6 | 
             
            module TTY
         | 
| 6 7 | 
             
              class Prompt
         | 
| @@ -9,8 +10,6 @@ module TTY | |
| 9 10 | 
             
                #
         | 
| 10 11 | 
             
                # @api private
         | 
| 11 12 | 
             
                class MultiList < List
         | 
| 12 | 
            -
                  HELP = '(Use %s arrow%s keys, press Space to select and Enter to finish%s)'
         | 
| 13 | 
            -
             | 
| 14 13 | 
             
                  # Create instance of TTY::Prompt::MultiList menu.
         | 
| 15 14 | 
             
                  #
         | 
| 16 15 | 
             
                  # @param [Prompt] :prompt
         | 
| @@ -19,12 +18,20 @@ module TTY | |
| 19 18 | 
             
                  # @api public
         | 
| 20 19 | 
             
                  def initialize(prompt, **options)
         | 
| 21 20 | 
             
                    super
         | 
| 22 | 
            -
                    @selected =  | 
| 21 | 
            +
                    @selected = SelectedChoices.new
         | 
| 23 22 | 
             
                    @help = options[:help]
         | 
| 24 23 | 
             
                    @echo = options.fetch(:echo, true)
         | 
| 24 | 
            +
                    @min  = options[:min]
         | 
| 25 25 | 
             
                    @max  = options[:max]
         | 
| 26 26 | 
             
                  end
         | 
| 27 27 |  | 
| 28 | 
            +
                  # Set a minimum number of choices
         | 
| 29 | 
            +
                  #
         | 
| 30 | 
            +
                  # @api public
         | 
| 31 | 
            +
                  def min(value)
         | 
| 32 | 
            +
                    @min = value
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 28 35 | 
             
                  # Set a maximum number of choices
         | 
| 29 36 | 
             
                  #
         | 
| 30 37 | 
             
                  # @api public
         | 
| @@ -32,19 +39,54 @@ module TTY | |
| 32 39 | 
             
                    @max = value
         | 
| 33 40 | 
             
                  end
         | 
| 34 41 |  | 
| 42 | 
            +
                  # Callback fired when enter/return key is pressed
         | 
| 43 | 
            +
                  #
         | 
| 44 | 
            +
                  # @api private
         | 
| 45 | 
            +
                  def keyenter(*)
         | 
| 46 | 
            +
                    valid = true
         | 
| 47 | 
            +
                    valid = @min <= @selected.size if @min
         | 
| 48 | 
            +
                    valid = @selected.size <= @max if @max
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    super if valid
         | 
| 51 | 
            +
                  end
         | 
| 52 | 
            +
                  alias keyreturn keyenter
         | 
| 53 | 
            +
             | 
| 35 54 | 
             
                  # Callback fired when space key is pressed
         | 
| 36 55 | 
             
                  #
         | 
| 37 56 | 
             
                  # @api private
         | 
| 38 57 | 
             
                  def keyspace(*)
         | 
| 39 58 | 
             
                    active_choice = choices[@active - 1]
         | 
| 40 59 | 
             
                    if @selected.include?(active_choice)
         | 
| 41 | 
            -
                      @selected. | 
| 60 | 
            +
                      @selected.delete_at(@active - 1)
         | 
| 42 61 | 
             
                    else
         | 
| 43 62 | 
             
                      return if @max && @selected.size >= @max
         | 
| 44 | 
            -
             | 
| 63 | 
            +
             | 
| 64 | 
            +
                      @selected.insert(@active - 1, active_choice)
         | 
| 45 65 | 
             
                    end
         | 
| 46 66 | 
             
                  end
         | 
| 47 67 |  | 
| 68 | 
            +
                  # Selects all choices when Ctrl+A is pressed
         | 
| 69 | 
            +
                  #
         | 
| 70 | 
            +
                  # @api private
         | 
| 71 | 
            +
                  def keyctrl_a(*)
         | 
| 72 | 
            +
                    return if @max && @max < choices.size
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    @selected = SelectedChoices.new(choices.enabled, choices.enabled_indexes)
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  # Revert currently selected choices when Ctrl+I is pressed
         | 
| 78 | 
            +
                  #
         | 
| 79 | 
            +
                  # @api private
         | 
| 80 | 
            +
                  def keyctrl_r(*)
         | 
| 81 | 
            +
                    return if @max && @max < choices.size
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    indexes = choices.each_with_index.reduce([]) do |acc, (choice, idx)|
         | 
| 84 | 
            +
                                acc << idx if !choice.disabled? && !@selected.include?(choice)
         | 
| 85 | 
            +
                                acc
         | 
| 86 | 
            +
                              end
         | 
| 87 | 
            +
                    @selected = SelectedChoices.new(choices.enabled - @selected.to_a, indexes)
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 48 90 | 
             
                  private
         | 
| 49 91 |  | 
| 50 92 | 
             
                  # Setup default options and active selection
         | 
| @@ -53,12 +95,23 @@ module TTY | |
| 53 95 | 
             
                  def setup_defaults
         | 
| 54 96 | 
             
                    validate_defaults
         | 
| 55 97 | 
             
                    # At this stage, @choices matches all the visible choices.
         | 
| 56 | 
            -
                     | 
| 98 | 
            +
                    default_indexes = @default.map do |d|
         | 
| 99 | 
            +
                      if d.to_s =~ INTEGER_MATCHER
         | 
| 100 | 
            +
                        d - 1
         | 
| 101 | 
            +
                      else
         | 
| 102 | 
            +
                        choices.index(choices.find_by(:name, d.to_s))
         | 
| 103 | 
            +
                      end
         | 
| 104 | 
            +
                    end
         | 
| 105 | 
            +
                    @selected = SelectedChoices.new(@choices.values_at(*default_indexes),
         | 
| 106 | 
            +
                                                    default_indexes)
         | 
| 57 107 |  | 
| 58 | 
            -
                    if  | 
| 108 | 
            +
                    if @default.empty?
         | 
| 109 | 
            +
                      # no default, pick the first non-disabled choice
         | 
| 110 | 
            +
                      @active = choices.index { |choice| !choice.disabled? } + 1
         | 
| 111 | 
            +
                    elsif @default.last.to_s =~ INTEGER_MATCHER
         | 
| 59 112 | 
             
                      @active = @default.last
         | 
| 60 | 
            -
                     | 
| 61 | 
            -
                      @active =  | 
| 113 | 
            +
                    elsif default_choice = choices.find_by(:name, @default.last.to_s)
         | 
| 114 | 
            +
                      @active = choices.index(default_choice) + 1
         | 
| 62 115 | 
             
                    end
         | 
| 63 116 | 
             
                  end
         | 
| 64 117 |  | 
| @@ -68,37 +121,63 @@ module TTY | |
| 68 121 | 
             
                  #
         | 
| 69 122 | 
             
                  # @api private
         | 
| 70 123 | 
             
                  def selected_names
         | 
| 71 | 
            -
                    @selected.map(&:name).join( | 
| 124 | 
            +
                    @selected.map(&:name).join(", ")
         | 
| 72 125 | 
             
                  end
         | 
| 73 126 |  | 
| 74 | 
            -
                  # Header part showing the maximum number of choices
         | 
| 127 | 
            +
                  # Header part showing the minimum/maximum number of choices
         | 
| 75 128 | 
             
                  #
         | 
| 76 129 | 
             
                  # @return [String]
         | 
| 77 130 | 
             
                  #
         | 
| 78 131 | 
             
                  # @api private
         | 
| 79 | 
            -
                  def  | 
| 80 | 
            -
                     | 
| 132 | 
            +
                  def minmax_help
         | 
| 133 | 
            +
                    help = []
         | 
| 134 | 
            +
                    help << "min. #{@min}" if @min
         | 
| 135 | 
            +
                    help << "max. #{@max}" if @max
         | 
| 136 | 
            +
                    "(%s) " % [help.join(", ")]
         | 
| 137 | 
            +
                  end
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                  # Build a default help text
         | 
| 140 | 
            +
                  #
         | 
| 141 | 
            +
                  # @return [String]
         | 
| 142 | 
            +
                  #
         | 
| 143 | 
            +
                  # @api private
         | 
| 144 | 
            +
                  def default_help
         | 
| 145 | 
            +
                    str = []
         | 
| 146 | 
            +
                    str << "(Press "
         | 
| 147 | 
            +
                    str << "#{arrows_help} arrow"
         | 
| 148 | 
            +
                    str << " or 1-#{choices.size} number" if enumerate?
         | 
| 149 | 
            +
                    str << " to move, Space"
         | 
| 150 | 
            +
                    str << "/Ctrl+A|R" if @max.nil?
         | 
| 151 | 
            +
                    str << " to select"
         | 
| 152 | 
            +
                    str << " (all|rev)" if @max.nil?
         | 
| 153 | 
            +
                    str << (filterable? ? "," : " and")
         | 
| 154 | 
            +
                    str << " Enter to finish"
         | 
| 155 | 
            +
                    str << " and letters to filter" if filterable?
         | 
| 156 | 
            +
                    str << ")"
         | 
| 157 | 
            +
                    str.join
         | 
| 81 158 | 
             
                  end
         | 
| 82 159 |  | 
| 83 160 | 
             
                  # Render initial help text and then currently selected choices
         | 
| 84 161 | 
             
                  #
         | 
| 85 162 | 
             
                  # @api private
         | 
| 86 163 | 
             
                  def render_header
         | 
| 87 | 
            -
                    instructions = @prompt.decorate(help,  | 
| 88 | 
            -
                     | 
| 164 | 
            +
                    instructions = @prompt.decorate(help, @help_color)
         | 
| 165 | 
            +
                    minmax_suffix = @min || @max ? minmax_help : ""
         | 
| 166 | 
            +
                    print_selected = @selected.size.nonzero? && @echo
         | 
| 89 167 |  | 
| 90 168 | 
             
                    if @done && @echo
         | 
| 91 169 | 
             
                      @prompt.decorate(selected_names, @active_color)
         | 
| 92 | 
            -
                    elsif @ | 
| 93 | 
            -
             | 
| 94 | 
            -
                       | 
| 95 | 
            -
                        ( | 
| 96 | 
            -
             | 
| 97 | 
            -
                      max_suffix + instructions
         | 
| 170 | 
            +
                    elsif (@first_render && (help_start? || help_always?)) ||
         | 
| 171 | 
            +
                          (help_always? && !@filter.any? && !@done)
         | 
| 172 | 
            +
                      minmax_suffix +
         | 
| 173 | 
            +
                        (print_selected ? "#{selected_names} " : "") +
         | 
| 174 | 
            +
                        instructions
         | 
| 98 175 | 
             
                    elsif filterable? && @filter.any?
         | 
| 99 | 
            -
                       | 
| 100 | 
            -
             | 
| 101 | 
            -
             | 
| 176 | 
            +
                      minmax_suffix +
         | 
| 177 | 
            +
                        (print_selected ? "#{selected_names} " : "") +
         | 
| 178 | 
            +
                        @prompt.decorate(filter_help, @help_color)
         | 
| 179 | 
            +
                    else
         | 
| 180 | 
            +
                      minmax_suffix + (print_selected ? selected_names : "")
         | 
| 102 181 | 
             
                    end
         | 
| 103 182 | 
             
                  end
         | 
| 104 183 |  | 
| @@ -121,9 +200,9 @@ module TTY | |
| 121 200 |  | 
| 122 201 | 
             
                    sync_paginators if @paging_changed
         | 
| 123 202 | 
             
                    paginator.paginate(choices, @active, @per_page) do |choice, index|
         | 
| 124 | 
            -
                      num = enumerate? ? (index + 1).to_s + @enum +  | 
| 125 | 
            -
                      indicator = (index + 1 == @active) ?  @symbols[:marker] :  | 
| 126 | 
            -
                      indicator +=  | 
| 203 | 
            +
                      num = enumerate? ? (index + 1).to_s + @enum + " " : ""
         | 
| 204 | 
            +
                      indicator = (index + 1 == @active) ?  @symbols[:marker] : " "
         | 
| 205 | 
            +
                      indicator += " "
         | 
| 127 206 | 
             
                      message = if @selected.include?(choice) && !choice.disabled?
         | 
| 128 207 | 
             
                                  selected = @prompt.decorate(@symbols[:radio_on], @active_color)
         | 
| 129 208 | 
             
                                  "#{selected} #{num}#{choice.name}"
         | 
| @@ -134,7 +213,7 @@ module TTY | |
| 134 213 | 
             
                                  "#{@symbols[:radio_off]} #{num}#{choice.name}"
         | 
| 135 214 | 
             
                                end
         | 
| 136 215 | 
             
                      end_index = paginated? ? paginator.end_index : choices.size - 1
         | 
| 137 | 
            -
                      newline = (index == end_index) ?  | 
| 216 | 
            +
                      newline = (index == end_index) ? "" : "\n"
         | 
| 138 217 | 
             
                      output << indicator + message + newline
         | 
| 139 218 | 
             
                    end
         | 
| 140 219 |  | 
    
        data/lib/tty/prompt/multiline.rb
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative  | 
| 4 | 
            -
            require_relative  | 
| 3 | 
            +
            require_relative "question"
         | 
| 4 | 
            +
            require_relative "symbols"
         | 
| 5 5 |  | 
| 6 6 | 
             
            module TTY
         | 
| 7 7 | 
             
              class Prompt
         | 
| @@ -9,9 +9,9 @@ module TTY | |
| 9 9 | 
             
                #
         | 
| 10 10 | 
             
                # @api private
         | 
| 11 11 | 
             
                class Multiline < Question
         | 
| 12 | 
            -
                  HELP =  | 
| 12 | 
            +
                  HELP = "(Press Ctrl+D or Ctrl+Z to finish)".freeze
         | 
| 13 13 |  | 
| 14 | 
            -
                  def initialize(prompt, options | 
| 14 | 
            +
                  def initialize(prompt, **options)
         | 
| 15 15 | 
             
                    super
         | 
| 16 16 | 
             
                    @help         = options[:help] || self.class::HELP
         | 
| 17 17 | 
             
                    @first_render = true
         | 
| @@ -25,6 +25,7 @@ module TTY | |
| 25 25 | 
             
                  # @api public
         | 
| 26 26 | 
             
                  def help(value = (not_set = true))
         | 
| 27 27 | 
             
                    return @help if not_set
         | 
| 28 | 
            +
             | 
| 28 29 | 
             
                    @help = value
         | 
| 29 30 | 
             
                  end
         | 
| 30 31 |  | 
| @@ -42,7 +43,7 @@ module TTY | |
| 42 43 | 
             
                    if !echo?
         | 
| 43 44 | 
             
                      header
         | 
| 44 45 | 
             
                    elsif @done
         | 
| 45 | 
            -
                      header << @prompt.decorate( | 
| 46 | 
            +
                      header << @prompt.decorate(@input.to_s, @active_color)
         | 
| 46 47 | 
             
                    elsif @first_render
         | 
| 47 48 | 
             
                      header << @prompt.decorate(help, @help_color)
         | 
| 48 49 | 
             
                      @first_render = false
         | 
| @@ -55,8 +56,9 @@ module TTY | |
| 55 56 | 
             
                    @prompt.print(question)
         | 
| 56 57 | 
             
                    @lines = read_input
         | 
| 57 58 | 
             
                    @input = "#{@lines.first.strip} ..." unless @lines.first.to_s.empty?
         | 
| 58 | 
            -
                    if Utils.blank?(@input)
         | 
| 59 | 
            -
                      @input = default | 
| 59 | 
            +
                    if Utils.blank?(@input) && default?
         | 
| 60 | 
            +
                      @input = default
         | 
| 61 | 
            +
                      @lines = default
         | 
| 60 62 | 
             
                    end
         | 
| 61 63 | 
             
                    @evaluator.(@lines)
         | 
| 62 64 | 
             
                  end
         | 
    
        data/lib/tty/prompt/paginator.rb
    CHANGED
    
    
    
        data/lib/tty/prompt/question.rb
    CHANGED
    
    | @@ -1,11 +1,11 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require_relative  | 
| 4 | 
            -
            require_relative  | 
| 5 | 
            -
            require_relative  | 
| 6 | 
            -
            require_relative  | 
| 7 | 
            -
            require_relative  | 
| 8 | 
            -
            require_relative  | 
| 3 | 
            +
            require_relative "converters"
         | 
| 4 | 
            +
            require_relative "evaluator"
         | 
| 5 | 
            +
            require_relative "question/modifier"
         | 
| 6 | 
            +
            require_relative "question/validation"
         | 
| 7 | 
            +
            require_relative "question/checks"
         | 
| 8 | 
            +
            require_relative "utils"
         | 
| 9 9 |  | 
| 10 10 | 
             
            module TTY
         | 
| 11 11 | 
             
              # A class responsible for shell prompt interactions.
         | 
| @@ -35,23 +35,30 @@ module TTY | |
| 35 35 | 
             
                  #
         | 
| 36 36 | 
             
                  # @api public
         | 
| 37 37 | 
             
                  def initialize(prompt, **options)
         | 
| 38 | 
            -
                     | 
| 39 | 
            -
                     | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
                     | 
| 43 | 
            -
             | 
| 44 | 
            -
                    @ | 
| 45 | 
            -
                    @ | 
| 46 | 
            -
                    @ | 
| 38 | 
            +
                    # Option deprecation
         | 
| 39 | 
            +
                    if options[:validation]
         | 
| 40 | 
            +
                      warn "[DEPRECATION] The `:validation` option is deprecated. Use `:validate` instead."
         | 
| 41 | 
            +
                      options[:validate] = options[:validation]
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    @prompt       = prompt
         | 
| 45 | 
            +
                    @prefix       = options.fetch(:prefix) { @prompt.prefix }
         | 
| 46 | 
            +
                    @default      = options.fetch(:default) { UndefinedSetting }
         | 
| 47 | 
            +
                    @required     = options.fetch(:required) { false }
         | 
| 48 | 
            +
                    @echo         = options.fetch(:echo) { true }
         | 
| 49 | 
            +
                    @in           = options.fetch(:in) { UndefinedSetting }
         | 
| 50 | 
            +
                    @modifier     = options.fetch(:modifier) { [] }
         | 
| 51 | 
            +
                    @validation   = options.fetch(:validate) { UndefinedSetting }
         | 
| 52 | 
            +
                    @convert      = options.fetch(:convert) { UndefinedSetting }
         | 
| 47 53 | 
             
                    @active_color = options.fetch(:active_color) { @prompt.active_color }
         | 
| 48 | 
            -
                    @help_color | 
| 49 | 
            -
                    @error_color | 
| 50 | 
            -
                    @value | 
| 51 | 
            -
                    @ | 
| 52 | 
            -
                    @ | 
| 54 | 
            +
                    @help_color   = options.fetch(:help_color) { @prompt.help_color }
         | 
| 55 | 
            +
                    @error_color  = options.fetch(:error_color) { :red }
         | 
| 56 | 
            +
                    @value        = options.fetch(:value) { UndefinedSetting }
         | 
| 57 | 
            +
                    @quiet        = options.fetch(:quiet) { @prompt.quiet }
         | 
| 58 | 
            +
                    @messages     = Utils.deep_copy(options.fetch(:messages) { {} })
         | 
| 59 | 
            +
                    @done         = false
         | 
| 53 60 | 
             
                    @first_render = true
         | 
| 54 | 
            -
                    @input | 
| 61 | 
            +
                    @input        = nil
         | 
| 55 62 |  | 
| 56 63 | 
             
                    @evaluator = Evaluator.new(self)
         | 
| 57 64 |  | 
| @@ -60,6 +67,7 @@ module TTY | |
| 60 67 | 
             
                    @evaluator << CheckRange
         | 
| 61 68 | 
             
                    @evaluator << CheckValidation
         | 
| 62 69 | 
             
                    @evaluator << CheckModifier
         | 
| 70 | 
            +
                    @evaluator << CheckConversion
         | 
| 63 71 | 
             
                  end
         | 
| 64 72 |  | 
| 65 73 | 
             
                  # Stores all the error messages displayed to user
         | 
| @@ -85,7 +93,7 @@ module TTY | |
| 85 93 | 
             
                    if template && !template.match(/\%\{/).nil?
         | 
| 86 94 | 
             
                      [template % tokens]
         | 
| 87 95 | 
             
                    else
         | 
| 88 | 
            -
                      [template ||  | 
| 96 | 
            +
                      [template || ""]
         | 
| 89 97 | 
             
                    end
         | 
| 90 98 | 
             
                  end
         | 
| 91 99 |  | 
| @@ -96,7 +104,7 @@ module TTY | |
| 96 104 | 
             
                  # @return [self]
         | 
| 97 105 | 
             
                  #
         | 
| 98 106 | 
             
                  # @api public
         | 
| 99 | 
            -
                  def call(message =  | 
| 107 | 
            +
                  def call(message = "", &block)
         | 
| 100 108 | 
             
                    @message = message
         | 
| 101 109 | 
             
                    block.call(self) if block
         | 
| 102 110 | 
             
                    @prompt.subscribe(self) do
         | 
| @@ -122,8 +130,8 @@ module TTY | |
| 122 130 | 
             
                      total_lines = @prompt.count_screen_lines(input_line)
         | 
| 123 131 | 
             
                      @prompt.print(refresh(question.lines.count, total_lines))
         | 
| 124 132 | 
             
                    end
         | 
| 125 | 
            -
                    @prompt.print(render_question)
         | 
| 126 | 
            -
                     | 
| 133 | 
            +
                    @prompt.print(render_question) unless @quiet
         | 
| 134 | 
            +
                    result.value
         | 
| 127 135 | 
             
                  end
         | 
| 128 136 |  | 
| 129 137 | 
             
                  # Render question
         | 
| @@ -141,7 +149,7 @@ module TTY | |
| 141 149 | 
             
                    elsif @done
         | 
| 142 150 | 
             
                      header << @prompt.decorate(@input.to_s, @active_color)
         | 
| 143 151 | 
             
                    elsif default? && !Utils.blank?(@default)
         | 
| 144 | 
            -
                      header << @prompt.decorate("(#{default})", @help_color) +  | 
| 152 | 
            +
                      header << @prompt.decorate("(#{default})", @help_color) + " "
         | 
| 145 153 | 
             
                    end
         | 
| 146 154 | 
             
                    header << "\n" if @done
         | 
| 147 155 | 
             
                    header.join
         | 
| @@ -162,12 +170,12 @@ module TTY | |
| 162 170 | 
             
                  #
         | 
| 163 171 | 
             
                  # @api private
         | 
| 164 172 | 
             
                  def read_input(question)
         | 
| 165 | 
            -
                    options = {echo: echo}
         | 
| 173 | 
            +
                    options = { echo: echo }
         | 
| 166 174 | 
             
                    if value? && @first_render
         | 
| 167 175 | 
             
                      options[:value] = @value
         | 
| 168 176 | 
             
                      @first_render = false
         | 
| 169 177 | 
             
                    end
         | 
| 170 | 
            -
                    @prompt.read_line(question, options).chomp
         | 
| 178 | 
            +
                    @prompt.read_line(question, **options).chomp
         | 
| 171 179 | 
             
                  end
         | 
| 172 180 |  | 
| 173 181 | 
             
                  # Handle error condition
         | 
| @@ -177,7 +185,7 @@ module TTY | |
| 177 185 | 
             
                  # @api private
         | 
| 178 186 | 
             
                  def render_error(errors)
         | 
| 179 187 | 
             
                    errors.reduce([]) do |acc, err|
         | 
| 180 | 
            -
                      acc << @prompt.decorate( | 
| 188 | 
            +
                      acc << @prompt.decorate(">>", :red) + " " + err
         | 
| 181 189 | 
             
                      acc
         | 
| 182 190 | 
             
                    end.join("\n")
         | 
| 183 191 | 
             
                  end
         | 
| @@ -211,8 +219,13 @@ module TTY | |
| 211 219 | 
             
                  #
         | 
| 212 220 | 
             
                  # @api private
         | 
| 213 221 | 
             
                  def convert_result(value)
         | 
| 214 | 
            -
                    if convert?  | 
| 215 | 
            -
                       | 
| 222 | 
            +
                    if convert? && !Utils.blank?(value)
         | 
| 223 | 
            +
                      case @convert
         | 
| 224 | 
            +
                      when Proc
         | 
| 225 | 
            +
                        @convert.call(value)
         | 
| 226 | 
            +
                      else
         | 
| 227 | 
            +
                        Converters.convert(@convert, value)
         | 
| 228 | 
            +
                      end
         | 
| 216 229 | 
             
                    else
         | 
| 217 230 | 
             
                      value
         | 
| 218 231 | 
             
                    end
         | 
| @@ -221,8 +234,13 @@ module TTY | |
| 221 234 | 
             
                  # Specify answer conversion
         | 
| 222 235 | 
             
                  #
         | 
| 223 236 | 
             
                  # @api public
         | 
| 224 | 
            -
                  def convert(value)
         | 
| 225 | 
            -
                     | 
| 237 | 
            +
                  def convert(value = (not_set = true), message = nil)
         | 
| 238 | 
            +
                    messages[:convert?] = message if message
         | 
| 239 | 
            +
                    if not_set
         | 
| 240 | 
            +
                      @convert
         | 
| 241 | 
            +
                    else
         | 
| 242 | 
            +
                      @convert = value
         | 
| 243 | 
            +
                    end
         | 
| 226 244 | 
             
                  end
         | 
| 227 245 |  | 
| 228 246 | 
             
                  # Check if conversion is set
         | 
| @@ -239,6 +257,7 @@ module TTY | |
| 239 257 | 
             
                  # @api public
         | 
| 240 258 | 
             
                  def default(value = (not_set = true))
         | 
| 241 259 | 
             
                    return @default if not_set
         | 
| 260 | 
            +
             | 
| 242 261 | 
             
                    @default = value
         | 
| 243 262 | 
             
                  end
         | 
| 244 263 |  | 
| @@ -259,9 +278,10 @@ module TTY | |
| 259 278 | 
             
                  def required(value = (not_set = true), message = nil)
         | 
| 260 279 | 
             
                    messages[:required?] = message if message
         | 
| 261 280 | 
             
                    return @required if not_set
         | 
| 281 | 
            +
             | 
| 262 282 | 
             
                    @required = value
         | 
| 263 283 | 
             
                  end
         | 
| 264 | 
            -
                   | 
| 284 | 
            +
                  alias required? required
         | 
| 265 285 |  | 
| 266 286 | 
             
                  # Set validation rule for an argument
         | 
| 267 287 | 
             
                  #
         | 
| @@ -280,6 +300,7 @@ module TTY | |
| 280 300 | 
             
                  # @api public
         | 
| 281 301 | 
             
                  def value(val)
         | 
| 282 302 | 
             
                    return @value if val.nil?
         | 
| 303 | 
            +
             | 
| 283 304 | 
             
                    @value = val
         | 
| 284 305 | 
             
                  end
         | 
| 285 306 |  | 
| @@ -309,18 +330,20 @@ module TTY | |
| 309 330 | 
             
                  # @api public
         | 
| 310 331 | 
             
                  def echo(value = nil)
         | 
| 311 332 | 
             
                    return @echo if value.nil?
         | 
| 333 | 
            +
             | 
| 312 334 | 
             
                    @echo = value
         | 
| 313 335 | 
             
                  end
         | 
| 314 | 
            -
                   | 
| 336 | 
            +
                  alias echo? echo
         | 
| 315 337 |  | 
| 316 338 | 
             
                  # Turn raw mode on or off. This enables character-based input.
         | 
| 317 339 | 
             
                  #
         | 
| 318 340 | 
             
                  # @api public
         | 
| 319 341 | 
             
                  def raw(value = nil)
         | 
| 320 342 | 
             
                    return @raw if value.nil?
         | 
| 343 | 
            +
             | 
| 321 344 | 
             
                    @raw = value
         | 
| 322 345 | 
             
                  end
         | 
| 323 | 
            -
                   | 
| 346 | 
            +
                  alias raw? raw
         | 
| 324 347 |  | 
| 325 348 | 
             
                  # Set expected range of values
         | 
| 326 349 | 
             
                  #
         | 
| @@ -333,6 +356,7 @@ module TTY | |
| 333 356 | 
             
                      @in = Converters.convert(:range, @in)
         | 
| 334 357 | 
             
                    end
         | 
| 335 358 | 
             
                    return @in if not_set
         | 
| 359 | 
            +
             | 
| 336 360 | 
             
                    @in = Converters.convert(:range, value)
         | 
| 337 361 | 
             
                  end
         | 
| 338 362 |  | 
| @@ -345,6 +369,13 @@ module TTY | |
| 345 369 | 
             
                    @in != UndefinedSetting
         | 
| 346 370 | 
             
                  end
         | 
| 347 371 |  | 
| 372 | 
            +
                  # Set quiet mode.
         | 
| 373 | 
            +
                  #
         | 
| 374 | 
            +
                  # @api public
         | 
| 375 | 
            +
                  def quiet(value)
         | 
| 376 | 
            +
                    @quiet = value
         | 
| 377 | 
            +
                  end
         | 
| 378 | 
            +
             | 
| 348 379 | 
             
                  # @api public
         | 
| 349 380 | 
             
                  def to_s
         | 
| 350 381 | 
             
                    message.to_s
         |