terminal_rb 1.0.8 → 1.0.9
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 +2 -2
- data/examples/bbcode.rb +13 -13
- data/lib/terminal/ansi.rb +167 -109
- data/lib/terminal/input/dumb.rb +2 -2
- data/lib/terminal/input/key_event.rb +4 -4
- data/lib/terminal/input.rb +2 -3
- data/lib/terminal/output/ansi.rb +9 -14
- data/lib/terminal/output/dumb.rb +3 -9
- data/lib/terminal/output.rb +2 -3
- data/lib/terminal/rspec/helper.rb +5 -4
- data/lib/terminal/text/char_width.rb +44 -21
- data/lib/terminal/text/formatter.rb +107 -29
- data/lib/terminal/text.rb +22 -5
- data/lib/terminal/version.rb +1 -1
- data/lib/terminal.rb +38 -13
- metadata +3 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8a5e3a8b375cd45eef93b2316251d14da8732b80e27fb1192bbf8fb87a9bf297
|
|
4
|
+
data.tar.gz: f10bcbf23b3e8ecd01bc51a8772f2099dbe5950bb561bc26d854e6c3d2fb382d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a3c0cbfe105fccd17923c6ba7f43faf797b2df1fcc5d76d8bf138b1593156c5e23dc2a9700be17fa61e8f744151bb756943a482f9eca501087ffabb257e7e608
|
|
7
|
+
data.tar.gz: 60616eb76bbc7d01e1759274220390611887bbafe12f2d3b7cda8eb68dcd6cee491ce6b477235ce9a8b5e43b372e6d3e6c7a8ff81962a67eb78a20cb58fe7523
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Terminal.rb v1.0.
|
|
1
|
+
# Terminal.rb v1.0.9
|
|
2
2
|
|
|
3
3
|
Terminal.rb supports you with input and output on your terminal. Simple [BBCode](https://en.wikipedia.org/wiki/BBCode)-like markup for attributes and coloring, word-wise line breaks, correct special key recognition and mouse event reporting enable you to implement your CLI app quickly and easily.
|
|
4
4
|
|
|
5
5
|
- Gem: [rubygems.org](https://rubygems.org/gems/terminal_rb)
|
|
6
6
|
- Source: [codeberg.org](https://codeberg.org/mblumtritt/Terminal.rb)
|
|
7
|
-
- Help: [rubydoc.info](https://rubydoc.info/gems/terminal_rb/1.0.
|
|
7
|
+
- Help: [rubydoc.info](https://rubydoc.info/gems/terminal_rb/1.0.9/Terminal)
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
data/examples/bbcode.rb
CHANGED
|
@@ -6,28 +6,28 @@ Terminal.puts <<~TEXT
|
|
|
6
6
|
|
|
7
7
|
✅ [b bright_green]Terminal.rb::Ansi[/b] — BBCode:[/]
|
|
8
8
|
|
|
9
|
-
[b]Bold[/b] [\\b][d]...[/d][\\/b] [\\bold][d]
|
|
10
|
-
[dim]Dim[/dim] [\\d][d]...[/d][\\/d] [\\dim][d]
|
|
11
|
-
[i]Italic[/i] [\\i][d]...[/d][\\/i] [\\italic][d]
|
|
9
|
+
[b]Bold[/b] [\\b][d]...[/d][\\/b] [\\bold][d]........[/d][\\/bold]
|
|
10
|
+
[dim]Dim[/dim] [\\d][d]...[/d][\\/d] [\\dim][d].........[/d][\\/dim]
|
|
11
|
+
[i]Italic[/i] [\\i][d]...[/d][\\/i] [\\italic][d]......[/d][\\/italic]
|
|
12
12
|
[u]Underline[/u] [\\u][d]...[/d][\\/u] [\\underline][d]...[/d][\\/underline]
|
|
13
|
-
[h]Hide[/h] [\\h][d]...[/d][\\/h] [\\hide][d]
|
|
13
|
+
[h]Hide[/h] [\\h][d]...[/d][\\/h] [\\hide][d]........[/d][\\/hide]
|
|
14
14
|
|
|
15
|
-
[inv]Invert[/inv] [\\inv][d]
|
|
15
|
+
[inv]Invert[/inv] [\\inv][d]......[/d][\\/inv] [\\invert][d]...[/d][\\/invert]
|
|
16
16
|
[strike]Strike[/strike] [\\strike][d]...[/d][\\/strike]
|
|
17
|
-
[blink]Blink[/blink] [\\blink][d]
|
|
17
|
+
[blink]Blink[/blink] [\\blink][d]....[/d][\\/blink]
|
|
18
18
|
|
|
19
|
-
[u]Underline[/u] [\\u][d]
|
|
20
|
-
[uu]Double underline[/uu] [\\uu][d]
|
|
21
|
-
[cu]Curly underline[/cu] [\\cu][d]
|
|
19
|
+
[u]Underline[/u] [\\u][d].....[/d][\\/u] [\\underline][d]..........[/d][\\/underline]
|
|
20
|
+
[uu]Double underline[/uu] [\\uu][d]....[/d][\\/uu] [\\double_underline][d]...[/d][\\/double_underline]
|
|
21
|
+
[cu]Curly underline[/cu] [\\cu][d]....[/d][\\/cu] [\\curly_underline][d]...[/d] [\\/curly_underline]
|
|
22
22
|
[dau]Dashed underline[/dau] [\\dau][d]...[/d][\\/dau] [\\dashed_underline][d]...[/d][\\/dashed_underline]
|
|
23
23
|
[dou]Dotted underline[/dou] [\\dou][d]...[/d][\\/dou] [\\dotted_underline][d]...[/d][\\/dotted_underline]
|
|
24
24
|
|
|
25
|
-
[fraktur]Fraktur[/fraktur] [\\fraktur][d]
|
|
26
|
-
[framed]Framed[/framed] [\\framed][d]
|
|
25
|
+
[fraktur]Fraktur[/fraktur] [\\fraktur][d].....[/d][\\/fraktur]
|
|
26
|
+
[framed]Framed[/framed] [\\framed][d]......[/d][\\/framed]
|
|
27
27
|
[encircled]Encircled[/encircled] [\\encircled][d]...[/d][\\/encircled]
|
|
28
28
|
|
|
29
|
-
[ovr]Overlined[/ovr] [\\ovr][d]...[/d][\\/ovr] [\\overlined][d]
|
|
30
|
-
[sub]Subscript[/sub] [\\sub][d]...[/d][\\/sub] [\\subscript][d]
|
|
29
|
+
[ovr]Overlined[/ovr] [\\ovr][d]...[/d][\\/ovr] [\\overlined][d].....[/d][\\/overlined]
|
|
30
|
+
[sub]Subscript[/sub] [\\sub][d]...[/d][\\/sub] [\\subscript][d].....[/d][\\/subscript]
|
|
31
31
|
[sup]Superscript[/sup] [\\sup][d]...[/d][\\/sup] [\\superscript][d]...[/d][\\/superscript]
|
|
32
32
|
|
|
33
33
|
TEXT
|
data/lib/terminal/ansi.rb
CHANGED
|
@@ -16,7 +16,7 @@ module Terminal
|
|
|
16
16
|
# Terminal::Ansi[:on_blue] # => "\e[44m"
|
|
17
17
|
#
|
|
18
18
|
# @example BBCode to ANSI
|
|
19
|
-
# Terminal::Ansi.bbcode('[bold]Hello[/bold]') # => "\e[1mHello\e[
|
|
19
|
+
# Terminal::Ansi.bbcode('[bold]Hello[/bold]') # => "\e[1mHello\e[22m"
|
|
20
20
|
#
|
|
21
21
|
module Ansi
|
|
22
22
|
class << self
|
|
@@ -39,7 +39,7 @@ module Terminal
|
|
|
39
39
|
def named_colors = NAMED_COLORS.keys.map!(&:to_sym)
|
|
40
40
|
|
|
41
41
|
#
|
|
42
|
-
# @!group
|
|
42
|
+
# @!group Text Attribute Code Generation
|
|
43
43
|
#
|
|
44
44
|
|
|
45
45
|
# Generate an ANSI escape sequence from attribute names or color
|
|
@@ -67,20 +67,12 @@ module Terminal
|
|
|
67
67
|
"\e[#{
|
|
68
68
|
attributes
|
|
69
69
|
.map! do |arg|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
"38;5;#{arg}"
|
|
77
|
-
when (256..511)
|
|
78
|
-
"48;5;#{arg - 256}"
|
|
79
|
-
when (512..767)
|
|
80
|
-
"58;5;#{arg - 512}"
|
|
81
|
-
else
|
|
82
|
-
_invalid(arg)
|
|
83
|
-
end
|
|
70
|
+
next @attr_str_map[arg] || _invalid(arg) if arg.is_a?(String)
|
|
71
|
+
next @attr_sym_map[arg] || _invalid(arg) if arg.is_a?(Symbol)
|
|
72
|
+
_invalid(arg) unless arg.is_a?(Integer)
|
|
73
|
+
next "38;5;#{arg}" if arg >= 0 && arg <= 255
|
|
74
|
+
next "48;5;#{arg - 256}" if arg >= 256 && arg <= 511
|
|
75
|
+
arg >= 512 && arg <= 767 ? "58;5;#{arg - 512}" : _invalid(arg)
|
|
84
76
|
end
|
|
85
77
|
.join(';')
|
|
86
78
|
}m"
|
|
@@ -130,10 +122,7 @@ module Terminal
|
|
|
130
122
|
# attribute is invalid
|
|
131
123
|
def try_convert(attributes, separator: ' ')
|
|
132
124
|
return unless attributes
|
|
133
|
-
|
|
134
|
-
hit = cache[key = attributes.to_s] and return hit
|
|
135
|
-
cache.clear if cache.size > 1024 # cache limit
|
|
136
|
-
cache[key] = unless (attributes = key.split(separator)).empty?
|
|
125
|
+
unless (attributes = attributes.to_s.split(separator)).empty?
|
|
137
126
|
# attributes.uniq!
|
|
138
127
|
codes = attributes.map { @attr_str_map[it] || break }
|
|
139
128
|
"\e[#{codes.join(';')}m" if codes
|
|
@@ -168,7 +157,7 @@ module Terminal
|
|
|
168
157
|
#
|
|
169
158
|
# @!endgroup
|
|
170
159
|
#
|
|
171
|
-
# @!group
|
|
160
|
+
# @!group BBCode Generation
|
|
172
161
|
#
|
|
173
162
|
|
|
174
163
|
# Convert BBCode markup to ANSI escape codes.
|
|
@@ -191,11 +180,16 @@ module Terminal
|
|
|
191
180
|
def bbcode(str)
|
|
192
181
|
str = str.to_s
|
|
193
182
|
return str.dup unless str.index('[')
|
|
194
|
-
str.gsub(@re_bbcode) do |
|
|
195
|
-
|
|
196
|
-
next
|
|
197
|
-
|
|
198
|
-
|
|
183
|
+
str.gsub(@re_bbcode) do |match|
|
|
184
|
+
hit = @bbcc[match] and next hit
|
|
185
|
+
next "[#{match[2..]}" if (fc = match[1]) == '\\'
|
|
186
|
+
@bbcc.clear if @bbcc.size == 1024
|
|
187
|
+
@bbcc[match] = if fc == ']' || (att = match[1..-2].split).empty?
|
|
188
|
+
match
|
|
189
|
+
else
|
|
190
|
+
att = att.map { @attr_str_map[it] || break }
|
|
191
|
+
att ? "\e[#{att.join(';')}m" : match
|
|
192
|
+
end
|
|
199
193
|
end
|
|
200
194
|
end
|
|
201
195
|
|
|
@@ -211,15 +205,12 @@ module Terminal
|
|
|
211
205
|
def unbbcode(str)
|
|
212
206
|
str = str.to_s
|
|
213
207
|
return str.dup unless str.index('[')
|
|
214
|
-
str.gsub(@re_bbcode) do |
|
|
215
|
-
|
|
216
|
-
if match[
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
next match_str if (match = match.split).empty?
|
|
221
|
-
next if match.all? { @attr_str_map[it] }
|
|
222
|
-
match_str
|
|
208
|
+
str.gsub(@re_bbcode) do |match|
|
|
209
|
+
hit = @bbcc[match] and next hit[0] == '[' ? hit : nil
|
|
210
|
+
next match if (fc = match[1]) == ']'
|
|
211
|
+
next "[#{match[2..]}" if fc == '\\'
|
|
212
|
+
next match if (att = match[1..-2].split).empty?
|
|
213
|
+
match unless att.map { @attr_str_map[it] || break }
|
|
223
214
|
end
|
|
224
215
|
end
|
|
225
216
|
|
|
@@ -237,13 +228,11 @@ module Terminal
|
|
|
237
228
|
def escape_bbcode(str)
|
|
238
229
|
str = str.to_s
|
|
239
230
|
return str.dup unless str.index('[')
|
|
240
|
-
str.gsub(@re_bbcode) do |
|
|
241
|
-
|
|
242
|
-
next
|
|
243
|
-
match =
|
|
244
|
-
|
|
245
|
-
next "[\\#{match}]" if parts.all? { @attr_str_map[it] }
|
|
246
|
-
match_str
|
|
231
|
+
str.gsub(@re_bbcode) do |match|
|
|
232
|
+
hit = @bbcc[match] and hit[1] == '\\' ? hit : "[\\#{match[1..]}"
|
|
233
|
+
next match if (fc = match[1]) == '\\' || fc == ']'
|
|
234
|
+
next match if (att = match[1..-2].split).empty?
|
|
235
|
+
att.map { @attr_str_map[it] || break } ? "[\\#{match[1..]}" : match
|
|
247
236
|
end
|
|
248
237
|
end
|
|
249
238
|
|
|
@@ -274,14 +263,9 @@ module Terminal
|
|
|
274
263
|
# @return [true, false]
|
|
275
264
|
def valid?(*attributes)
|
|
276
265
|
attributes.all? do |arg|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
when Symbol
|
|
281
|
-
@attr_sym_map[arg]
|
|
282
|
-
when (0..767)
|
|
283
|
-
true
|
|
284
|
-
end
|
|
266
|
+
next @attr_str_map[arg] if arg.is_a?(String)
|
|
267
|
+
next @attr_sym_map[arg] if arg.is_a?(Symbol)
|
|
268
|
+
arg.is_a?(Integer) && arg >= 0 && arg <= 767
|
|
285
269
|
end
|
|
286
270
|
end
|
|
287
271
|
|
|
@@ -303,7 +287,7 @@ module Terminal
|
|
|
303
287
|
#
|
|
304
288
|
# @!endgroup
|
|
305
289
|
#
|
|
306
|
-
# @!group
|
|
290
|
+
# @!group Cursor and Screen Control Codes
|
|
307
291
|
#
|
|
308
292
|
|
|
309
293
|
# Move cursor up.
|
|
@@ -458,54 +442,26 @@ module Terminal
|
|
|
458
442
|
# @return (see .[])
|
|
459
443
|
def line_erase(part = :all) = "\e[#{@line_erase[part]}K"
|
|
460
444
|
|
|
461
|
-
# Set the terminal window title.
|
|
462
|
-
# @note Supported by Hyper, iTerm2, Kitty, macOS Terminal, Tabby,
|
|
463
|
-
# WezTerm.
|
|
464
|
-
#
|
|
465
|
-
# @example
|
|
466
|
-
# Terminal.raw_write(Terminal::Ansi.title('My App'))
|
|
467
|
-
#
|
|
468
|
-
# @param title [String] the title text
|
|
469
|
-
# @return (see .[])
|
|
470
|
-
def title(title) = "\e]0;#{title}\a"
|
|
471
|
-
|
|
472
|
-
# Start a hyperlink (OSC 8).
|
|
473
445
|
#
|
|
474
|
-
#
|
|
475
|
-
#
|
|
476
|
-
# @see .link
|
|
477
|
-
# @see .link_end
|
|
478
|
-
#
|
|
479
|
-
# @param url [#to_s] the link URL
|
|
480
|
-
# @param params [Hash] optional link parameters (e.g., `id:`)
|
|
481
|
-
# @return (see .[])
|
|
482
|
-
def link_start(url, **params)
|
|
483
|
-
"\e]8;#{params.map { it.join('=') }.join(':')};#{url}\a"
|
|
484
|
-
end
|
|
485
|
-
|
|
486
|
-
# End a hyperlink.
|
|
446
|
+
# @!endgroup
|
|
487
447
|
#
|
|
488
|
-
#
|
|
448
|
+
# @!group Terminal Integration Codes
|
|
489
449
|
#
|
|
490
|
-
# @return (see .[])
|
|
491
|
-
def link_end = +LINK_END
|
|
492
450
|
|
|
493
|
-
#
|
|
451
|
+
# Set the terminal window title.
|
|
494
452
|
#
|
|
495
|
-
# @
|
|
496
|
-
#
|
|
453
|
+
# @note Supported by iTerm2, Kitty, macOS Terminal, Tabby,
|
|
454
|
+
# WezTerm.
|
|
497
455
|
#
|
|
498
456
|
# @example
|
|
499
|
-
# Terminal::Ansi.
|
|
457
|
+
# Terminal.raw_write(Terminal::Ansi.title('My App'))
|
|
500
458
|
#
|
|
501
|
-
# @param
|
|
502
|
-
# @param text [String] the visible link text
|
|
459
|
+
# @param title [#to_s] the title text
|
|
503
460
|
# @return (see .[])
|
|
504
|
-
def
|
|
505
|
-
"#{link_start(url, **params)}#{text}#{LINK_END}"
|
|
506
|
-
end
|
|
461
|
+
def title(title) = "\e]0;#{_no_bell(title)}\a"
|
|
507
462
|
|
|
508
463
|
# Send a desktop notification via the terminal.
|
|
464
|
+
#
|
|
509
465
|
# @note Supported by Ghostty, iTerm2, Kitty, WezTerm.
|
|
510
466
|
#
|
|
511
467
|
# @example
|
|
@@ -513,7 +469,7 @@ module Terminal
|
|
|
513
469
|
#
|
|
514
470
|
# @param text [to_s] the notification text
|
|
515
471
|
# @return (see .[])
|
|
516
|
-
def notify(text) = "\e]9;#{text}\a"
|
|
472
|
+
def notify(text) = "\e]9;#{_no_bell(text)}\a"
|
|
517
473
|
|
|
518
474
|
# Show or update a progress indicator in the terminal tab/title
|
|
519
475
|
# bar.
|
|
@@ -550,10 +506,100 @@ module Terminal
|
|
|
550
506
|
"\e]9;4;#{state};#{percent.to_i.clamp(0, 100)}\a"
|
|
551
507
|
end
|
|
552
508
|
|
|
509
|
+
# @comment Should we implement this?
|
|
510
|
+
#
|
|
511
|
+
# # Request attention.
|
|
512
|
+
# #
|
|
513
|
+
# # @note Supported by iTerm2.
|
|
514
|
+
# #
|
|
515
|
+
# # @param action [true, :bounce, :once, :fireworks, false, :stop]
|
|
516
|
+
# # start bouncing dock icon (`true`, `:bounce`),
|
|
517
|
+
# # bounce dock icon once (`:once`)
|
|
518
|
+
# # or stop attention (`false`, `:stop`)
|
|
519
|
+
# # @return (see .[])
|
|
520
|
+
# def attention(action = :bounce)
|
|
521
|
+
# "\e]1337;RequestAttention=#{
|
|
522
|
+
# case action
|
|
523
|
+
# when true, :bounce
|
|
524
|
+
# 'yes'
|
|
525
|
+
# when :once, :fireworks
|
|
526
|
+
# action
|
|
527
|
+
# else
|
|
528
|
+
# 'no'
|
|
529
|
+
# end
|
|
530
|
+
# }\a"
|
|
531
|
+
# end
|
|
532
|
+
|
|
533
|
+
# Create or start a hyperlink.
|
|
534
|
+
#
|
|
535
|
+
# @note Supported by Ghostty, iTerm2, Kitty, Rio, Tabby, WezTerm.
|
|
536
|
+
#
|
|
537
|
+
# @see .link_end
|
|
538
|
+
#
|
|
539
|
+
# @example
|
|
540
|
+
# Terminal::Ansi.link('https://example.com', 'Example')
|
|
541
|
+
#
|
|
542
|
+
# @param url [#to_s] the link URL
|
|
543
|
+
# @param text [nil, #to_s] the visible link text
|
|
544
|
+
# @param params [Hash] optional link parameters (e.g., `id:`)
|
|
545
|
+
# @return (see .[])
|
|
546
|
+
def link(url, text = nil, **params)
|
|
547
|
+
start =
|
|
548
|
+
"\e]8;#{
|
|
549
|
+
_no_bell(params.map { it.join('=') }.join(':'))
|
|
550
|
+
};#{_no_bell(url)}\a"
|
|
551
|
+
text ? "#{start}#{_no_bell(text)}#{LINK_END}" : start
|
|
552
|
+
end
|
|
553
|
+
|
|
554
|
+
# End a hyperlink.
|
|
555
|
+
#
|
|
556
|
+
# @see .link
|
|
557
|
+
#
|
|
558
|
+
# @return (see .[])
|
|
559
|
+
def link_end = +LINK_END
|
|
560
|
+
|
|
561
|
+
# Copy text to clipboard.
|
|
562
|
+
#
|
|
563
|
+
# @note Supported by Ghostty, iTerm2, Kitty, Rio, Tabby, WezTerm.
|
|
564
|
+
#
|
|
565
|
+
# @param text [#to_s] text to copy
|
|
566
|
+
# @return (see .[])
|
|
567
|
+
def clipboard(text) = "\e]52;c;#{[text.to_s].pack('m0')}\a"
|
|
568
|
+
|
|
569
|
+
# Show or update the session status of the terminal.
|
|
570
|
+
#
|
|
571
|
+
# @note Supported by iTerm2.
|
|
572
|
+
#
|
|
573
|
+
# @example
|
|
574
|
+
# Terminal.raw_write(Terminal::Ansi.status(text: 'Working'))
|
|
575
|
+
# Terminal.raw_write(Terminal::Ansi.status(indicator: '#fa00fa'))
|
|
576
|
+
# Terminal.raw_write(Terminal::Ansi.status(indicator: :f00))
|
|
577
|
+
# Terminal.raw_write(Terminal::Ansi.status) # reset the status
|
|
578
|
+
#
|
|
579
|
+
# @param text [nil, #to_s] status text
|
|
580
|
+
# @param detail [nil, #to_s] status detail text
|
|
581
|
+
# @param color [nil, String, Symbol] text color
|
|
582
|
+
# @param indicator [nil, String, Symbol] status indicator color
|
|
583
|
+
# @return (see .[])
|
|
584
|
+
def status(text: nil, detail: nil, color: nil, indicator: nil)
|
|
585
|
+
str = text ? ";status=#{_no_bell(text)}" : ''
|
|
586
|
+
indicator = _try_rgb(indicator) and str << ";indicator=#{indicator}"
|
|
587
|
+
color = _try_rgb(color) and str << ";color=#{color}"
|
|
588
|
+
str << "detail=#{_no_bell(detail)}" if detail
|
|
589
|
+
str.empty? ? +STATUS_RESET : "\e]21337#{str}\a"
|
|
590
|
+
end
|
|
591
|
+
|
|
592
|
+
# Reset session status of the terminal.
|
|
593
|
+
#
|
|
594
|
+
# @see .status
|
|
595
|
+
#
|
|
596
|
+
# @return (see .[])
|
|
597
|
+
def status_reset = +STATUS_RESET
|
|
598
|
+
|
|
553
599
|
# Scale text using the
|
|
554
600
|
# [text sizing protocol](https://sw.kovidgoyal.net/kitty/text-sizing-protocol).
|
|
555
601
|
#
|
|
556
|
-
# @note
|
|
602
|
+
# @note Supported by Kitty.
|
|
557
603
|
#
|
|
558
604
|
# @example Double-height Greeting
|
|
559
605
|
# Terminal::Ansi.scale('Hello Ruby!', scale: 2)
|
|
@@ -619,16 +665,23 @@ module Terminal
|
|
|
619
665
|
caller(1)
|
|
620
666
|
)
|
|
621
667
|
end
|
|
668
|
+
|
|
669
|
+
def _try_rgb(str)
|
|
670
|
+
mt = /\A?#?([[:xdigit:]]{3,6})\z/.match(str.to_s)&.[](1) or return
|
|
671
|
+
"##{mt.size == 6 ? mt : mt[..2]}"
|
|
672
|
+
end
|
|
673
|
+
|
|
674
|
+
def _no_bell(str) = str.to_s.tr("\a", '')
|
|
622
675
|
end
|
|
623
676
|
|
|
624
677
|
@re_test =
|
|
625
678
|
/
|
|
626
|
-
|
|
627
|
-
|
|
|
628
|
-
|
|
629
|
-
/x
|
|
679
|
+
(?:\e\[[\x30-\x3f]*[\x20-\x2f]*[a-zA-Z])
|
|
680
|
+
| (?:\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
|
|
681
|
+
/x
|
|
630
682
|
|
|
631
|
-
@
|
|
683
|
+
@re_bbcode_old = /(?:\[((?~[\[\]]))\])/
|
|
684
|
+
@re_bbcode = /(?:\[(?~[\[\]]))\]/
|
|
632
685
|
|
|
633
686
|
color_map = {
|
|
634
687
|
# foreground
|
|
@@ -818,13 +871,12 @@ module Terminal
|
|
|
818
871
|
b = /\A(on|ul)?_?([a-z]{3,}[0-9]{0,3})\z/.match(s) or next h[s] = nil
|
|
819
872
|
next h[s] = (v = NAMED_COLORS[b[2]]) ? "#{@cbase[b[1]]};#{v}" : nil
|
|
820
873
|
end
|
|
821
|
-
h[s] =
|
|
822
|
-
when 1, 2
|
|
823
|
-
"#{@cbase[b]};5;#{v.hex}"
|
|
824
|
-
when 3
|
|
825
|
-
"#{@cbase[b]};2;#{(v[0] * 2).hex};#{(v[1] * 2).hex};#{(v[2] * 2).hex}"
|
|
826
|
-
when 6
|
|
874
|
+
h[s] = if v.size == 6
|
|
827
875
|
"#{@cbase[b]};2;#{v[0, 2].hex};#{v[2, 2].hex};#{v[4, 2].hex}"
|
|
876
|
+
elsif v.size == 3
|
|
877
|
+
"#{@cbase[b]};2;#{(v[0] * 2).hex};#{(v[1] * 2).hex};#{(v[2] * 2).hex}"
|
|
878
|
+
elsif v.size < 3
|
|
879
|
+
"#{@cbase[b]};5;#{v.hex}"
|
|
828
880
|
end
|
|
829
881
|
end
|
|
830
882
|
|
|
@@ -832,7 +884,7 @@ module Terminal
|
|
|
832
884
|
@attr_sym_map.compare_by_identity
|
|
833
885
|
@attr_sym_map.default_proc = proc { |h, s| h[s] = @attr_str_map[s.to_s] }
|
|
834
886
|
|
|
835
|
-
@
|
|
887
|
+
@bbcc = {} # BBCode cache
|
|
836
888
|
|
|
837
889
|
@screen_erase = { below: nil, above: '1', scrollback: '3' }
|
|
838
890
|
@screen_erase.default = '2'
|
|
@@ -917,6 +969,16 @@ module Terminal
|
|
|
917
969
|
# @private
|
|
918
970
|
LINE_ERASE_PREV = -"#{cursor_prev_line(nil)}#{LINE_ERASE}"
|
|
919
971
|
|
|
972
|
+
# @comment Supported by Terminal, iTerm, WezTerm:
|
|
973
|
+
# @private
|
|
974
|
+
LINE_DOUBLE_HEIGHT_TOP = "\e#3"
|
|
975
|
+
# @private
|
|
976
|
+
LINE_DOUBLE_HEIGHT_BOTTOM = "\e#4"
|
|
977
|
+
# @private
|
|
978
|
+
LINE_SINGLE_WIDTH = "\e#5"
|
|
979
|
+
# @private
|
|
980
|
+
LINE_DOUBLE_WIDTH = "\e#6"
|
|
981
|
+
|
|
920
982
|
# @private
|
|
921
983
|
LINK_END = "\e]8;;\a"
|
|
922
984
|
|
|
@@ -925,6 +987,9 @@ module Terminal
|
|
|
925
987
|
# @private
|
|
926
988
|
PROGRESS_SHOW_INDETERMINATE = "\e]9;4;3;0\a"
|
|
927
989
|
|
|
990
|
+
# @private
|
|
991
|
+
STATUS_RESET = "\e]21337;status=;indicator=;status-color=\a"
|
|
992
|
+
|
|
928
993
|
# @comment seems not widely supported:
|
|
929
994
|
# doubled: def cursor_column(column = 1) = "\e[#{column}`"
|
|
930
995
|
# doubled: def cursor_row(row = 1) = "\e[#{row}d"
|
|
@@ -942,13 +1007,6 @@ module Terminal
|
|
|
942
1007
|
#
|
|
943
1008
|
# def set_scroll_region(top = nil, bottom = nil) = "\e[#{top};#{bottom}r"
|
|
944
1009
|
|
|
945
|
-
# @comment other:
|
|
946
|
-
# "\eE" same as "\r\n"
|
|
947
|
-
# "\eD" same as "\n" but preserves column
|
|
948
|
-
# "\eM" reverse "\n"
|
|
949
|
-
# "\e[6n" report Cursor Position as "ESC \[ row ; col R"
|
|
950
|
-
# "\e[21t report window’s title as ESC ] l title ESC \"
|
|
951
|
-
|
|
952
1010
|
# @comment TODO:
|
|
953
1011
|
# https://sw.kovidgoyal.net/kitty/desktop-notifications
|
|
954
1012
|
# https://sw.kovidgoyal.net/kitty/pointer-shapes
|
data/lib/terminal/input/dumb.rb
CHANGED
|
@@ -9,9 +9,9 @@ module Terminal
|
|
|
9
9
|
# @see Terminal.read_key_event
|
|
10
10
|
#
|
|
11
11
|
# @example Parse a raw escape sequence
|
|
12
|
-
# event = Terminal::KeyEvent["
|
|
13
|
-
# event.key # =>
|
|
14
|
-
# event.name # => "
|
|
12
|
+
# event = Terminal::KeyEvent["a"]
|
|
13
|
+
# event.key # => "a"
|
|
14
|
+
# event.name # => "a"
|
|
15
15
|
# event.simple? # => true
|
|
16
16
|
#
|
|
17
17
|
# @example Inspect modifier keys
|
|
@@ -33,7 +33,7 @@ module Terminal
|
|
|
33
33
|
# @see #modifiers
|
|
34
34
|
# @see #modifier?
|
|
35
35
|
#
|
|
36
|
-
# @return [Integer]
|
|
36
|
+
# @return [Integer] bitmask of the active modifiers
|
|
37
37
|
attr_reader :modifier_bits
|
|
38
38
|
|
|
39
39
|
# Names of active modifier keys.
|
data/lib/terminal/input.rb
CHANGED
|
@@ -14,7 +14,6 @@ module Terminal
|
|
|
14
14
|
# @example
|
|
15
15
|
# Terminal.input_mode # => :csi_u
|
|
16
16
|
#
|
|
17
|
-
# @attribute [r] input_mode
|
|
18
17
|
# @return [Symbol] `:csi_u`, `:legacy`, `:dumb`, or `:error`
|
|
19
18
|
|
|
20
19
|
#
|
|
@@ -33,7 +32,7 @@ module Terminal
|
|
|
33
32
|
#
|
|
34
33
|
# @example
|
|
35
34
|
# Terminal.on_key_event(mouse: true) do |event|
|
|
36
|
-
# break if event.name == '
|
|
35
|
+
# break if event.name == 'Esc'
|
|
37
36
|
# puts event.name
|
|
38
37
|
# end
|
|
39
38
|
#
|
|
@@ -69,7 +68,7 @@ module Terminal
|
|
|
69
68
|
extend AnsiInput
|
|
70
69
|
when :dumb
|
|
71
70
|
require_relative('input/dumb')
|
|
72
|
-
extend
|
|
71
|
+
extend DumbInput
|
|
73
72
|
else
|
|
74
73
|
__input_error
|
|
75
74
|
end
|
data/lib/terminal/output/ansi.rb
CHANGED
|
@@ -88,19 +88,13 @@ module Terminal
|
|
|
88
88
|
objects.flatten!
|
|
89
89
|
return @out.puts if objects.empty?
|
|
90
90
|
@out.puts(
|
|
91
|
-
Text::Formatter.
|
|
91
|
+
Text::Formatter.new(
|
|
92
92
|
*objects,
|
|
93
93
|
ansi: true,
|
|
94
94
|
bbcode:,
|
|
95
95
|
spaces:,
|
|
96
|
-
eol
|
|
97
|
-
|
|
98
|
-
width:,
|
|
99
|
-
height:,
|
|
100
|
-
padding:,
|
|
101
|
-
prefix:,
|
|
102
|
-
suffix:
|
|
103
|
-
)
|
|
96
|
+
eol:
|
|
97
|
+
).format(align:, width:, height:, padding:, prefix:, suffix:)
|
|
104
98
|
)
|
|
105
99
|
rescue IOError, SystemCallError
|
|
106
100
|
__output_error(nil)
|
|
@@ -139,16 +133,17 @@ module Terminal
|
|
|
139
133
|
require('io/console')
|
|
140
134
|
@con = IO.console
|
|
141
135
|
return unless Signal.list.key?('WINCH')
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
136
|
+
prev_winch =
|
|
137
|
+
Signal.trap('WINCH') do
|
|
138
|
+
@size = nil
|
|
139
|
+
@on_resize&.call
|
|
140
|
+
prev_winch.call if prev_winch.respond_to?(:call)
|
|
141
|
+
end
|
|
146
142
|
end
|
|
147
143
|
|
|
148
144
|
private_class_method def self.extended(mod)
|
|
149
145
|
mod.instance_variable_set(:@cc, 0)
|
|
150
146
|
mod.instance_variable_set(:@as, 0)
|
|
151
|
-
# at_exit { raw_write("#{Ansi::CURSOR_SHOW}#{Ansi::SCREEN_ALTERNATE_OFF}") }
|
|
152
147
|
end
|
|
153
148
|
end
|
|
154
149
|
|
data/lib/terminal/output/dumb.rb
CHANGED
|
@@ -69,19 +69,13 @@ module Terminal
|
|
|
69
69
|
objects.flatten!
|
|
70
70
|
return @out.puts if objects.empty?
|
|
71
71
|
@out.puts(
|
|
72
|
-
Text::Formatter.
|
|
72
|
+
Text::Formatter.new(
|
|
73
73
|
*objects,
|
|
74
74
|
ansi: false,
|
|
75
75
|
bbcode:,
|
|
76
76
|
spaces:,
|
|
77
|
-
eol
|
|
78
|
-
|
|
79
|
-
width:,
|
|
80
|
-
height:,
|
|
81
|
-
padding:,
|
|
82
|
-
prefix:,
|
|
83
|
-
suffix:
|
|
84
|
-
)
|
|
77
|
+
eol:
|
|
78
|
+
).format(align:, width:, height:, padding:, prefix:, suffix:)
|
|
85
79
|
)
|
|
86
80
|
rescue IOError, SystemCallError
|
|
87
81
|
__output_error(nil)
|
data/lib/terminal/output.rb
CHANGED
|
@@ -27,7 +27,7 @@ module Terminal
|
|
|
27
27
|
# @!group Output Attributes
|
|
28
28
|
#
|
|
29
29
|
|
|
30
|
-
#
|
|
30
|
+
# @attribute [r] tui?
|
|
31
31
|
#
|
|
32
32
|
# Whether the terminal supports full TUI interaction (ANSI output
|
|
33
33
|
# with CSI-u or legacy keyboard input).
|
|
@@ -36,7 +36,7 @@ module Terminal
|
|
|
36
36
|
#
|
|
37
37
|
# @return [true, false]
|
|
38
38
|
|
|
39
|
-
#
|
|
39
|
+
# @attribute [r] ansi?
|
|
40
40
|
#
|
|
41
41
|
# Whether ANSI escape codes are supported.
|
|
42
42
|
#
|
|
@@ -181,7 +181,6 @@ module Terminal
|
|
|
181
181
|
# @!method show_cursor
|
|
182
182
|
# Show the cursor.
|
|
183
183
|
#
|
|
184
|
-
#
|
|
185
184
|
# @note Reference-counted; safe for nested use.
|
|
186
185
|
#
|
|
187
186
|
# @see .hide_cursor
|
|
@@ -4,9 +4,10 @@ require 'stringio'
|
|
|
4
4
|
|
|
5
5
|
# RSpec shared context for testing code that uses Terminal.rb.
|
|
6
6
|
#
|
|
7
|
-
# Stubs all Terminal I/O methods and provides `stdout`, `
|
|
8
|
-
# `
|
|
9
|
-
#
|
|
7
|
+
# Stubs all Terminal I/O methods and provides the `stdout`, `stdout_lines`,
|
|
8
|
+
# and `stdin` test helpers. Output is captured in the `stdout` StringIO;
|
|
9
|
+
# `stdout_lines` returns its lines without line endings. Input is read from
|
|
10
|
+
# the `stdin` array of raw key sequences.
|
|
10
11
|
#
|
|
11
12
|
# @example Basic usage
|
|
12
13
|
# RSpec.describe MyClass do
|
|
@@ -14,7 +15,7 @@ require 'stringio'
|
|
|
14
15
|
#
|
|
15
16
|
# it 'prints a greeting' do
|
|
16
17
|
# my_object.greet
|
|
17
|
-
# expect(
|
|
18
|
+
# expect(stdout.string).to include('Hello')
|
|
18
19
|
# end
|
|
19
20
|
# end
|
|
20
21
|
#
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Terminal
|
|
4
4
|
module Text
|
|
5
5
|
# supported Unicode Standard version
|
|
6
|
-
UNICODE_VERSION = '
|
|
6
|
+
UNICODE_VERSION = '18.0.0'
|
|
7
7
|
|
|
8
8
|
#
|
|
9
9
|
# This lookup was generated based on
|
|
@@ -11,7 +11,6 @@ module Terminal
|
|
|
11
11
|
#
|
|
12
12
|
CharWidth =
|
|
13
13
|
Hash.new do |h, ord|
|
|
14
|
-
next h[ord] = 1 if ord < 0xa1
|
|
15
14
|
h[ord] = @char_width[@char_width_last.bsearch_index { ord <= it }] ||
|
|
16
15
|
@ambiguous_char_width
|
|
17
16
|
end
|
|
@@ -163,7 +162,7 @@ module Terminal
|
|
|
163
162
|
0x5c3,
|
|
164
163
|
0x5c5,
|
|
165
164
|
0x5c6,
|
|
166
|
-
|
|
165
|
+
0x5c9,
|
|
167
166
|
0x5ff,
|
|
168
167
|
0x605,
|
|
169
168
|
0x60f,
|
|
@@ -276,7 +275,7 @@ module Terminal
|
|
|
276
275
|
0xb44,
|
|
277
276
|
0xb4c,
|
|
278
277
|
0xb4d,
|
|
279
|
-
|
|
278
|
+
0xb52,
|
|
280
279
|
0xb56,
|
|
281
280
|
0xb61,
|
|
282
281
|
0xb63,
|
|
@@ -441,9 +440,7 @@ module Terminal
|
|
|
441
440
|
0x1a7e,
|
|
442
441
|
0x1a7f,
|
|
443
442
|
0x1aaf,
|
|
444
|
-
|
|
445
|
-
0x1adf,
|
|
446
|
-
0x1aeb,
|
|
443
|
+
0x1af0,
|
|
447
444
|
0x1aff,
|
|
448
445
|
0x1b03,
|
|
449
446
|
0x1b33,
|
|
@@ -956,7 +953,9 @@ module Terminal
|
|
|
956
953
|
0x10d6d,
|
|
957
954
|
0x10eaa,
|
|
958
955
|
0x10eac,
|
|
959
|
-
|
|
956
|
+
0x10ec8,
|
|
957
|
+
0x10ecf,
|
|
958
|
+
0x10eef,
|
|
960
959
|
0x10eff,
|
|
961
960
|
0x10f45,
|
|
962
961
|
0x10f50,
|
|
@@ -1146,6 +1145,8 @@ module Terminal
|
|
|
1146
1145
|
0x11d95,
|
|
1147
1146
|
0x11d96,
|
|
1148
1147
|
0x11d97,
|
|
1148
|
+
0x11def,
|
|
1149
|
+
0x11df0,
|
|
1149
1150
|
0x11ef2,
|
|
1150
1151
|
0x11ef4,
|
|
1151
1152
|
0x11eff,
|
|
@@ -1180,11 +1181,15 @@ module Terminal
|
|
|
1180
1181
|
0x16fef,
|
|
1181
1182
|
0x16ff6,
|
|
1182
1183
|
0x16fff,
|
|
1183
|
-
|
|
1184
|
+
0x18cda,
|
|
1184
1185
|
0x18cfe,
|
|
1185
|
-
|
|
1186
|
+
0x18d20,
|
|
1186
1187
|
0x18d7f,
|
|
1187
1188
|
0x18df2,
|
|
1189
|
+
0x18dff,
|
|
1190
|
+
0x19191,
|
|
1191
|
+
0x1919f,
|
|
1192
|
+
0x191d2,
|
|
1188
1193
|
0x1afef,
|
|
1189
1194
|
0x1aff3,
|
|
1190
1195
|
0x1aff4,
|
|
@@ -1192,7 +1197,7 @@ module Terminal
|
|
|
1192
1197
|
0x1affc,
|
|
1193
1198
|
0x1affe,
|
|
1194
1199
|
0x1afff,
|
|
1195
|
-
|
|
1200
|
+
0x1b128,
|
|
1196
1201
|
0x1b131,
|
|
1197
1202
|
0x1b132,
|
|
1198
1203
|
0x1b14f,
|
|
@@ -1200,7 +1205,7 @@ module Terminal
|
|
|
1200
1205
|
0x1b154,
|
|
1201
1206
|
0x1b155,
|
|
1202
1207
|
0x1b163,
|
|
1203
|
-
|
|
1208
|
+
0x1b168,
|
|
1204
1209
|
0x1b16f,
|
|
1205
1210
|
0x1b2fb,
|
|
1206
1211
|
0x1bc9c,
|
|
@@ -1211,6 +1216,8 @@ module Terminal
|
|
|
1211
1216
|
0x1cf2d,
|
|
1212
1217
|
0x1cf2f,
|
|
1213
1218
|
0x1cf46,
|
|
1219
|
+
0x1d126,
|
|
1220
|
+
0x1d128,
|
|
1214
1221
|
0x1d166,
|
|
1215
1222
|
0x1d169,
|
|
1216
1223
|
0x1d172,
|
|
@@ -1221,6 +1228,8 @@ module Terminal
|
|
|
1221
1228
|
0x1d1ad,
|
|
1222
1229
|
0x1d241,
|
|
1223
1230
|
0x1d244,
|
|
1231
|
+
0x1d25a,
|
|
1232
|
+
0x1d25c,
|
|
1224
1233
|
0x1d2ff,
|
|
1225
1234
|
0x1d356,
|
|
1226
1235
|
0x1d35f,
|
|
@@ -1287,6 +1296,8 @@ module Terminal
|
|
|
1287
1296
|
0x1f190,
|
|
1288
1297
|
0x1f19a,
|
|
1289
1298
|
0x1f1ac,
|
|
1299
|
+
0x1f1ad,
|
|
1300
|
+
0x1f1ae,
|
|
1290
1301
|
0x1f1ff,
|
|
1291
1302
|
0x1f202,
|
|
1292
1303
|
0x1f20f,
|
|
@@ -1342,13 +1353,15 @@ module Terminal
|
|
|
1342
1353
|
0x1f6cf,
|
|
1343
1354
|
0x1f6d2,
|
|
1344
1355
|
0x1f6d4,
|
|
1345
|
-
|
|
1356
|
+
0x1f6d9,
|
|
1346
1357
|
0x1f6db,
|
|
1347
1358
|
0x1f6df,
|
|
1348
1359
|
0x1f6ea,
|
|
1349
1360
|
0x1f6ec,
|
|
1350
1361
|
0x1f6f3,
|
|
1351
1362
|
0x1f6fc,
|
|
1363
|
+
0x1f7d9,
|
|
1364
|
+
0x1f7da,
|
|
1352
1365
|
0x1f7df,
|
|
1353
1366
|
0x1f7eb,
|
|
1354
1367
|
0x1f7ef,
|
|
@@ -1362,17 +1375,15 @@ module Terminal
|
|
|
1362
1375
|
0x1fa6f,
|
|
1363
1376
|
0x1fa7c,
|
|
1364
1377
|
0x1fa7f,
|
|
1365
|
-
0x1fa8a,
|
|
1366
|
-
0x1fa8d,
|
|
1367
1378
|
0x1fac6,
|
|
1368
1379
|
0x1fac7,
|
|
1369
1380
|
0x1fac8,
|
|
1370
|
-
|
|
1371
|
-
|
|
1381
|
+
0x1facb,
|
|
1382
|
+
0x1fadd,
|
|
1372
1383
|
0x1fade,
|
|
1373
|
-
|
|
1384
|
+
0x1faeb,
|
|
1374
1385
|
0x1faee,
|
|
1375
|
-
|
|
1386
|
+
0x1fafa,
|
|
1376
1387
|
0x1ffff,
|
|
1377
1388
|
0x2fffd,
|
|
1378
1389
|
0x2ffff,
|
|
@@ -1876,8 +1887,6 @@ module Terminal
|
|
|
1876
1887
|
0,
|
|
1877
1888
|
1,
|
|
1878
1889
|
0,
|
|
1879
|
-
1,
|
|
1880
|
-
0,
|
|
1881
1890
|
nil,
|
|
1882
1891
|
1,
|
|
1883
1892
|
nil,
|
|
@@ -2549,6 +2558,10 @@ module Terminal
|
|
|
2549
2558
|
1,
|
|
2550
2559
|
0,
|
|
2551
2560
|
1,
|
|
2561
|
+
0,
|
|
2562
|
+
1,
|
|
2563
|
+
0,
|
|
2564
|
+
1,
|
|
2552
2565
|
2,
|
|
2553
2566
|
0,
|
|
2554
2567
|
1,
|
|
@@ -2578,6 +2591,14 @@ module Terminal
|
|
|
2578
2591
|
1,
|
|
2579
2592
|
2,
|
|
2580
2593
|
1,
|
|
2594
|
+
2,
|
|
2595
|
+
1,
|
|
2596
|
+
2,
|
|
2597
|
+
1,
|
|
2598
|
+
0,
|
|
2599
|
+
1,
|
|
2600
|
+
0,
|
|
2601
|
+
1,
|
|
2581
2602
|
0,
|
|
2582
2603
|
1,
|
|
2583
2604
|
0,
|
|
@@ -2689,6 +2710,8 @@ module Terminal
|
|
|
2689
2710
|
2,
|
|
2690
2711
|
1,
|
|
2691
2712
|
2,
|
|
2713
|
+
1,
|
|
2714
|
+
2,
|
|
2692
2715
|
0,
|
|
2693
2716
|
2,
|
|
2694
2717
|
1,
|
|
@@ -9,15 +9,15 @@ module Terminal
|
|
|
9
9
|
# @see Terminal::Text
|
|
10
10
|
#
|
|
11
11
|
# @example Basic word-wrapping
|
|
12
|
-
#
|
|
13
|
-
#
|
|
12
|
+
# formatter = Terminal::Text::Formatter.new('Hello World, this is a test')
|
|
13
|
+
# formatter.lines(width: 12)
|
|
14
14
|
# # => ["Hello World,", "this is a", "test"]
|
|
15
15
|
#
|
|
16
16
|
# @example Formatted output with alignment
|
|
17
17
|
# Terminal::Text::Formatter.format(
|
|
18
18
|
# 'Hello', align: :center, width: 20
|
|
19
19
|
# )
|
|
20
|
-
# # => ["
|
|
20
|
+
# # => [" Hello "]
|
|
21
21
|
class Formatter
|
|
22
22
|
# Parse text into lines, optionally with display widths.
|
|
23
23
|
#
|
|
@@ -55,7 +55,7 @@ module Terminal
|
|
|
55
55
|
#
|
|
56
56
|
# @param (see #initialize)
|
|
57
57
|
# @param (see #format)
|
|
58
|
-
# @
|
|
58
|
+
# @return (see #format)
|
|
59
59
|
# @raise (see #format)
|
|
60
60
|
def self.format(
|
|
61
61
|
*str,
|
|
@@ -80,6 +80,31 @@ module Terminal
|
|
|
80
80
|
)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
+
# Format text with alignment.
|
|
84
|
+
#
|
|
85
|
+
# @see #fmt
|
|
86
|
+
#
|
|
87
|
+
# @example
|
|
88
|
+
# Terminal::Text::Formatter.fmt('Hi', align: :center, width: 10)
|
|
89
|
+
# # => [" Hi "]
|
|
90
|
+
#
|
|
91
|
+
# @param (see #initialize)
|
|
92
|
+
# @param (see #fmt)
|
|
93
|
+
# @return (see #fmt)
|
|
94
|
+
# @raise (see #fmt)
|
|
95
|
+
def self.fmt(
|
|
96
|
+
*str,
|
|
97
|
+
ansi: true,
|
|
98
|
+
bbcode: true,
|
|
99
|
+
spaces: true,
|
|
100
|
+
eol: true,
|
|
101
|
+
align: nil,
|
|
102
|
+
width: nil,
|
|
103
|
+
height: nil
|
|
104
|
+
)
|
|
105
|
+
new(*str, ansi:, bbcode:, spaces:, eol:).fmt(align:, width:, height:)
|
|
106
|
+
end
|
|
107
|
+
|
|
83
108
|
# Create a new formatter with given text and options.
|
|
84
109
|
#
|
|
85
110
|
# @param str [Array<#to_s>] text to process
|
|
@@ -108,8 +133,8 @@ module Terminal
|
|
|
108
133
|
# Word-wrap and return lines with their display widths.
|
|
109
134
|
#
|
|
110
135
|
# @example
|
|
111
|
-
#
|
|
112
|
-
#
|
|
136
|
+
# formatter = Terminal::Text::Formatter.new('Hello World')
|
|
137
|
+
# formatter.lines_with_size(width: 5)
|
|
113
138
|
# # => [["Hello", 5], ["World", 5]]
|
|
114
139
|
#
|
|
115
140
|
# @param width [#to_i, nil] maximum line width in columns;
|
|
@@ -127,8 +152,8 @@ module Terminal
|
|
|
127
152
|
# Word-wrap and return lines as strings.
|
|
128
153
|
#
|
|
129
154
|
# @example
|
|
130
|
-
#
|
|
131
|
-
#
|
|
155
|
+
# formatter = Terminal::Text::Formatter.new('Hello World')
|
|
156
|
+
# formatter.lines(width: 5)
|
|
132
157
|
# # => ["Hello", "World"]
|
|
133
158
|
#
|
|
134
159
|
# @param width [#to_i, nil] maximum line width in columns
|
|
@@ -138,16 +163,22 @@ module Terminal
|
|
|
138
163
|
|
|
139
164
|
# Format lines with alignment, padding, and decorations.
|
|
140
165
|
#
|
|
141
|
-
# @
|
|
142
|
-
#
|
|
143
|
-
#
|
|
144
|
-
#
|
|
166
|
+
# @see #fmt
|
|
167
|
+
#
|
|
168
|
+
# @example Prefix each line
|
|
169
|
+
# formatter = Terminal::Text::Formatter.new("Hello\nWorld")
|
|
170
|
+
# formatter.format(prefix: '> ')
|
|
171
|
+
# # => ["> Hello", "> World"]
|
|
172
|
+
# @example Framed box with padding
|
|
173
|
+
# formatter = Terminal::Text::Formatter.new('Hi')
|
|
174
|
+
# formatter.format(width: 6, padding: [1, 2], prefix: '|', suffix: '|')
|
|
175
|
+
# # => ["| |", "| Hi |", "| |"]
|
|
145
176
|
#
|
|
146
177
|
# @param align [Symbol, nil] text alignment:
|
|
147
178
|
# `:left`, `:right`, `:center`, or `nil` (no fill)
|
|
148
179
|
# @param width [#to_i, nil] output line width in columns
|
|
149
180
|
# @param height [#to_i, nil] number of output lines;
|
|
150
|
-
# negative values take lines from the end
|
|
181
|
+
# negative values take lines from the end; `0` returns `[""]`
|
|
151
182
|
# @param padding [#to_i, Array, nil] CSS-style padding
|
|
152
183
|
# - 1 value: all sides;
|
|
153
184
|
# - 2 values: [vertical, horizontal];
|
|
@@ -165,6 +196,10 @@ module Terminal
|
|
|
165
196
|
prefix: nil,
|
|
166
197
|
suffix: nil
|
|
167
198
|
)
|
|
199
|
+
if padding.nil? && prefix.nil? && suffix.nil?
|
|
200
|
+
return fmt(align:, width:, height:)
|
|
201
|
+
end
|
|
202
|
+
|
|
168
203
|
top, right, bottom, left = _padding(padding)
|
|
169
204
|
|
|
170
205
|
if height
|
|
@@ -200,11 +235,9 @@ module Terminal
|
|
|
200
235
|
left = left < 1 ? prefix.to_s : "#{prefix}#{space[left]}"
|
|
201
236
|
right = right < 1 ? suffix.to_s : "#{space[right]}#{suffix}"
|
|
202
237
|
|
|
203
|
-
# TODO: styling
|
|
204
|
-
|
|
205
238
|
case align
|
|
206
|
-
when
|
|
207
|
-
lws.each { |l,
|
|
239
|
+
when nil, false
|
|
240
|
+
lws.each { |l, _| ret << "#{left}#{l}#{right}" }
|
|
208
241
|
when :right
|
|
209
242
|
lws.each { |l, s| ret << "#{left}#{space[w - s]}#{l}#{right}" }
|
|
210
243
|
when :center
|
|
@@ -214,13 +247,58 @@ module Terminal
|
|
|
214
247
|
ret << "#{left}#{space[s - rs]}#{l}#{space[rs]}#{right}"
|
|
215
248
|
end
|
|
216
249
|
else
|
|
217
|
-
lws.each { |l,
|
|
250
|
+
lws.each { |l, s| ret << "#{left}#{l}#{space[w - s]}#{right}" }
|
|
218
251
|
end
|
|
219
252
|
|
|
220
253
|
return ret.fill(empty, ret.size, bottom) if bottom > 0
|
|
221
254
|
ret.empty? ? ret << '' : ret
|
|
222
255
|
end
|
|
223
256
|
|
|
257
|
+
# Format lines with alignment.
|
|
258
|
+
#
|
|
259
|
+
# Like {#format} without padding, prefix, and suffix.
|
|
260
|
+
#
|
|
261
|
+
# @see #format
|
|
262
|
+
#
|
|
263
|
+
# @example
|
|
264
|
+
# Terminal::Text::Formatter.new('Hi').fmt(align: :right, width: 6)
|
|
265
|
+
# # => [" Hi"]
|
|
266
|
+
#
|
|
267
|
+
# @param align (see #format)
|
|
268
|
+
# @param width (see #format)
|
|
269
|
+
# @param height (see #format)
|
|
270
|
+
# @return (see #format)
|
|
271
|
+
# @raise (see #format)
|
|
272
|
+
def fmt(width: nil, height: nil, align: nil)
|
|
273
|
+
if height
|
|
274
|
+
return [] if (height = height.to_i) == 0
|
|
275
|
+
height = -height if (tail = (height < 0))
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
lines =
|
|
279
|
+
if width
|
|
280
|
+
w = (width = width.to_i)
|
|
281
|
+
raise(ArgumentError, "invalid width - #{width.inspect}") if w < 1
|
|
282
|
+
_limited(w)
|
|
283
|
+
else
|
|
284
|
+
@unlimited || _unlimited
|
|
285
|
+
end
|
|
286
|
+
|
|
287
|
+
return [+''] if lines.empty?
|
|
288
|
+
|
|
289
|
+
lines = tail ? lines.last(height) : lines.take(height) if height
|
|
290
|
+
w ||= lines.max_by(&:last)[-1] # unless width
|
|
291
|
+
|
|
292
|
+
return lines.map(&:first) unless align
|
|
293
|
+
space = SPACE_CACHE.dup
|
|
294
|
+
return lines.map { |l, s| "#{space[w - s]}#{l}" } if align == :right
|
|
295
|
+
return lines.map { |l, s| "#{l}#{space[w - s]}" } if align != :center
|
|
296
|
+
lines.map do |l, s|
|
|
297
|
+
rs = ((s = w - s) / 2.0).round
|
|
298
|
+
rs < 1 ? l : "#{space[s - rs]}#{l}#{space[rs]}"
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
|
|
224
302
|
# Whether the formatter has any content.
|
|
225
303
|
#
|
|
226
304
|
# @return [true, false]
|
|
@@ -229,8 +307,8 @@ module Terminal
|
|
|
229
307
|
# Maximum display width of any line.
|
|
230
308
|
#
|
|
231
309
|
# @example
|
|
232
|
-
#
|
|
233
|
-
#
|
|
310
|
+
# formatter = Terminal::Text::Formatter.new("short\na longer line")
|
|
311
|
+
# formatter.max_line_width # => 13
|
|
234
312
|
#
|
|
235
313
|
# @return [Integer] widest line in columns, `0` when empty
|
|
236
314
|
def max_line_width
|
|
@@ -352,9 +430,7 @@ module Terminal
|
|
|
352
430
|
next if current.is_a?(Space) # ignore trailing space
|
|
353
431
|
|
|
354
432
|
# handle Word...
|
|
355
|
-
splitted = current.split(width)
|
|
356
|
-
last = splitted.pop
|
|
357
|
-
|
|
433
|
+
*splitted, last = current.split(width)
|
|
358
434
|
if csi.empty?
|
|
359
435
|
splitted.each { ret << [it.to_str, it.size] }
|
|
360
436
|
else
|
|
@@ -368,7 +444,10 @@ module Terminal
|
|
|
368
444
|
size = last.size
|
|
369
445
|
end
|
|
370
446
|
|
|
371
|
-
ret.
|
|
447
|
+
if ret.size > 1
|
|
448
|
+
str, width = ret[-1]
|
|
449
|
+
ret.pop if width == 0 && str.empty?
|
|
450
|
+
end
|
|
372
451
|
ret
|
|
373
452
|
end
|
|
374
453
|
|
|
@@ -596,11 +675,10 @@ module Terminal
|
|
|
596
675
|
@chunks.each_with_index do |chunk, idx|
|
|
597
676
|
if chunk.bytesize > 1 && chunk.getbyte(0) < 0x80 &&
|
|
598
677
|
chunk.ascii_only?
|
|
599
|
-
chunk.each_char { chars << it and sizes << 1 }
|
|
600
|
-
else
|
|
601
|
-
chars << chunk
|
|
602
|
-
sizes << @sizes[idx]
|
|
678
|
+
next chunk.each_char { chars << it and sizes << 1 }
|
|
603
679
|
end
|
|
680
|
+
chars << chunk
|
|
681
|
+
sizes << @sizes[idx]
|
|
604
682
|
end
|
|
605
683
|
else
|
|
606
684
|
chars = @chunks
|
|
@@ -676,7 +754,7 @@ module Terminal
|
|
|
676
754
|
| \e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\)
|
|
677
755
|
| \s
|
|
678
756
|
| \X
|
|
679
|
-
|
|
757
|
+
/x
|
|
680
758
|
|
|
681
759
|
SPACE_BYTE =
|
|
682
760
|
Hash
|
data/lib/terminal/text.rb
CHANGED
|
@@ -42,7 +42,7 @@ module Terminal
|
|
|
42
42
|
# @example
|
|
43
43
|
# Terminal::Text.width('Hello') # => 5
|
|
44
44
|
# Terminal::Text.width('[bold]Hi[/bold]') # => 2
|
|
45
|
-
# Terminal::Text.width('[bold]Hi[/bold]', bbcode: false) # =>
|
|
45
|
+
# Terminal::Text.width('[bold]Hi[/bold]', bbcode: false) # => 15
|
|
46
46
|
#
|
|
47
47
|
# @param str [#to_s] the string to measure
|
|
48
48
|
# @param bbcode (see Formatter#initialize)
|
|
@@ -106,11 +106,30 @@ module Terminal
|
|
|
106
106
|
#
|
|
107
107
|
# @see Formatter.format
|
|
108
108
|
#
|
|
109
|
+
# @example
|
|
110
|
+
# Terminal::Text.format(
|
|
111
|
+
# 'Hello World', width: 9, align: :left, prefix: '| ', suffix: ' |'
|
|
112
|
+
# )
|
|
113
|
+
# # => ["| Hello |", "| World |"]
|
|
114
|
+
#
|
|
109
115
|
# @param (see Formatter.format)
|
|
110
116
|
# @return (see Formatter.format)
|
|
111
117
|
# @raise (see Formatter.format)
|
|
112
118
|
def format(...) = Formatter.format(...)
|
|
113
119
|
|
|
120
|
+
# Format text with alignment.
|
|
121
|
+
#
|
|
122
|
+
# @see Formatter.fmt
|
|
123
|
+
#
|
|
124
|
+
# @example
|
|
125
|
+
# Terminal::Text.fmt('Hi', align: :center, width: 10)
|
|
126
|
+
# # => [" Hi "]
|
|
127
|
+
#
|
|
128
|
+
# @param (see Formatter.fmt)
|
|
129
|
+
# @return (see Formatter.fmt)
|
|
130
|
+
# @raise (see Formatter.fmt)
|
|
131
|
+
def fmt(...) = Formatter.fmt(...)
|
|
132
|
+
|
|
114
133
|
# @deprecated Use {.lines} and iterate over the result.
|
|
115
134
|
#
|
|
116
135
|
# @comment @param str [Array<#to_s>] text to process
|
|
@@ -163,7 +182,7 @@ module Terminal
|
|
|
163
182
|
)
|
|
164
183
|
warn(
|
|
165
184
|
'[DEPRECATED] `each_line_with_size` is deprecated ' \
|
|
166
|
-
'– use `.
|
|
185
|
+
'– use `.lines_with_size.each` instead',
|
|
167
186
|
category: :deprecated,
|
|
168
187
|
uplevel: 1
|
|
169
188
|
)
|
|
@@ -212,7 +231,6 @@ module Terminal
|
|
|
212
231
|
def char_width(char)
|
|
213
232
|
ord = char.ord
|
|
214
233
|
return @ctrlchar_width[ord] if ord < 0x20
|
|
215
|
-
# CharWidth knows this rule too - a comparison just beats a memo hit
|
|
216
234
|
return ord < 0xa1 ? 1 : CharWidth[ord] if char.bytesize == 1
|
|
217
235
|
return 2 if ord == 0x1f3f3 && @very_special_flags.include?(char)
|
|
218
236
|
sum = 0
|
|
@@ -220,7 +238,7 @@ module Terminal
|
|
|
220
238
|
char.each_codepoint do |cp|
|
|
221
239
|
next skip = false if skip
|
|
222
240
|
next skip = true if cp == 0x200d # zero width joiner
|
|
223
|
-
sum += CharWidth[cp]
|
|
241
|
+
sum += cp < 0xa1 ? 1 : CharWidth[cp]
|
|
224
242
|
end
|
|
225
243
|
sum
|
|
226
244
|
end
|
|
@@ -231,7 +249,6 @@ module Terminal
|
|
|
231
249
|
"🏳\u{FE0F}\u{200D}⚧\u{FE0F}" # transgender flag
|
|
232
250
|
].freeze
|
|
233
251
|
|
|
234
|
-
# indexed by ordinal, only ever asked for control characters
|
|
235
252
|
@ctrlchar_width = [
|
|
236
253
|
0,
|
|
237
254
|
1,
|
data/lib/terminal/version.rb
CHANGED
data/lib/terminal.rb
CHANGED
|
@@ -72,28 +72,52 @@ module Terminal
|
|
|
72
72
|
|
|
73
73
|
# Execute a shell command.
|
|
74
74
|
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
75
|
+
# Output lines are collected or yielded without their trailing newline.
|
|
76
|
+
#
|
|
77
|
+
# @overload sh(*cmd, env: {}, shell: false, input: nil, **options)
|
|
78
|
+
# Run a command and capture its output.
|
|
79
|
+
#
|
|
80
|
+
# @example
|
|
81
|
+
# status, output, error = Terminal.sh('echo', 'hello')
|
|
82
|
+
# output # => ["hello"]
|
|
83
|
+
#
|
|
77
84
|
# @param cmd [Array<String>] command and arguments
|
|
78
|
-
# @param
|
|
85
|
+
# @param env [Hash] environment variables for the command
|
|
86
|
+
# @param shell [true, false]
|
|
87
|
+
# when true the `cmd` is executed via the shell,
|
|
88
|
+
# when false, the executable is direct executed
|
|
89
|
+
# (see `Process.spawn` for this)
|
|
90
|
+
# @param input [String, IO, Array<String>, Enumerable, nil]
|
|
91
|
+
# data written to the command's standard input
|
|
92
|
+
# @param options [Hash] passed to `Process.spawn`
|
|
93
|
+
# (`:in`, `:out`, `:err` are ignored)
|
|
79
94
|
# @return [Array<Process::Status, Array<String>, Array<String>>]
|
|
80
95
|
# `[status, stdout_lines, stderr_lines]`
|
|
96
|
+
# @return [nil] when the command can not be executed
|
|
97
|
+
#
|
|
98
|
+
# @overload sh(*cmd, env: {}, shell: false, input: nil, **options, &block)
|
|
99
|
+
# Run a command and stream its output to a block.
|
|
100
|
+
#
|
|
81
101
|
# @example
|
|
82
|
-
#
|
|
83
|
-
#
|
|
102
|
+
# Terminal.sh('make') do |line, type|
|
|
103
|
+
# Terminal.puts(type == :error ? "[red]#{line}[/]" : line)
|
|
104
|
+
# end
|
|
84
105
|
#
|
|
85
|
-
# @overload sh(*cmd, **options, &block)
|
|
86
|
-
# Run a command and stream output to a block.
|
|
87
106
|
# @param cmd [Array<String>] command and arguments
|
|
88
|
-
# @param
|
|
107
|
+
# @param env [Hash] environment variables for the command
|
|
108
|
+
# @param shell [true, false]
|
|
109
|
+
# when true the `cmd` is executed via the shell,
|
|
110
|
+
# when false, the executable is direct executed
|
|
111
|
+
# (see `Process.spawn` for this)
|
|
112
|
+
# @param input [String, IO, Array<String>, Enumerable, nil]
|
|
113
|
+
# data written to the command's standard input
|
|
114
|
+
# @param options [Hash] passed to `Process.spawn`
|
|
115
|
+
# (`:in`, `:out`, `:err` are ignored)
|
|
89
116
|
# @yield [line, type] called for each output line
|
|
90
117
|
# @yieldparam line [String] the output line
|
|
91
118
|
# @yieldparam type [Symbol] `:output` or `:error`
|
|
92
119
|
# @return [Process::Status] the exit status
|
|
93
|
-
# @
|
|
94
|
-
# Terminal.sh('make') do |line, type|
|
|
95
|
-
# Terminal.puts(type == :error ? "[red]#{line}[/]" : line)
|
|
96
|
-
# end
|
|
120
|
+
# @return [nil] when the command can not be executed
|
|
97
121
|
#
|
|
98
122
|
# @raise [ArgumentError] if no command is given
|
|
99
123
|
def sh(*cmd, **, &)
|
|
@@ -101,7 +125,8 @@ module Terminal
|
|
|
101
125
|
return Shell.run(*cmd, **, &) if block_given?
|
|
102
126
|
out = []
|
|
103
127
|
err = []
|
|
104
|
-
|
|
128
|
+
ret = Shell.run(*cmd, **) { |l, t| (t == :error ? err : out) << l }
|
|
129
|
+
[ret, out, err] if ret
|
|
105
130
|
end
|
|
106
131
|
|
|
107
132
|
#
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: terminal_rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.9
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Blumtritt
|
|
@@ -60,7 +60,7 @@ metadata:
|
|
|
60
60
|
yard.run: yard
|
|
61
61
|
source_code_uri: https://codeberg.org/mblumtritt/Terminal.rb
|
|
62
62
|
bug_tracker_uri: https://codeberg.org/mblumtritt/Terminal.rb/issues
|
|
63
|
-
documentation_uri: https://rubydoc.info/gems/terminal_rb/1.0.
|
|
63
|
+
documentation_uri: https://rubydoc.info/gems/terminal_rb/1.0.9
|
|
64
64
|
rdoc_options: []
|
|
65
65
|
require_paths:
|
|
66
66
|
- lib
|
|
@@ -75,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
75
75
|
- !ruby/object:Gem::Version
|
|
76
76
|
version: '0'
|
|
77
77
|
requirements: []
|
|
78
|
-
rubygems_version: 4.0.
|
|
78
|
+
rubygems_version: 4.0.21
|
|
79
79
|
specification_version: 4
|
|
80
80
|
summary: Fast terminal access with ANSI, CSIu, mouse events, BBCode, word-wise line
|
|
81
81
|
break support and much more.
|