gorails 0.1.2 → 0.1.3
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/CHANGELOG.md +14 -1
- data/Gemfile.lock +1 -1
- data/bin/update-deps +95 -0
- data/exe/gorails +3 -2
- data/lib/gorails/commands/railsbytes.rb +10 -10
- data/lib/gorails/commands.rb +1 -4
- data/lib/gorails/version.rb +1 -1
- data/lib/gorails.rb +11 -20
- data/vendor/deps/cli-kit/REVISION +1 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/definition.rb +301 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/evaluation.rb +237 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/parser/node.rb +131 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/parser.rb +128 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args/tokenizer.rb +132 -0
- data/vendor/deps/cli-kit/lib/cli/kit/args.rb +15 -0
- data/vendor/deps/cli-kit/lib/cli/kit/base_command.rb +29 -0
- data/vendor/deps/cli-kit/lib/cli/kit/command_help.rb +256 -0
- data/vendor/deps/cli-kit/lib/cli/kit/command_registry.rb +141 -0
- data/vendor/deps/cli-kit/lib/cli/kit/config.rb +137 -0
- data/vendor/deps/cli-kit/lib/cli/kit/core_ext.rb +30 -0
- data/vendor/deps/cli-kit/lib/cli/kit/error_handler.rb +165 -0
- data/vendor/deps/cli-kit/lib/cli/kit/executor.rb +99 -0
- data/vendor/deps/cli-kit/lib/cli/kit/ini.rb +94 -0
- data/vendor/deps/cli-kit/lib/cli/kit/levenshtein.rb +89 -0
- data/vendor/deps/cli-kit/lib/cli/kit/logger.rb +95 -0
- data/vendor/deps/cli-kit/lib/cli/kit/opts.rb +284 -0
- data/vendor/deps/cli-kit/lib/cli/kit/resolver.rb +67 -0
- data/vendor/deps/cli-kit/lib/cli/kit/sorbet_runtime_stub.rb +142 -0
- data/vendor/deps/cli-kit/lib/cli/kit/support/test_helper.rb +253 -0
- data/vendor/deps/cli-kit/lib/cli/kit/support.rb +10 -0
- data/vendor/deps/cli-kit/lib/cli/kit/system.rb +350 -0
- data/vendor/deps/cli-kit/lib/cli/kit/util.rb +133 -0
- data/vendor/deps/cli-kit/lib/cli/kit/version.rb +7 -0
- data/vendor/deps/cli-kit/lib/cli/kit.rb +151 -0
- data/vendor/deps/cli-ui/REVISION +1 -0
- data/vendor/deps/cli-ui/lib/cli/ui/ansi.rb +180 -0
- data/vendor/deps/cli-ui/lib/cli/ui/color.rb +98 -0
- data/vendor/deps/cli-ui/lib/cli/ui/formatter.rb +216 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_stack.rb +116 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/box.rb +176 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style/bracket.rb +149 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame/frame_style.rb +112 -0
- data/vendor/deps/cli-ui/lib/cli/ui/frame.rb +300 -0
- data/vendor/deps/cli-ui/lib/cli/ui/glyph.rb +92 -0
- data/vendor/deps/cli-ui/lib/cli/ui/os.rb +58 -0
- data/vendor/deps/cli-ui/lib/cli/ui/printer.rb +72 -0
- data/vendor/deps/cli-ui/lib/cli/ui/progress.rb +102 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/interactive_options.rb +534 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt/options_handler.rb +36 -0
- data/vendor/deps/cli-ui/lib/cli/ui/prompt.rb +354 -0
- data/vendor/deps/cli-ui/lib/cli/ui/sorbet_runtime_stub.rb +143 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/async.rb +46 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner/spin_group.rb +292 -0
- data/vendor/deps/cli-ui/lib/cli/ui/spinner.rb +82 -0
- data/vendor/deps/cli-ui/lib/cli/ui/stdout_router.rb +264 -0
- data/vendor/deps/cli-ui/lib/cli/ui/terminal.rb +53 -0
- data/vendor/deps/cli-ui/lib/cli/ui/truncater.rb +107 -0
- data/vendor/deps/cli-ui/lib/cli/ui/version.rb +6 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets/base.rb +37 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets/status.rb +75 -0
- data/vendor/deps/cli-ui/lib/cli/ui/widgets.rb +91 -0
- data/vendor/deps/cli-ui/lib/cli/ui/wrap.rb +63 -0
- data/vendor/deps/cli-ui/lib/cli/ui.rb +356 -0
- metadata +57 -1
@@ -0,0 +1,112 @@
|
|
1
|
+
# typed: true
|
2
|
+
require 'cli/ui/frame'
|
3
|
+
|
4
|
+
module CLI
|
5
|
+
module UI
|
6
|
+
module Frame
|
7
|
+
module FrameStyle
|
8
|
+
include Kernel
|
9
|
+
extend T::Sig
|
10
|
+
extend T::Helpers
|
11
|
+
abstract!
|
12
|
+
|
13
|
+
autoload(:Box, 'cli/ui/frame/frame_style/box')
|
14
|
+
autoload(:Bracket, 'cli/ui/frame/frame_style/bracket')
|
15
|
+
|
16
|
+
MAP = {
|
17
|
+
box: -> { FrameStyle::Box },
|
18
|
+
bracket: -> { FrameStyle::Bracket },
|
19
|
+
}
|
20
|
+
|
21
|
+
# Lookup a frame style via its name
|
22
|
+
#
|
23
|
+
# ==== Attributes
|
24
|
+
#
|
25
|
+
# * +symbol+ - frame style name to lookup
|
26
|
+
sig { params(name: T.any(String, Symbol)).returns(FrameStyle) }
|
27
|
+
def self.lookup(name)
|
28
|
+
MAP.fetch(name.to_sym).call
|
29
|
+
rescue KeyError
|
30
|
+
raise(InvalidFrameStyleName, name)
|
31
|
+
end
|
32
|
+
|
33
|
+
sig { abstract.returns(Symbol) }
|
34
|
+
def style_name; end
|
35
|
+
|
36
|
+
# Returns the character(s) that should be printed at the beginning
|
37
|
+
# of lines inside this frame
|
38
|
+
sig { abstract.returns(String) }
|
39
|
+
def prefix; end
|
40
|
+
|
41
|
+
# Returns the printing width of the prefix
|
42
|
+
sig { returns(Integer) }
|
43
|
+
def prefix_width
|
44
|
+
CLI::UI::ANSI.printing_width(prefix)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Draws the "Open" line for this frame style
|
48
|
+
#
|
49
|
+
# ==== Attributes
|
50
|
+
#
|
51
|
+
# * +text+ - (required) the text/title to output in the frame
|
52
|
+
#
|
53
|
+
# ==== Options
|
54
|
+
#
|
55
|
+
# * +:color+ - (required) The color of the frame.
|
56
|
+
#
|
57
|
+
sig { abstract.params(text: String, color: CLI::UI::Color).returns(String) }
|
58
|
+
def start(text, color:); end
|
59
|
+
|
60
|
+
# Draws the "Close" line for this frame style
|
61
|
+
#
|
62
|
+
# ==== Attributes
|
63
|
+
#
|
64
|
+
# * +text+ - (required) the text/title to output in the frame
|
65
|
+
#
|
66
|
+
# ==== Options
|
67
|
+
#
|
68
|
+
# * +:color+ - (required) The color of the frame.
|
69
|
+
# * +:right_text+ - Text to print at the right of the line. Defaults to nil
|
70
|
+
#
|
71
|
+
sig { abstract.params(text: String, color: CLI::UI::Color, right_text: T.nilable(String)).returns(String) }
|
72
|
+
def close(text, color:, right_text: nil); end
|
73
|
+
|
74
|
+
# Draws a "divider" line for the current frame style
|
75
|
+
#
|
76
|
+
# ==== Attributes
|
77
|
+
#
|
78
|
+
# * +text+ - (required) the text/title to output in the frame
|
79
|
+
#
|
80
|
+
# ==== Options
|
81
|
+
#
|
82
|
+
# * +:color+ - (required) The color of the frame.
|
83
|
+
#
|
84
|
+
sig { abstract.params(text: String, color: CLI::UI::Color).returns(String) }
|
85
|
+
def divider(text, color:); end
|
86
|
+
|
87
|
+
sig { params(x: Integer, str: String).returns(String) }
|
88
|
+
def print_at_x(x, str)
|
89
|
+
CLI::UI::ANSI.cursor_horizontal_absolute(1 + x) + str
|
90
|
+
end
|
91
|
+
|
92
|
+
class InvalidFrameStyleName < ArgumentError
|
93
|
+
extend T::Sig
|
94
|
+
|
95
|
+
sig { params(name: T.any(String, Symbol)).void }
|
96
|
+
def initialize(name)
|
97
|
+
super
|
98
|
+
@name = name
|
99
|
+
end
|
100
|
+
|
101
|
+
sig { returns(String) }
|
102
|
+
def message
|
103
|
+
keys = FrameStyle::MAP.keys.map(&:inspect).join(', ')
|
104
|
+
"invalid frame style: #{@name.inspect}" \
|
105
|
+
' -- must be one of CLI::UI::Frame::FrameStyle::MAP ' \
|
106
|
+
"(#{keys})"
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,300 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
# typed: true
|
4
|
+
|
5
|
+
require 'cli/ui'
|
6
|
+
require 'cli/ui/frame/frame_stack'
|
7
|
+
require 'cli/ui/frame/frame_style'
|
8
|
+
|
9
|
+
module CLI
|
10
|
+
module UI
|
11
|
+
module Frame
|
12
|
+
class UnnestedFrameException < StandardError; end
|
13
|
+
DEFAULT_FRAME_COLOR = CLI::UI.resolve_color(:cyan)
|
14
|
+
|
15
|
+
class << self
|
16
|
+
extend T::Sig
|
17
|
+
|
18
|
+
sig { returns(FrameStyle) }
|
19
|
+
def frame_style
|
20
|
+
@frame_style ||= FrameStyle::Box
|
21
|
+
end
|
22
|
+
|
23
|
+
# Set the default frame style.
|
24
|
+
#
|
25
|
+
# Raises ArgumentError if +frame_style+ is not valid
|
26
|
+
#
|
27
|
+
# ==== Attributes
|
28
|
+
#
|
29
|
+
# * +symbol+ or +FrameStyle+ - the default frame style to use for frames
|
30
|
+
#
|
31
|
+
sig { params(frame_style: FrameStylable).void }
|
32
|
+
def frame_style=(frame_style)
|
33
|
+
@frame_style = CLI::UI.resolve_style(frame_style)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Opens a new frame. Can be nested
|
37
|
+
# Can be invoked in two ways: block and blockless
|
38
|
+
# * In block form, the frame is closed automatically when the block returns
|
39
|
+
# * In blockless form, caller MUST call +Frame.close+ when the frame is logically done
|
40
|
+
# * Blockless form is strongly discouraged in cases where block form can be made to work
|
41
|
+
#
|
42
|
+
# https://user-images.githubusercontent.com/3074765/33799861-cb5dcb5c-dd01-11e7-977e-6fad38cee08c.png
|
43
|
+
#
|
44
|
+
# The return value of the block determines if the block is a "success" or a "failure"
|
45
|
+
#
|
46
|
+
# ==== Attributes
|
47
|
+
#
|
48
|
+
# * +text+ - (required) the text/title to output in the frame
|
49
|
+
#
|
50
|
+
# ==== Options
|
51
|
+
#
|
52
|
+
# * +:color+ - The color of the frame. Defaults to +DEFAULT_FRAME_COLOR+
|
53
|
+
# * +:failure_text+ - If the block failed, what do we output? Defaults to nil
|
54
|
+
# * +:success_text+ - If the block succeeds, what do we output? Defaults to nil
|
55
|
+
# * +:timing+ - How long did the frame content take? Invalid for blockless. Defaults to true for the block form
|
56
|
+
# * +frame_style+ - The frame style to use for this frame
|
57
|
+
#
|
58
|
+
# ==== Example
|
59
|
+
#
|
60
|
+
# ===== Block Form (Assumes +CLI::UI::StdoutRouter.enable+ has been called)
|
61
|
+
#
|
62
|
+
# CLI::UI::Frame.open('Open') { puts 'hi' }
|
63
|
+
#
|
64
|
+
# Default Output:
|
65
|
+
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
66
|
+
# ┃ hi
|
67
|
+
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ (0.0s) ━━
|
68
|
+
#
|
69
|
+
# ===== Blockless Form
|
70
|
+
#
|
71
|
+
# CLI::UI::Frame.open('Open')
|
72
|
+
#
|
73
|
+
# Default Output:
|
74
|
+
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
75
|
+
#
|
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
|
88
|
+
def open(
|
89
|
+
text,
|
90
|
+
color: DEFAULT_FRAME_COLOR,
|
91
|
+
failure_text: nil,
|
92
|
+
success_text: nil,
|
93
|
+
timing: block_given?,
|
94
|
+
frame_style: self.frame_style,
|
95
|
+
&block
|
96
|
+
)
|
97
|
+
frame_style = CLI::UI.resolve_style(frame_style)
|
98
|
+
color = CLI::UI.resolve_color(color)
|
99
|
+
|
100
|
+
unless block_given?
|
101
|
+
if failure_text
|
102
|
+
raise ArgumentError, 'failure_text is not compatible with blockless invocation'
|
103
|
+
elsif success_text
|
104
|
+
raise ArgumentError, 'success_text is not compatible with blockless invocation'
|
105
|
+
elsif timing
|
106
|
+
raise ArgumentError, 'timing is not compatible with blockless invocation'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
t_start = Time.now
|
111
|
+
CLI::UI.raw do
|
112
|
+
print(prefix.chop)
|
113
|
+
puts frame_style.start(text, color: color)
|
114
|
+
end
|
115
|
+
FrameStack.push(color: color, style: frame_style)
|
116
|
+
|
117
|
+
return unless block_given?
|
118
|
+
|
119
|
+
closed = false
|
120
|
+
begin
|
121
|
+
success = false
|
122
|
+
success = yield
|
123
|
+
rescue
|
124
|
+
closed = true
|
125
|
+
t_diff = elapsed(t_start, timing)
|
126
|
+
close(failure_text, color: :red, elapsed: t_diff)
|
127
|
+
raise
|
128
|
+
else
|
129
|
+
success
|
130
|
+
ensure
|
131
|
+
unless closed
|
132
|
+
t_diff = elapsed(t_start, timing)
|
133
|
+
if T.unsafe(success) != false
|
134
|
+
close(success_text, color: color, elapsed: t_diff)
|
135
|
+
else
|
136
|
+
close(failure_text, color: :red, elapsed: t_diff)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
# Adds a divider in a frame
|
143
|
+
# Used to separate information within a single frame
|
144
|
+
#
|
145
|
+
# ==== Attributes
|
146
|
+
#
|
147
|
+
# * +text+ - (required) the text/title to output in the frame
|
148
|
+
#
|
149
|
+
# ==== Options
|
150
|
+
#
|
151
|
+
# * +:color+ - The color of the frame. Defaults to +DEFAULT_FRAME_COLOR+
|
152
|
+
# * +frame_style+ - The frame style to use for this frame
|
153
|
+
#
|
154
|
+
# ==== Example
|
155
|
+
#
|
156
|
+
# CLI::UI::Frame.open('Open') { CLI::UI::Frame.divider('Divider') }
|
157
|
+
#
|
158
|
+
# Default Output:
|
159
|
+
# ┏━━ Open ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
160
|
+
# ┣━━ Divider ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
161
|
+
# ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
162
|
+
#
|
163
|
+
# ==== Raises
|
164
|
+
#
|
165
|
+
# MUST be inside an open frame or it raises a +UnnestedFrameException+
|
166
|
+
#
|
167
|
+
sig { params(text: T.nilable(String), color: T.nilable(Colorable), frame_style: T.nilable(FrameStylable)).void }
|
168
|
+
def divider(text, color: nil, frame_style: nil)
|
169
|
+
fs_item = FrameStack.pop
|
170
|
+
raise UnnestedFrameException, 'No frame nesting to unnest' unless fs_item
|
171
|
+
|
172
|
+
divider_color = CLI::UI.resolve_color(color || fs_item.color)
|
173
|
+
frame_style = CLI::UI.resolve_style(frame_style || fs_item.frame_style)
|
174
|
+
|
175
|
+
CLI::UI.raw do
|
176
|
+
print(prefix.chop)
|
177
|
+
puts frame_style.divider(text.to_s, color: divider_color)
|
178
|
+
end
|
179
|
+
|
180
|
+
FrameStack.push(fs_item)
|
181
|
+
end
|
182
|
+
|
183
|
+
# Closes a frame
|
184
|
+
# Automatically called for a block-form +open+
|
185
|
+
#
|
186
|
+
# ==== Attributes
|
187
|
+
#
|
188
|
+
# * +text+ - (required) the text/title to output in the frame
|
189
|
+
#
|
190
|
+
# ==== Options
|
191
|
+
#
|
192
|
+
# * +:color+ - The color of the frame. Defaults to nil
|
193
|
+
# * +:elapsed+ - How long did the frame take? Defaults to nil
|
194
|
+
# * +frame_style+ - The frame style to use for this frame. Defaults to nil
|
195
|
+
#
|
196
|
+
# ==== Example
|
197
|
+
#
|
198
|
+
# CLI::UI::Frame.close('Close')
|
199
|
+
#
|
200
|
+
# Default Output:
|
201
|
+
# ┗━━ Close ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
202
|
+
#
|
203
|
+
# ==== Raises
|
204
|
+
#
|
205
|
+
# MUST be inside an open frame or it raises a +UnnestedFrameException+
|
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
|
215
|
+
def close(text, color: nil, elapsed: nil, frame_style: nil)
|
216
|
+
fs_item = FrameStack.pop
|
217
|
+
raise UnnestedFrameException, 'No frame nesting to unnest' unless fs_item
|
218
|
+
|
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
|
222
|
+
|
223
|
+
CLI::UI.raw do
|
224
|
+
print(prefix.chop)
|
225
|
+
puts frame_style.close(text.to_s, color: close_color, right_text: elapsed_string)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# Determines the prefix of a frame entry taking multi-nested frames into account
|
230
|
+
#
|
231
|
+
# ==== Options
|
232
|
+
#
|
233
|
+
# * +:color+ - The color of the prefix. Defaults to +Thread.current[:cliui_frame_color_override]+
|
234
|
+
#
|
235
|
+
sig { params(color: T.nilable(Colorable)).returns(String) }
|
236
|
+
def prefix(color: Thread.current[:cliui_frame_color_override])
|
237
|
+
+''.tap do |output|
|
238
|
+
items = FrameStack.items
|
239
|
+
|
240
|
+
items[0..-2].to_a.each do |item|
|
241
|
+
output << item.color.code << item.frame_style.prefix
|
242
|
+
end
|
243
|
+
|
244
|
+
if (item = items.last)
|
245
|
+
final_color = color || item.color
|
246
|
+
output << CLI::UI.resolve_color(final_color).code \
|
247
|
+
<< item.frame_style.prefix \
|
248
|
+
<< ' ' \
|
249
|
+
<< CLI::UI::Color::RESET.code
|
250
|
+
end
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# The width of a prefix given the number of Frames in the stack
|
255
|
+
sig { returns(Integer) }
|
256
|
+
def prefix_width
|
257
|
+
w = FrameStack.items.reduce(0) do |width, item|
|
258
|
+
width + item.frame_style.prefix_width
|
259
|
+
end
|
260
|
+
|
261
|
+
w.zero? ? w : w + 1
|
262
|
+
end
|
263
|
+
|
264
|
+
# Override a color for a given thread.
|
265
|
+
#
|
266
|
+
# ==== Attributes
|
267
|
+
#
|
268
|
+
# * +color+ - The color to override to
|
269
|
+
#
|
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)
|
276
|
+
prev = Thread.current[:cliui_frame_color_override]
|
277
|
+
Thread.current[:cliui_frame_color_override] = color
|
278
|
+
yield
|
279
|
+
ensure
|
280
|
+
Thread.current[:cliui_frame_color_override] = prev
|
281
|
+
end
|
282
|
+
|
283
|
+
private
|
284
|
+
|
285
|
+
# If timing is:
|
286
|
+
# Numeric: return it
|
287
|
+
# false: return nil
|
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)
|
291
|
+
return timing if timing.is_a?(Numeric)
|
292
|
+
return if timing.is_a?(FalseClass)
|
293
|
+
|
294
|
+
timing = Time.new
|
295
|
+
timing - start
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# typed: true
|
2
|
+
require 'cli/ui'
|
3
|
+
|
4
|
+
module CLI
|
5
|
+
module UI
|
6
|
+
class Glyph
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
class InvalidGlyphHandle < ArgumentError
|
10
|
+
extend T::Sig
|
11
|
+
|
12
|
+
sig { params(handle: String).void }
|
13
|
+
def initialize(handle)
|
14
|
+
super
|
15
|
+
@handle = handle
|
16
|
+
end
|
17
|
+
|
18
|
+
sig { returns(String) }
|
19
|
+
def message
|
20
|
+
keys = Glyph.available.join(',')
|
21
|
+
"invalid glyph handle: #{@handle} " \
|
22
|
+
"-- must be one of CLI::UI::Glyph.available (#{keys})"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { returns(String) }
|
27
|
+
attr_reader :handle, :to_s, :fmt, :char
|
28
|
+
|
29
|
+
sig { returns(T.any(Integer, T::Array[Integer])) }
|
30
|
+
attr_reader :codepoint
|
31
|
+
|
32
|
+
sig { returns(Color) }
|
33
|
+
attr_reader :color
|
34
|
+
|
35
|
+
# Creates a new glyph
|
36
|
+
#
|
37
|
+
# ==== Attributes
|
38
|
+
#
|
39
|
+
# * +handle+ - The handle in the +MAP+ constant
|
40
|
+
# * +codepoint+ - The codepoint used to create the glyph (e.g. +0x2717+ for a ballot X)
|
41
|
+
# * +plain+ - A fallback plain string to be used in case glyphs are disabled
|
42
|
+
# * +color+ - What color to output the glyph. Check +CLI::UI::Color+ for options.
|
43
|
+
#
|
44
|
+
sig { params(handle: String, codepoint: T.any(Integer, T::Array[Integer]), plain: String, color: Color).void }
|
45
|
+
def initialize(handle, codepoint, plain, color)
|
46
|
+
@handle = handle
|
47
|
+
@codepoint = codepoint
|
48
|
+
@color = color
|
49
|
+
@char = CLI::UI::OS.current.use_emoji? ? Array(codepoint).pack('U*') : plain
|
50
|
+
@to_s = color.code + @char + Color::RESET.code
|
51
|
+
@fmt = "{{#{color.name}:#{@char}}}"
|
52
|
+
|
53
|
+
MAP[handle] = self
|
54
|
+
end
|
55
|
+
|
56
|
+
# Mapping of glyphs to terminal output
|
57
|
+
MAP = {}
|
58
|
+
STAR = new('*', 0x2b51, '*', Color::YELLOW) # YELLOW SMALL STAR (⭑)
|
59
|
+
INFO = new('i', 0x1d4be, 'i', Color::BLUE) # BLUE MATHEMATICAL SCRIPT SMALL i (𝒾)
|
60
|
+
QUESTION = new('?', 0x003f, '?', Color::BLUE) # BLUE QUESTION MARK (?)
|
61
|
+
CHECK = new('v', 0x2713, '√', Color::GREEN) # GREEN CHECK MARK (✓)
|
62
|
+
X = new('x', 0x2717, 'X', Color::RED) # RED BALLOT X (✗)
|
63
|
+
BUG = new('b', 0x1f41b, '!', Color::WHITE) # Bug emoji (🐛)
|
64
|
+
CHEVRON = new('>', 0xbb, '»', Color::YELLOW) # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK (»)
|
65
|
+
HOURGLASS = new('H', [0x231b, 0xfe0e], 'H', Color::BLUE) # HOURGLASS + VARIATION SELECTOR 15 (⌛︎)
|
66
|
+
WARNING = new('!', [0x26a0, 0xfe0f], '!', Color::YELLOW) # WARNING SIGN + VARIATION SELECTOR 16 (⚠️ )
|
67
|
+
|
68
|
+
# Looks up a glyph by name
|
69
|
+
#
|
70
|
+
# ==== Raises
|
71
|
+
# Raises a InvalidGlyphHandle if the glyph is not available
|
72
|
+
# You likely need to create it with +.new+ or you made a typo
|
73
|
+
#
|
74
|
+
# ==== Returns
|
75
|
+
# Returns a terminal output-capable string
|
76
|
+
#
|
77
|
+
sig { params(name: String).returns(Glyph) }
|
78
|
+
def self.lookup(name)
|
79
|
+
MAP.fetch(name.to_s)
|
80
|
+
rescue KeyError
|
81
|
+
raise InvalidGlyphHandle, name
|
82
|
+
end
|
83
|
+
|
84
|
+
# All available glyphs by name
|
85
|
+
#
|
86
|
+
sig { returns(T::Array[String]) }
|
87
|
+
def self.available
|
88
|
+
MAP.keys
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# typed: true
|
2
|
+
require 'rbconfig'
|
3
|
+
|
4
|
+
module CLI
|
5
|
+
module UI
|
6
|
+
class OS
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { params(emoji: T::Boolean, color_prompt: T::Boolean, arrow_keys: T::Boolean, shift_cursor: T::Boolean).void }
|
10
|
+
def initialize(emoji: true, color_prompt: true, arrow_keys: true, shift_cursor: false)
|
11
|
+
@emoji = emoji
|
12
|
+
@color_prompt = color_prompt
|
13
|
+
@arrow_keys = arrow_keys
|
14
|
+
@shift_cursor = shift_cursor
|
15
|
+
end
|
16
|
+
|
17
|
+
sig { returns(T::Boolean) }
|
18
|
+
def use_emoji?
|
19
|
+
@emoji
|
20
|
+
end
|
21
|
+
|
22
|
+
sig { returns(T::Boolean) }
|
23
|
+
def use_color_prompt?
|
24
|
+
@color_prompt
|
25
|
+
end
|
26
|
+
|
27
|
+
sig { returns(T::Boolean) }
|
28
|
+
def suggest_arrow_keys?
|
29
|
+
@arrow_keys
|
30
|
+
end
|
31
|
+
|
32
|
+
sig { returns(T::Boolean) }
|
33
|
+
def shift_cursor_back_on_horizontal_absolute?
|
34
|
+
@shift_cursor
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { returns(OS) }
|
38
|
+
def self.current
|
39
|
+
@current_os ||= case RbConfig::CONFIG['host_os']
|
40
|
+
when /darwin/
|
41
|
+
MAC
|
42
|
+
when /linux/
|
43
|
+
LINUX
|
44
|
+
else
|
45
|
+
if RUBY_PLATFORM !~ /cygwin/ && ENV['OS'] == 'Windows_NT'
|
46
|
+
WINDOWS
|
47
|
+
else
|
48
|
+
raise "Could not determine OS from host_os #{RbConfig::CONFIG["host_os"]}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
MAC = OS.new
|
54
|
+
LINUX = OS.new
|
55
|
+
WINDOWS = OS.new(emoji: false, color_prompt: false, arrow_keys: false, shift_cursor: true)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# typed: true
|
2
|
+
require 'cli/ui'
|
3
|
+
|
4
|
+
module CLI
|
5
|
+
module UI
|
6
|
+
class Printer
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
# Print a message to a stream with common utilities.
|
10
|
+
# Allows overriding the color, encoding, and target stream.
|
11
|
+
# By default, it formats the string using CLI:UI and rescues common stream errors.
|
12
|
+
#
|
13
|
+
# ==== Attributes
|
14
|
+
#
|
15
|
+
# * +msg+ - (required) the string to output. Can be frozen.
|
16
|
+
#
|
17
|
+
# ==== Options
|
18
|
+
#
|
19
|
+
# * +:frame_color+ - Override the frame color. Defaults to nil.
|
20
|
+
# * +:to+ - Target stream, like $stdout or $stderr. Can be anything with a puts method. Defaults to $stdout.
|
21
|
+
# * +:encoding+ - Force the output to be in a certain encoding. Defaults to UTF-8.
|
22
|
+
# * +:format+ - Whether to format the string using CLI::UI.fmt. Defaults to true.
|
23
|
+
# * +:graceful+ - Whether to gracefully ignore common I/O errors. Defaults to true.
|
24
|
+
# * +:wrap+ - Whether to wrap text at word boundaries to terminal width. Defaults to true.
|
25
|
+
#
|
26
|
+
# ==== Returns
|
27
|
+
# Returns whether the message was successfully printed,
|
28
|
+
# which can be useful if +:graceful+ is set to true.
|
29
|
+
#
|
30
|
+
# ==== Example
|
31
|
+
#
|
32
|
+
# CLI::UI::Printer.puts('{{x}} Ouch', to: $stderr)
|
33
|
+
#
|
34
|
+
sig do
|
35
|
+
params(
|
36
|
+
msg: String,
|
37
|
+
frame_color: T.nilable(Colorable),
|
38
|
+
to: IOLike,
|
39
|
+
encoding: T.nilable(Encoding),
|
40
|
+
format: T::Boolean,
|
41
|
+
graceful: T::Boolean,
|
42
|
+
wrap: T::Boolean
|
43
|
+
).returns(T::Boolean)
|
44
|
+
end
|
45
|
+
def self.puts(
|
46
|
+
msg,
|
47
|
+
frame_color: nil,
|
48
|
+
to: $stdout,
|
49
|
+
encoding: Encoding::UTF_8,
|
50
|
+
format: true,
|
51
|
+
graceful: true,
|
52
|
+
wrap: true
|
53
|
+
)
|
54
|
+
msg = (+msg).force_encoding(encoding) if encoding
|
55
|
+
msg = CLI::UI.fmt(msg) if format
|
56
|
+
msg = CLI::UI.wrap(msg) if wrap
|
57
|
+
|
58
|
+
if frame_color
|
59
|
+
CLI::UI::Frame.with_frame_color_override(frame_color) { to.puts(msg) }
|
60
|
+
else
|
61
|
+
to.puts(msg)
|
62
|
+
end
|
63
|
+
|
64
|
+
true
|
65
|
+
rescue Errno::EIO, Errno::EPIPE, IOError => e
|
66
|
+
raise(e) unless graceful
|
67
|
+
|
68
|
+
false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|