cli-ui 1.5.1 → 2.1.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.
data/lib/cli/ui/frame.rb CHANGED
@@ -1,4 +1,7 @@
1
1
  # coding: utf-8
2
+
3
+ # typed: true
4
+
2
5
  require 'cli/ui'
3
6
  require 'cli/ui/frame/frame_stack'
4
7
  require 'cli/ui/frame/frame_style'
@@ -7,9 +10,12 @@ module CLI
7
10
  module UI
8
11
  module Frame
9
12
  class UnnestedFrameException < StandardError; end
13
+ DEFAULT_FRAME_COLOR = CLI::UI.resolve_color(:cyan)
14
+
10
15
  class << self
11
- DEFAULT_FRAME_COLOR = CLI::UI.resolve_color(:cyan)
16
+ extend T::Sig
12
17
 
18
+ sig { returns(FrameStyle) }
13
19
  def frame_style
14
20
  @frame_style ||= FrameStyle::Box
15
21
  end
@@ -22,6 +28,7 @@ module CLI
22
28
  #
23
29
  # * +symbol+ or +FrameStyle+ - the default frame style to use for frames
24
30
  #
31
+ sig { params(frame_style: FrameStylable).void }
25
32
  def frame_style=(frame_style)
26
33
  @frame_style = CLI::UI.resolve_style(frame_style)
27
34
  end
@@ -67,13 +74,25 @@ module CLI
67
74
  # ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
68
75
  #
69
76
  #
77
+ sig do
78
+ type_parameters(:T).params(
79
+ text: String,
80
+ color: Colorable,
81
+ failure_text: T.nilable(String),
82
+ success_text: T.nilable(String),
83
+ timing: T.any(T::Boolean, Numeric),
84
+ frame_style: FrameStylable,
85
+ block: T.nilable(T.proc.returns(T.type_parameter(:T))),
86
+ ).returns(T.nilable(T.type_parameter(:T)))
87
+ end
70
88
  def open(
71
89
  text,
72
90
  color: DEFAULT_FRAME_COLOR,
73
91
  failure_text: nil,
74
92
  success_text: nil,
75
- timing: nil,
76
- frame_style: self.frame_style
93
+ timing: block_given?,
94
+ frame_style: self.frame_style,
95
+ &block
77
96
  )
78
97
  frame_style = CLI::UI.resolve_style(frame_style)
79
98
  color = CLI::UI.resolve_color(color)
@@ -91,7 +110,7 @@ module CLI
91
110
  t_start = Time.now
92
111
  CLI::UI.raw do
93
112
  print(prefix.chop)
94
- puts frame_style.open(text, color: color)
113
+ puts frame_style.start(text, color: color)
95
114
  end
96
115
  FrameStack.push(color: color, style: frame_style)
97
116
 
@@ -103,15 +122,15 @@ module CLI
103
122
  success = yield
104
123
  rescue
105
124
  closed = true
106
- t_diff = elasped(t_start, timing)
125
+ t_diff = elapsed(t_start, timing)
107
126
  close(failure_text, color: :red, elapsed: t_diff)
108
127
  raise
109
128
  else
110
129
  success
111
130
  ensure
112
131
  unless closed
113
- t_diff = elasped(t_start, timing)
114
- if success != false
132
+ t_diff = elapsed(t_start, timing)
133
+ if T.unsafe(success) != false
115
134
  close(success_text, color: color, elapsed: t_diff)
116
135
  else
117
136
  close(failure_text, color: :red, elapsed: t_diff)
@@ -145,16 +164,17 @@ module CLI
145
164
  #
146
165
  # MUST be inside an open frame or it raises a +UnnestedFrameException+
147
166
  #
167
+ sig { params(text: T.nilable(String), color: T.nilable(Colorable), frame_style: T.nilable(FrameStylable)).void }
148
168
  def divider(text, color: nil, frame_style: nil)
149
169
  fs_item = FrameStack.pop
150
170
  raise UnnestedFrameException, 'No frame nesting to unnest' unless fs_item
151
171
 
152
- color = CLI::UI.resolve_color(color) || fs_item.color
153
- frame_style = CLI::UI.resolve_style(frame_style) || fs_item.frame_style
172
+ divider_color = CLI::UI.resolve_color(color || fs_item.color)
173
+ frame_style = CLI::UI.resolve_style(frame_style || fs_item.frame_style)
154
174
 
155
175
  CLI::UI.raw do
156
176
  print(prefix.chop)
157
- puts frame_style.divider(text, color: color)
177
+ puts frame_style.divider(text.to_s, color: divider_color)
158
178
  end
159
179
 
160
180
  FrameStack.push(fs_item)
@@ -184,21 +204,25 @@ module CLI
184
204
  #
185
205
  # MUST be inside an open frame or it raises a +UnnestedFrameException+
186
206
  #
207
+ sig do
208
+ params(
209
+ text: T.nilable(String),
210
+ color: T.nilable(Colorable),
211
+ elapsed: T.nilable(Numeric),
212
+ frame_style: T.nilable(FrameStylable),
213
+ ).void
214
+ end
187
215
  def close(text, color: nil, elapsed: nil, frame_style: nil)
188
216
  fs_item = FrameStack.pop
189
217
  raise UnnestedFrameException, 'No frame nesting to unnest' unless fs_item
190
218
 
191
- color = CLI::UI.resolve_color(color) || fs_item.color
192
- frame_style = CLI::UI.resolve_style(frame_style) || fs_item.frame_style
193
-
194
- kwargs = {}
195
- if elapsed
196
- kwargs[:right_text] = "(#{elapsed.round(2)}s)"
197
- end
219
+ close_color = CLI::UI.resolve_color(color || fs_item.color)
220
+ frame_style = CLI::UI.resolve_style(frame_style || fs_item.frame_style)
221
+ elapsed_string = elapsed ? "(#{elapsed.round(2)}s)" : nil
198
222
 
199
223
  CLI::UI.raw do
200
224
  print(prefix.chop)
201
- puts frame_style.close(text, color: color, **kwargs)
225
+ puts frame_style.close(text.to_s, color: close_color, right_text: elapsed_string)
202
226
  end
203
227
  end
204
228
 
@@ -208,11 +232,12 @@ module CLI
208
232
  #
209
233
  # * +:color+ - The color of the prefix. Defaults to +Thread.current[:cliui_frame_color_override]+
210
234
  #
235
+ sig { params(color: T.nilable(Colorable)).returns(String) }
211
236
  def prefix(color: Thread.current[:cliui_frame_color_override])
212
237
  +''.tap do |output|
213
238
  items = FrameStack.items
214
239
 
215
- items[0..-2].each do |item|
240
+ items[0..-2].to_a.each do |item|
216
241
  output << item.color.code << item.frame_style.prefix
217
242
  end
218
243
 
@@ -227,6 +252,7 @@ module CLI
227
252
  end
228
253
 
229
254
  # The width of a prefix given the number of Frames in the stack
255
+ sig { returns(Integer) }
230
256
  def prefix_width
231
257
  w = FrameStack.items.reduce(0) do |width, item|
232
258
  width + item.frame_style.prefix_width
@@ -241,7 +267,12 @@ module CLI
241
267
  #
242
268
  # * +color+ - The color to override to
243
269
  #
244
- def with_frame_color_override(color)
270
+ sig do
271
+ type_parameters(:T)
272
+ .params(color: Colorable, block: T.proc.returns(T.type_parameter(:T)))
273
+ .returns(T.type_parameter(:T))
274
+ end
275
+ def with_frame_color_override(color, &block)
245
276
  prev = Thread.current[:cliui_frame_color_override]
246
277
  Thread.current[:cliui_frame_color_override] = color
247
278
  yield
@@ -254,13 +285,13 @@ module CLI
254
285
  # If timing is:
255
286
  # Numeric: return it
256
287
  # false: return nil
257
- # true or nil: defaults to Time.new
258
- # Time: return the difference with start
259
- def elasped(start, timing)
288
+ # true: defaults to Time.new
289
+ sig { params(start: Time, timing: T.any(Numeric, T::Boolean)).returns(T.nilable(Numeric)) }
290
+ def elapsed(start, timing)
260
291
  return timing if timing.is_a?(Numeric)
261
292
  return if timing.is_a?(FalseClass)
262
293
 
263
- timing = Time.new if timing.is_a?(TrueClass) || timing.nil?
294
+ timing = Time.new
264
295
  timing - start
265
296
  end
266
297
  end
data/lib/cli/ui/glyph.rb CHANGED
@@ -1,14 +1,22 @@
1
+ # typed: true
2
+
1
3
  require 'cli/ui'
2
4
 
3
5
  module CLI
4
6
  module UI
5
7
  class Glyph
8
+ extend T::Sig
9
+
6
10
  class InvalidGlyphHandle < ArgumentError
11
+ extend T::Sig
12
+
13
+ sig { params(handle: String).void }
7
14
  def initialize(handle)
8
15
  super
9
16
  @handle = handle
10
17
  end
11
18
 
19
+ sig { returns(String) }
12
20
  def message
13
21
  keys = Glyph.available.join(',')
14
22
  "invalid glyph handle: #{@handle} " \
@@ -16,7 +24,14 @@ module CLI
16
24
  end
17
25
  end
18
26
 
19
- attr_reader :handle, :codepoint, :color, :to_s, :fmt
27
+ sig { returns(String) }
28
+ attr_reader :handle, :to_s, :fmt, :char
29
+
30
+ sig { returns(T.any(Integer, T::Array[Integer])) }
31
+ attr_reader :codepoint
32
+
33
+ sig { returns(Color) }
34
+ attr_reader :color
20
35
 
21
36
  # Creates a new glyph
22
37
  #
@@ -27,26 +42,18 @@ module CLI
27
42
  # * +plain+ - A fallback plain string to be used in case glyphs are disabled
28
43
  # * +color+ - What color to output the glyph. Check +CLI::UI::Color+ for options.
29
44
  #
45
+ sig { params(handle: String, codepoint: T.any(Integer, T::Array[Integer]), plain: String, color: Color).void }
30
46
  def initialize(handle, codepoint, plain, color)
31
47
  @handle = handle
32
48
  @codepoint = codepoint
33
49
  @color = color
34
- @plain = plain
35
- @char = Array(codepoint).pack('U*')
36
- @to_s = color.code + char + Color::RESET.code
37
- @fmt = "{{#{color.name}:#{char}}}"
50
+ @char = CLI::UI::OS.current.use_emoji? ? Array(codepoint).pack('U*') : plain
51
+ @to_s = color.code + @char + Color::RESET.code
52
+ @fmt = "{{#{color.name}:#{@char}}}"
38
53
 
39
54
  MAP[handle] = self
40
55
  end
41
56
 
42
- # Fetches the actual character(s) to be displayed for a glyph, based on the current OS support
43
- #
44
- # ==== Returns
45
- # Returns the glyph string
46
- def char
47
- CLI::UI::OS.current.supports_emoji? ? @char : @plain
48
- end
49
-
50
57
  # Mapping of glyphs to terminal output
51
58
  MAP = {}
52
59
  STAR = new('*', 0x2b51, '*', Color::YELLOW) # YELLOW SMALL STAR (⭑)
@@ -59,25 +66,31 @@ module CLI
59
66
  HOURGLASS = new('H', [0x231b, 0xfe0e], 'H', Color::BLUE) # HOURGLASS + VARIATION SELECTOR 15 (⌛︎)
60
67
  WARNING = new('!', [0x26a0, 0xfe0f], '!', Color::YELLOW) # WARNING SIGN + VARIATION SELECTOR 16 (⚠️ )
61
68
 
62
- # Looks up a glyph by name
63
- #
64
- # ==== Raises
65
- # Raises a InvalidGlyphHandle if the glyph is not available
66
- # You likely need to create it with +.new+ or you made a typo
67
- #
68
- # ==== Returns
69
- # Returns a terminal output-capable string
70
- #
71
- def self.lookup(name)
72
- MAP.fetch(name.to_s)
73
- rescue KeyError
74
- raise InvalidGlyphHandle, name
75
- end
69
+ class << self
70
+ extend T::Sig
76
71
 
77
- # All available glyphs by name
78
- #
79
- def self.available
80
- MAP.keys
72
+ # Looks up a glyph by name
73
+ #
74
+ # ==== Raises
75
+ # Raises a InvalidGlyphHandle if the glyph is not available
76
+ # You likely need to create it with +.new+ or you made a typo
77
+ #
78
+ # ==== Returns
79
+ # Returns a terminal output-capable string
80
+ #
81
+ sig { params(name: String).returns(Glyph) }
82
+ def lookup(name)
83
+ MAP.fetch(name.to_s)
84
+ rescue KeyError
85
+ raise InvalidGlyphHandle, name
86
+ end
87
+
88
+ # All available glyphs by name
89
+ #
90
+ sig { returns(T::Array[String]) }
91
+ def available
92
+ MAP.keys
93
+ end
81
94
  end
82
95
  end
83
96
  end
data/lib/cli/ui/os.rb CHANGED
@@ -1,67 +1,63 @@
1
+ # typed: true
2
+
1
3
  require 'rbconfig'
2
4
 
3
5
  module CLI
4
6
  module UI
5
- module OS
6
- # Determines which OS is currently running the UI, to make it easier to
7
- # adapt its behaviour to the features of the OS.
8
- def self.current
9
- @current_os ||= case RbConfig::CONFIG['host_os']
10
- when /darwin/
11
- Mac
12
- when /linux/
13
- Linux
14
- else
15
- if RUBY_PLATFORM !~ /cygwin/ && ENV['OS'] == 'Windows_NT'
16
- Windows
17
- else
18
- raise "Could not determine OS from host_os #{RbConfig::CONFIG["host_os"]}"
19
- end
20
- end
7
+ class OS
8
+ extend T::Sig
9
+
10
+ sig { params(emoji: T::Boolean, color_prompt: T::Boolean, arrow_keys: T::Boolean, shift_cursor: T::Boolean).void }
11
+ def initialize(emoji: true, color_prompt: true, arrow_keys: true, shift_cursor: false)
12
+ @emoji = emoji
13
+ @color_prompt = color_prompt
14
+ @arrow_keys = arrow_keys
15
+ @shift_cursor = shift_cursor
21
16
  end
22
17
 
23
- class Mac
24
- class << self
25
- def supports_emoji?
26
- true
27
- end
28
-
29
- def supports_color_prompt?
30
- true
31
- end
32
-
33
- def supports_arrow_keys?
34
- true
35
- end
36
-
37
- def shift_cursor_on_line_reset?
38
- false
39
- end
40
- end
18
+ sig { returns(T::Boolean) }
19
+ def use_emoji?
20
+ @emoji
41
21
  end
42
22
 
43
- class Linux < Mac
23
+ sig { returns(T::Boolean) }
24
+ def use_color_prompt?
25
+ @color_prompt
44
26
  end
45
27
 
46
- class Windows
47
- class << self
48
- def supports_emoji?
49
- false
50
- end
28
+ sig { returns(T::Boolean) }
29
+ def suggest_arrow_keys?
30
+ @arrow_keys
31
+ end
51
32
 
52
- def supports_color_prompt?
53
- false
54
- end
33
+ sig { returns(T::Boolean) }
34
+ def shift_cursor_back_on_horizontal_absolute?
35
+ @shift_cursor
36
+ end
55
37
 
56
- def supports_arrow_keys?
57
- false
58
- end
38
+ class << self
39
+ extend T::Sig
59
40
 
60
- def shift_cursor_on_line_reset?
61
- true
41
+ sig { returns(OS) }
42
+ def current
43
+ @current_os ||= case RbConfig::CONFIG['host_os']
44
+ when /darwin/
45
+ MAC
46
+ when /linux/
47
+ LINUX
48
+ else
49
+ if RUBY_PLATFORM !~ /cygwin/ && ENV['OS'] == 'Windows_NT'
50
+ WINDOWS
51
+ else
52
+ raise "Could not determine OS from host_os #{RbConfig::CONFIG["host_os"]}"
53
+ end
62
54
  end
63
55
  end
64
56
  end
57
+
58
+ MAC = OS.new
59
+ LINUX = OS.new
60
+ WINDOWS = OS.new(emoji: false, color_prompt: false, arrow_keys: false, shift_cursor: true)
65
61
  end
66
62
  end
67
63
  end
@@ -1,58 +1,76 @@
1
+ # typed: true
2
+
1
3
  require 'cli/ui'
2
4
 
3
5
  module CLI
4
6
  module UI
5
7
  class Printer
6
- # Print a message to a stream with common utilities.
7
- # Allows overriding the color, encoding, and target stream.
8
- # By default, it formats the string using CLI:UI and rescues common stream errors.
9
- #
10
- # ==== Attributes
11
- #
12
- # * +msg+ - (required) the string to output. Can be frozen.
13
- #
14
- # ==== Options
15
- #
16
- # * +:frame_color+ - Override the frame color. Defaults to nil.
17
- # * +:to+ - Target stream, like $stdout or $stderr. Can be anything with a puts method. Defaults to $stdout.
18
- # * +:encoding+ - Force the output to be in a certain encoding. Defaults to UTF-8.
19
- # * +:format+ - Whether to format the string using CLI::UI.fmt. Defaults to true.
20
- # * +:graceful+ - Whether to gracefully ignore common I/O errors. Defaults to true.
21
- # * +:wrap+ - Whether to wrap text at word boundaries to terminal width. Defaults to true.
22
- #
23
- # ==== Returns
24
- # Returns whether the message was successfully printed,
25
- # which can be useful if +:graceful+ is set to true.
26
- #
27
- # ==== Example
28
- #
29
- # CLI::UI::Printer.puts('{{x}} Ouch', to: $stderr)
30
- #
31
- def self.puts(
32
- msg,
33
- frame_color:
34
- nil,
35
- to:
36
- $stdout,
37
- encoding: Encoding::UTF_8,
38
- format: true,
39
- graceful: true,
40
- wrap: true
41
- )
42
- msg = (+msg).force_encoding(encoding) if encoding
43
- msg = CLI::UI.fmt(msg) if format
44
- msg = CLI::UI.wrap(msg) if wrap
8
+ extend T::Sig
9
+
10
+ class << self
11
+ extend T::Sig
45
12
 
46
- if frame_color
47
- CLI::UI::Frame.with_frame_color_override(frame_color) { to.puts(msg) }
48
- else
49
- to.puts(msg)
13
+ # Print a message to a stream with common utilities.
14
+ # Allows overriding the color, encoding, and target stream.
15
+ # By default, it formats the string using CLI:UI and rescues common stream errors.
16
+ #
17
+ # ==== Attributes
18
+ #
19
+ # * +msg+ - (required) the string to output. Can be frozen.
20
+ #
21
+ # ==== Options
22
+ #
23
+ # * +:frame_color+ - Override the frame color. Defaults to nil.
24
+ # * +:to+ - Target stream, like $stdout or $stderr. Can be anything with a puts method. Defaults to $stdout.
25
+ # * +:encoding+ - Force the output to be in a certain encoding. Defaults to UTF-8.
26
+ # * +:format+ - Whether to format the string using CLI::UI.fmt. Defaults to true.
27
+ # * +:graceful+ - Whether to gracefully ignore common I/O errors. Defaults to true.
28
+ # * +:wrap+ - Whether to wrap text at word boundaries to terminal width. Defaults to true.
29
+ #
30
+ # ==== Returns
31
+ # Returns whether the message was successfully printed,
32
+ # which can be useful if +:graceful+ is set to true.
33
+ #
34
+ # ==== Example
35
+ #
36
+ # CLI::UI::Printer.puts('{{x}} Ouch', to: $stderr)
37
+ #
38
+ sig do
39
+ params(
40
+ msg: String,
41
+ frame_color: T.nilable(Colorable),
42
+ to: IOLike,
43
+ encoding: T.nilable(Encoding),
44
+ format: T::Boolean,
45
+ graceful: T::Boolean,
46
+ wrap: T::Boolean,
47
+ ).returns(T::Boolean)
50
48
  end
49
+ def puts(
50
+ msg,
51
+ frame_color: nil,
52
+ to: $stdout,
53
+ encoding: Encoding::UTF_8,
54
+ format: true,
55
+ graceful: true,
56
+ wrap: true
57
+ )
58
+ msg = (+msg).force_encoding(encoding) if encoding
59
+ msg = CLI::UI.fmt(msg) if format
60
+ msg = CLI::UI.wrap(msg) if wrap
61
+
62
+ if frame_color
63
+ CLI::UI::Frame.with_frame_color_override(frame_color) { to.puts(msg) }
64
+ else
65
+ to.puts(msg)
66
+ end
51
67
 
52
- true
53
- rescue Errno::EIO, Errno::EPIPE, IOError => e
54
- raise(e) unless graceful
55
- false
68
+ true
69
+ rescue Errno::EIO, Errno::EPIPE, IOError => e
70
+ raise(e) unless graceful
71
+
72
+ false
73
+ end
56
74
  end
57
75
  end
58
76
  end
@@ -1,41 +1,54 @@
1
+ # typed: true
2
+
1
3
  require 'cli/ui'
2
4
 
3
5
  module CLI
4
6
  module UI
5
7
  class Progress
8
+ extend T::Sig
9
+
6
10
  # A Cyan filled block
7
11
  FILLED_BAR = "\e[46m"
8
12
  # A bright white block
9
13
  UNFILLED_BAR = "\e[1;47m"
10
14
 
11
- # Add a progress bar to the terminal output
12
- #
13
- # https://user-images.githubusercontent.com/3074765/33799794-cc4c940e-dd00-11e7-9bdc-90f77ec9167c.gif
14
- #
15
- # ==== Example Usage:
16
- #
17
- # Set the percent to X
18
- # CLI::UI::Progress.progress do |bar|
19
- # bar.tick(set_percent: percent)
20
- # end
21
- #
22
- # Increase the percent by 1 percent
23
- # CLI::UI::Progress.progress do |bar|
24
- # bar.tick
25
- # end
26
- #
27
- # Increase the percent by X
28
- # CLI::UI::Progress.progress do |bar|
29
- # bar.tick(percent: 0.05)
30
- # end
31
- def self.progress(width: Terminal.width)
32
- bar = Progress.new(width: width)
33
- print(CLI::UI::ANSI.hide_cursor)
34
- yield(bar)
35
- ensure
36
- puts bar.to_s
37
- CLI::UI.raw do
38
- print(ANSI.show_cursor)
15
+ class << self
16
+ extend T::Sig
17
+
18
+ # Add a progress bar to the terminal output
19
+ #
20
+ # https://user-images.githubusercontent.com/3074765/33799794-cc4c940e-dd00-11e7-9bdc-90f77ec9167c.gif
21
+ #
22
+ # ==== Example Usage:
23
+ #
24
+ # Set the percent to X
25
+ # CLI::UI::Progress.progress do |bar|
26
+ # bar.tick(set_percent: percent)
27
+ # end
28
+ #
29
+ # Increase the percent by 1 percent
30
+ # CLI::UI::Progress.progress do |bar|
31
+ # bar.tick
32
+ # end
33
+ #
34
+ # Increase the percent by X
35
+ # CLI::UI::Progress.progress do |bar|
36
+ # bar.tick(percent: 0.05)
37
+ # end
38
+ sig do
39
+ type_parameters(:T)
40
+ .params(width: Integer, block: T.proc.params(bar: Progress).returns(T.type_parameter(:T)))
41
+ .returns(T.type_parameter(:T))
42
+ end
43
+ def progress(width: Terminal.width, &block)
44
+ bar = Progress.new(width: width)
45
+ print(CLI::UI::ANSI.hide_cursor)
46
+ yield(bar)
47
+ ensure
48
+ puts bar.to_s
49
+ CLI::UI.raw do
50
+ print(ANSI.show_cursor)
51
+ end
39
52
  end
40
53
  end
41
54
 
@@ -46,8 +59,9 @@ module CLI
46
59
  #
47
60
  # * +:width+ - The width of the terminal
48
61
  #
62
+ sig { params(width: Integer).void }
49
63
  def initialize(width: Terminal.width)
50
- @percent_done = 0
64
+ @percent_done = T.let(0, Numeric)
51
65
  @max_width = width
52
66
  end
53
67
 
@@ -61,9 +75,11 @@ module CLI
61
75
  #
62
76
  # *Note:* The +:percent+ and +:set_percent must be between 0.00 and 1.0
63
77
  #
64
- def tick(percent: 0.01, set_percent: nil)
65
- raise ArgumentError, 'percent and set_percent cannot both be specified' if percent != 0.01 && set_percent
66
- @percent_done += percent
78
+ sig { params(percent: T.nilable(Numeric), set_percent: T.nilable(Numeric)).void }
79
+ def tick(percent: nil, set_percent: nil)
80
+ raise ArgumentError, 'percent and set_percent cannot both be specified' if percent && set_percent
81
+
82
+ @percent_done += percent || 0.01
67
83
  @percent_done = set_percent if set_percent
68
84
  @percent_done = [@percent_done, 1.0].min # Make sure we can't go above 1.0
69
85
 
@@ -73,6 +89,7 @@ module CLI
73
89
 
74
90
  # Format the progress bar to be printed to terminal
75
91
  #
92
+ sig { returns(String) }
76
93
  def to_s
77
94
  suffix = " #{(@percent_done * 100).floor}%".ljust(5)
78
95
  workable_width = @max_width - Frame.prefix_width - suffix.size