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/formatter.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
|
+
# typed: true
|
1
2
|
# frozen_string_literal: true
|
3
|
+
|
2
4
|
require('cli/ui')
|
3
5
|
require('strscan')
|
4
6
|
|
5
7
|
module CLI
|
6
8
|
module UI
|
7
9
|
class Formatter
|
10
|
+
extend T::Sig
|
11
|
+
|
8
12
|
# Available mappings of formattings
|
9
13
|
# To use any of them, you can use {{<key>:<string>}}
|
10
14
|
# There are presentational (colours and formatters)
|
@@ -19,6 +23,8 @@ module CLI
|
|
19
23
|
'blue' => '94', # 9x = high-intensity fg color x
|
20
24
|
'magenta' => '35',
|
21
25
|
'cyan' => '36',
|
26
|
+
'gray' => '38;5;244',
|
27
|
+
'white' => '97',
|
22
28
|
'bold' => '1',
|
23
29
|
'italic' => '3',
|
24
30
|
'underline' => '4',
|
@@ -49,12 +55,21 @@ module CLI
|
|
49
55
|
|
50
56
|
DISCARD_BRACES = 0..-3
|
51
57
|
|
52
|
-
LITERAL_BRACES =
|
58
|
+
LITERAL_BRACES = Class.new
|
59
|
+
|
60
|
+
Stack = T.type_alias { T::Array[T.any(String, LITERAL_BRACES)] }
|
53
61
|
|
54
62
|
class FormatError < StandardError
|
55
|
-
|
63
|
+
extend T::Sig
|
64
|
+
|
65
|
+
sig { returns(String) }
|
66
|
+
attr_accessor :input
|
56
67
|
|
57
|
-
|
68
|
+
sig { returns(Integer) }
|
69
|
+
attr_accessor :index
|
70
|
+
|
71
|
+
sig { params(message: String, input: String, index: Integer).void }
|
72
|
+
def initialize(message, input, index)
|
58
73
|
super(message)
|
59
74
|
@input = input
|
60
75
|
@index = index
|
@@ -67,8 +82,10 @@ module CLI
|
|
67
82
|
#
|
68
83
|
# * +text+ - the text to format
|
69
84
|
#
|
85
|
+
sig { params(text: String).void }
|
70
86
|
def initialize(text)
|
71
87
|
@text = text
|
88
|
+
@nodes = T.let([], T::Array[[String, Stack]])
|
72
89
|
end
|
73
90
|
|
74
91
|
# Format the text using a map.
|
@@ -81,10 +98,11 @@ module CLI
|
|
81
98
|
#
|
82
99
|
# * +:enable_color+ - enable color output? Default is true unless output is redirected
|
83
100
|
#
|
101
|
+
sig { params(sgr_map: T::Hash[String, String], enable_color: T::Boolean).returns(String) }
|
84
102
|
def format(sgr_map = SGR_MAP, enable_color: CLI::UI.enable_color?)
|
85
|
-
@nodes
|
103
|
+
@nodes.replace([])
|
86
104
|
stack = parse_body(StringScanner.new(@text))
|
87
|
-
prev_fmt = nil
|
105
|
+
prev_fmt = T.let(nil, T.nilable(Stack))
|
88
106
|
content = @nodes.each_with_object(+'') do |(text, fmt), str|
|
89
107
|
if prev_fmt != fmt && enable_color
|
90
108
|
text = apply_format(text, fmt, sgr_map)
|
@@ -93,12 +111,12 @@ module CLI
|
|
93
111
|
prev_fmt = fmt
|
94
112
|
end
|
95
113
|
|
96
|
-
stack.reject! { |e| e
|
114
|
+
stack.reject! { |e| e.is_a?(LITERAL_BRACES) }
|
97
115
|
|
98
116
|
return content unless enable_color
|
99
117
|
return content if stack == prev_fmt
|
100
118
|
|
101
|
-
unless stack.empty? && (@nodes.size.zero? || @nodes.last[1].empty?)
|
119
|
+
unless stack.empty? && (@nodes.size.zero? || T.must(@nodes.last)[1].empty?)
|
102
120
|
content << apply_format('', stack, sgr_map)
|
103
121
|
end
|
104
122
|
content
|
@@ -106,25 +124,28 @@ module CLI
|
|
106
124
|
|
107
125
|
private
|
108
126
|
|
127
|
+
sig { params(text: String, fmt: Stack, sgr_map: T::Hash[String, String]).returns(String) }
|
109
128
|
def apply_format(text, fmt, sgr_map)
|
110
129
|
sgr = fmt.each_with_object(+'0') do |name, str|
|
111
|
-
next if name
|
130
|
+
next if name.is_a?(LITERAL_BRACES)
|
131
|
+
|
112
132
|
begin
|
113
133
|
str << ';' << sgr_map.fetch(name)
|
114
134
|
rescue KeyError
|
115
135
|
raise FormatError.new(
|
116
136
|
"invalid format specifier: #{name}",
|
117
137
|
@text,
|
118
|
-
-1
|
138
|
+
-1,
|
119
139
|
)
|
120
140
|
end
|
121
141
|
end
|
122
142
|
CLI::UI::ANSI.sgr(sgr) + text
|
123
143
|
end
|
124
144
|
|
145
|
+
sig { params(sc: StringScanner, stack: Stack).returns(Stack) }
|
125
146
|
def parse_expr(sc, stack)
|
126
147
|
if (match = sc.scan(SCAN_GLYPH))
|
127
|
-
glyph_handle = match[0]
|
148
|
+
glyph_handle = T.must(match[0])
|
128
149
|
begin
|
129
150
|
glyph = Glyph.lookup(glyph_handle)
|
130
151
|
emit(glyph.char, [glyph.color.name.to_s])
|
@@ -133,20 +154,20 @@ module CLI
|
|
133
154
|
raise FormatError.new(
|
134
155
|
"invalid glyph handle at index #{index}: '#{glyph_handle}'",
|
135
156
|
@text,
|
136
|
-
index
|
157
|
+
index,
|
137
158
|
)
|
138
159
|
end
|
139
160
|
elsif (match = sc.scan(SCAN_WIDGET))
|
140
|
-
match_data = SCAN_WIDGET.match(match) # Regexp.last_match doesn't work here
|
141
|
-
widget_handle = match_data['handle']
|
161
|
+
match_data = T.must(SCAN_WIDGET.match(match)) # Regexp.last_match doesn't work here
|
162
|
+
widget_handle = T.must(match_data['handle'])
|
142
163
|
begin
|
143
164
|
widget = Widgets.lookup(widget_handle)
|
144
|
-
emit(widget.call(match_data['args']), stack)
|
165
|
+
emit(widget.call(T.must(match_data['args'])), stack)
|
145
166
|
rescue Widgets::InvalidWidgetHandle
|
146
167
|
index = sc.pos - 2 # rewind past '}}'
|
147
168
|
raise(FormatError.new(
|
148
169
|
"invalid widget handle at index #{index}: '#{widget_handle}'",
|
149
|
-
@text, index
|
170
|
+
@text, index
|
150
171
|
))
|
151
172
|
end
|
152
173
|
elsif (match = sc.scan(SCAN_FUNCNAME))
|
@@ -158,20 +179,21 @@ module CLI
|
|
158
179
|
# We do kind of assume that the text will probably have balanced
|
159
180
|
# pairs of {{ }} at least.
|
160
181
|
emit('{{', stack)
|
161
|
-
stack.push(LITERAL_BRACES)
|
182
|
+
stack.push(LITERAL_BRACES.new)
|
162
183
|
end
|
163
184
|
parse_body(sc, stack)
|
164
185
|
stack
|
165
186
|
end
|
166
187
|
|
188
|
+
sig { params(sc: StringScanner, stack: Stack).returns(Stack) }
|
167
189
|
def parse_body(sc, stack = [])
|
168
190
|
match = sc.scan(SCAN_BODY)
|
169
191
|
if match&.end_with?(BEGIN_EXPR)
|
170
|
-
emit(match[DISCARD_BRACES], stack)
|
192
|
+
emit(T.must(match[DISCARD_BRACES]), stack)
|
171
193
|
parse_expr(sc, stack)
|
172
194
|
elsif match&.end_with?(END_EXPR)
|
173
|
-
emit(match[DISCARD_BRACES], stack)
|
174
|
-
if stack.pop
|
195
|
+
emit(T.must(match[DISCARD_BRACES]), stack)
|
196
|
+
if stack.pop.is_a?(LITERAL_BRACES)
|
175
197
|
emit('}}', stack)
|
176
198
|
end
|
177
199
|
parse_body(sc, stack)
|
@@ -183,9 +205,11 @@ module CLI
|
|
183
205
|
stack
|
184
206
|
end
|
185
207
|
|
208
|
+
sig { params(text: String, stack: Stack).void }
|
186
209
|
def emit(text, stack)
|
187
|
-
return if text.
|
188
|
-
|
210
|
+
return if text.empty?
|
211
|
+
|
212
|
+
@nodes << [text, stack.reject { |n| n.is_a?(LITERAL_BRACES) }]
|
189
213
|
end
|
190
214
|
end
|
191
215
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
module CLI
|
2
4
|
module UI
|
3
5
|
module Frame
|
@@ -6,8 +8,18 @@ module CLI
|
|
6
8
|
STYLE_ENVVAR = 'CLI_STYLE_STACK'
|
7
9
|
|
8
10
|
class StackItem
|
9
|
-
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
sig { returns(CLI::UI::Color) }
|
14
|
+
attr_reader :color
|
15
|
+
|
16
|
+
sig { returns(CLI::UI::Frame::FrameStyle) }
|
17
|
+
attr_reader :frame_style
|
10
18
|
|
19
|
+
sig do
|
20
|
+
params(color_name: CLI::UI::Colorable, style_name: FrameStylable)
|
21
|
+
.void
|
22
|
+
end
|
11
23
|
def initialize(color_name, style_name)
|
12
24
|
@color = CLI::UI.resolve_color(color_name)
|
13
25
|
@frame_style = CLI::UI.resolve_style(style_name)
|
@@ -15,13 +27,16 @@ module CLI
|
|
15
27
|
end
|
16
28
|
|
17
29
|
class << self
|
30
|
+
extend T::Sig
|
31
|
+
|
18
32
|
# Fetch all items off the frame stack
|
33
|
+
sig { returns(T::Array[StackItem]) }
|
19
34
|
def items
|
20
35
|
colors = ENV.fetch(COLOR_ENVVAR, '').split(':').map(&:to_sym)
|
21
36
|
styles = ENV.fetch(STYLE_ENVVAR, '').split(':').map(&:to_sym)
|
22
37
|
|
23
|
-
colors.
|
24
|
-
StackItem.new(
|
38
|
+
colors.each_with_index.map do |color, i|
|
39
|
+
StackItem.new(color, styles[i] || Frame.frame_style)
|
25
40
|
end
|
26
41
|
end
|
27
42
|
|
@@ -43,18 +58,20 @@ module CLI
|
|
43
58
|
# If both an item and a color/style pair are given, raises an +ArgumentError+
|
44
59
|
# If the given item is not a +StackItem+, raises an +ArgumentError+
|
45
60
|
#
|
61
|
+
sig do
|
62
|
+
params(
|
63
|
+
item: T.nilable(StackItem),
|
64
|
+
color: T.nilable(CLI::UI::Color),
|
65
|
+
style: T.nilable(CLI::UI::Frame::FrameStyle),
|
66
|
+
)
|
67
|
+
.void
|
68
|
+
end
|
46
69
|
def push(item = nil, color: nil, style: nil)
|
47
|
-
|
48
|
-
|
49
|
-
raise ArgumentError, 'item must be a StackItem'
|
50
|
-
end
|
51
|
-
|
52
|
-
unless color.nil? && style.nil?
|
53
|
-
raise ArgumentError, 'Must give one of item or color: and style:'
|
54
|
-
end
|
70
|
+
if color.nil? != style.nil? || item.nil? == color.nil?
|
71
|
+
raise ArgumentError, 'Must give one of item or color: and style:'
|
55
72
|
end
|
56
73
|
|
57
|
-
item ||= StackItem.new(color, style)
|
74
|
+
item ||= StackItem.new(T.must(color), T.must(style))
|
58
75
|
|
59
76
|
curr = items
|
60
77
|
curr << item
|
@@ -63,6 +80,7 @@ module CLI
|
|
63
80
|
end
|
64
81
|
|
65
82
|
# Removes and returns the last stack item off the stack
|
83
|
+
sig { returns(T.nilable(StackItem)) }
|
66
84
|
def pop
|
67
85
|
curr = items
|
68
86
|
ret = curr.pop
|
@@ -79,13 +97,14 @@ module CLI
|
|
79
97
|
# This is done to preserve backward compatibility with earlier versions of cli/ui.
|
80
98
|
# This ensures that any code that relied upon previous stack behavior should continue
|
81
99
|
# to work.
|
100
|
+
sig { params(items: T::Array[StackItem]).void }
|
82
101
|
def serialize(items)
|
83
102
|
colors = []
|
84
103
|
styles = []
|
85
104
|
|
86
105
|
items.each do |item|
|
87
106
|
colors << item.color.name
|
88
|
-
styles << item.frame_style.
|
107
|
+
styles << item.frame_style.style_name
|
89
108
|
end
|
90
109
|
|
91
110
|
ENV[COLOR_ENVVAR] = colors.join(':')
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
module CLI
|
2
4
|
module UI
|
3
5
|
module Frame
|
@@ -12,10 +14,14 @@ module CLI
|
|
12
14
|
BOTTOM_LEFT = '┗'
|
13
15
|
|
14
16
|
class << self
|
15
|
-
|
16
|
-
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
sig { override.returns(Symbol) }
|
20
|
+
def style_name
|
21
|
+
:box
|
17
22
|
end
|
18
23
|
|
24
|
+
sig { override.returns(String) }
|
19
25
|
def prefix
|
20
26
|
VERTICAL
|
21
27
|
end
|
@@ -34,7 +40,8 @@ module CLI
|
|
34
40
|
#
|
35
41
|
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
36
42
|
#
|
37
|
-
|
43
|
+
sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
|
44
|
+
def start(text, color:)
|
38
45
|
edge(text, color: color, first: TOP_LEFT)
|
39
46
|
end
|
40
47
|
|
@@ -52,6 +59,7 @@ module CLI
|
|
52
59
|
#
|
53
60
|
# ┣━━ Divider ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
54
61
|
#
|
62
|
+
sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
|
55
63
|
def divider(text, color:)
|
56
64
|
edge(text, color: color, first: DIVIDER)
|
57
65
|
end
|
@@ -71,12 +79,16 @@ module CLI
|
|
71
79
|
#
|
72
80
|
# ┗━━ Close ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
73
81
|
#
|
82
|
+
sig { override.params(text: String, color: CLI::UI::Color, right_text: T.nilable(String)).returns(String) }
|
74
83
|
def close(text, color:, right_text: nil)
|
75
84
|
edge(text, color: color, right_text: right_text, first: BOTTOM_LEFT)
|
76
85
|
end
|
77
86
|
|
78
87
|
private
|
79
88
|
|
89
|
+
sig do
|
90
|
+
params(text: String, color: CLI::UI::Color, first: String, right_text: T.nilable(String)).returns(String)
|
91
|
+
end
|
80
92
|
def edge(text, color:, first:, right_text: nil)
|
81
93
|
color = CLI::UI.resolve_color(color)
|
82
94
|
|
@@ -84,7 +96,6 @@ module CLI
|
|
84
96
|
|
85
97
|
preamble << color.code << first << (HORIZONTAL * 2)
|
86
98
|
|
87
|
-
text ||= ''
|
88
99
|
unless text.empty?
|
89
100
|
preamble << ' ' << CLI::UI.resolve_text("{{#{color.name}:#{text}}}") << ' '
|
90
101
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
module CLI
|
2
4
|
module UI
|
3
5
|
module Frame
|
@@ -12,10 +14,14 @@ module CLI
|
|
12
14
|
BOTTOM_LEFT = '┗'
|
13
15
|
|
14
16
|
class << self
|
15
|
-
|
16
|
-
|
17
|
+
extend T::Sig
|
18
|
+
|
19
|
+
sig { override.returns(Symbol) }
|
20
|
+
def style_name
|
21
|
+
:bracket
|
17
22
|
end
|
18
23
|
|
24
|
+
sig { override.returns(String) }
|
19
25
|
def prefix
|
20
26
|
VERTICAL
|
21
27
|
end
|
@@ -34,7 +40,8 @@ module CLI
|
|
34
40
|
#
|
35
41
|
# ┏━━ Open
|
36
42
|
#
|
37
|
-
|
43
|
+
sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
|
44
|
+
def start(text, color:)
|
38
45
|
edge(text, color: color, first: TOP_LEFT)
|
39
46
|
end
|
40
47
|
|
@@ -52,6 +59,7 @@ module CLI
|
|
52
59
|
#
|
53
60
|
# ┣━━ Divider
|
54
61
|
#
|
62
|
+
sig { override.params(text: String, color: CLI::UI::Color).returns(String) }
|
55
63
|
def divider(text, color:)
|
56
64
|
edge(text, color: color, first: DIVIDER)
|
57
65
|
end
|
@@ -71,12 +79,16 @@ module CLI
|
|
71
79
|
#
|
72
80
|
# ┗━━ Close
|
73
81
|
#
|
82
|
+
sig { override.params(text: String, color: CLI::UI::Color, right_text: T.nilable(String)).returns(String) }
|
74
83
|
def close(text, color:, right_text: nil)
|
75
84
|
edge(text, color: color, right_text: right_text, first: BOTTOM_LEFT)
|
76
85
|
end
|
77
86
|
|
78
87
|
private
|
79
88
|
|
89
|
+
sig do
|
90
|
+
params(text: String, color: CLI::UI::Color, first: String, right_text: T.nilable(String)).returns(String)
|
91
|
+
end
|
80
92
|
def edge(text, color:, first:, right_text: nil)
|
81
93
|
color = CLI::UI.resolve_color(color)
|
82
94
|
|
@@ -84,7 +96,6 @@ module CLI
|
|
84
96
|
|
85
97
|
preamble << color.code << first << (HORIZONTAL * 2)
|
86
98
|
|
87
|
-
text ||= ''
|
88
99
|
unless text.empty?
|
89
100
|
preamble << ' ' << CLI::UI.resolve_text("{{#{color.name}:#{text}}}") << ' '
|
90
101
|
end
|
@@ -117,16 +128,16 @@ module CLI
|
|
117
128
|
# the final space, since we're going to write over it.
|
118
129
|
preamble_start -= 1 unless preamble_start.zero?
|
119
130
|
|
120
|
-
#
|
121
|
-
# want to remove. The clamping is done to avoid a negative
|
122
|
-
# preamble start which can occur for the first frame.
|
131
|
+
# Jumping around the line can cause some unwanted flashes
|
123
132
|
o << CLI::UI::ANSI.hide_cursor
|
124
133
|
|
125
134
|
# reset to column 1 so that things like ^C don't ruin formatting
|
126
135
|
o << "\r"
|
136
|
+
|
127
137
|
o << color.code
|
128
138
|
o << print_at_x(preamble_start, preamble + color.code + suffix)
|
129
139
|
o << CLI::UI::Color::RESET.code
|
140
|
+
o << CLI::UI::ANSI.show_cursor
|
130
141
|
o << "\n"
|
131
142
|
|
132
143
|
o
|
@@ -1,120 +1,117 @@
|
|
1
|
+
# typed: true
|
2
|
+
|
1
3
|
require 'cli/ui/frame'
|
2
4
|
|
3
5
|
module CLI
|
4
6
|
module UI
|
5
7
|
module Frame
|
6
8
|
module FrameStyle
|
7
|
-
|
8
|
-
|
9
|
-
|
9
|
+
include Kernel
|
10
|
+
extend T::Sig
|
11
|
+
extend T::Helpers
|
12
|
+
abstract!
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
+
autoload(:Box, 'cli/ui/frame/frame_style/box')
|
15
|
+
autoload(:Bracket, 'cli/ui/frame/frame_style/bracket')
|
16
|
+
|
17
|
+
MAP = {
|
18
|
+
box: -> { FrameStyle::Box },
|
19
|
+
bracket: -> { FrameStyle::Bracket },
|
20
|
+
}
|
21
|
+
|
22
|
+
class << self
|
23
|
+
extend T::Sig
|
14
24
|
|
15
25
|
# Lookup a frame style via its name
|
16
26
|
#
|
17
27
|
# ==== Attributes
|
18
28
|
#
|
19
29
|
# * +symbol+ - frame style name to lookup
|
30
|
+
sig { params(name: T.any(String, Symbol)).returns(FrameStyle) }
|
20
31
|
def lookup(name)
|
21
|
-
|
22
|
-
|
23
|
-
|
32
|
+
MAP.fetch(name.to_sym).call
|
33
|
+
rescue KeyError
|
34
|
+
raise(InvalidFrameStyleName, name)
|
24
35
|
end
|
25
|
-
|
26
|
-
def extended(base)
|
27
|
-
@@loaded_styles << base
|
28
|
-
base.extend(Interface)
|
29
|
-
end
|
30
|
-
# rubocop:enable Style/ClassVars
|
31
36
|
end
|
32
37
|
|
33
|
-
|
34
|
-
|
35
|
-
super
|
36
|
-
@name = name
|
37
|
-
end
|
38
|
+
sig { abstract.returns(Symbol) }
|
39
|
+
def style_name; end
|
38
40
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
# Returns the character(s) that should be printed at the beginning
|
42
|
+
# of lines inside this frame
|
43
|
+
sig { abstract.returns(String) }
|
44
|
+
def prefix; end
|
45
|
+
|
46
|
+
# Returns the printing width of the prefix
|
47
|
+
sig { returns(Integer) }
|
48
|
+
def prefix_width
|
49
|
+
CLI::UI::ANSI.printing_width(prefix)
|
45
50
|
end
|
46
51
|
|
47
|
-
#
|
48
|
-
#
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
52
|
+
# Draws the "Open" line for this frame style
|
53
|
+
#
|
54
|
+
# ==== Attributes
|
55
|
+
#
|
56
|
+
# * +text+ - (required) the text/title to output in the frame
|
57
|
+
#
|
58
|
+
# ==== Options
|
59
|
+
#
|
60
|
+
# * +:color+ - (required) The color of the frame.
|
61
|
+
#
|
62
|
+
sig { abstract.params(text: String, color: CLI::UI::Color).returns(String) }
|
63
|
+
def start(text, color:); end
|
53
64
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
65
|
+
# Draws the "Close" line for this frame style
|
66
|
+
#
|
67
|
+
# ==== Attributes
|
68
|
+
#
|
69
|
+
# * +text+ - (required) the text/title to output in the frame
|
70
|
+
#
|
71
|
+
# ==== Options
|
72
|
+
#
|
73
|
+
# * +:color+ - (required) The color of the frame.
|
74
|
+
# * +:right_text+ - Text to print at the right of the line. Defaults to nil
|
75
|
+
#
|
76
|
+
sig { abstract.params(text: String, color: CLI::UI::Color, right_text: T.nilable(String)).returns(String) }
|
77
|
+
def close(text, color:, right_text: nil); end
|
59
78
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
79
|
+
# Draws a "divider" line for the current frame style
|
80
|
+
#
|
81
|
+
# ==== Attributes
|
82
|
+
#
|
83
|
+
# * +text+ - (required) the text/title to output in the frame
|
84
|
+
#
|
85
|
+
# ==== Options
|
86
|
+
#
|
87
|
+
# * +:color+ - (required) The color of the frame.
|
88
|
+
#
|
89
|
+
sig { abstract.params(text: String, color: CLI::UI::Color).returns(String) }
|
90
|
+
def divider(text, color:); end
|
64
91
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
# * +text+ - (required) the text/title to output in the frame
|
70
|
-
#
|
71
|
-
# ==== Options
|
72
|
-
#
|
73
|
-
# * +:color+ - (required) The color of the frame.
|
74
|
-
#
|
75
|
-
def open(text, color:)
|
76
|
-
raise NotImplementedError
|
77
|
-
end
|
92
|
+
sig { params(x: Integer, str: String).returns(String) }
|
93
|
+
def print_at_x(x, str)
|
94
|
+
CLI::UI::ANSI.cursor_horizontal_absolute(1 + x) + str
|
95
|
+
end
|
78
96
|
|
79
|
-
|
80
|
-
|
81
|
-
# ==== Attributes
|
82
|
-
#
|
83
|
-
# * +text+ - (required) the text/title to output in the frame
|
84
|
-
#
|
85
|
-
# ==== Options
|
86
|
-
#
|
87
|
-
# * +:color+ - (required) The color of the frame.
|
88
|
-
# * +:right_text+ - Text to print at the right of the line. Defaults to nil
|
89
|
-
#
|
90
|
-
def close(text, color:, right_text: nil)
|
91
|
-
raise NotImplementedError
|
92
|
-
end
|
97
|
+
class InvalidFrameStyleName < ArgumentError
|
98
|
+
extend T::Sig
|
93
99
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
# * +text+ - (required) the text/title to output in the frame
|
99
|
-
#
|
100
|
-
# ==== Options
|
101
|
-
#
|
102
|
-
# * +:color+ - (required) The color of the frame.
|
103
|
-
#
|
104
|
-
def divider(text, color: nil)
|
105
|
-
raise NotImplementedError
|
100
|
+
sig { params(name: T.any(String, Symbol)).void }
|
101
|
+
def initialize(name)
|
102
|
+
super
|
103
|
+
@name = name
|
106
104
|
end
|
107
105
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
106
|
+
sig { returns(String) }
|
107
|
+
def message
|
108
|
+
keys = FrameStyle::MAP.keys.map(&:inspect).join(', ')
|
109
|
+
"invalid frame style: #{@name.inspect}" \
|
110
|
+
' -- must be one of CLI::UI::Frame::FrameStyle::MAP ' \
|
111
|
+
"(#{keys})"
|
112
112
|
end
|
113
113
|
end
|
114
114
|
end
|
115
115
|
end
|
116
116
|
end
|
117
117
|
end
|
118
|
-
|
119
|
-
require 'cli/ui/frame/frame_style/box'
|
120
|
-
require 'cli/ui/frame/frame_style/bracket'
|