tty-prompt 0.18.1 → 0.23.0

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.
Files changed (131) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +95 -0
  3. data/README.md +598 -256
  4. data/lib/tty-prompt.rb +1 -2
  5. data/lib/tty/prompt.rb +192 -144
  6. data/lib/tty/prompt/answers_collector.rb +5 -5
  7. data/lib/tty/prompt/{enum_paginator.rb → block_paginator.rb} +20 -19
  8. data/lib/tty/prompt/choice.rb +9 -10
  9. data/lib/tty/prompt/choices.rb +30 -12
  10. data/lib/tty/prompt/confirm_question.rb +42 -16
  11. data/lib/tty/prompt/const.rb +17 -0
  12. data/lib/tty/prompt/converter_dsl.rb +6 -7
  13. data/lib/tty/prompt/converter_registry.rb +31 -26
  14. data/lib/tty/prompt/converters.rb +139 -32
  15. data/lib/tty/prompt/enum_list.rb +81 -26
  16. data/lib/tty/prompt/errors.rb +31 -0
  17. data/lib/tty/prompt/evaluator.rb +2 -2
  18. data/lib/tty/prompt/expander.rb +45 -15
  19. data/lib/tty/prompt/keypress.rb +33 -36
  20. data/lib/tty/prompt/list.rb +198 -63
  21. data/lib/tty/prompt/mask_question.rb +11 -8
  22. data/lib/tty/prompt/multi_list.rb +131 -28
  23. data/lib/tty/prompt/multiline.rb +9 -7
  24. data/lib/tty/prompt/paginator.rb +38 -26
  25. data/lib/tty/prompt/question.rb +92 -37
  26. data/lib/tty/prompt/question/checks.rb +20 -2
  27. data/lib/tty/prompt/question/modifier.rb +4 -2
  28. data/lib/tty/prompt/question/validation.rb +3 -3
  29. data/lib/tty/prompt/selected_choices.rb +77 -0
  30. data/lib/tty/prompt/slider.rb +125 -30
  31. data/lib/tty/prompt/statement.rb +3 -3
  32. data/lib/tty/prompt/suggestion.rb +7 -6
  33. data/lib/tty/prompt/symbols.rb +58 -34
  34. data/lib/tty/prompt/test.rb +36 -0
  35. data/lib/tty/prompt/timer.rb +75 -0
  36. data/lib/tty/prompt/utils.rb +1 -3
  37. data/lib/tty/prompt/version.rb +1 -1
  38. metadata +29 -227
  39. data/Rakefile +0 -8
  40. data/examples/ask.rb +0 -7
  41. data/examples/ask_valid.rb +0 -12
  42. data/examples/collect.rb +0 -21
  43. data/examples/echo.rb +0 -11
  44. data/examples/enum_select.rb +0 -7
  45. data/examples/enum_select_disabled.rb +0 -16
  46. data/examples/enum_select_paged.rb +0 -9
  47. data/examples/enum_select_wrapped.rb +0 -15
  48. data/examples/expand.rb +0 -29
  49. data/examples/in.rb +0 -9
  50. data/examples/inputs.rb +0 -10
  51. data/examples/key_events.rb +0 -15
  52. data/examples/keypress.rb +0 -9
  53. data/examples/mask.rb +0 -13
  54. data/examples/multi_select.rb +0 -8
  55. data/examples/multi_select_disabled.rb +0 -17
  56. data/examples/multi_select_paged.rb +0 -9
  57. data/examples/multi_select_wrapped.rb +0 -15
  58. data/examples/multiline.rb +0 -9
  59. data/examples/pause.rb +0 -9
  60. data/examples/select.rb +0 -24
  61. data/examples/select_disabled.rb +0 -18
  62. data/examples/select_enum.rb +0 -8
  63. data/examples/select_filtered.rb +0 -11
  64. data/examples/select_paginated.rb +0 -11
  65. data/examples/select_wrapped.rb +0 -15
  66. data/examples/slider.rb +0 -6
  67. data/examples/validation.rb +0 -9
  68. data/examples/yes_no.rb +0 -7
  69. data/lib/tty/prompt/messages.rb +0 -49
  70. data/lib/tty/prompt/timeout.rb +0 -78
  71. data/lib/tty/test_prompt.rb +0 -20
  72. data/spec/spec_helper.rb +0 -45
  73. data/spec/unit/ask_spec.rb +0 -132
  74. data/spec/unit/choice/eql_spec.rb +0 -22
  75. data/spec/unit/choice/from_spec.rb +0 -96
  76. data/spec/unit/choices/add_spec.rb +0 -12
  77. data/spec/unit/choices/each_spec.rb +0 -13
  78. data/spec/unit/choices/find_by_spec.rb +0 -10
  79. data/spec/unit/choices/new_spec.rb +0 -10
  80. data/spec/unit/choices/pluck_spec.rb +0 -9
  81. data/spec/unit/collect_spec.rb +0 -96
  82. data/spec/unit/converters/convert_bool_spec.rb +0 -58
  83. data/spec/unit/converters/convert_char_spec.rb +0 -11
  84. data/spec/unit/converters/convert_custom_spec.rb +0 -14
  85. data/spec/unit/converters/convert_date_spec.rb +0 -34
  86. data/spec/unit/converters/convert_file_spec.rb +0 -18
  87. data/spec/unit/converters/convert_number_spec.rb +0 -39
  88. data/spec/unit/converters/convert_path_spec.rb +0 -15
  89. data/spec/unit/converters/convert_range_spec.rb +0 -22
  90. data/spec/unit/converters/convert_regex_spec.rb +0 -12
  91. data/spec/unit/converters/convert_string_spec.rb +0 -21
  92. data/spec/unit/converters/on_error_spec.rb +0 -9
  93. data/spec/unit/distance/distance_spec.rb +0 -73
  94. data/spec/unit/enum_paginator_spec.rb +0 -75
  95. data/spec/unit/enum_select_spec.rb +0 -446
  96. data/spec/unit/error_spec.rb +0 -20
  97. data/spec/unit/evaluator_spec.rb +0 -67
  98. data/spec/unit/expand_spec.rb +0 -198
  99. data/spec/unit/keypress_spec.rb +0 -72
  100. data/spec/unit/mask_spec.rb +0 -132
  101. data/spec/unit/multi_select_spec.rb +0 -511
  102. data/spec/unit/multiline_spec.rb +0 -77
  103. data/spec/unit/new_spec.rb +0 -20
  104. data/spec/unit/ok_spec.rb +0 -10
  105. data/spec/unit/paginator_spec.rb +0 -73
  106. data/spec/unit/question/checks_spec.rb +0 -97
  107. data/spec/unit/question/default_spec.rb +0 -31
  108. data/spec/unit/question/echo_spec.rb +0 -38
  109. data/spec/unit/question/in_spec.rb +0 -115
  110. data/spec/unit/question/initialize_spec.rb +0 -12
  111. data/spec/unit/question/modifier/apply_to_spec.rb +0 -24
  112. data/spec/unit/question/modifier/letter_case_spec.rb +0 -41
  113. data/spec/unit/question/modifier/whitespace_spec.rb +0 -51
  114. data/spec/unit/question/modify_spec.rb +0 -41
  115. data/spec/unit/question/required_spec.rb +0 -92
  116. data/spec/unit/question/validate_spec.rb +0 -115
  117. data/spec/unit/question/validation/call_spec.rb +0 -31
  118. data/spec/unit/question/validation/coerce_spec.rb +0 -30
  119. data/spec/unit/result_spec.rb +0 -40
  120. data/spec/unit/say_spec.rb +0 -67
  121. data/spec/unit/select_spec.rb +0 -660
  122. data/spec/unit/slider_spec.rb +0 -100
  123. data/spec/unit/statement/initialize_spec.rb +0 -15
  124. data/spec/unit/subscribe_spec.rb +0 -22
  125. data/spec/unit/suggest_spec.rb +0 -28
  126. data/spec/unit/warn_spec.rb +0 -21
  127. data/spec/unit/yes_no_spec.rb +0 -251
  128. data/tasks/console.rake +0 -11
  129. data/tasks/coverage.rake +0 -11
  130. data/tasks/spec.rake +0 -29
  131. data/tty-prompt.gemspec +0 -33
@@ -1,20 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'question'
4
- require_relative 'symbols'
3
+ require_relative "question"
5
4
 
6
5
  module TTY
7
6
  class Prompt
8
7
  class MaskQuestion < Question
8
+ # Names for delete keys
9
+ DELETE_KEYS = %i[backspace delete].freeze
10
+
9
11
  # Create masked question
10
12
  #
11
13
  # @param [Hash] options
12
14
  # @option options [String] :mask
13
15
  #
14
16
  # @api public
15
- def initialize(prompt, options = {})
17
+ def initialize(prompt, **options)
16
18
  super
17
- @mask = options.fetch(:mask) { Symbols.symbols[:dot] }
19
+ @mask = options.fetch(:mask) { @prompt.symbols[:dot] }
18
20
  @done_masked = false
19
21
  @failure = false
20
22
  end
@@ -28,19 +30,20 @@ module TTY
28
30
  # @api public
29
31
  def mask(char = (not_set = true))
30
32
  return @mask if not_set
33
+
31
34
  @mask = char
32
35
  end
33
36
 
34
- def keyreturn(event)
37
+ def keyreturn(_event)
35
38
  @done_masked = true
36
39
  end
37
40
 
38
- def keyenter(event)
41
+ def keyenter(_event)
39
42
  @done_masked = true
40
43
  end
41
44
 
42
45
  def keypress(event)
43
- if [:backspace, :delete].include?(event.key.name)
46
+ if DELETE_KEYS.include?(event.key.name)
44
47
  @input.chop! unless @input.empty?
45
48
  elsif event.value =~ /^[^\e\n\r]/
46
49
  @input += event.value
@@ -76,7 +79,7 @@ module TTY
76
79
  def read_input(question)
77
80
  @done_masked = false
78
81
  @failure = false
79
- @input = ''
82
+ @input = ""
80
83
  @prompt.print(question)
81
84
  until @done_masked
82
85
  @prompt.read_keypress
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'list'
3
+ require_relative "list"
4
+ require_relative "selected_choices"
4
5
 
5
6
  module TTY
6
7
  class Prompt
@@ -9,33 +10,83 @@ module TTY
9
10
  #
10
11
  # @api private
11
12
  class MultiList < List
12
- HELP = '(Use 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
17
16
  # @param [Hash] options
18
17
  #
19
18
  # @api public
20
- def initialize(prompt, options)
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
+ @max = options[:max]
26
+ end
27
+
28
+ # Set a minimum number of choices
29
+ #
30
+ # @api public
31
+ def min(value)
32
+ @min = value
25
33
  end
26
34
 
35
+ # Set a maximum number of choices
36
+ #
37
+ # @api public
38
+ def max(value)
39
+ @max = value
40
+ end
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
+
27
54
  # Callback fired when space key is pressed
28
55
  #
29
56
  # @api private
30
57
  def keyspace(*)
31
58
  active_choice = choices[@active - 1]
32
59
  if @selected.include?(active_choice)
33
- @selected.delete(active_choice)
60
+ @selected.delete_at(@active - 1)
34
61
  else
35
- @selected << active_choice
62
+ return if @max && @selected.size >= @max
63
+
64
+ @selected.insert(@active - 1, active_choice)
36
65
  end
37
66
  end
38
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
+
39
90
  private
40
91
 
41
92
  # Setup default options and active selection
@@ -44,12 +95,23 @@ module TTY
44
95
  def setup_defaults
45
96
  validate_defaults
46
97
  # At this stage, @choices matches all the visible choices.
47
- @selected = @choices.values_at(*@default.map { |d| d - 1 })
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)
48
107
 
49
- if !@default.empty?
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
50
112
  @active = @default.last
51
- else
52
- @active = @choices.index { |choice| !choice.disabled? } + 1
113
+ elsif default_choice = choices.find_by(:name, @default.last.to_s)
114
+ @active = choices.index(default_choice) + 1
53
115
  end
54
116
  end
55
117
 
@@ -59,23 +121,63 @@ module TTY
59
121
  #
60
122
  # @api private
61
123
  def selected_names
62
- @selected.map(&:name).join(', ')
124
+ @selected.map(&:name).join(", ")
125
+ end
126
+
127
+ # Header part showing the minimum/maximum number of choices
128
+ #
129
+ # @return [String]
130
+ #
131
+ # @api private
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
63
158
  end
64
159
 
65
160
  # Render initial help text and then currently selected choices
66
161
  #
67
162
  # @api private
68
163
  def render_header
69
- instructions = @prompt.decorate(help, :bright_black)
164
+ instructions = @prompt.decorate(help, @help_color)
165
+ minmax_suffix = @min || @max ? minmax_help : ""
166
+ print_selected = @selected.size.nonzero? && @echo
167
+
70
168
  if @done && @echo
71
169
  @prompt.decorate(selected_names, @active_color)
72
- elsif @selected.size.nonzero? && @echo
73
- help_suffix = filterable? && @filter.any? ? " #{filter_help}" : ""
74
- selected_names + (@first_render ? " #{instructions}" : help_suffix)
75
- elsif @first_render
76
- 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
77
175
  elsif filterable? && @filter.any?
78
- filter_help
176
+ minmax_suffix +
177
+ (print_selected ? "#{selected_names} " : "") +
178
+ @prompt.decorate(filter_help, @help_color)
179
+ else
180
+ minmax_suffix + (print_selected ? selected_names : "")
79
181
  end
80
182
  end
81
183
 
@@ -96,21 +198,22 @@ module TTY
96
198
  def render_menu
97
199
  output = []
98
200
 
99
- @paginator.paginate(choices, @active, @per_page) do |choice, index|
100
- num = enumerate? ? (index + 1).to_s + @enum + ' ' : ''
101
- indicator = (index + 1 == @active) ? @marker : ' '
102
- indicator += ' '
201
+ sync_paginators if @paging_changed
202
+ paginator.paginate(choices, @active, @per_page) do |choice, index|
203
+ num = enumerate? ? (index + 1).to_s + @enum + " " : ""
204
+ indicator = (index + 1 == @active) ? @symbols[:marker] : " "
205
+ indicator += " "
103
206
  message = if @selected.include?(choice) && !choice.disabled?
104
- selected = @prompt.decorate(symbols[:radio_on], @active_color)
207
+ selected = @prompt.decorate(@symbols[:radio_on], @active_color)
105
208
  "#{selected} #{num}#{choice.name}"
106
209
  elsif choice.disabled?
107
- @prompt.decorate(symbols[:cross], :red) +
210
+ @prompt.decorate(@symbols[:cross], :red) +
108
211
  " #{num}#{choice.name} #{choice.disabled}"
109
212
  else
110
- "#{symbols[:radio_off]} #{num}#{choice.name}"
213
+ "#{@symbols[:radio_off]} #{num}#{choice.name}"
111
214
  end
112
- max_index = paginated? ? @paginator.max_index : choices.size - 1
113
- newline = (index == max_index) ? '' : "\n"
215
+ end_index = paginated? ? paginator.end_index : choices.size - 1
216
+ newline = (index == end_index) ? "" : "\n"
114
217
  output << indicator + message + newline
115
218
  end
116
219
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'question'
4
- require_relative 'symbols'
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 = '(Press CTRL-D or CTRL-Z to finish)'.freeze
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("#{@input}", @active_color)
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? ? default : nil
59
+ if Utils.blank?(@input) && default?
60
+ @input = default
61
+ @lines = default
60
62
  end
61
63
  @evaluator.(@lines)
62
64
  end
@@ -5,23 +5,33 @@ module TTY
5
5
  class Paginator
6
6
  DEFAULT_PAGE_SIZE = 6
7
7
 
8
+ # The 0-based index of the first item on this page
9
+ attr_accessor :start_index
10
+
11
+ # The 0-based index of the last item on this page
12
+ attr_reader :end_index
13
+
14
+ # The 0-based index of the active item on this page
15
+ attr_reader :current_index
16
+
17
+ # The 0-based index of the previously active item on this page
18
+ attr_reader :last_index
19
+
8
20
  # Create a Paginator
9
21
  #
10
22
  # @api private
11
- def initialize(options = {})
23
+ def initialize(**options)
12
24
  @last_index = Array(options[:default]).flatten.first || 0
13
25
  @per_page = options[:per_page]
14
- @lower_index = Array(options[:default]).flatten.first
26
+ @start_index = Array(options[:default]).flatten.first
15
27
  end
16
28
 
17
- # Maximum index for current pagination
29
+ # Reset current page indexes
18
30
  #
19
- # @return [Integer]
20
- #
21
- # @api public
22
- def max_index
23
- raise ArgumentError, 'no max index' unless @per_page
24
- @lower_index + @per_page - 1
31
+ # @api private
32
+ def reset!
33
+ @start_index = nil
34
+ @end_index = nil
25
35
  end
26
36
 
27
37
  # Check if page size is valid
@@ -30,7 +40,7 @@ module TTY
30
40
  #
31
41
  # @api private
32
42
  def check_page_size!
33
- raise InvalidArgument, 'per_page must be > 0' if @per_page < 1
43
+ raise InvalidArgument, "per_page must be > 0" if @per_page < 1
34
44
  end
35
45
 
36
46
  # Paginate collection given an active index
@@ -43,21 +53,21 @@ module TTY
43
53
  # number of choice items per page
44
54
  #
45
55
  # @return [Enumerable]
56
+ # the list between start and end index
46
57
  #
47
58
  # @api public
48
59
  def paginate(list, active, per_page = nil, &block)
49
60
  current_index = active - 1
50
61
  default_size = (list.size <= DEFAULT_PAGE_SIZE ? list.size : DEFAULT_PAGE_SIZE)
51
62
  @per_page = @per_page || per_page || default_size
52
- @lower_index ||= current_index
53
- @upper_index ||= max_index
54
-
55
63
  check_page_size!
64
+ @start_index ||= (current_index / @per_page) * @per_page
65
+ @end_index ||= @start_index + @per_page - 1
56
66
 
57
67
  # Don't paginate short lists
58
68
  if list.size <= @per_page
59
- @lower_index = 0
60
- @upper_index = list.size - 1
69
+ @start_index = 0
70
+ @end_index = list.size - 1
61
71
  if block
62
72
  return list.each_with_index(&block)
63
73
  else
@@ -65,33 +75,35 @@ module TTY
65
75
  end
66
76
  end
67
77
 
78
+ step = (current_index - @last_index).abs
68
79
  if current_index > @last_index # going up
69
- if current_index > @upper_index && current_index < list.size - 1
70
- @lower_index += 1
80
+ if current_index >= @end_index && current_index < list.size - 1
81
+ last_page = list.size - @per_page
82
+ @start_index = [@start_index + step, last_page].min
71
83
  end
72
84
  elsif current_index < @last_index # going down
73
- if current_index < @lower_index && current_index > 0
74
- @lower_index -= 1
85
+ if current_index <= @start_index && current_index > 0
86
+ @start_index = [@start_index - step, 0].max
75
87
  end
76
88
  end
77
89
 
78
90
  # Cycle list
79
91
  if current_index.zero?
80
- @lower_index = 0
92
+ @start_index = 0
81
93
  elsif current_index == list.size - 1
82
- @lower_index = list.size - 1 - (@per_page - 1)
94
+ @start_index = list.size - 1 - (@per_page - 1)
83
95
  end
84
96
 
85
- @upper_index = @lower_index + (@per_page - 1)
97
+ @end_index = @start_index + (@per_page - 1)
86
98
  @last_index = current_index
87
99
 
88
- sliced_list = list[@lower_index..@upper_index]
89
- indices = (@lower_index..@upper_index)
100
+ sliced_list = list[@start_index..@end_index]
101
+ page_range = (@start_index..@end_index)
90
102
 
91
- return sliced_list.zip(indices).to_enum unless block_given?
103
+ return sliced_list.zip(page_range).to_enum unless block_given?
92
104
 
93
105
  sliced_list.each_with_index do |item, index|
94
- block[item, @lower_index + index]
106
+ block[item, @start_index + index]
95
107
  end
96
108
  end
97
109
  end # Paginator