terminal_rb 1.0.5 → 1.0.8
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/key-codes.rb +1 -1
- data/lib/terminal/ansi/screen_viewer.rb +5 -5
- data/lib/terminal/ansi.rb +57 -47
- data/lib/terminal/input/ansi.rb +21 -8
- data/lib/terminal/input/dumb.rb +1 -4
- data/lib/terminal/input/key_event.rb +7 -7
- data/lib/terminal/input.rb +5 -6
- data/lib/terminal/output/ansi.rb +3 -0
- data/lib/terminal/output/dumb.rb +2 -1
- data/lib/terminal/output.rb +4 -4
- data/lib/terminal/rspec/helper.rb +9 -9
- data/lib/terminal/shell.rb +1 -1
- data/lib/terminal/text/char_width.rb +2754 -2743
- data/lib/terminal/text/formatter.rb +162 -69
- data/lib/terminal/text.rb +60 -54
- data/lib/terminal/version.rb +1 -1
- data/lib/terminal.rb +27 -27
- metadata +4 -4
|
@@ -32,8 +32,8 @@ module Terminal
|
|
|
32
32
|
# # => [["Hello", 5]]
|
|
33
33
|
#
|
|
34
34
|
# @param (see #initialize)
|
|
35
|
-
# @param with_size [true, false] when
|
|
36
|
-
# return
|
|
35
|
+
# @param with_size [true, false] when `true`,
|
|
36
|
+
# return `[line, width]` pairs instead of plain strings
|
|
37
37
|
# @param (see #lines_with_size)
|
|
38
38
|
# @return (see #lines_with_size)
|
|
39
39
|
def self.[](
|
|
@@ -86,17 +86,23 @@ module Terminal
|
|
|
86
86
|
# @param ansi [true, false] recognize ANSI escape codes
|
|
87
87
|
# @param bbcode [true, false] process BBCode markup
|
|
88
88
|
# @param spaces [true, false] preserve whitespace;
|
|
89
|
-
# when
|
|
89
|
+
# when `false` leading/trailing spaces and multiple spaces are
|
|
90
90
|
# collapsed
|
|
91
91
|
# @param eol [true, false] preserve line endings;
|
|
92
|
-
# when
|
|
92
|
+
# when `false` newlines are treated as spaces
|
|
93
93
|
def initialize(*str, ansi: true, bbcode: true, spaces: true, eol: true)
|
|
94
94
|
@lex = []
|
|
95
95
|
return if str.empty?
|
|
96
96
|
ansi ? _generate_ansi(str, bbcode) : _generate(str, bbcode)
|
|
97
97
|
return if @lex.empty?
|
|
98
|
+
@keep_paragraph = true
|
|
98
99
|
@lex = _ignore_whitespace unless spaces
|
|
99
|
-
|
|
100
|
+
case eol
|
|
101
|
+
when false, nil
|
|
102
|
+
@lex = _ignore_newline
|
|
103
|
+
when :no_paragraph
|
|
104
|
+
@keep_paragraph = false
|
|
105
|
+
end
|
|
100
106
|
end
|
|
101
107
|
|
|
102
108
|
# Word-wrap and return lines with their display widths.
|
|
@@ -106,10 +112,10 @@ module Terminal
|
|
|
106
112
|
# fmt.lines_with_size(width: 5)
|
|
107
113
|
# # => [["Hello", 5], ["World", 5]]
|
|
108
114
|
#
|
|
109
|
-
# @param width [
|
|
110
|
-
#
|
|
115
|
+
# @param width [#to_i, nil] maximum line width in columns;
|
|
116
|
+
# `nil` returns unwrapped lines
|
|
111
117
|
# @return [Array<String, Integer>] pairs of
|
|
112
|
-
#
|
|
118
|
+
# `[line_text, display_width]`
|
|
113
119
|
# @raise (see #format)
|
|
114
120
|
def lines_with_size(width: nil)
|
|
115
121
|
return [] if @lex.empty?
|
|
@@ -125,9 +131,9 @@ module Terminal
|
|
|
125
131
|
# fmt.lines(width: 5)
|
|
126
132
|
# # => ["Hello", "World"]
|
|
127
133
|
#
|
|
128
|
-
# @param width [
|
|
134
|
+
# @param width [#to_i, nil] maximum line width in columns
|
|
129
135
|
# @return [Array<String>]
|
|
130
|
-
# @raise [ArgumentError] if
|
|
136
|
+
# @raise [ArgumentError] if `width` is zero or negative
|
|
131
137
|
def lines(width: nil) = lines_with_size(width:).map(&:first)
|
|
132
138
|
|
|
133
139
|
# Format lines with alignment, padding, and decorations.
|
|
@@ -138,11 +144,11 @@ module Terminal
|
|
|
138
144
|
# # => [" Hi "]
|
|
139
145
|
#
|
|
140
146
|
# @param align [Symbol, nil] text alignment:
|
|
141
|
-
#
|
|
142
|
-
# @param width [
|
|
143
|
-
# @param height [
|
|
147
|
+
# `:left`, `:right`, `:center`, or `nil` (no fill)
|
|
148
|
+
# @param width [#to_i, nil] output line width in columns
|
|
149
|
+
# @param height [#to_i, nil] number of output lines;
|
|
144
150
|
# negative values take lines from the end
|
|
145
|
-
# @param padding [
|
|
151
|
+
# @param padding [#to_i, Array, nil] CSS-style padding
|
|
146
152
|
# - 1 value: all sides;
|
|
147
153
|
# - 2 values: [vertical, horizontal];
|
|
148
154
|
# - 3 values: [top, horizontal, bottom];
|
|
@@ -150,7 +156,7 @@ module Terminal
|
|
|
150
156
|
# @param prefix [#to_s, nil] string prepended to each line
|
|
151
157
|
# @param suffix [#to_s, nil] string appended to each line
|
|
152
158
|
# @return [Array<String>] formatted output lines
|
|
153
|
-
# @raise [ArgumentError] if
|
|
159
|
+
# @raise [ArgumentError] if `width` is zero or negative
|
|
154
160
|
def format(
|
|
155
161
|
align: nil,
|
|
156
162
|
width: nil,
|
|
@@ -226,7 +232,7 @@ module Terminal
|
|
|
226
232
|
# fmt = Terminal::Text::Formatter.new("short\na longer line")
|
|
227
233
|
# fmt.max_line_width # => 13
|
|
228
234
|
#
|
|
229
|
-
# @return [Integer] widest line in columns,
|
|
235
|
+
# @return [Integer] widest line in columns, `0` when empty
|
|
230
236
|
def max_line_width
|
|
231
237
|
@max_line_width ||=
|
|
232
238
|
(@lex.empty? ? 0 : (@unlimited || _unlimited).max_by(&:last)[-1])
|
|
@@ -239,7 +245,7 @@ module Terminal
|
|
|
239
245
|
# Terminal::Text::Formatter.new('Hello World').to_s(width: 5)
|
|
240
246
|
# # => "Hello\nWorld"
|
|
241
247
|
#
|
|
242
|
-
# @param width [
|
|
248
|
+
# @param width [#to_i, nil] maximum line width in columns
|
|
243
249
|
# @return [String]
|
|
244
250
|
def to_s(width: nil) = lines(width:).join("\n")
|
|
245
251
|
|
|
@@ -298,7 +304,7 @@ module Terminal
|
|
|
298
304
|
size -= last.size
|
|
299
305
|
next add_line.call
|
|
300
306
|
end
|
|
301
|
-
add_line.call if size > 0
|
|
307
|
+
add_line.call if (current != EOP) || (@keep_paragraph || size > 0)
|
|
302
308
|
next
|
|
303
309
|
end
|
|
304
310
|
|
|
@@ -386,18 +392,41 @@ module Terminal
|
|
|
386
392
|
|
|
387
393
|
line.encode!(ENCODING) if line.encoding != ENCODING
|
|
388
394
|
|
|
389
|
-
line.scan(SCAN_REGEX) do |
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
395
|
+
line.scan(SCAN_REGEX) do |token|
|
|
396
|
+
b = token.getbyte(0)
|
|
397
|
+
|
|
398
|
+
if b > 0x20 # word chunk: an ASCII run or a single grapheme
|
|
399
|
+
# a single-byte token is printable ASCII: one character, one
|
|
400
|
+
# column - no need to ask for its width
|
|
401
|
+
size = token.bytesize
|
|
402
|
+
if size == 1
|
|
403
|
+
(last = @lex[-1]).is_a?(Word) and next last.add(token, 1)
|
|
404
|
+
next @lex << Word.new(token, 1)
|
|
405
|
+
end
|
|
406
|
+
# an ASCII run is the only multi-byte token which cannot be a
|
|
407
|
+
# grapheme cluster - there its width is simply its byte count
|
|
408
|
+
size = nil unless b < 0x80 && token.ascii_only?
|
|
409
|
+
if (last = @lex[-1]).is_a?(Word)
|
|
410
|
+
next size ? last.add(token, size, true) : last.add(token)
|
|
411
|
+
end
|
|
412
|
+
next @lex << Word.new(token, size, true) if size
|
|
413
|
+
next @lex << Word.new(token)
|
|
393
414
|
end
|
|
394
415
|
|
|
395
|
-
if
|
|
396
|
-
|
|
397
|
-
|
|
416
|
+
if SPACE_BYTE[b] && !(b == 0x0d && token.bytesize == 2)
|
|
417
|
+
size = token.bytesize
|
|
418
|
+
(last = @lex[-1]).is_a?(Space) and next last.size += size
|
|
419
|
+
next @lex << Space.new(size)
|
|
398
420
|
end
|
|
399
421
|
|
|
400
|
-
next @lex << EOL if
|
|
422
|
+
next @lex << EOL if b == 0x0a || b == 0x0d
|
|
423
|
+
|
|
424
|
+
# escape sequences are dropped without `ansi`, any other control
|
|
425
|
+
# character is text
|
|
426
|
+
next if b == 0x1b && ((c = token.getbyte(1)) == 0x5b || c == 0x5d)
|
|
427
|
+
|
|
428
|
+
(last = @lex[-1]).is_a?(Word) and next last.add(token)
|
|
429
|
+
@lex << Word.new(token)
|
|
401
430
|
end
|
|
402
431
|
|
|
403
432
|
@lex[-1] == EOL ? @lex[-1] = EOP : @lex << EOP
|
|
@@ -415,23 +444,46 @@ module Terminal
|
|
|
415
444
|
line.encode!(ENCODING) if line.encoding != ENCODING
|
|
416
445
|
csis = 0
|
|
417
446
|
|
|
418
|
-
line.scan(SCAN_REGEX) do |
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
447
|
+
line.scan(SCAN_REGEX) do |token|
|
|
448
|
+
b = token.getbyte(0)
|
|
449
|
+
|
|
450
|
+
if b > 0x20 # word chunk: an ASCII run or a single grapheme
|
|
451
|
+
# a single-byte token is printable ASCII: one character, one
|
|
452
|
+
# column - no need to ask for its width
|
|
453
|
+
size = token.bytesize
|
|
454
|
+
if size == 1
|
|
455
|
+
(last = @lex[-1]).is_a?(Word) and next last.add(token, 1)
|
|
456
|
+
next @lex << Word.new(token, 1)
|
|
457
|
+
end
|
|
458
|
+
# an ASCII run is the only multi-byte token which cannot be a
|
|
459
|
+
# grapheme cluster - there its width is simply its byte count
|
|
460
|
+
size = nil unless b < 0x80 && token.ascii_only?
|
|
461
|
+
if (last = @lex[-1]).is_a?(Word)
|
|
462
|
+
next size ? last.add(token, size, true) : last.add(token)
|
|
463
|
+
end
|
|
464
|
+
next @lex << Word.new(token, size, true) if size
|
|
465
|
+
next @lex << Word.new(token)
|
|
422
466
|
end
|
|
423
467
|
|
|
424
|
-
if
|
|
425
|
-
|
|
426
|
-
|
|
468
|
+
if SPACE_BYTE[b] && !(b == 0x0d && token.bytesize == 2)
|
|
469
|
+
size = token.bytesize
|
|
470
|
+
(last = @lex[-1]).is_a?(Space) and next last.size += size
|
|
471
|
+
next @lex << Space.new(size)
|
|
427
472
|
end
|
|
428
473
|
|
|
429
|
-
next @lex << EOL if
|
|
474
|
+
next @lex << EOL if b == 0x0a || b == 0x0d
|
|
430
475
|
|
|
431
|
-
|
|
476
|
+
if (c = token.getbyte(1)) == 0x5d # Osc
|
|
477
|
+
next @lex << Osc.new(token)
|
|
478
|
+
end
|
|
479
|
+
if c != 0x5b # Csi
|
|
480
|
+
# a bare escape is just text
|
|
481
|
+
(last = @lex[-1]).is_a?(Word) and next last.add(token)
|
|
482
|
+
next @lex << Word.new(token)
|
|
483
|
+
end
|
|
432
484
|
|
|
433
|
-
# Handle
|
|
434
|
-
if
|
|
485
|
+
# Handle Csi...
|
|
486
|
+
if token == "\e[m" || token == "\e[0m"
|
|
435
487
|
next if csis == 0
|
|
436
488
|
|
|
437
489
|
if @lex[-1].is_a?(Csi)
|
|
@@ -443,21 +495,17 @@ module Terminal
|
|
|
443
495
|
next @lex << CsiEnd
|
|
444
496
|
end
|
|
445
497
|
|
|
446
|
-
(last = @lex[-1]).is_a?(Csi) and next last.add(
|
|
498
|
+
(last = @lex[-1]).is_a?(Csi) and next last.add(token)
|
|
447
499
|
|
|
448
500
|
csis += 1
|
|
449
|
-
@lex << Csi.new(
|
|
501
|
+
@lex << Csi.new(token)
|
|
450
502
|
end
|
|
451
503
|
|
|
452
504
|
@lex.pop if (last = @lex[-1]) == EOL
|
|
453
505
|
|
|
454
506
|
next @lex << EOP if csis == 0
|
|
455
507
|
|
|
456
|
-
|
|
457
|
-
@lex.pop
|
|
458
|
-
else
|
|
459
|
-
@lex << CsiEnd
|
|
460
|
-
end
|
|
508
|
+
last.is_a?(Csi) && csis == 1 ? @lex.pop : @lex << CsiEnd
|
|
461
509
|
@lex << EOP
|
|
462
510
|
end
|
|
463
511
|
|
|
@@ -510,34 +558,58 @@ module Terminal
|
|
|
510
558
|
# @private
|
|
511
559
|
class Space
|
|
512
560
|
attr_accessor :size
|
|
513
|
-
def to_str = (' ' * @size)
|
|
514
|
-
def initialize = (@size =
|
|
561
|
+
def to_str = (@size == 1 ? ' ' : ' ' * @size)
|
|
562
|
+
def initialize(size = 1) = (@size = size)
|
|
515
563
|
def inc = (@size += 1)
|
|
516
564
|
def inspect = @size == 1 ? '<Space>' : "<Space #{@size}>"
|
|
517
565
|
end
|
|
518
566
|
|
|
519
567
|
# @private
|
|
568
|
+
# A chunk is either a single grapheme cluster or a run of printable
|
|
569
|
+
# ASCII, for which the display width equals the character count.
|
|
520
570
|
class Word
|
|
521
|
-
def to_str = (@to_str ||= @
|
|
522
|
-
def size = (@size ||= @sizes.sum)
|
|
523
|
-
def inspect = "<Word #{@sizes.sum}:#{@
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
571
|
+
def to_str = (@to_str ||= @chunks.size == 1 ? @chunks[0] : @chunks.join)
|
|
572
|
+
def size = (@size ||= @sizes.size == 1 ? @sizes[0] : @sizes.sum)
|
|
573
|
+
def inspect = "<Word #{@sizes.sum}:#{@chunks.join.inspect}>"
|
|
574
|
+
|
|
575
|
+
# `run` marks a chunk holding more than one character; only the lexer
|
|
576
|
+
# creates those and it knows without having to inspect the chunk again
|
|
577
|
+
def initialize(chunk, size = Text.char_width(chunk), run = false)
|
|
578
|
+
@chunks = [chunk]
|
|
527
579
|
@sizes = [size]
|
|
580
|
+
@runs = run
|
|
528
581
|
end
|
|
529
582
|
|
|
530
|
-
def add(
|
|
531
|
-
@
|
|
583
|
+
def add(chunk, size = Text.char_width(chunk), run = false)
|
|
584
|
+
@chunks << chunk
|
|
532
585
|
@sizes << size
|
|
586
|
+
@runs ||= run
|
|
533
587
|
nil
|
|
534
588
|
end
|
|
535
589
|
|
|
536
590
|
def split(width)
|
|
537
|
-
|
|
591
|
+
if @runs
|
|
592
|
+
# ASCII runs have to become single characters again to be split -
|
|
593
|
+
# a multi-character grapheme cluster must stay untouched
|
|
594
|
+
chars = []
|
|
595
|
+
sizes = []
|
|
596
|
+
@chunks.each_with_index do |chunk, idx|
|
|
597
|
+
if chunk.bytesize > 1 && chunk.getbyte(0) < 0x80 &&
|
|
598
|
+
chunk.ascii_only?
|
|
599
|
+
chunk.each_char { chars << it and sizes << 1 }
|
|
600
|
+
else
|
|
601
|
+
chars << chunk
|
|
602
|
+
sizes << @sizes[idx]
|
|
603
|
+
end
|
|
604
|
+
end
|
|
605
|
+
else
|
|
606
|
+
chars = @chunks
|
|
607
|
+
sizes = @sizes
|
|
608
|
+
end
|
|
609
|
+
return [self] if chars.size == 1
|
|
538
610
|
ret = last = size = nil
|
|
539
|
-
|
|
540
|
-
csize =
|
|
611
|
+
chars.each_with_index do |char, idx|
|
|
612
|
+
csize = sizes[idx]
|
|
541
613
|
next ret = [last = Word.new(char, size = csize)] unless ret
|
|
542
614
|
next last.add(char, csize) if (size += csize) <= width
|
|
543
615
|
ret << (last = Word.new(char, size = csize))
|
|
@@ -568,7 +640,7 @@ module Terminal
|
|
|
568
640
|
class << self
|
|
569
641
|
attr_reader :to_str, :size, :inspect
|
|
570
642
|
end
|
|
571
|
-
@to_str =
|
|
643
|
+
@to_str = Ansi::RESET
|
|
572
644
|
@size = 0
|
|
573
645
|
@inspect = '<CsiEnd>'
|
|
574
646
|
end
|
|
@@ -584,22 +656,43 @@ module Terminal
|
|
|
584
656
|
|
|
585
657
|
ENCODING = Encoding::UTF_8
|
|
586
658
|
|
|
587
|
-
|
|
659
|
+
space_cache =
|
|
660
|
+
Hash.new { |h, s| h[s] = s < 1 ? '' : ' ' * s }.compare_by_identity
|
|
661
|
+
space_cache[0]
|
|
662
|
+
space_cache[1]
|
|
663
|
+
space_cache[2]
|
|
664
|
+
SPACE_CACHE = space_cache.freeze
|
|
665
|
+
|
|
666
|
+
# A run of printable ASCII is only taken when the following character is
|
|
667
|
+
# ASCII too: everything which can extend a grapheme cluster (combining
|
|
668
|
+
# marks, ZWJ, variation selectors, ...) is non-ASCII, so a run can never
|
|
669
|
+
# swallow the base of a cluster `\X` would have kept together.
|
|
670
|
+
SCAN_REGEX =
|
|
671
|
+
/
|
|
672
|
+
[\x21-\x7e]+(?=[\x00-\x7f]|\z)
|
|
673
|
+
| \x20+
|
|
674
|
+
| \r?\n
|
|
675
|
+
| \e\[[\x30-\x3f]*[\x20-\x2f]*[a-zA-Z]
|
|
676
|
+
| \e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\)
|
|
677
|
+
| \s
|
|
678
|
+
| \X
|
|
679
|
+
/x
|
|
680
|
+
|
|
681
|
+
SPACE_BYTE =
|
|
588
682
|
Hash
|
|
589
|
-
.new
|
|
683
|
+
.new(false)
|
|
590
684
|
.compare_by_identity
|
|
685
|
+
.merge!(
|
|
686
|
+
0x20 => true,
|
|
687
|
+
0x09 => true,
|
|
688
|
+
0x0b => true,
|
|
689
|
+
0x0c => true,
|
|
690
|
+
0x0d => true
|
|
691
|
+
)
|
|
591
692
|
.freeze
|
|
592
693
|
|
|
593
|
-
SCAN_REGEX =
|
|
594
|
-
/\G(?:
|
|
595
|
-
(\r?\n)
|
|
596
|
-
| (\s)
|
|
597
|
-
| (\e\[[\x30-\x3f]*[\x20-\x2f]*[a-zA-Z])
|
|
598
|
-
| (\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
|
|
599
|
-
| (\X)
|
|
600
|
-
)/x
|
|
601
|
-
|
|
602
694
|
private_constant(
|
|
695
|
+
:SPACE_BYTE,
|
|
603
696
|
:Space,
|
|
604
697
|
:Word,
|
|
605
698
|
:Osc,
|
data/lib/terminal/text.rb
CHANGED
|
@@ -28,8 +28,14 @@ module Terminal
|
|
|
28
28
|
# Terminal::Text.ambiguous_char_width # => 1
|
|
29
29
|
# Terminal::Text.ambiguous_char_width = 2 # for CJK terminals
|
|
30
30
|
#
|
|
31
|
-
# @return [Integer] default:
|
|
32
|
-
|
|
31
|
+
# @return [Integer] default: `1`
|
|
32
|
+
attr_reader :ambiguous_char_width
|
|
33
|
+
|
|
34
|
+
# @attribute [w] ambiguous_char_width
|
|
35
|
+
def ambiguous_char_width=(value)
|
|
36
|
+
autoload?(:CharWidth) or CharWidth.clear
|
|
37
|
+
@ambiguous_char_width = value
|
|
38
|
+
end
|
|
33
39
|
|
|
34
40
|
# Calculate the display width of a string in terminal columns.
|
|
35
41
|
#
|
|
@@ -206,66 +212,66 @@ module Terminal
|
|
|
206
212
|
def char_width(char)
|
|
207
213
|
ord = char.ord
|
|
208
214
|
return @ctrlchar_width[ord] if ord < 0x20
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
215
|
+
# CharWidth knows this rule too - a comparison just beats a memo hit
|
|
216
|
+
return ord < 0xa1 ? 1 : CharWidth[ord] if char.bytesize == 1
|
|
217
|
+
return 2 if ord == 0x1f3f3 && @very_special_flags.include?(char)
|
|
212
218
|
sum = 0
|
|
213
|
-
|
|
214
|
-
char.
|
|
215
|
-
next
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
sum += CharWidth[ord] || @ambiguous_char_width
|
|
219
|
+
skip = false
|
|
220
|
+
char.each_codepoint do |cp|
|
|
221
|
+
next skip = false if skip
|
|
222
|
+
next skip = true if cp == 0x200d # zero width joiner
|
|
223
|
+
sum += CharWidth[cp]
|
|
219
224
|
end
|
|
220
225
|
sum
|
|
221
226
|
end
|
|
222
227
|
end
|
|
223
228
|
|
|
224
|
-
@
|
|
229
|
+
@very_special_flags = [
|
|
230
|
+
"🏳\u{FE0F}\u{200D}🌈", # rainbow flag
|
|
231
|
+
"🏳\u{FE0F}\u{200D}⚧\u{FE0F}" # transgender flag
|
|
232
|
+
].freeze
|
|
225
233
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
)
|
|
264
|
-
.freeze
|
|
234
|
+
# indexed by ordinal, only ever asked for control characters
|
|
235
|
+
@ctrlchar_width = [
|
|
236
|
+
0,
|
|
237
|
+
1,
|
|
238
|
+
1,
|
|
239
|
+
1,
|
|
240
|
+
1,
|
|
241
|
+
0,
|
|
242
|
+
1,
|
|
243
|
+
0,
|
|
244
|
+
0,
|
|
245
|
+
1,
|
|
246
|
+
0,
|
|
247
|
+
0,
|
|
248
|
+
0,
|
|
249
|
+
0,
|
|
250
|
+
0,
|
|
251
|
+
0,
|
|
252
|
+
1,
|
|
253
|
+
1,
|
|
254
|
+
1,
|
|
255
|
+
1,
|
|
256
|
+
1,
|
|
257
|
+
1,
|
|
258
|
+
1,
|
|
259
|
+
1,
|
|
260
|
+
1,
|
|
261
|
+
1,
|
|
262
|
+
1,
|
|
263
|
+
1,
|
|
264
|
+
1,
|
|
265
|
+
1,
|
|
266
|
+
1,
|
|
267
|
+
1
|
|
268
|
+
].freeze
|
|
269
|
+
|
|
270
|
+
@ambiguous_char_width = 1
|
|
265
271
|
|
|
266
|
-
|
|
267
|
-
autoload :CharWidth,
|
|
268
|
-
autoload :UNICODE_VERSION,
|
|
272
|
+
char_width_file = "#{__dir__}/text/char_width.rb"
|
|
273
|
+
autoload :CharWidth, char_width_file
|
|
274
|
+
autoload :UNICODE_VERSION, char_width_file
|
|
269
275
|
private_constant :CharWidth
|
|
270
276
|
end
|
|
271
277
|
end
|
data/lib/terminal/version.rb
CHANGED
data/lib/terminal.rb
CHANGED
|
@@ -30,32 +30,32 @@ module Terminal
|
|
|
30
30
|
# Detect the terminal emulator application.
|
|
31
31
|
#
|
|
32
32
|
# Known applications:
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
33
|
+
# `:alacritty`,
|
|
34
|
+
# `:amiga`,
|
|
35
|
+
# `:code_edit`,
|
|
36
|
+
# `:dg_unix`,
|
|
37
|
+
# `:docker`,
|
|
38
|
+
# `:fluent`,
|
|
39
|
+
# `:hpterm`,
|
|
40
|
+
# `:hyper`,
|
|
41
|
+
# `:iterm`,
|
|
42
|
+
# `:kitty`,
|
|
43
|
+
# `:macos`,
|
|
44
|
+
# `:mintty`,
|
|
45
|
+
# `:ms_terminal`,
|
|
46
|
+
# `:ncr260`,
|
|
47
|
+
# `:nsterm`,
|
|
48
|
+
# `:terminator`,
|
|
49
|
+
# `:terminology`,
|
|
50
|
+
# `:termite`,
|
|
51
|
+
# `:vscode`,
|
|
52
|
+
# `:vt100`,
|
|
53
|
+
# `:warp`,
|
|
54
|
+
# `:wyse`,
|
|
55
|
+
# `:xnuppc`
|
|
56
56
|
#
|
|
57
|
-
# Any unrecognized
|
|
58
|
-
# symbol. Returns
|
|
57
|
+
# Any unrecognized `TERM_PROGRAM` value is converted to a sanitized
|
|
58
|
+
# symbol. Returns `nil` when no terminal can be identified.
|
|
59
59
|
#
|
|
60
60
|
# @example
|
|
61
61
|
# Terminal.application # => :kitty
|
|
@@ -77,7 +77,7 @@ module Terminal
|
|
|
77
77
|
# @param cmd [Array<String>] command and arguments
|
|
78
78
|
# @param options [Hash] options passed to the shell runner
|
|
79
79
|
# @return [Array<Process::Status, Array<String>, Array<String>>]
|
|
80
|
-
#
|
|
80
|
+
# `[status, stdout_lines, stderr_lines]`
|
|
81
81
|
# @example
|
|
82
82
|
# status, output, error = Terminal.sh('echo', 'hello')
|
|
83
83
|
# output # => ["hello\n"]
|
|
@@ -88,7 +88,7 @@ module Terminal
|
|
|
88
88
|
# @param options [Hash] options passed to the shell runner
|
|
89
89
|
# @yield [line, type] called for each output line
|
|
90
90
|
# @yieldparam line [String] the output line
|
|
91
|
-
# @yieldparam type [Symbol]
|
|
91
|
+
# @yieldparam type [Symbol] `:output` or `:error`
|
|
92
92
|
# @return [Process::Status] the exit status
|
|
93
93
|
# @example
|
|
94
94
|
# Terminal.sh('make') do |line, type|
|
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.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Blumtritt
|
|
@@ -60,13 +60,13 @@ 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.8
|
|
64
64
|
rdoc_options: []
|
|
65
65
|
require_paths:
|
|
66
66
|
- lib
|
|
67
67
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
68
|
requirements:
|
|
69
|
-
- - "
|
|
69
|
+
- - ">="
|
|
70
70
|
- !ruby/object:Gem::Version
|
|
71
71
|
version: '3.4'
|
|
72
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
@@ -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.18
|
|
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.
|