tty-prompt 0.18.0 → 0.22.0

Sign up to get free protection for your applications and to get access to all the features.
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