cli-ui 1.5.0 → 2.0.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 +17 -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 +67 -11
- 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 -21
- data/.dependabot/config.yml +0 -8
- data/.github/CODEOWNERS +0 -1
- data/.github/probots.yml +0 -2
- data/.gitignore +0 -14
- data/.rubocop.yml +0 -41
- data/.travis.yml +0 -7
- data/Gemfile +0 -17
- data/Gemfile.lock +0 -60
- data/Rakefile +0 -20
- data/bin/console +0 -14
- data/cli-ui.gemspec +0 -27
- data/dev.yml +0 -14
@@ -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'
|
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
|
-
|
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:
|
76
|
-
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.
|
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 =
|
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 =
|
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
|
-
|
153
|
-
frame_style = CLI::UI.resolve_style(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:
|
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
|
-
|
192
|
-
frame_style = CLI::UI.resolve_style(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:
|
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
|
-
|
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
|
258
|
-
|
259
|
-
def
|
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
|
294
|
+
timing = Time.new
|
264
295
|
timing - start
|
265
296
|
end
|
266
297
|
end
|