cli-ui 1.5.1 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/lib/cli/ui.rb CHANGED
@@ -1,5 +1,13 @@
1
+ # typed: true
2
+
3
+ unless defined?(T)
4
+ require('cli/ui/sorbet_runtime_stub')
5
+ end
6
+
1
7
  module CLI
2
8
  module UI
9
+ extend T::Sig
10
+
3
11
  autoload :ANSI, 'cli/ui/ansi'
4
12
  autoload :Glyph, 'cli/ui/glyph'
5
13
  autoload :Color, 'cli/ui/color'
@@ -18,215 +26,344 @@ module CLI
18
26
  # Convenience accessor to +CLI::UI::Spinner::SpinGroup+
19
27
  SpinGroup = Spinner::SpinGroup
20
28
 
21
- # Glyph resolution using +CLI::UI::Glyph.lookup+
22
- # Look at the method signature for +Glyph.lookup+ for more details
23
- #
24
- # ==== Attributes
25
- #
26
- # * +handle+ - handle of the glyph to resolve
27
- #
28
- def self.glyph(handle)
29
- CLI::UI::Glyph.lookup(handle)
30
- end
29
+ Colorable = T.type_alias { T.any(Symbol, String, CLI::UI::Color) }
30
+ FrameStylable = T.type_alias { T.any(Symbol, String, CLI::UI::Frame::FrameStyle) }
31
+ IOLike = T.type_alias { T.any(IO, StringIO) }
32
+
33
+ class << self
34
+ extend T::Sig
31
35
 
32
- # Color resolution using +CLI::UI::Color.lookup+
33
- # Will lookup using +Color.lookup+ unless it's already a CLI::UI::Color (or nil)
34
- #
35
- # ==== Attributes
36
- #
37
- # * +input+ - color to resolve
38
- #
39
- def self.resolve_color(input)
40
- case input
41
- when CLI::UI::Color, nil
42
- input
43
- else
44
- CLI::UI::Color.lookup(input)
36
+ # Glyph resolution using +CLI::UI::Glyph.lookup+
37
+ # Look at the method signature for +Glyph.lookup+ for more details
38
+ #
39
+ # ==== Attributes
40
+ #
41
+ # * +handle+ - handle of the glyph to resolve
42
+ #
43
+ sig { params(handle: String).returns(Glyph) }
44
+ def glyph(handle)
45
+ CLI::UI::Glyph.lookup(handle)
45
46
  end
46
- end
47
47
 
48
- # Frame style resolution using +CLI::UI::Frame::FrameStyle.lookup+.
49
- # Will lookup using +FrameStyle.lookup+ unless it's already a CLI::UI::Frame::FrameStyle(or nil)
50
- #
51
- # ==== Attributes
52
- #
53
- # * +input+ - frame style to resolve
54
- def self.resolve_style(input)
55
- case input
56
- when CLI::UI::Frame::FrameStyle, nil
57
- input
58
- else
59
- CLI::UI::Frame::FrameStyle.lookup(input)
48
+ # Color resolution using +CLI::UI::Color.lookup+
49
+ # Will lookup using +Color.lookup+ unless it's already a CLI::UI::Color (or nil)
50
+ #
51
+ # ==== Attributes
52
+ #
53
+ # * +input+ - color to resolve
54
+ #
55
+ sig { params(input: Colorable).returns(CLI::UI::Color) }
56
+ def resolve_color(input)
57
+ case input
58
+ when CLI::UI::Color
59
+ input
60
+ else
61
+ CLI::UI::Color.lookup(input)
62
+ end
60
63
  end
61
- end
62
64
 
63
- # Convenience Method for +CLI::UI::Prompt.confirm+
64
- #
65
- # ==== Attributes
66
- #
67
- # * +question+ - question to confirm
68
- #
69
- def self.confirm(question, **kwargs)
70
- CLI::UI::Prompt.confirm(question, **kwargs)
71
- end
65
+ # Frame style resolution using +CLI::UI::Frame::FrameStyle.lookup+.
66
+ # Will lookup using +FrameStyle.lookup+ unless it's already a CLI::UI::Frame::FrameStyle(or nil)
67
+ #
68
+ # ==== Attributes
69
+ #
70
+ # * +input+ - frame style to resolve
71
+ sig { params(input: FrameStylable).returns(CLI::UI::Frame::FrameStyle) }
72
+ def resolve_style(input)
73
+ case input
74
+ when CLI::UI::Frame::FrameStyle
75
+ input
76
+ else
77
+ CLI::UI::Frame::FrameStyle.lookup(input.to_s)
78
+ end
79
+ end
72
80
 
73
- # Convenience Method for +CLI::UI::Prompt.ask+
74
- #
75
- # ==== Attributes
76
- #
77
- # * +question+ - question to ask
78
- # * +kwargs+ - arguments for +Prompt.ask+
79
- #
80
- def self.ask(question, **kwargs)
81
- CLI::UI::Prompt.ask(question, **kwargs)
82
- end
81
+ # Convenience Method for +CLI::UI::Prompt.confirm+
82
+ #
83
+ # ==== Attributes
84
+ #
85
+ # * +question+ - question to confirm
86
+ #
87
+ sig { params(question: String, default: T::Boolean).returns(T::Boolean) }
88
+ def confirm(question, default: true)
89
+ CLI::UI::Prompt.confirm(question, default: default)
90
+ end
83
91
 
84
- # Convenience Method to resolve text using +CLI::UI::Formatter.format+
85
- # Check +CLI::UI::Formatter::SGR_MAP+ for available formatting options
86
- #
87
- # ==== Attributes
88
- #
89
- # * +input+ - input to format
90
- # * +truncate_to+ - number of characters to truncate the string to (or nil)
91
- #
92
- def self.resolve_text(input, truncate_to: nil)
93
- return input if input.nil?
94
- formatted = CLI::UI::Formatter.new(input).format
95
- return formatted unless truncate_to
96
- CLI::UI::Truncater.call(formatted, truncate_to)
97
- end
92
+ # Convenience Method for +CLI::UI::Prompt.ask+
93
+ sig do
94
+ params(
95
+ question: String,
96
+ options: T.nilable(T::Array[String]),
97
+ default: T.nilable(T.any(String, T::Array[String])),
98
+ is_file: T::Boolean,
99
+ allow_empty: T::Boolean,
100
+ multiple: T::Boolean,
101
+ filter_ui: T::Boolean,
102
+ select_ui: T::Boolean,
103
+ options_proc: T.nilable(T.proc.params(handler: Prompt::OptionsHandler).void),
104
+ ).returns(T.any(String, T::Array[String]))
105
+ end
106
+ def ask(
107
+ question,
108
+ options: nil,
109
+ default: nil,
110
+ is_file: false,
111
+ allow_empty: true,
112
+ multiple: false,
113
+ filter_ui: true,
114
+ select_ui: true,
115
+ &options_proc
116
+ )
117
+ CLI::UI::Prompt.ask(
118
+ question,
119
+ options: options,
120
+ default: default,
121
+ is_file: is_file,
122
+ allow_empty: allow_empty,
123
+ multiple: multiple,
124
+ filter_ui: filter_ui,
125
+ select_ui: select_ui,
126
+ &options_proc
127
+ )
128
+ end
98
129
 
99
- # Convenience Method to format text using +CLI::UI::Formatter.format+
100
- # Check +CLI::UI::Formatter::SGR_MAP+ for available formatting options
101
- #
102
- # https://user-images.githubusercontent.com/3074765/33799827-6d0721a2-dd01-11e7-9ab5-c3d455264afe.png
103
- # https://user-images.githubusercontent.com/3074765/33799847-9ec03fd0-dd01-11e7-93f7-5f5cc540e61e.png
104
- #
105
- # ==== Attributes
106
- #
107
- # * +input+ - input to format
108
- #
109
- # ==== Options
110
- #
111
- # * +enable_color+ - should color be used? default to true unless output is redirected.
112
- #
113
- def self.fmt(input, enable_color: enable_color?)
114
- CLI::UI::Formatter.new(input).format(enable_color: enable_color)
115
- end
130
+ # Convenience Method to resolve text using +CLI::UI::Formatter.format+
131
+ # Check +CLI::UI::Formatter::SGR_MAP+ for available formatting options
132
+ #
133
+ # ==== Attributes
134
+ #
135
+ # * +input+ - input to format
136
+ # * +truncate_to+ - number of characters to truncate the string to (or nil)
137
+ #
138
+ sig { params(input: String, truncate_to: T.nilable(Integer)).returns(String) }
139
+ def resolve_text(input, truncate_to: nil)
140
+ formatted = CLI::UI::Formatter.new(input).format
141
+ return formatted unless truncate_to
116
142
 
117
- def self.wrap(input)
118
- CLI::UI::Wrap.new(input).wrap
119
- end
143
+ CLI::UI::Truncater.call(formatted, truncate_to)
144
+ end
120
145
 
121
- # Convenience Method for +CLI::UI::Printer.puts+
122
- #
123
- # ==== Attributes
124
- #
125
- # * +msg+ - Message to print
126
- # * +kwargs+ - keyword arguments for +Printer.puts+
127
- #
128
- def self.puts(msg, **kwargs)
129
- CLI::UI::Printer.puts(msg, **kwargs)
130
- end
146
+ # Convenience Method to format text using +CLI::UI::Formatter.format+
147
+ # Check +CLI::UI::Formatter::SGR_MAP+ for available formatting options
148
+ #
149
+ # https://user-images.githubusercontent.com/3074765/33799827-6d0721a2-dd01-11e7-9ab5-c3d455264afe.png
150
+ # https://user-images.githubusercontent.com/3074765/33799847-9ec03fd0-dd01-11e7-93f7-5f5cc540e61e.png
151
+ #
152
+ # ==== Attributes
153
+ #
154
+ # * +input+ - input to format
155
+ #
156
+ # ==== Options
157
+ #
158
+ # * +enable_color+ - should color be used? default to true unless output is redirected.
159
+ #
160
+ sig { params(input: String, enable_color: T::Boolean).returns(String) }
161
+ def fmt(input, enable_color: enable_color?)
162
+ CLI::UI::Formatter.new(input).format(enable_color: enable_color)
163
+ end
131
164
 
132
- # Convenience Method for +CLI::UI::Frame.open+
133
- #
134
- # ==== Attributes
135
- #
136
- # * +args+ - arguments for +Frame.open+
137
- # * +block+ - block for +Frame.open+
138
- #
139
- def self.frame(*args, **kwargs, &block)
140
- CLI::UI::Frame.open(*args, **kwargs, &block)
141
- end
165
+ sig { params(input: String).returns(String) }
166
+ def wrap(input)
167
+ CLI::UI::Wrap.new(input).wrap
168
+ end
142
169
 
143
- # Convenience Method for +CLI::UI::Spinner.spin+
144
- #
145
- # ==== Attributes
146
- #
147
- # * +args+ - arguments for +Spinner.open+
148
- # * +block+ - block for +Spinner.open+
149
- #
150
- def self.spinner(*args, **kwargs, &block)
151
- CLI::UI::Spinner.spin(*args, **kwargs, &block)
152
- end
170
+ # Convenience Method for +CLI::UI::Printer.puts+
171
+ #
172
+ # ==== Attributes
173
+ #
174
+ # * +msg+ - Message to print
175
+ # * +kwargs+ - keyword arguments for +Printer.puts+
176
+ #
177
+ sig do
178
+ params(
179
+ msg: String,
180
+ frame_color: T.nilable(Colorable),
181
+ to: IOLike,
182
+ encoding: Encoding,
183
+ format: T::Boolean,
184
+ graceful: T::Boolean,
185
+ wrap: T::Boolean,
186
+ ).void
187
+ end
188
+ def puts(
189
+ msg,
190
+ frame_color: nil,
191
+ to: $stdout,
192
+ encoding: Encoding::UTF_8,
193
+ format: true,
194
+ graceful: true,
195
+ wrap: true
196
+ )
197
+ CLI::UI::Printer.puts(
198
+ msg,
199
+ frame_color: frame_color,
200
+ to: to,
201
+ encoding: encoding,
202
+ format: format,
203
+ graceful: graceful,
204
+ wrap: wrap,
205
+ )
206
+ end
153
207
 
154
- # Convenience Method to override frame color using +CLI::UI::Frame.with_frame_color+
155
- #
156
- # ==== Attributes
157
- #
158
- # * +color+ - color to override to
159
- # * +block+ - block for +Frame.with_frame_color_override+
160
- #
161
- def self.with_frame_color(color, &block)
162
- CLI::UI::Frame.with_frame_color_override(color, &block)
163
- end
208
+ # Convenience Method for +CLI::UI::Frame.open+
209
+ #
210
+ # ==== Attributes
211
+ #
212
+ # * +args+ - arguments for +Frame.open+
213
+ # * +block+ - block for +Frame.open+
214
+ #
215
+ sig do
216
+ type_parameters(:T).params(
217
+ text: String,
218
+ color: T.nilable(Colorable),
219
+ failure_text: T.nilable(String),
220
+ success_text: T.nilable(String),
221
+ timing: T.any(T::Boolean, Numeric),
222
+ frame_style: FrameStylable,
223
+ block: T.nilable(T.proc.returns(T.type_parameter(:T))),
224
+ ).returns(T.nilable(T.type_parameter(:T)))
225
+ end
226
+ def frame(
227
+ text,
228
+ color: Frame::DEFAULT_FRAME_COLOR,
229
+ failure_text: nil,
230
+ success_text: nil,
231
+ timing: block_given?,
232
+ frame_style: Frame.frame_style,
233
+ &block
234
+ )
235
+ CLI::UI::Frame.open(
236
+ text,
237
+ color: color,
238
+ failure_text: failure_text,
239
+ success_text: success_text,
240
+ timing: timing,
241
+ frame_style: frame_style,
242
+ &block
243
+ )
244
+ end
164
245
 
165
- # Duplicate output to a file path
166
- #
167
- # ==== Attributes
168
- #
169
- # * +path+ - path to duplicate output to
170
- #
171
- def self.log_output_to(path)
172
- if CLI::UI::StdoutRouter.duplicate_output_to
173
- raise 'multiple logs not allowed'
174
- end
175
- CLI::UI::StdoutRouter.duplicate_output_to = File.open(path, 'w')
176
- yield
177
- ensure
178
- if (file_descriptor = CLI::UI::StdoutRouter.duplicate_output_to)
179
- file_descriptor.close
180
- CLI::UI::StdoutRouter.duplicate_output_to = nil
246
+ # Convenience Method for +CLI::UI::Spinner.spin+
247
+ #
248
+ # ==== Attributes
249
+ #
250
+ # * +args+ - arguments for +Spinner.open+
251
+ # * +block+ - block for +Spinner.open+
252
+ #
253
+ sig do
254
+ params(title: String, auto_debrief: T::Boolean, block: T.proc.params(task: Spinner::SpinGroup::Task).void)
255
+ .returns(T::Boolean)
256
+ end
257
+ def spinner(title, auto_debrief: true, &block)
258
+ CLI::UI::Spinner.spin(title, auto_debrief: auto_debrief, &block)
181
259
  end
182
- end
183
260
 
184
- # Disable all framing within a block
185
- #
186
- # ==== Attributes
187
- #
188
- # * +block+ - block in which to disable frames
189
- #
190
- def self.raw
191
- prev = Thread.current[:no_cliui_frame_inset]
192
- Thread.current[:no_cliui_frame_inset] = true
193
- yield
194
- ensure
195
- Thread.current[:no_cliui_frame_inset] = prev
196
- end
261
+ # Convenience Method to override frame color using +CLI::UI::Frame.with_frame_color+
262
+ #
263
+ # ==== Attributes
264
+ #
265
+ # * +color+ - color to override to
266
+ # * +block+ - block for +Frame.with_frame_color_override+
267
+ #
268
+ sig do
269
+ type_parameters(:T)
270
+ .params(color: Colorable, block: T.proc.returns(T.type_parameter(:T)))
271
+ .returns(T.type_parameter(:T))
272
+ end
273
+ def with_frame_color(color, &block)
274
+ CLI::UI::Frame.with_frame_color_override(color, &block)
275
+ end
197
276
 
198
- # Check whether colour is enabled in Formatter output. By default, colour
199
- # is enabled when STDOUT is a TTY; that is, when output has not been
200
- # redirected to another program or to a file.
201
- #
202
- def self.enable_color?
203
- @enable_color
204
- end
277
+ # Duplicate output to a file path
278
+ #
279
+ # ==== Attributes
280
+ #
281
+ # * +path+ - path to duplicate output to
282
+ #
283
+ sig do
284
+ type_parameters(:T)
285
+ .params(path: String, block: T.proc.returns(T.type_parameter(:T)))
286
+ .returns(T.type_parameter(:T))
287
+ end
288
+ def log_output_to(path, &block)
289
+ if CLI::UI::StdoutRouter.duplicate_output_to
290
+ raise 'multiple logs not allowed'
291
+ end
205
292
 
206
- # Turn colour output in Formatter on or off.
207
- #
208
- # ==== Attributes
209
- #
210
- # * +bool+ - true or false; enable or disable colour.
211
- #
212
- def self.enable_color=(bool)
213
- @enable_color = !!bool
214
- end
293
+ CLI::UI::StdoutRouter.duplicate_output_to = File.open(path, 'w')
294
+ yield
295
+ ensure
296
+ if (file_descriptor = CLI::UI::StdoutRouter.duplicate_output_to)
297
+ begin
298
+ file_descriptor.close
299
+ rescue IOError
300
+ nil
301
+ end
302
+ CLI::UI::StdoutRouter.duplicate_output_to = nil
303
+ end
304
+ end
215
305
 
216
- self.enable_color = $stdout.tty?
306
+ # Disable all framing within a block
307
+ #
308
+ # ==== Attributes
309
+ #
310
+ # * +block+ - block in which to disable frames
311
+ #
312
+ sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
313
+ def raw(&block)
314
+ prev = Thread.current[:no_cliui_frame_inset]
315
+ Thread.current[:no_cliui_frame_inset] = true
316
+ yield
317
+ ensure
318
+ Thread.current[:no_cliui_frame_inset] = prev
319
+ end
320
+
321
+ # Check whether colour is enabled in Formatter output. By default, colour
322
+ # is enabled when STDOUT is a TTY; that is, when output has not been
323
+ # redirected to another program or to a file.
324
+ #
325
+ sig { returns(T::Boolean) }
326
+ def enable_color?
327
+ @enable_color
328
+ end
329
+
330
+ # Turn colour output in Formatter on or off.
331
+ #
332
+ # ==== Attributes
333
+ #
334
+ # * +bool+ - true or false; enable or disable colour.
335
+ #
336
+ sig { params(bool: T::Boolean).void }
337
+ def enable_color=(bool)
338
+ @enable_color = !!bool
339
+ end
217
340
 
218
- # Set the default frame style.
219
- # Convenience method for setting the default frame style with +CLI::UI::Frame.frame_style=+
220
- #
221
- # Raises ArgumentError if +frame_style+ is not valid
222
- #
223
- # ==== Attributes
224
- #
225
- # * +symbol+ - the default frame style to use for frames
226
- #
227
- def self.frame_style=(frame_style)
228
- Frame.frame_style = frame_style.to_sym
341
+ # Set the default frame style.
342
+ # Convenience method for setting the default frame style with +CLI::UI::Frame.frame_style=+
343
+ #
344
+ # Raises ArgumentError if +frame_style+ is not valid
345
+ #
346
+ # ==== Attributes
347
+ #
348
+ # * +symbol+ - the default frame style to use for frames
349
+ #
350
+ sig { params(frame_style: FrameStylable).void }
351
+ def frame_style=(frame_style)
352
+ Frame.frame_style = frame_style
353
+ end
354
+
355
+ # Create a terminal link
356
+ sig { params(url: String, text: String, format: T::Boolean, blue_underline: T::Boolean).returns(String) }
357
+ def link(url, text, format: true, blue_underline: format)
358
+ raise 'cannot use blue_underline without format' if blue_underline && !format
359
+
360
+ text = "{{blue:{{underline:#{text}}}}}" if blue_underline
361
+ text = CLI::UI.fmt(text) if format
362
+ "\x1b]8;;#{url}\x1b\\#{text}\x1b]8;;\x1b\\"
363
+ end
229
364
  end
365
+
366
+ self.enable_color = $stdout.tty?
230
367
  end
231
368
  end
232
369
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cli-ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.1
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burke Libbey
@@ -10,36 +10,36 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2021-04-15 00:00:00.000000000 Z
13
+ date: 2022-11-18 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- name: rake
16
+ name: minitest
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
19
  - - "~>"
20
20
  - !ruby/object:Gem::Version
21
- version: '13.0'
21
+ version: '5.0'
22
22
  type: :development
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
26
  - - "~>"
27
27
  - !ruby/object:Gem::Version
28
- version: '13.0'
28
+ version: '5.0'
29
29
  - !ruby/object:Gem::Dependency
30
- name: minitest
30
+ name: rake
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
33
  - - "~>"
34
34
  - !ruby/object:Gem::Version
35
- version: '5.0'
35
+ version: '13.0'
36
36
  type: :development
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
40
  - - "~>"
41
41
  - !ruby/object:Gem::Version
42
- version: '5.0'
42
+ version: '13.0'
43
43
  description: Terminal UI framework
44
44
  email:
45
45
  - burke.libbey@shopify.com
@@ -67,6 +67,7 @@ files:
67
67
  - lib/cli/ui/prompt.rb
68
68
  - lib/cli/ui/prompt/interactive_options.rb
69
69
  - lib/cli/ui/prompt/options_handler.rb
70
+ - lib/cli/ui/sorbet_runtime_stub.rb
70
71
  - lib/cli/ui/spinner.rb
71
72
  - lib/cli/ui/spinner/async.rb
72
73
  - lib/cli/ui/spinner/spin_group.rb
@@ -97,7 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
97
98
  - !ruby/object:Gem::Version
98
99
  version: '0'
99
100
  requirements: []
100
- rubygems_version: 3.0.2
101
+ rubygems_version: 3.3.7
101
102
  signing_key:
102
103
  specification_version: 4
103
104
  summary: Terminal UI framework