tty-prompt 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. checksums.yaml +5 -5
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +17 -0
  4. data/Gemfile +2 -3
  5. data/README.md +140 -12
  6. data/appveyor.yml +1 -0
  7. data/examples/enum_select_disabled.rb +16 -0
  8. data/examples/{enum_paged.rb → enum_select_paged.rb} +0 -0
  9. data/examples/enum_select_wrapped.rb +15 -0
  10. data/examples/multi_select_disabled.rb +17 -0
  11. data/examples/multi_select_wrapped.rb +15 -0
  12. data/examples/select.rb +3 -1
  13. data/examples/select_disabled.rb +18 -0
  14. data/examples/{enum.rb → select_enum.rb} +0 -0
  15. data/examples/select_filtered.rb +3 -1
  16. data/examples/select_paginated.rb +3 -1
  17. data/examples/select_wrapped.rb +15 -0
  18. data/lib/tty/prompt.rb +1 -0
  19. data/lib/tty/prompt/answers_collector.rb +1 -0
  20. data/lib/tty/prompt/choice.rb +67 -25
  21. data/lib/tty/prompt/choices.rb +1 -0
  22. data/lib/tty/prompt/confirm_question.rb +3 -4
  23. data/lib/tty/prompt/converter_dsl.rb +1 -0
  24. data/lib/tty/prompt/converter_registry.rb +1 -0
  25. data/lib/tty/prompt/converters.rb +1 -0
  26. data/lib/tty/prompt/distance.rb +1 -0
  27. data/lib/tty/prompt/enum_list.rb +58 -17
  28. data/lib/tty/prompt/enum_paginator.rb +1 -0
  29. data/lib/tty/prompt/evaluator.rb +1 -0
  30. data/lib/tty/prompt/expander.rb +14 -14
  31. data/lib/tty/prompt/keypress.rb +1 -1
  32. data/lib/tty/prompt/list.rb +78 -39
  33. data/lib/tty/prompt/mask_question.rb +5 -4
  34. data/lib/tty/prompt/multi_list.rb +13 -3
  35. data/lib/tty/prompt/multiline.rb +6 -5
  36. data/lib/tty/prompt/paginator.rb +1 -0
  37. data/lib/tty/prompt/question.rb +10 -9
  38. data/lib/tty/prompt/question/checks.rb +1 -0
  39. data/lib/tty/prompt/question/modifier.rb +1 -0
  40. data/lib/tty/prompt/question/validation.rb +1 -0
  41. data/lib/tty/prompt/result.rb +1 -0
  42. data/lib/tty/prompt/slider.rb +3 -2
  43. data/lib/tty/prompt/statement.rb +1 -0
  44. data/lib/tty/prompt/suggestion.rb +4 -6
  45. data/lib/tty/prompt/symbols.rb +2 -1
  46. data/lib/tty/prompt/timeout.rb +16 -11
  47. data/lib/tty/prompt/utils.rb +1 -0
  48. data/lib/tty/prompt/version.rb +1 -1
  49. data/lib/tty/test_prompt.rb +1 -0
  50. metadata +11 -5
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'question'
4
5
  require_relative 'symbols'
@@ -52,18 +53,18 @@ module TTY
52
53
  #
53
54
  # @api private
54
55
  def render_question
55
- header = "#{@prefix}#{message} "
56
+ header = ["#{@prefix}#{message} "]
56
57
  if echo?
57
- masked = "#{@mask * "#{@input}".length}"
58
+ masked = @mask.to_s * @input.to_s.length
58
59
  if @done_masked && !@failure
59
60
  masked = @prompt.decorate(masked, @active_color)
60
61
  elsif @done_masked && @failure
61
62
  masked = @prompt.decorate(masked, @error_color)
62
63
  end
63
- header += masked
64
+ header << masked
64
65
  end
65
66
  header << "\n" if @done
66
- header
67
+ header.join
67
68
  end
68
69
 
69
70
  def render_error(errors)
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'list'
4
5
 
@@ -47,6 +48,10 @@ module TTY
47
48
  # At this stage, @choices matches all the visible choices.
48
49
  @selected = @choices.values_at(*@default.map { |d| d - 1 })
49
50
  @active = @default.last unless @selected.empty?
51
+ if choices[@active - 1] && choices[@active - 1].disabled?
52
+ raise ConfigurationError,
53
+ "active choice '#{choices[@active - 1]}' matches disabled item"
54
+ end
50
55
  end
51
56
 
52
57
  # Generate selected items names
@@ -90,14 +95,18 @@ module TTY
90
95
  #
91
96
  # @api private
92
97
  def render_menu
93
- output = ''
98
+ output = []
99
+
94
100
  @paginator.paginate(choices, @active, @per_page) do |choice, index|
95
101
  num = enumerate? ? (index + 1).to_s + @enum + ' ' : ''
96
102
  indicator = (index + 1 == @active) ? @marker : ' '
97
103
  indicator += ' '
98
- message = if @selected.include?(choice)
104
+ message = if @selected.include?(choice) && !choice.disabled?
99
105
  selected = @prompt.decorate(symbols[:radio_on], @active_color)
100
106
  selected + ' ' + num + choice.name
107
+ elsif choice.disabled?
108
+ @prompt.decorate(symbols[:cross], :red) +
109
+ ' ' + num + choice.name + ' ' + choice.disabled.to_s
101
110
  else
102
111
  symbols[:radio_off] + ' ' + num + choice.name
103
112
  end
@@ -105,7 +114,8 @@ module TTY
105
114
  newline = (index == max_index) ? '' : "\n"
106
115
  output << indicator + message + newline
107
116
  end
108
- output
117
+
118
+ output.join
109
119
  end
110
120
  end # MultiList
111
121
  end # Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'question'
4
5
  require_relative 'symbols'
@@ -40,17 +41,17 @@ module TTY
40
41
  alias keyenter keyreturn
41
42
 
42
43
  def render_question
43
- header = "#{@prefix}#{message} "
44
+ header = ["#{@prefix}#{message} "]
44
45
  if !echo?
45
46
  header
46
47
  elsif @done
47
- header += @prompt.decorate("#{@input}", @active_color)
48
+ header << @prompt.decorate("#{@input}", @active_color)
48
49
  elsif @first_render
49
- header += @prompt.decorate(help, @help_color)
50
+ header << @prompt.decorate(help, @help_color)
50
51
  @first_render = false
51
52
  end
52
- header += "\n"
53
- header
53
+ header << "\n"
54
+ header.join
54
55
  end
55
56
 
56
57
  def process_input(question)
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'converters'
4
5
  require_relative 'evaluator'
@@ -20,7 +21,7 @@ module TTY
20
21
  def to_s
21
22
  "undefined"
22
23
  end
23
- alias inspect to_s
24
+ alias_method :inspect, :to_s
24
25
  end
25
26
 
26
27
  # Store question message
@@ -46,7 +47,7 @@ module TTY
46
47
  @convert = options.fetch(:convert) { UndefinedSetting }
47
48
  @active_color = options.fetch(:active_color) { @prompt.active_color }
48
49
  @help_color = options.fetch(:help_color) { @prompt.help_color }
49
- @error_color = options.fetch(:error_color) { :red }
50
+ @error_color = options.fetch(:error_color) { :red }
50
51
  @messages = Utils.deep_copy(options.fetch(:messages) { { } })
51
52
  @done = false
52
53
  @input = nil
@@ -129,16 +130,16 @@ module TTY
129
130
  #
130
131
  # @api private
131
132
  def render_question
132
- header = "#{@prefix}#{message} "
133
+ header = ["#{@prefix}#{message} "]
133
134
  if !echo?
134
135
  header
135
136
  elsif @done
136
- header += @prompt.decorate("#{@input}", @active_color)
137
+ header << @prompt.decorate(@input.to_s, @active_color)
137
138
  elsif default? && !Utils.blank?(@default)
138
- header += @prompt.decorate("(#{default})", @help_color) + ' '
139
+ header << @prompt.decorate("(#{default})", @help_color) + ' '
139
140
  end
140
141
  header << "\n" if @done
141
- header
142
+ header.join
142
143
  end
143
144
 
144
145
  # Decide how to handle input from user
@@ -180,7 +181,7 @@ module TTY
180
181
  #
181
182
  # @api private
182
183
  def refresh(lines, lines_to_clear)
183
- output = ''
184
+ output = []
184
185
  if @done
185
186
  if @errors.count.zero?
186
187
  output << @prompt.cursor.up(lines)
@@ -191,7 +192,7 @@ module TTY
191
192
  else
192
193
  output << @prompt.cursor.up(lines)
193
194
  end
194
- output + @prompt.clear_lines(lines_to_clear)
195
+ output.join + @prompt.clear_lines(lines_to_clear)
195
196
  end
196
197
 
197
198
  # Convert value to expected type
@@ -321,7 +322,7 @@ module TTY
321
322
 
322
323
  # @api public
323
324
  def to_s
324
- "#{message}"
325
+ message.to_s
325
326
  end
326
327
 
327
328
  # String representation of this question
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'symbols'
4
5
 
@@ -160,7 +161,7 @@ module TTY
160
161
  #
161
162
  # @api private
162
163
  def render_question
163
- header = "#{@prefix}#{@question} "
164
+ header = ["#{@prefix}#{@question} "]
164
165
  if @done
165
166
  header << @prompt.decorate(answer.to_s, @active_color)
166
167
  header << "\n"
@@ -171,7 +172,7 @@ module TTY
171
172
  header << "\n" + @prompt.decorate(HELP, @help_color)
172
173
  @first_render = false
173
174
  end
174
- header
175
+ header.join
175
176
  end
176
177
 
177
178
  # Render slider representation
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  # A class responsible for shell prompt interactions.
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'distance'
4
5
 
@@ -97,16 +98,13 @@ module TTY
97
98
 
98
99
  # @api private
99
100
  def build_single_suggestion
100
- suggestion = ''
101
- suggestion << single_text + "\n"
102
- suggestion << (' ' * indent + @suggestions.first)
101
+ single_text + "\n" + (' ' * indent) + @suggestions.first
103
102
  end
104
103
 
105
104
  # @api private
106
105
  def build_multiple_suggestions
107
- suggestion = ''
108
- suggestion << plural_text + "\n"
109
- suggestion << @suggestions.map do |sugest|
106
+ plural_text + "\n" +
107
+ @suggestions.map do |sugest|
110
108
  ' ' * indent + sugest
111
109
  end.join("\n")
112
110
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  class Prompt
@@ -28,7 +29,7 @@ module TTY
28
29
 
29
30
  WIN_KEYS = {
30
31
  tick: '√',
31
- cross: '×',
32
+ cross: 'x',
32
33
  star: '*',
33
34
  square: '[█]',
34
35
  square_empty: '[ ]',
@@ -1,16 +1,15 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require 'timers'
4
5
 
5
6
  module TTY
6
7
  class Prompt
7
8
  class Timeout
8
- Error = Class.new(RuntimeError)
9
-
10
- TIMEOUT_HANDLER = proc { |t| t.raise Error, 'timeout expired' }
11
-
9
+ # A class responsible for measuring interval
10
+ #
11
+ # @api private
12
12
  def initialize(options = {})
13
- @timeout_handler = options.fetch(:timeout_handler) { TIMEOUT_HANDLER }
14
13
  @interval_handler = options.fetch(:interval_handler) { proc {} }
15
14
  @lock = Mutex.new
16
15
  @running = true
@@ -30,17 +29,23 @@ module TTY
30
29
  #
31
30
  # @api public
32
31
  def timeout(time, interval, &job)
33
- @runner = async_run(time, interval)
34
- job.()
35
- @runner.join
32
+ input_thread = Thread.new { job.() }
33
+ timing_thread = measure_intervals(time, interval, input_thread)
34
+ [input_thread, timing_thread].each(&:join)
36
35
  end
37
36
 
37
+ # Cancel this timeout measurement
38
+ #
39
+ # @api public
38
40
  def cancel
39
41
  return unless @running
40
42
  @running = false
41
43
  end
42
44
 
43
- def async_run(time, interval)
45
+ # Measure intervals and terminate input
46
+ #
47
+ # @api private
48
+ def measure_intervals(time, interval, input_thread)
44
49
  Thread.new do
45
50
  Thread.current.abort_on_exception = true
46
51
  start = Time.now
@@ -60,12 +65,12 @@ module TTY
60
65
  delta = time - runtime
61
66
 
62
67
  if delta <= 0.0
63
- @timeout_handler.(Thread.current)
64
- break
68
+ @running = false
65
69
  end
66
70
  }
67
71
  end
68
72
 
73
+ input_thread.terminate
69
74
  interval_timer.cancel
70
75
  end
71
76
  end
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  module TTY
4
5
  module Utils
@@ -2,6 +2,6 @@
2
2
 
3
3
  module TTY
4
4
  class Prompt
5
- VERSION = '0.15.0'.freeze
5
+ VERSION = '0.16.0'.freeze
6
6
  end # Prompt
7
7
  end # TTY
@@ -1,4 +1,5 @@
1
1
  # encoding: utf-8
2
+ # frozen_string_literal: true
2
3
 
3
4
  require_relative 'prompt'
4
5
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tty-prompt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.0
4
+ version: 0.16.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Murach
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-02-08 00:00:00.000000000 Z
11
+ date: 2018-03-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: necromancer
@@ -151,9 +151,10 @@ files:
151
151
  - examples/ask_valid.rb
152
152
  - examples/collect.rb
153
153
  - examples/echo.rb
154
- - examples/enum.rb
155
- - examples/enum_paged.rb
156
154
  - examples/enum_select.rb
155
+ - examples/enum_select_disabled.rb
156
+ - examples/enum_select_paged.rb
157
+ - examples/enum_select_wrapped.rb
157
158
  - examples/expand.rb
158
159
  - examples/in.rb
159
160
  - examples/inputs.rb
@@ -161,12 +162,17 @@ files:
161
162
  - examples/keypress.rb
162
163
  - examples/mask.rb
163
164
  - examples/multi_select.rb
165
+ - examples/multi_select_disabled.rb
164
166
  - examples/multi_select_paged.rb
167
+ - examples/multi_select_wrapped.rb
165
168
  - examples/multiline.rb
166
169
  - examples/pause.rb
167
170
  - examples/select.rb
171
+ - examples/select_disabled.rb
172
+ - examples/select_enum.rb
168
173
  - examples/select_filtered.rb
169
174
  - examples/select_paginated.rb
175
+ - examples/select_wrapped.rb
170
176
  - examples/slider.rb
171
177
  - examples/validation.rb
172
178
  - examples/yes_no.rb
@@ -227,7 +233,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
227
233
  version: '0'
228
234
  requirements: []
229
235
  rubyforge_project:
230
- rubygems_version: 2.5.1
236
+ rubygems_version: 2.7.3
231
237
  signing_key:
232
238
  specification_version: 4
233
239
  summary: A beautiful and powerful interactive command line prompt.