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.
- checksums.yaml +4 -4
- data/README.md +23 -17
- data/lib/cli/ui/ansi.rb +157 -129
- data/lib/cli/ui/color.rb +39 -20
- data/lib/cli/ui/formatter.rb +45 -21
- data/lib/cli/ui/frame/frame_stack.rb +32 -13
- data/lib/cli/ui/frame/frame_style/box.rb +15 -4
- data/lib/cli/ui/frame/frame_style/bracket.rb +18 -7
- data/lib/cli/ui/frame/frame_style.rb +84 -87
- data/lib/cli/ui/frame.rb +55 -24
- data/lib/cli/ui/glyph.rb +44 -31
- data/lib/cli/ui/os.rb +44 -48
- data/lib/cli/ui/printer.rb +65 -47
- data/lib/cli/ui/progress.rb +49 -32
- data/lib/cli/ui/prompt/interactive_options.rb +91 -44
- data/lib/cli/ui/prompt/options_handler.rb +8 -0
- data/lib/cli/ui/prompt.rb +84 -31
- data/lib/cli/ui/sorbet_runtime_stub.rb +157 -0
- data/lib/cli/ui/spinner/async.rb +15 -4
- data/lib/cli/ui/spinner/spin_group.rb +83 -15
- data/lib/cli/ui/spinner.rb +48 -28
- data/lib/cli/ui/stdout_router.rb +71 -34
- data/lib/cli/ui/terminal.rb +37 -25
- data/lib/cli/ui/truncater.rb +7 -2
- data/lib/cli/ui/version.rb +3 -1
- data/lib/cli/ui/widgets/base.rb +23 -4
- data/lib/cli/ui/widgets/status.rb +19 -1
- data/lib/cli/ui/widgets.rb +42 -23
- data/lib/cli/ui/wrap.rb +8 -1
- data/lib/cli/ui.rb +325 -188
- metadata +10 -9
data/lib/cli/ui/stdout_router.rb
CHANGED
@@ -1,19 +1,21 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
require 'cli/ui'
|
2
4
|
require 'stringio'
|
3
5
|
|
4
6
|
module CLI
|
5
7
|
module UI
|
6
8
|
module StdoutRouter
|
7
|
-
class << self
|
8
|
-
attr_accessor :duplicate_output_to
|
9
|
-
end
|
10
|
-
|
11
9
|
class Writer
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { params(stream: IOLike, name: Symbol).void }
|
12
13
|
def initialize(stream, name)
|
13
14
|
@stream = stream
|
14
15
|
@name = name
|
15
16
|
end
|
16
17
|
|
18
|
+
sig { params(args: String).void }
|
17
19
|
def write(*args)
|
18
20
|
args = args.map do |str|
|
19
21
|
if auto_frame_inset?
|
@@ -31,34 +33,42 @@ module CLI
|
|
31
33
|
return if hook.call(args.map(&:to_s).join, @name) == false
|
32
34
|
end
|
33
35
|
|
34
|
-
@stream.write_without_cli_ui(*prepend_id(@stream, args))
|
36
|
+
T.unsafe(@stream).write_without_cli_ui(*prepend_id(@stream, args))
|
35
37
|
if (dup = StdoutRouter.duplicate_output_to)
|
36
|
-
dup.write(*prepend_id(dup, args))
|
38
|
+
T.unsafe(dup).write(*prepend_id(dup, args))
|
37
39
|
end
|
38
40
|
end
|
39
41
|
|
40
42
|
private
|
41
43
|
|
44
|
+
sig { params(stream: IOLike, args: T::Array[String]).returns(T::Array[String]) }
|
42
45
|
def prepend_id(stream, args)
|
43
46
|
return args unless prepend_id_for_stream(stream)
|
47
|
+
|
44
48
|
args.map do |a|
|
45
49
|
next a if a.chomp.empty? # allow new lines to be new lines
|
50
|
+
|
46
51
|
"[#{Thread.current[:cliui_output_id][:id]}] #{a}"
|
47
52
|
end
|
48
53
|
end
|
49
54
|
|
55
|
+
sig { params(stream: IOLike).returns(T::Boolean) }
|
50
56
|
def prepend_id_for_stream(stream)
|
51
57
|
return false unless Thread.current[:cliui_output_id]
|
52
58
|
return true if Thread.current[:cliui_output_id][:streams].include?(stream)
|
59
|
+
|
53
60
|
false
|
54
61
|
end
|
55
62
|
|
63
|
+
sig { returns(T::Boolean) }
|
56
64
|
def auto_frame_inset?
|
57
65
|
!Thread.current[:no_cliui_frame_inset]
|
58
66
|
end
|
59
67
|
|
68
|
+
sig { params(str: String, prefix: String).returns(String) }
|
60
69
|
def apply_line_prefix(str, prefix)
|
61
70
|
return '' if str.empty?
|
71
|
+
|
62
72
|
prefixed = +''
|
63
73
|
str.force_encoding(Encoding::UTF_8).lines.each do |line|
|
64
74
|
if @pending_newline
|
@@ -74,39 +84,52 @@ module CLI
|
|
74
84
|
end
|
75
85
|
|
76
86
|
class Capture
|
87
|
+
extend T::Sig
|
88
|
+
|
77
89
|
@m = Mutex.new
|
78
90
|
@active_captures = 0
|
79
91
|
@saved_stdin = nil
|
80
92
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
93
|
+
class << self
|
94
|
+
extend T::Sig
|
95
|
+
|
96
|
+
sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
|
97
|
+
def with_stdin_masked(&block)
|
98
|
+
@m.synchronize do
|
99
|
+
if @active_captures.zero?
|
100
|
+
@saved_stdin = $stdin
|
101
|
+
$stdin, w = IO.pipe
|
102
|
+
$stdin.close
|
103
|
+
w.close
|
104
|
+
end
|
105
|
+
@active_captures += 1
|
88
106
|
end
|
89
|
-
@active_captures += 1
|
90
|
-
end
|
91
107
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
108
|
+
yield
|
109
|
+
ensure
|
110
|
+
@m.synchronize do
|
111
|
+
@active_captures -= 1
|
112
|
+
if @active_captures.zero?
|
113
|
+
$stdin = @saved_stdin
|
114
|
+
end
|
98
115
|
end
|
99
116
|
end
|
100
117
|
end
|
101
118
|
|
102
|
-
|
119
|
+
sig do
|
120
|
+
params(with_frame_inset: T::Boolean, block: T.proc.void).void
|
121
|
+
end
|
122
|
+
def initialize(with_frame_inset: true, &block)
|
103
123
|
@with_frame_inset = with_frame_inset
|
104
|
-
@block_args = block_args
|
105
124
|
@block = block
|
125
|
+
@stdout = ''
|
126
|
+
@stderr = ''
|
106
127
|
end
|
107
128
|
|
129
|
+
sig { returns(String) }
|
108
130
|
attr_reader :stdout, :stderr
|
109
131
|
|
132
|
+
sig { returns(T.untyped) }
|
110
133
|
def run
|
111
134
|
require 'stringio'
|
112
135
|
|
@@ -134,7 +157,7 @@ module CLI
|
|
134
157
|
end
|
135
158
|
|
136
159
|
begin
|
137
|
-
@block.call
|
160
|
+
@block.call
|
138
161
|
ensure
|
139
162
|
@stdout = out.string
|
140
163
|
@stderr = err.string
|
@@ -147,39 +170,44 @@ module CLI
|
|
147
170
|
end
|
148
171
|
|
149
172
|
class << self
|
173
|
+
extend T::Sig
|
174
|
+
|
150
175
|
WRITE_WITHOUT_CLI_UI = :write_without_cli_ui
|
151
176
|
|
152
177
|
NotEnabled = Class.new(StandardError)
|
153
178
|
|
154
|
-
|
155
|
-
|
156
|
-
raise ArgumentError, <<~EOF
|
157
|
-
on_streams must be an array of objects that respond to `write`
|
158
|
-
These do not respond to write
|
159
|
-
#{on_streams.reject { |s| s.respond_to?(:write) }.map.with_index { |s| s.class.to_s }.join("\n")}
|
160
|
-
EOF
|
161
|
-
end
|
179
|
+
sig { returns(T.nilable(IOLike)) }
|
180
|
+
attr_accessor :duplicate_output_to
|
162
181
|
|
182
|
+
sig do
|
183
|
+
type_parameters(:T)
|
184
|
+
.params(on_streams: T::Array[IOLike], block: T.proc.params(id: String).returns(T.type_parameter(:T)))
|
185
|
+
.returns(T.type_parameter(:T))
|
186
|
+
end
|
187
|
+
def with_id(on_streams:, &block)
|
163
188
|
require 'securerandom'
|
164
189
|
id = format('%05d', rand(10**5))
|
165
190
|
Thread.current[:cliui_output_id] = {
|
166
191
|
id: id,
|
167
|
-
streams: on_streams,
|
192
|
+
streams: on_streams.map { |stream| T.cast(stream, IOLike) },
|
168
193
|
}
|
169
194
|
yield(id)
|
170
195
|
ensure
|
171
196
|
Thread.current[:cliui_output_id] = nil
|
172
197
|
end
|
173
198
|
|
199
|
+
sig { returns(T.nilable(T::Hash[Symbol, T.any(String, IOLike)])) }
|
174
200
|
def current_id
|
175
201
|
Thread.current[:cliui_output_id]
|
176
202
|
end
|
177
203
|
|
204
|
+
sig { void }
|
178
205
|
def assert_enabled!
|
179
206
|
raise NotEnabled unless enabled?
|
180
207
|
end
|
181
208
|
|
182
|
-
|
209
|
+
sig { type_parameters(:T).params(block: T.proc.returns(T.type_parameter(:T))).returns(T.type_parameter(:T)) }
|
210
|
+
def with_enabled(&block)
|
183
211
|
enable
|
184
212
|
yield
|
185
213
|
ensure
|
@@ -187,23 +215,29 @@ module CLI
|
|
187
215
|
end
|
188
216
|
|
189
217
|
# TODO: remove this
|
218
|
+
sig { void }
|
190
219
|
def ensure_activated
|
191
220
|
enable unless enabled?
|
192
221
|
end
|
193
222
|
|
223
|
+
sig { returns(T::Boolean) }
|
194
224
|
def enable
|
195
225
|
return false if enabled?($stdout) || enabled?($stderr)
|
226
|
+
|
196
227
|
activate($stdout, :stdout)
|
197
228
|
activate($stderr, :stderr)
|
198
229
|
true
|
199
230
|
end
|
200
231
|
|
232
|
+
sig { params(stream: IOLike).returns(T::Boolean) }
|
201
233
|
def enabled?(stream = $stdout)
|
202
234
|
stream.respond_to?(WRITE_WITHOUT_CLI_UI)
|
203
235
|
end
|
204
236
|
|
237
|
+
sig { returns(T::Boolean) }
|
205
238
|
def disable
|
206
239
|
return false unless enabled?($stdout) && enabled?($stderr)
|
240
|
+
|
207
241
|
deactivate($stdout)
|
208
242
|
deactivate($stderr)
|
209
243
|
true
|
@@ -211,16 +245,19 @@ module CLI
|
|
211
245
|
|
212
246
|
private
|
213
247
|
|
248
|
+
sig { params(stream: IOLike).void }
|
214
249
|
def deactivate(stream)
|
215
250
|
sc = stream.singleton_class
|
216
251
|
sc.send(:remove_method, :write)
|
217
252
|
sc.send(:alias_method, :write, WRITE_WITHOUT_CLI_UI)
|
218
253
|
end
|
219
254
|
|
255
|
+
sig { params(stream: IOLike, streamname: Symbol).void }
|
220
256
|
def activate(stream, streamname)
|
221
257
|
writer = StdoutRouter::Writer.new(stream, streamname)
|
222
258
|
|
223
259
|
raise if stream.respond_to?(WRITE_WITHOUT_CLI_UI)
|
260
|
+
|
224
261
|
stream.singleton_class.send(:alias_method, WRITE_WITHOUT_CLI_UI, :write)
|
225
262
|
stream.define_singleton_method(:write) do |*args|
|
226
263
|
writer.write(*args)
|
data/lib/cli/ui/terminal.rb
CHANGED
@@ -1,44 +1,56 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
require 'cli/ui'
|
2
4
|
require 'io/console'
|
3
5
|
|
4
6
|
module CLI
|
5
7
|
module UI
|
6
8
|
module Terminal
|
9
|
+
extend T::Sig
|
10
|
+
|
7
11
|
DEFAULT_WIDTH = 80
|
8
12
|
DEFAULT_HEIGHT = 24
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
#
|
13
|
-
def self.width
|
14
|
-
winsize[1]
|
15
|
-
end
|
14
|
+
class << self
|
15
|
+
extend T::Sig
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
# Returns the width of the terminal, if possible
|
18
|
+
# Otherwise will return DEFAULT_WIDTH
|
19
|
+
#
|
20
|
+
sig { returns(Integer) }
|
21
|
+
def width
|
22
|
+
winsize[1]
|
23
|
+
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
25
|
+
# Returns the width of the terminal, if possible
|
26
|
+
# Otherwise, will return DEFAULT_HEIGHT
|
27
|
+
#
|
28
|
+
sig { returns(Integer) }
|
29
|
+
def height
|
30
|
+
winsize[0]
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { returns([Integer, Integer]) }
|
34
|
+
def winsize
|
35
|
+
@winsize ||= begin
|
36
|
+
winsize = IO.console.winsize
|
37
|
+
setup_winsize_trap
|
28
38
|
|
29
|
-
|
39
|
+
if winsize.any?(&:zero?)
|
40
|
+
[DEFAULT_HEIGHT, DEFAULT_WIDTH]
|
41
|
+
else
|
42
|
+
winsize
|
43
|
+
end
|
44
|
+
rescue
|
30
45
|
[DEFAULT_HEIGHT, DEFAULT_WIDTH]
|
31
|
-
else
|
32
|
-
winsize
|
33
46
|
end
|
34
|
-
rescue
|
35
|
-
[DEFAULT_HEIGHT, DEFAULT_WIDTH]
|
36
47
|
end
|
37
|
-
end
|
38
48
|
|
39
|
-
|
40
|
-
|
41
|
-
@
|
49
|
+
sig { void }
|
50
|
+
def setup_winsize_trap
|
51
|
+
@winsize_trap ||= Signal.trap('WINCH') do
|
52
|
+
@winsize = nil
|
53
|
+
end
|
42
54
|
end
|
43
55
|
end
|
44
56
|
end
|
data/lib/cli/ui/truncater.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
2
3
|
|
3
4
|
require 'cli/ui'
|
@@ -27,12 +28,15 @@ module CLI
|
|
27
28
|
TRUNCATED = "\x1b[0m…"
|
28
29
|
|
29
30
|
class << self
|
31
|
+
extend T::Sig
|
32
|
+
|
33
|
+
sig { params(text: String, printing_width: Integer).returns(String) }
|
30
34
|
def call(text, printing_width)
|
31
35
|
return text if text.size <= printing_width
|
32
36
|
|
33
37
|
width = 0
|
34
38
|
mode = PARSE_ROOT
|
35
|
-
truncation_index = nil
|
39
|
+
truncation_index = T.let(nil, T.nilable(Integer))
|
36
40
|
|
37
41
|
codepoints = text.codepoints
|
38
42
|
codepoints.each.with_index do |cp, index|
|
@@ -83,11 +87,12 @@ module CLI
|
|
83
87
|
# the end of the string.
|
84
88
|
return text if !truncation_index || width <= printing_width
|
85
89
|
|
86
|
-
codepoints[0...truncation_index].pack('U*') + TRUNCATED
|
90
|
+
T.must(codepoints[0...truncation_index]).pack('U*') + TRUNCATED
|
87
91
|
end
|
88
92
|
|
89
93
|
private
|
90
94
|
|
95
|
+
sig { params(printable_codepoint: Integer).returns(Integer) }
|
91
96
|
def width(printable_codepoint)
|
92
97
|
case printable_codepoint
|
93
98
|
when EMOJI_RANGE
|
data/lib/cli/ui/version.rb
CHANGED
data/lib/cli/ui/widgets/base.rb
CHANGED
@@ -1,26 +1,45 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
require('cli/ui')
|
2
4
|
|
3
5
|
module CLI
|
4
6
|
module UI
|
5
7
|
module Widgets
|
6
8
|
class Base
|
7
|
-
|
8
|
-
|
9
|
+
extend T::Sig
|
10
|
+
extend T::Helpers
|
11
|
+
abstract!
|
12
|
+
|
13
|
+
class << self
|
14
|
+
extend T::Sig
|
15
|
+
|
16
|
+
sig { params(argstring: String).returns(String) }
|
17
|
+
def call(argstring)
|
18
|
+
new(argstring).render
|
19
|
+
end
|
9
20
|
end
|
10
21
|
|
22
|
+
sig { params(argstring: String).void }
|
11
23
|
def initialize(argstring)
|
12
24
|
pat = self.class.argparse_pattern
|
13
25
|
unless (@match_data = pat.match(argstring))
|
14
26
|
raise(Widgets::InvalidWidgetArguments.new(argstring, pat))
|
15
27
|
end
|
28
|
+
|
16
29
|
@match_data.names.each do |name|
|
17
30
|
instance_variable_set(:"@#{name}", @match_data[name])
|
18
31
|
end
|
19
32
|
end
|
20
33
|
|
21
|
-
|
22
|
-
|
34
|
+
class << self
|
35
|
+
extend T::Sig
|
36
|
+
|
37
|
+
sig { abstract.returns(Regexp) }
|
38
|
+
def argparse_pattern; end
|
23
39
|
end
|
40
|
+
|
41
|
+
sig { abstract.returns(String) }
|
42
|
+
def render; end
|
24
43
|
end
|
25
44
|
end
|
26
45
|
end
|
@@ -1,4 +1,6 @@
|
|
1
|
-
#
|
1
|
+
# typed: true
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require('cli/ui')
|
3
5
|
|
4
6
|
module CLI
|
@@ -19,6 +21,16 @@ module CLI
|
|
19
21
|
SPINNER_STOPPED = '⠿'
|
20
22
|
EMPTY_SET = '∅'
|
21
23
|
|
24
|
+
class << self
|
25
|
+
extend T::Sig
|
26
|
+
|
27
|
+
sig { override.returns(Regexp) }
|
28
|
+
def argparse_pattern
|
29
|
+
ARGPARSE_PATTERN
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { override.returns(String) }
|
22
34
|
def render
|
23
35
|
if zero?(@succeeded) && zero?(@failed) && zero?(@working) && zero?(@pending)
|
24
36
|
Color::RESET.code + Color::BOLD.code + EMPTY_SET + Color::RESET.code
|
@@ -30,28 +42,34 @@ module CLI
|
|
30
42
|
|
31
43
|
private
|
32
44
|
|
45
|
+
sig { params(num_str: String).returns(T::Boolean) }
|
33
46
|
def zero?(num_str)
|
34
47
|
num_str == '0'
|
35
48
|
end
|
36
49
|
|
50
|
+
sig { params(num_str: String, rune: String, color: Color).returns(String) }
|
37
51
|
def colorize_if_nonzero(num_str, rune, color)
|
38
52
|
color = Color::GRAY if zero?(num_str)
|
39
53
|
color.code + num_str + rune
|
40
54
|
end
|
41
55
|
|
56
|
+
sig { returns(String) }
|
42
57
|
def succeeded_part
|
43
58
|
colorize_if_nonzero(@succeeded, Glyph::CHECK.char, Color::GREEN)
|
44
59
|
end
|
45
60
|
|
61
|
+
sig { returns(String) }
|
46
62
|
def failed_part
|
47
63
|
colorize_if_nonzero(@failed, Glyph::X.char, Color::RED)
|
48
64
|
end
|
49
65
|
|
66
|
+
sig { returns(String) }
|
50
67
|
def working_part
|
51
68
|
rune = zero?(@working) ? SPINNER_STOPPED : Spinner.current_rune
|
52
69
|
colorize_if_nonzero(@working, rune, Color::BLUE)
|
53
70
|
end
|
54
71
|
|
72
|
+
sig { returns(String) }
|
55
73
|
def pending_part
|
56
74
|
colorize_if_nonzero(@pending, Glyph::HOURGLASS.char, Color::WHITE)
|
57
75
|
end
|
data/lib/cli/ui/widgets.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
require('cli/ui')
|
2
4
|
|
3
5
|
module CLI
|
@@ -16,57 +18,74 @@ module CLI
|
|
16
18
|
# CLI::UI::Widgets.register('my-widget') { MyWidget }
|
17
19
|
# puts(CLI::UI.fmt("{{@widget/my-widget:args}}"))
|
18
20
|
module Widgets
|
21
|
+
extend T::Sig
|
22
|
+
|
19
23
|
MAP = {}
|
20
24
|
|
21
25
|
autoload(:Base, 'cli/ui/widgets/base')
|
26
|
+
autoload(:Status, 'cli/ui/widgets/status')
|
22
27
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
28
|
+
class << self
|
29
|
+
extend T::Sig
|
26
30
|
|
27
|
-
|
28
|
-
|
31
|
+
sig { params(name: String, cb: T.proc.returns(T.class_of(Widgets::Base))).void }
|
32
|
+
def register(name, &cb)
|
33
|
+
MAP[name] = cb
|
34
|
+
end
|
29
35
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
36
|
+
# Looks up a widget by handle
|
37
|
+
#
|
38
|
+
# ==== Raises
|
39
|
+
# Raises InvalidWidgetHandle if the widget is not available.
|
40
|
+
#
|
41
|
+
# ==== Returns
|
42
|
+
# A callable widget, to be invoked like `.call(argstring)`
|
43
|
+
#
|
44
|
+
sig { params(handle: String).returns(T.class_of(Widgets::Base)) }
|
45
|
+
def lookup(handle)
|
46
|
+
MAP.fetch(handle).call
|
47
|
+
rescue KeyError, NameError
|
48
|
+
raise(InvalidWidgetHandle, handle)
|
49
|
+
end
|
43
50
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
51
|
+
# All available widgets by name
|
52
|
+
#
|
53
|
+
sig { returns(T::Array[String]) }
|
54
|
+
def available
|
55
|
+
MAP.keys
|
56
|
+
end
|
48
57
|
end
|
49
58
|
|
59
|
+
register('status') { Widgets::Status }
|
60
|
+
|
50
61
|
class InvalidWidgetHandle < ArgumentError
|
62
|
+
extend T::Sig
|
63
|
+
|
64
|
+
sig { params(handle: String).void }
|
51
65
|
def initialize(handle)
|
52
66
|
super
|
53
67
|
@handle = handle
|
54
68
|
end
|
55
69
|
|
70
|
+
sig { returns(String) }
|
56
71
|
def message
|
57
|
-
keys =
|
72
|
+
keys = Widgets.available.join(',')
|
58
73
|
"invalid widget handle: #{@handle} " \
|
59
74
|
"-- must be one of CLI::UI::Widgets.available (#{keys})"
|
60
75
|
end
|
61
76
|
end
|
62
77
|
|
63
78
|
class InvalidWidgetArguments < ArgumentError
|
79
|
+
extend T::Sig
|
80
|
+
|
81
|
+
sig { params(argstring: String, pattern: Regexp).void }
|
64
82
|
def initialize(argstring, pattern)
|
65
83
|
super
|
66
84
|
@argstring = argstring
|
67
85
|
@pattern = pattern
|
68
86
|
end
|
69
87
|
|
88
|
+
sig { returns(String) }
|
70
89
|
def message
|
71
90
|
"invalid widget arguments: #{@argstring} " \
|
72
91
|
"-- must match pattern: #{@pattern.inspect}"
|
data/lib/cli/ui/wrap.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'
|
@@ -6,13 +9,17 @@ require 'cli/ui/frame/frame_style'
|
|
6
9
|
module CLI
|
7
10
|
module UI
|
8
11
|
class Wrap
|
12
|
+
extend T::Sig
|
13
|
+
|
14
|
+
sig { params(input: String).void }
|
9
15
|
def initialize(input)
|
10
16
|
@input = input
|
11
17
|
end
|
12
18
|
|
19
|
+
sig { returns(String) }
|
13
20
|
def wrap
|
14
21
|
max_width = Terminal.width - Frame.prefix_width
|
15
|
-
width = 0
|
22
|
+
width = T.let(0, Integer)
|
16
23
|
final = []
|
17
24
|
# Create an alternation of format codes of parameter lengths 1-20, since + and {1,n} not allowed in lookbehind
|
18
25
|
format_codes = (1..20).map { |n| /\x1b\[[\d;]{#{n}}m/ }.join('|')
|