cli-ui 2.3.1 → 2.6.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.
@@ -9,15 +9,13 @@ module CLI
9
9
  module UI
10
10
  module StdoutRouter
11
11
  class Writer
12
- extend T::Sig
13
-
14
- sig { params(stream: IOLike, name: Symbol).void }
12
+ #: (io_like stream, Symbol name) -> void
15
13
  def initialize(stream, name)
16
14
  @stream = stream
17
15
  @name = name
18
16
  end
19
17
 
20
- sig { params(args: Object).returns(Integer) }
18
+ #: (*Object args) -> Integer
21
19
  def write(*args)
22
20
  strs = args.map do |obj|
23
21
  str = obj.to_s
@@ -36,10 +34,12 @@ module CLI
36
34
  return 0 if hook.call(strs.join, @name) == false
37
35
  end
38
36
 
39
- ret = T.unsafe(@stream).write_without_cli_ui(*prepend_id(@stream, strs))
37
+ stream_args = prepend_id(@stream, strs) #: as untyped
38
+ ret = @stream.write_without_cli_ui(*stream_args) #: as Integer
40
39
  if (dup = StdoutRouter.duplicate_output_to)
41
40
  begin
42
- T.unsafe(dup).write(*prepend_id(dup, strs))
41
+ dup_args = prepend_id(dup, strs) #: as untyped
42
+ dup.write(*dup_args) #: as Integer
43
43
  rescue IOError
44
44
  # Ignore
45
45
  end
@@ -49,7 +49,7 @@ module CLI
49
49
 
50
50
  private
51
51
 
52
- sig { params(stream: IOLike, args: T::Array[String]).returns(T::Array[String]) }
52
+ #: (io_like stream, Array[String] args) -> Array[String]
53
53
  def prepend_id(stream, args)
54
54
  return args unless prepend_id_for_stream(stream)
55
55
 
@@ -60,7 +60,7 @@ module CLI
60
60
  end
61
61
  end
62
62
 
63
- sig { params(stream: IOLike).returns(T::Boolean) }
63
+ #: (io_like stream) -> bool
64
64
  def prepend_id_for_stream(stream)
65
65
  return false unless Thread.current[:cliui_output_id]
66
66
  return true if Thread.current[:cliui_output_id][:streams].include?(stream)
@@ -68,12 +68,12 @@ module CLI
68
68
  false
69
69
  end
70
70
 
71
- sig { returns(T::Boolean) }
71
+ #: -> bool
72
72
  def auto_frame_inset?
73
73
  !Thread.current[:no_cliui_frame_inset]
74
74
  end
75
75
 
76
- sig { params(str: String, prefix: String).returns(String) }
76
+ #: (String str, String prefix) -> String
77
77
  def apply_line_prefix(str, prefix)
78
78
  return '' if str.empty?
79
79
 
@@ -92,27 +92,23 @@ module CLI
92
92
  end
93
93
 
94
94
  class Capture
95
- extend T::Sig
96
-
97
95
  @capture_mutex = Mutex.new
98
96
  @stdin_mutex = CLI::UI::ReentrantMutex.new
99
97
  @active_captures = 0
100
98
  @saved_stdin = nil
101
99
 
102
100
  class << self
103
- extend T::Sig
104
-
105
- sig { returns(T.nilable(Capture)) }
101
+ #: -> Capture?
106
102
  def current_capture
107
103
  Thread.current[:cliui_current_capture]
108
104
  end
109
105
 
110
- sig { returns(Capture) }
106
+ #: -> Capture
111
107
  def current_capture!
112
- T.must(current_capture)
108
+ current_capture #: as !nil
113
109
  end
114
110
 
115
- sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
111
+ #: [T] { -> T } -> T
116
112
  def in_alternate_screen(&block)
117
113
  stdin_synchronize do
118
114
  previous_print_captured_output = current_capture&.print_captured_output
@@ -139,7 +135,7 @@ module CLI
139
135
  end
140
136
  end
141
137
 
142
- sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
138
+ #: [T] { -> T } -> T
143
139
  def stdin_synchronize(&block)
144
140
  @stdin_mutex.synchronize do
145
141
  case $stdin
@@ -153,7 +149,7 @@ module CLI
153
149
  end
154
150
  end
155
151
 
156
- sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
152
+ #: [T] { -> T } -> T
157
153
  def with_stdin_masked(&block)
158
154
  @capture_mutex.synchronize do
159
155
  if @active_captures.zero?
@@ -179,20 +175,13 @@ module CLI
179
175
 
180
176
  private
181
177
 
182
- sig { returns(T::Boolean) }
178
+ #: -> bool
183
179
  def outermost_uncaptured?
184
180
  @stdin_mutex.count == 1 && $stdin.is_a?(BlockingInput)
185
181
  end
186
182
  end
187
183
 
188
- sig do
189
- params(
190
- with_frame_inset: T::Boolean,
191
- merged_output: T::Boolean,
192
- duplicate_output_to: IO,
193
- block: T.proc.void,
194
- ).void
195
- end
184
+ #: (?with_frame_inset: bool, ?merged_output: bool, ?duplicate_output_to: IO) { -> void } -> void
196
185
  def initialize(
197
186
  with_frame_inset: true,
198
187
  merged_output: false,
@@ -208,10 +197,10 @@ module CLI
208
197
  @err = StringIO.new
209
198
  end
210
199
 
211
- sig { returns(T::Boolean) }
200
+ #: bool
212
201
  attr_accessor :print_captured_output
213
202
 
214
- sig { returns(T.untyped) }
203
+ #: -> untyped
215
204
  def run
216
205
  require 'stringio'
217
206
 
@@ -249,26 +238,24 @@ module CLI
249
238
  Thread.current[:cliui_current_capture] = nil
250
239
  end
251
240
 
252
- sig { returns(String) }
241
+ #: -> String
253
242
  def stdout
254
243
  @out.string
255
244
  end
256
245
 
257
- sig { returns(String) }
246
+ #: -> String
258
247
  def stderr
259
248
  @err.string
260
249
  end
261
250
 
262
251
  class BlockingInput
263
- extend T::Sig
264
-
265
- sig { params(stream: IO).void }
252
+ #: (IO stream) -> void
266
253
  def initialize(stream)
267
254
  @stream = stream
268
255
  @m = CLI::UI::ReentrantMutex.new
269
256
  end
270
257
 
271
- sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
258
+ #: [T] { -> T } -> T
272
259
  def synchronize(&block)
273
260
  @m.synchronize do
274
261
  previous_allowed_to_read = Thread.current[:cliui_allowed_to_read]
@@ -317,43 +304,39 @@ module CLI
317
304
  end
318
305
 
319
306
  class << self
320
- extend T::Sig
321
-
322
307
  WRITE_WITHOUT_CLI_UI = :write_without_cli_ui
323
308
 
324
309
  NotEnabled = Class.new(StandardError)
325
310
 
326
- sig { returns(T.nilable(IOLike)) }
311
+ #: io_like?
327
312
  attr_accessor :duplicate_output_to
328
313
 
329
- sig do
330
- type_parameters(:T)
331
- .params(on_streams: T::Array[IOLike], block: T.proc.params(id: String).returns(T.type_parameter(:T)))
332
- .returns(T.type_parameter(:T))
333
- end
314
+ #: [T] (on_streams: Array[io_like]) { (String id) -> T } -> T
334
315
  def with_id(on_streams:, &block)
335
316
  require 'securerandom'
336
317
  id = format('%05d', rand(10**5))
337
318
  Thread.current[:cliui_output_id] = {
338
319
  id: id,
339
- streams: on_streams.map { |stream| T.cast(stream, IOLike) },
320
+ streams: on_streams.map do |stream|
321
+ stream #: as io_like
322
+ end,
340
323
  }
341
324
  yield(id)
342
325
  ensure
343
326
  Thread.current[:cliui_output_id] = nil
344
327
  end
345
328
 
346
- sig { returns(T.nilable(T::Hash[Symbol, T.any(String, IOLike)])) }
329
+ #: -> Hash[Symbol, (String | io_like)]?
347
330
  def current_id
348
331
  Thread.current[:cliui_output_id]
349
332
  end
350
333
 
351
- sig { void }
334
+ #: -> void
352
335
  def assert_enabled!
353
336
  raise NotEnabled unless enabled?
354
337
  end
355
338
 
356
- sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
339
+ #: [T] { -> T } -> T
357
340
  def with_enabled(&block)
358
341
  enable
359
342
  yield
@@ -362,12 +345,12 @@ module CLI
362
345
  end
363
346
 
364
347
  # TODO: remove this
365
- sig { void }
348
+ #: -> void
366
349
  def ensure_activated
367
350
  enable unless enabled?
368
351
  end
369
352
 
370
- sig { returns(T::Boolean) }
353
+ #: -> bool
371
354
  def enable
372
355
  return false if enabled?($stdout) || enabled?($stderr)
373
356
 
@@ -376,12 +359,12 @@ module CLI
376
359
  true
377
360
  end
378
361
 
379
- sig { params(stream: IOLike).returns(T::Boolean) }
362
+ #: (?io_like stream) -> bool
380
363
  def enabled?(stream = $stdout)
381
364
  stream.respond_to?(WRITE_WITHOUT_CLI_UI)
382
365
  end
383
366
 
384
- sig { returns(T::Boolean) }
367
+ #: -> bool
385
368
  def disable
386
369
  return false unless enabled?($stdout) && enabled?($stderr)
387
370
 
@@ -392,14 +375,14 @@ module CLI
392
375
 
393
376
  private
394
377
 
395
- sig { params(stream: IOLike).void }
378
+ #: (io_like stream) -> void
396
379
  def deactivate(stream)
397
380
  sc = stream.singleton_class
398
381
  sc.send(:remove_method, :write)
399
382
  sc.send(:alias_method, :write, WRITE_WITHOUT_CLI_UI)
400
383
  end
401
384
 
402
- sig { params(stream: IOLike, streamname: Symbol).void }
385
+ #: (io_like stream, Symbol streamname) -> void
403
386
  def activate(stream, streamname)
404
387
  writer = StdoutRouter::Writer.new(stream, streamname)
405
388
 
data/lib/cli/ui/table.rb CHANGED
@@ -3,11 +3,7 @@
3
3
  module CLI
4
4
  module UI
5
5
  module Table
6
- extend T::Sig
7
-
8
6
  class << self
9
- extend T::Sig
10
-
11
7
  # Prints a formatted table to the specified output
12
8
  # Automatically pads columns to align based on the longest cell in each column,
13
9
  # ignoring the width of ANSI color codes.
@@ -35,7 +31,7 @@ module CLI
35
31
  # really_long_cell short
36
32
  # row2 row2
37
33
  #
38
- sig { params(table: T::Array[T::Array[String]], col_spacing: Integer, to: IOLike).void }
34
+ #: (Array[Array[String]] table, ?col_spacing: Integer, ?to: io_like) -> void
39
35
  def puts_table(table, col_spacing: 1, to: $stdout)
40
36
  col_sizes = table.transpose.map do |col|
41
37
  col.map { |cell| CLI::UI::ANSI.printing_width(CLI::UI.resolve_text(cell)) }.max
@@ -43,7 +39,7 @@ module CLI
43
39
 
44
40
  table.each do |row|
45
41
  padded_row = row.each_with_index.map do |cell, i|
46
- col_size = T.must(col_sizes[i]) # guaranteed to be non-nil
42
+ col_size = col_sizes[i] #: as !nil # guaranteed to be non-nil
47
43
  cell_size = CLI::UI::ANSI.printing_width(CLI::UI.resolve_text(cell))
48
44
  padded_cell = cell + ' ' * (col_size - cell_size)
49
45
  padded_cell
@@ -75,7 +71,7 @@ module CLI
75
71
  # ["row2", "row2"]
76
72
  # ])
77
73
  #
78
- sig { params(table: T::Array[T::Array[String]], col_spacing: Integer).returns(T::Array[String]) }
74
+ #: (Array[Array[String]] table, ?col_spacing: Integer) -> Array[String]
79
75
  def capture_table(table, col_spacing: 1)
80
76
  strio = StringIO.new
81
77
  puts_table(table, col_spacing: col_spacing, to: strio)
@@ -7,18 +7,14 @@ require 'io/console'
7
7
  module CLI
8
8
  module UI
9
9
  module Terminal
10
- extend T::Sig
11
-
12
10
  DEFAULT_WIDTH = 80
13
11
  DEFAULT_HEIGHT = 24
14
12
 
15
13
  class << self
16
- extend T::Sig
17
-
18
14
  # Returns the width of the terminal, if possible
19
15
  # Otherwise will return DEFAULT_WIDTH
20
16
  #
21
- sig { returns(Integer) }
17
+ #: -> Integer
22
18
  def width
23
19
  winsize[1]
24
20
  end
@@ -26,12 +22,12 @@ module CLI
26
22
  # Returns the width of the terminal, if possible
27
23
  # Otherwise, will return DEFAULT_HEIGHT
28
24
  #
29
- sig { returns(Integer) }
25
+ #: -> Integer
30
26
  def height
31
27
  winsize[0]
32
28
  end
33
29
 
34
- sig { returns([Integer, Integer]) }
30
+ #: -> [Integer, Integer]
35
31
  def winsize
36
32
  @winsize ||= begin
37
33
  winsize = IO.console.winsize
@@ -47,7 +43,7 @@ module CLI
47
43
  end
48
44
  end
49
45
 
50
- sig { void }
46
+ #: -> void
51
47
  def setup_winsize_trap
52
48
  @winsize_trap ||= Signal.trap('WINCH') do
53
49
  @winsize = nil
@@ -28,15 +28,13 @@ module CLI
28
28
  TRUNCATED = "\x1b[0m…"
29
29
 
30
30
  class << self
31
- extend T::Sig
32
-
33
- sig { params(text: String, printing_width: Integer).returns(String) }
31
+ #: (String text, Integer printing_width) -> String
34
32
  def call(text, printing_width)
35
33
  return text if text.size <= printing_width
36
34
 
37
35
  width = 0
38
36
  mode = PARSE_ROOT
39
- truncation_index = T.let(nil, T.nilable(Integer))
37
+ truncation_index = nil #: Integer?
40
38
 
41
39
  codepoints = text.codepoints
42
40
  codepoints.each.with_index do |cp, index|
@@ -87,12 +85,13 @@ module CLI
87
85
  # the end of the string.
88
86
  return text if !truncation_index || width <= printing_width
89
87
 
90
- T.must(codepoints[0...truncation_index]).pack('U*') + TRUNCATED
88
+ slice = codepoints[0...truncation_index] #: as !nil
89
+ slice.pack('U*') + TRUNCATED
91
90
  end
92
91
 
93
92
  private
94
93
 
95
- sig { params(printable_codepoint: Integer).returns(Integer) }
94
+ #: (Integer printable_codepoint) -> Integer
96
95
  def width(printable_codepoint)
97
96
  case printable_codepoint
98
97
  when EMOJI_RANGE
@@ -3,6 +3,6 @@
3
3
 
4
4
  module CLI
5
5
  module UI
6
- VERSION = '2.3.1'
6
+ VERSION = '2.6.0'
7
7
  end
8
8
  end
@@ -6,21 +6,16 @@ require('cli/ui')
6
6
  module CLI
7
7
  module UI
8
8
  module Widgets
9
+ # @abstract
9
10
  class Base
10
- extend T::Sig
11
- extend T::Helpers
12
- abstract!
13
-
14
11
  class << self
15
- extend T::Sig
16
-
17
- sig { params(argstring: String).returns(String) }
12
+ #: (String argstring) -> String
18
13
  def call(argstring)
19
14
  new(argstring).render
20
15
  end
21
16
  end
22
17
 
23
- sig { params(argstring: String).void }
18
+ #: (String argstring) -> void
24
19
  def initialize(argstring)
25
20
  pat = self.class.argparse_pattern
26
21
  unless (@match_data = pat.match(argstring))
@@ -33,14 +28,18 @@ module CLI
33
28
  end
34
29
 
35
30
  class << self
36
- extend T::Sig
37
-
38
- sig { abstract.returns(Regexp) }
39
- def argparse_pattern; end
31
+ # @abstract
32
+ #: -> Regexp
33
+ def argparse_pattern
34
+ raise(NotImplementedError)
35
+ end
40
36
  end
41
37
 
42
- sig { abstract.returns(String) }
43
- def render; end
38
+ # @abstract
39
+ #: -> String
40
+ def render
41
+ raise(NotImplementedError)
42
+ end
44
43
  end
45
44
  end
46
45
  end
@@ -22,15 +22,15 @@ module CLI
22
22
  EMPTY_SET = '∅'
23
23
 
24
24
  class << self
25
- extend T::Sig
26
-
27
- sig { override.returns(Regexp) }
25
+ # @override
26
+ #: -> Regexp
28
27
  def argparse_pattern
29
28
  ARGPARSE_PATTERN
30
29
  end
31
30
  end
32
31
 
33
- sig { override.returns(String) }
32
+ # @override
33
+ #: -> String
34
34
  def render
35
35
  if zero?(@succeeded) && zero?(@failed) && zero?(@working) && zero?(@pending)
36
36
  Color::RESET.code + Color::BOLD.code + EMPTY_SET + Color::RESET.code
@@ -42,34 +42,34 @@ module CLI
42
42
 
43
43
  private
44
44
 
45
- sig { params(num_str: String).returns(T::Boolean) }
45
+ #: (String num_str) -> bool
46
46
  def zero?(num_str)
47
47
  num_str == '0'
48
48
  end
49
49
 
50
- sig { params(num_str: String, rune: String, color: Color).returns(String) }
50
+ #: (String num_str, String rune, Color color) -> String
51
51
  def colorize_if_nonzero(num_str, rune, color)
52
52
  color = Color::GRAY if zero?(num_str)
53
53
  color.code + num_str + rune
54
54
  end
55
55
 
56
- sig { returns(String) }
56
+ #: -> String
57
57
  def succeeded_part
58
58
  colorize_if_nonzero(@succeeded, Glyph::CHECK.char, Color::GREEN)
59
59
  end
60
60
 
61
- sig { returns(String) }
61
+ #: -> String
62
62
  def failed_part
63
63
  colorize_if_nonzero(@failed, Glyph::X.char, Color::RED)
64
64
  end
65
65
 
66
- sig { returns(String) }
66
+ #: -> String
67
67
  def working_part
68
68
  rune = zero?(@working) ? SPINNER_STOPPED : Spinner.current_rune
69
69
  colorize_if_nonzero(@working, rune, Color::BLUE)
70
70
  end
71
71
 
72
- sig { returns(String) }
72
+ #: -> String
73
73
  def pending_part
74
74
  colorize_if_nonzero(@pending, Glyph::HOURGLASS.char, Color::WHITE)
75
75
  end
@@ -19,17 +19,13 @@ module CLI
19
19
  # CLI::UI::Widgets.register('my-widget') { MyWidget }
20
20
  # puts(CLI::UI.fmt("{{@widget/my-widget:args}}"))
21
21
  module Widgets
22
- extend T::Sig
23
-
24
22
  MAP = {}
25
23
 
26
24
  autoload(:Base, 'cli/ui/widgets/base')
27
25
  autoload(:Status, 'cli/ui/widgets/status')
28
26
 
29
27
  class << self
30
- extend T::Sig
31
-
32
- sig { params(name: String, cb: T.proc.returns(T.class_of(Widgets::Base))).void }
28
+ #: (String name) { -> singleton(Widgets::Base) } -> void
33
29
  def register(name, &cb)
34
30
  MAP[name] = cb
35
31
  end
@@ -42,7 +38,7 @@ module CLI
42
38
  # ==== Returns
43
39
  # A callable widget, to be invoked like `.call(argstring)`
44
40
  #
45
- sig { params(handle: String).returns(T.class_of(Widgets::Base)) }
41
+ #: (String handle) -> singleton(Widgets::Base)
46
42
  def lookup(handle)
47
43
  MAP.fetch(handle).call
48
44
  rescue KeyError, NameError
@@ -51,7 +47,7 @@ module CLI
51
47
 
52
48
  # All available widgets by name
53
49
  #
54
- sig { returns(T::Array[String]) }
50
+ #: -> Array[String]
55
51
  def available
56
52
  MAP.keys
57
53
  end
@@ -60,15 +56,13 @@ module CLI
60
56
  register('status') { Widgets::Status }
61
57
 
62
58
  class InvalidWidgetHandle < ArgumentError
63
- extend T::Sig
64
-
65
- sig { params(handle: String).void }
59
+ #: (String handle) -> void
66
60
  def initialize(handle)
67
61
  super
68
62
  @handle = handle
69
63
  end
70
64
 
71
- sig { returns(String) }
65
+ #: -> String
72
66
  def message
73
67
  keys = Widgets.available.join(',')
74
68
  "invalid widget handle: #{@handle} " \
@@ -77,16 +71,14 @@ module CLI
77
71
  end
78
72
 
79
73
  class InvalidWidgetArguments < ArgumentError
80
- extend T::Sig
81
-
82
- sig { params(argstring: String, pattern: Regexp).void }
74
+ #: (String argstring, Regexp pattern) -> void
83
75
  def initialize(argstring, pattern)
84
76
  super(nil)
85
77
  @argstring = argstring
86
78
  @pattern = pattern
87
79
  end
88
80
 
89
- sig { returns(String) }
81
+ #: -> String
90
82
  def message
91
83
  "invalid widget arguments: #{@argstring} " \
92
84
  "-- must match pattern: #{@pattern.inspect}"