tty-prompt 0.18.0 → 0.22.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 (130) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +92 -0
  3. data/README.md +549 -248
  4. data/lib/tty-prompt.rb +1 -2
  5. data/lib/tty/prompt.rb +187 -143
  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 +5 -7
  9. data/lib/tty/prompt/choices.rb +29 -11
  10. data/lib/tty/prompt/confirm_question.rb +38 -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 +57 -27
  16. data/lib/tty/prompt/errors.rb +31 -0
  17. data/lib/tty/prompt/evaluator.rb +1 -1
  18. data/lib/tty/prompt/expander.rb +39 -13
  19. data/lib/tty/prompt/keypress.rb +31 -36
  20. data/lib/tty/prompt/list.rb +175 -65
  21. data/lib/tty/prompt/mask_question.rb +4 -5
  22. data/lib/tty/prompt/multi_list.rb +124 -33
  23. data/lib/tty/prompt/multiline.rb +7 -6
  24. data/lib/tty/prompt/paginator.rb +38 -26
  25. data/lib/tty/prompt/question.rb +83 -34
  26. data/lib/tty/prompt/question/checks.rb +18 -0
  27. data/lib/tty/prompt/question/validation.rb +3 -3
  28. data/lib/tty/prompt/selected_choices.rb +76 -0
  29. data/lib/tty/prompt/slider.rb +83 -16
  30. data/lib/tty/prompt/statement.rb +3 -3
  31. data/lib/tty/prompt/suggestion.rb +6 -6
  32. data/lib/tty/prompt/symbols.rb +58 -34
  33. data/lib/tty/prompt/test.rb +36 -0
  34. data/lib/tty/prompt/timer.rb +75 -0
  35. data/lib/tty/prompt/utils.rb +1 -3
  36. data/lib/tty/prompt/version.rb +1 -1
  37. metadata +29 -227
  38. data/Rakefile +0 -8
  39. data/examples/ask.rb +0 -7
  40. data/examples/ask_valid.rb +0 -12
  41. data/examples/collect.rb +0 -21
  42. data/examples/echo.rb +0 -11
  43. data/examples/enum_select.rb +0 -7
  44. data/examples/enum_select_disabled.rb +0 -16
  45. data/examples/enum_select_paged.rb +0 -9
  46. data/examples/enum_select_wrapped.rb +0 -15
  47. data/examples/expand.rb +0 -29
  48. data/examples/in.rb +0 -9
  49. data/examples/inputs.rb +0 -10
  50. data/examples/key_events.rb +0 -15
  51. data/examples/keypress.rb +0 -9
  52. data/examples/mask.rb +0 -13
  53. data/examples/multi_select.rb +0 -8
  54. data/examples/multi_select_disabled.rb +0 -17
  55. data/examples/multi_select_paged.rb +0 -9
  56. data/examples/multi_select_wrapped.rb +0 -15
  57. data/examples/multiline.rb +0 -9
  58. data/examples/pause.rb +0 -9
  59. data/examples/select.rb +0 -20
  60. data/examples/select_disabled.rb +0 -18
  61. data/examples/select_enum.rb +0 -8
  62. data/examples/select_filtered.rb +0 -11
  63. data/examples/select_paginated.rb +0 -11
  64. data/examples/select_wrapped.rb +0 -15
  65. data/examples/slider.rb +0 -6
  66. data/examples/validation.rb +0 -9
  67. data/examples/yes_no.rb +0 -7
  68. data/lib/tty/prompt/messages.rb +0 -49
  69. data/lib/tty/prompt/timeout.rb +0 -78
  70. data/lib/tty/test_prompt.rb +0 -20
  71. data/spec/spec_helper.rb +0 -45
  72. data/spec/unit/ask_spec.rb +0 -132
  73. data/spec/unit/choice/eql_spec.rb +0 -22
  74. data/spec/unit/choice/from_spec.rb +0 -96
  75. data/spec/unit/choices/add_spec.rb +0 -12
  76. data/spec/unit/choices/each_spec.rb +0 -13
  77. data/spec/unit/choices/find_by_spec.rb +0 -10
  78. data/spec/unit/choices/new_spec.rb +0 -10
  79. data/spec/unit/choices/pluck_spec.rb +0 -9
  80. data/spec/unit/collect_spec.rb +0 -96
  81. data/spec/unit/converters/convert_bool_spec.rb +0 -58
  82. data/spec/unit/converters/convert_char_spec.rb +0 -11
  83. data/spec/unit/converters/convert_custom_spec.rb +0 -14
  84. data/spec/unit/converters/convert_date_spec.rb +0 -34
  85. data/spec/unit/converters/convert_file_spec.rb +0 -18
  86. data/spec/unit/converters/convert_number_spec.rb +0 -39
  87. data/spec/unit/converters/convert_path_spec.rb +0 -15
  88. data/spec/unit/converters/convert_range_spec.rb +0 -22
  89. data/spec/unit/converters/convert_regex_spec.rb +0 -12
  90. data/spec/unit/converters/convert_string_spec.rb +0 -21
  91. data/spec/unit/converters/on_error_spec.rb +0 -9
  92. data/spec/unit/distance/distance_spec.rb +0 -73
  93. data/spec/unit/enum_paginator_spec.rb +0 -75
  94. data/spec/unit/enum_select_spec.rb +0 -446
  95. data/spec/unit/error_spec.rb +0 -20
  96. data/spec/unit/evaluator_spec.rb +0 -67
  97. data/spec/unit/expand_spec.rb +0 -198
  98. data/spec/unit/keypress_spec.rb +0 -72
  99. data/spec/unit/mask_spec.rb +0 -132
  100. data/spec/unit/multi_select_spec.rb +0 -495
  101. data/spec/unit/multiline_spec.rb +0 -77
  102. data/spec/unit/new_spec.rb +0 -20
  103. data/spec/unit/ok_spec.rb +0 -10
  104. data/spec/unit/paginator_spec.rb +0 -73
  105. data/spec/unit/question/checks_spec.rb +0 -97
  106. data/spec/unit/question/default_spec.rb +0 -31
  107. data/spec/unit/question/echo_spec.rb +0 -38
  108. data/spec/unit/question/in_spec.rb +0 -115
  109. data/spec/unit/question/initialize_spec.rb +0 -12
  110. data/spec/unit/question/modifier/apply_to_spec.rb +0 -24
  111. data/spec/unit/question/modifier/letter_case_spec.rb +0 -41
  112. data/spec/unit/question/modifier/whitespace_spec.rb +0 -51
  113. data/spec/unit/question/modify_spec.rb +0 -41
  114. data/spec/unit/question/required_spec.rb +0 -92
  115. data/spec/unit/question/validate_spec.rb +0 -115
  116. data/spec/unit/question/validation/call_spec.rb +0 -31
  117. data/spec/unit/question/validation/coerce_spec.rb +0 -30
  118. data/spec/unit/result_spec.rb +0 -40
  119. data/spec/unit/say_spec.rb +0 -67
  120. data/spec/unit/select_spec.rb +0 -643
  121. data/spec/unit/slider_spec.rb +0 -100
  122. data/spec/unit/statement/initialize_spec.rb +0 -15
  123. data/spec/unit/subscribe_spec.rb +0 -22
  124. data/spec/unit/suggest_spec.rb +0 -28
  125. data/spec/unit/warn_spec.rb +0 -21
  126. data/spec/unit/yes_no_spec.rb +0 -251
  127. data/tasks/console.rake +0 -11
  128. data/tasks/coverage.rake +0 -11
  129. data/tasks/spec.rake +0 -29
  130. data/tty-prompt.gemspec +0 -33
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../const"
4
+
3
5
  module TTY
4
6
  class Prompt
5
7
  class Question
@@ -81,6 +83,22 @@ module TTY
81
83
  end
82
84
  end
83
85
  end
86
+
87
+ class CheckConversion
88
+ def self.call(question, value)
89
+ if question.convert? && !Utils.blank?(value)
90
+ result = question.convert_result(value)
91
+ if result == Const::Undefined
92
+ tokens = {value: value, type: question.convert}
93
+ [value, question.message_for(:convert?, tokens)]
94
+ else
95
+ [result]
96
+ end
97
+ else
98
+ [value]
99
+ end
100
+ end
101
+ end
84
102
  end # Checks
85
103
  end # Question
86
104
  end # Prompt
@@ -57,11 +57,11 @@ module TTY
57
57
  def call(input)
58
58
  if pattern.is_a?(String) || pattern.is_a?(Symbol)
59
59
  VALIDATORS.key?(pattern.to_sym)
60
- !VALIDATORS[pattern.to_sym].match(input).nil?
60
+ !VALIDATORS[pattern.to_sym].match(input.to_s).nil?
61
61
  elsif pattern.is_a?(Regexp)
62
- !pattern.match(input).nil?
62
+ !pattern.match(input.to_s).nil?
63
63
  elsif pattern.is_a?(Proc)
64
- result = pattern.call(input)
64
+ result = pattern.call(input.to_s)
65
65
  result.nil? ? false : result
66
66
  else false
67
67
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module TTY
4
+ class Prompt
5
+ # @api private
6
+ class SelectedChoices
7
+ include Enumerable
8
+
9
+ attr_reader :size
10
+
11
+ # Create selected choices
12
+ #
13
+ # @param [Array<Choice>] selected
14
+ # @param [Array<Integer>] indexes
15
+ #
16
+ # @api public
17
+ def initialize(selected = [], indexes = [])
18
+ @selected = selected
19
+ @indexes = indexes
20
+ @size = @selected.size
21
+ end
22
+
23
+ # Clear selected choices
24
+ #
25
+ # @api public
26
+ def clear
27
+ @indexes.clear
28
+ @selected.clear
29
+ @size = 0
30
+ end
31
+
32
+ # Iterate over selected choices
33
+ #
34
+ # @api public
35
+ def each(&block)
36
+ return to_enum unless block_given?
37
+ @selected.each(&block)
38
+ end
39
+
40
+ # Insert choice at index
41
+ #
42
+ # @param [Integer] index
43
+ # @param [Choice] choice
44
+ #
45
+ # @api public
46
+ def insert(index, choice)
47
+ insert_idx = find_index_by { |i| index < @indexes[i] }
48
+ insert_idx ||= -1
49
+ @indexes.insert(insert_idx, index)
50
+ @selected.insert(insert_idx, choice)
51
+ @size += 1
52
+ self
53
+ end
54
+
55
+ # Delete choice at index
56
+ #
57
+ # @return [Choice]
58
+ # the deleted choice
59
+ #
60
+ # @api public
61
+ def delete_at(index)
62
+ delete_idx = @indexes.each_index.find { |i| index == @indexes[i] }
63
+ return nil unless delete_idx
64
+
65
+ @indexes.delete_at(delete_idx)
66
+ choice = @selected.delete_at(delete_idx)
67
+ @size -= 1
68
+ choice
69
+ end
70
+
71
+ def find_index_by(&search)
72
+ (0...@size).bsearch(&search)
73
+ end
74
+ end # SelectedChoices
75
+ end # Prompt
76
+ end # TTY
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'symbols'
4
-
5
3
  module TTY
6
4
  # A class responsible for shell prompt interactions.
7
5
  class Prompt
@@ -9,11 +7,9 @@ module TTY
9
7
  #
10
8
  # @api public
11
9
  class Slider
12
- include Symbols
13
-
14
- HELP = '(Use arrow keys, press Enter to select)'.freeze
10
+ HELP = "(Use %s arrow keys, press Enter to select)"
15
11
 
16
- FORMAT = ':slider %d'.freeze
12
+ FORMAT = ":slider %d"
17
13
 
18
14
  # Initailize a Slider
19
15
  #
@@ -27,7 +23,7 @@ module TTY
27
23
  # @option options [String] :format The display format
28
24
  #
29
25
  # @api public
30
- def initialize(prompt, options = {})
26
+ def initialize(prompt, **options)
31
27
  @prompt = prompt
32
28
  @prefix = options.fetch(:prefix) { @prompt.prefix }
33
29
  @min = options.fetch(:min) { 0 }
@@ -37,10 +33,26 @@ module TTY
37
33
  @active_color = options.fetch(:active_color) { @prompt.active_color }
38
34
  @help_color = options.fetch(:help_color) { @prompt.help_color }
39
35
  @format = options.fetch(:format) { FORMAT }
36
+ @quiet = options.fetch(:quiet) { @prompt.quiet }
37
+ @help = options[:help]
38
+ @show_help = options.fetch(:show_help) { :start }
39
+ @symbols = @prompt.symbols.merge(options.fetch(:symbols, {}))
40
40
  @first_render = true
41
41
  @done = false
42
42
  end
43
43
 
44
+ # Change symbols used by this prompt
45
+ #
46
+ # @param [Hash] new_symbols
47
+ # the new symbols to use
48
+ #
49
+ # @api public
50
+ def symbols(new_symbols = (not_set = true))
51
+ return @symbols if not_set
52
+
53
+ @symbols.merge!(new_symbols)
54
+ end
55
+
44
56
  # Setup initial active position
45
57
  #
46
58
  # @return [Integer]
@@ -54,11 +66,39 @@ module TTY
54
66
  end
55
67
  end
56
68
 
69
+ # Default help text
70
+ #
71
+ # @api public
72
+ def default_help
73
+ arrows = @symbols[:arrow_left] + "/" + @symbols[:arrow_right]
74
+ sprintf(HELP, arrows)
75
+ end
76
+
77
+ # Set help text
78
+ #
79
+ # @param [String] text
80
+ #
81
+ # @api private
82
+ def help(text = (not_set = true))
83
+ return @help if !@help.nil? && not_set
84
+
85
+ @help = (@help.nil? && not_set) ? default_help : text
86
+ end
87
+
88
+ # Change when help is displayed
89
+ #
90
+ # @api public
91
+ def show_help(value = (not_set = true))
92
+ return @show_ehlp if not_set
93
+
94
+ @show_help = value
95
+ end
96
+
57
97
  # Range of numbers to render
58
98
  #
59
99
  # @return [Array[Integer]]
60
100
  #
61
- # @apip private
101
+ # @api private
62
102
  def range
63
103
  (@min..@max).step(@step).to_a
64
104
  end
@@ -87,6 +127,13 @@ module TTY
87
127
  @format = value
88
128
  end
89
129
 
130
+ # Set quiet mode.
131
+ #
132
+ # @api public
133
+ def quiet(value)
134
+ @quiet = value
135
+ end
136
+
90
137
  # Call the slider by passing question
91
138
  #
92
139
  # @param [String] question
@@ -120,6 +167,20 @@ module TTY
120
167
 
121
168
  private
122
169
 
170
+ # Check if help is shown only on start
171
+ #
172
+ # @api private
173
+ def help_start?
174
+ @show_help =~ /start/i
175
+ end
176
+
177
+ # Check if help is always displayed
178
+ #
179
+ # @api private
180
+ def help_always?
181
+ @show_help =~ /always/i
182
+ end
183
+
123
184
  # Render an interactive range slider.
124
185
  #
125
186
  # @api private
@@ -131,7 +192,7 @@ module TTY
131
192
  @prompt.read_keypress
132
193
  refresh(question.lines.count)
133
194
  end
134
- @prompt.print(render_question)
195
+ @prompt.print(render_question) unless @quiet
135
196
  answer
136
197
  ensure
137
198
  @prompt.print(@prompt.show)
@@ -167,8 +228,9 @@ module TTY
167
228
  else
168
229
  header << render_slider
169
230
  end
170
- if @first_render
171
- header << "\n" + @prompt.decorate(HELP, @help_color)
231
+ if @first_render && (help_start? || help_always?) ||
232
+ (help_always? && !@done)
233
+ header << "\n" + @prompt.decorate(help, @help_color)
172
234
  @first_render = false
173
235
  end
174
236
  header.join
@@ -180,11 +242,16 @@ module TTY
180
242
  #
181
243
  # @api private
182
244
  def render_slider
183
- slider = (symbols[:line] * @active) +
184
- @prompt.decorate(symbols[:handle], @active_color) +
185
- (symbols[:line] * (range.size - @active - 1))
186
- value = " #{range[@active]}"
187
- @format.gsub(':slider', slider) % [value]
245
+ slider = (@symbols[:line] * @active) +
246
+ @prompt.decorate(@symbols[:bullet], @active_color) +
247
+ (@symbols[:line] * (range.size - @active - 1))
248
+ value = range[@active]
249
+ case @format
250
+ when Proc
251
+ @format.call(slider, value)
252
+ else
253
+ @format.gsub(":slider", slider) % [value]
254
+ end
188
255
  end
189
256
  end # Slider
190
257
  end # Prompt
@@ -28,10 +28,10 @@ module TTY
28
28
  # change the message display to color
29
29
  #
30
30
  # @api public
31
- def initialize(prompt, options = {})
31
+ def initialize(prompt, newline: true, color: false)
32
32
  @prompt = prompt
33
- @newline = options.fetch(:newline) { true }
34
- @color = options.fetch(:color) { false }
33
+ @newline = newline
34
+ @color = color
35
35
  end
36
36
 
37
37
  # Output the message to the prompt
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'distance'
3
+ require_relative "distance"
4
4
 
5
5
  module TTY
6
6
  # A class responsible for terminal prompt interactions.
@@ -11,9 +11,9 @@ module TTY
11
11
  class Suggestion
12
12
  DEFAULT_INDENT = 8
13
13
 
14
- SINGLE_TEXT = 'Did you mean this?'
14
+ SINGLE_TEXT = "Did you mean this?"
15
15
 
16
- PLURAL_TEXT = 'Did you mean one of these?'
16
+ PLURAL_TEXT = "Did you mean one of these?"
17
17
 
18
18
  # Number of spaces
19
19
  #
@@ -33,7 +33,7 @@ module TTY
33
33
  # Initialize a Suggestion
34
34
  #
35
35
  # @api public
36
- def initialize(options = {})
36
+ def initialize(**options)
37
37
  @indent = options.fetch(:indent) { DEFAULT_INDENT }
38
38
  @single_text = options.fetch(:single_text) { SINGLE_TEXT }
39
39
  @plural_text = options.fetch(:plural_text) { PLURAL_TEXT }
@@ -97,14 +97,14 @@ module TTY
97
97
 
98
98
  # @api private
99
99
  def build_single_suggestion
100
- single_text + "\n" + (' ' * indent) + @suggestions.first
100
+ single_text + "\n" + (" " * indent) + @suggestions.first
101
101
  end
102
102
 
103
103
  # @api private
104
104
  def build_multiple_suggestions
105
105
  plural_text + "\n" +
106
106
  @suggestions.map do |sugest|
107
- ' ' * indent + sugest
107
+ " " * indent + sugest
108
108
  end.join("\n")
109
109
  end
110
110
  end # Suggestion
@@ -7,43 +7,67 @@ module TTY
7
7
  # @api public
8
8
  module Symbols
9
9
  KEYS = {
10
- tick: '',
11
- cross: '',
12
- star: '',
13
- square: '',
14
- square_empty: '',
15
- dot: '',
16
- pointer: '‣',
17
- line: '─',
18
- pipe: '|',
19
- handle: 'O',
20
- ellipsis: '…',
21
- radio_on: '⬢',
22
- radio_off: '⬡',
23
- checkbox_on: '☒',
24
- checkbox_off: '☐',
25
- circle_on: 'ⓧ',
26
- circle_off: 'Ⓘ'
10
+ tick: "",
11
+ cross: "",
12
+ star: "",
13
+ square: "",
14
+ square_empty: "",
15
+ dot: "",
16
+ bullet: "●",
17
+ bullet_empty: "○",
18
+ marker: "‣",
19
+ line: "─",
20
+ pipe: "|",
21
+ ellipsis: "…",
22
+ radio_on: "⬢",
23
+ radio_off: "⬡",
24
+ checkbox_on: "☒",
25
+ checkbox_off: "☐",
26
+ circle: "◯",
27
+ circle_on: "ⓧ",
28
+ circle_off: "Ⓘ",
29
+ arrow_up: "↑",
30
+ arrow_down: "↓",
31
+ arrow_up_down: "↕",
32
+ arrow_left: "←",
33
+ arrow_right: "→",
34
+ arrow_left_right: "↔",
35
+ heart: "♥",
36
+ diamond: "♦",
37
+ club: "♣",
38
+ spade: "♠"
27
39
  }.freeze
28
40
 
29
41
  WIN_KEYS = {
30
- tick: '',
31
- cross: 'x',
32
- star: '*',
33
- square: '[█]',
34
- square_empty: '[ ]',
35
- dot: '.',
36
- pointer: '>',
37
- line: '-',
38
- pipe: '|',
39
- handle: 'O',
40
- ellipsis: '...',
41
- radio_on: '(*)',
42
- radio_off: '( )',
43
- checkbox_on: '[×]',
44
- checkbox_off: '[ ]',
45
- circle_on: '(x)',
46
- circle_off: '( )'
42
+ tick: "",
43
+ cross: "x",
44
+ star: "*",
45
+ square: "[█]",
46
+ square_empty: "[ ]",
47
+ dot: ".",
48
+ bullet: "O",
49
+ bullet_empty: "○",
50
+ marker: ">",
51
+ line: "-",
52
+ pipe: "|",
53
+ ellipsis: "...",
54
+ radio_on: "(*)",
55
+ radio_off: "( )",
56
+ checkbox_on: "[×]",
57
+ checkbox_off: "[ ]",
58
+ circle: "( )",
59
+ circle_on: "(x)",
60
+ circle_off: "( )",
61
+ arrow_up: "↑",
62
+ arrow_down: "↓",
63
+ arrow_up_down: "↕",
64
+ arrow_left: "←",
65
+ arrow_right: "→",
66
+ arrow_left_right: "↔",
67
+ heart: "♥",
68
+ diamond: "♦",
69
+ club: "♣",
70
+ spade: "♠"
47
71
  }.freeze
48
72
 
49
73
  def symbols