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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 91c150887b2ab21e509a1d8da160bc89879d9750de3246a2f84ee0c87c5a3992
|
|
4
|
+
data.tar.gz: 640cc99db38489ca6ccb57f3d7605fdc199a581ff674f20cda145b189a62747c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9a2f57d900145e027ea44bad3bea885a46c37c451e31cd9c0623c0d4935022ab3eb3479a76095a094d0ed3acb91d4248c2052d7b635538cf95d540af3a2b8512
|
|
7
|
+
data.tar.gz: 17b15d64bb67d748202fe7db329565d9bce197c50c00bb6005e15483acea2461d59b11f16d9910c4092532299c49047817d9e4934d54b8e8d59f1580040d4314
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Terminal.rb v1.0.
|
|
1
|
+
# Terminal.rb v1.0.8
|
|
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.8/Terminal)
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
data/examples/key-codes.rb
CHANGED
|
@@ -6,7 +6,7 @@ Terminal.puts <<~TEXT
|
|
|
6
6
|
|
|
7
7
|
✅ [b bright_green]Terminal.rb[/b] — Key Codes:[/fg]
|
|
8
8
|
Press any key to display it's control code and name.
|
|
9
|
-
[bright_black]([b]#{Terminal.input_mode}[/b] mode
|
|
9
|
+
[bright_black]([b]#{Terminal.input_mode}[/b] mode – exit with ESC)[/fg]
|
|
10
10
|
|
|
11
11
|
TEXT
|
|
12
12
|
|
|
@@ -52,7 +52,7 @@ module Terminal
|
|
|
52
52
|
# Total number of lines.
|
|
53
53
|
#
|
|
54
54
|
# @attribute [r] line_count
|
|
55
|
-
# @return [Integer, nil]
|
|
55
|
+
# @return [Integer, nil] `nil` when not already calculated
|
|
56
56
|
def line_count = @buf&.size
|
|
57
57
|
|
|
58
58
|
# Index of the topmost visible line.
|
|
@@ -68,7 +68,7 @@ module Terminal
|
|
|
68
68
|
|
|
69
69
|
# Resize to the current terminal dimensions and redraw.
|
|
70
70
|
#
|
|
71
|
-
# @return [self, nil]
|
|
71
|
+
# @return [self, nil] `nil` when resize is not required
|
|
72
72
|
def resize_to_screen = _resize(*Terminal.size)
|
|
73
73
|
|
|
74
74
|
# Resize to specific dimensions (clamped to terminal bounds)
|
|
@@ -76,7 +76,7 @@ module Terminal
|
|
|
76
76
|
#
|
|
77
77
|
# @param rows [Integer] new row count
|
|
78
78
|
# @param columns [Integer] new column count
|
|
79
|
-
# @return [self, nil]
|
|
79
|
+
# @return [self, nil] `nil` when resize is not required
|
|
80
80
|
def resize(rows, columns)
|
|
81
81
|
_resize(
|
|
82
82
|
rows.clamp(1, Terminal.rows),
|
|
@@ -99,7 +99,7 @@ module Terminal
|
|
|
99
99
|
|
|
100
100
|
# Scroll to the beginning of the content.
|
|
101
101
|
#
|
|
102
|
-
# @return [self, nil]
|
|
102
|
+
# @return [self, nil] `nil` when already on begin
|
|
103
103
|
def begin
|
|
104
104
|
return draw unless @buf
|
|
105
105
|
return if @line_top == 0
|
|
@@ -109,7 +109,7 @@ module Terminal
|
|
|
109
109
|
|
|
110
110
|
# Scroll to the end of the content.
|
|
111
111
|
#
|
|
112
|
-
# @return [self, nil]
|
|
112
|
+
# @return [self, nil] `nil` when already on end
|
|
113
113
|
def end
|
|
114
114
|
return draw unless @buf
|
|
115
115
|
idx = @buf.size - @rows_visible
|
data/lib/terminal/ansi.rb
CHANGED
|
@@ -45,7 +45,7 @@ module Terminal
|
|
|
45
45
|
# Generate an ANSI escape sequence from attribute names or color
|
|
46
46
|
# indices.
|
|
47
47
|
#
|
|
48
|
-
# Accepts symbols (`:bold`,
|
|
48
|
+
# Accepts symbols (`:bold`, `:red`), strings (`"bold"`, `"#ff0000"`),
|
|
49
49
|
# or integers for 256-color palette (0-255 foreground, 256-511
|
|
50
50
|
# background, 512-767 underline color).
|
|
51
51
|
#
|
|
@@ -69,9 +69,9 @@ module Terminal
|
|
|
69
69
|
.map! do |arg|
|
|
70
70
|
case arg
|
|
71
71
|
when String
|
|
72
|
-
@
|
|
72
|
+
@attr_str_map[arg] || _invalid(arg)
|
|
73
73
|
when Symbol
|
|
74
|
-
@
|
|
74
|
+
@attr_sym_map[arg] || _invalid(arg)
|
|
75
75
|
when (0..255)
|
|
76
76
|
"38;5;#{arg}"
|
|
77
77
|
when (256..511)
|
|
@@ -122,17 +122,22 @@ module Terminal
|
|
|
122
122
|
#
|
|
123
123
|
# @example
|
|
124
124
|
# Terminal::Ansi.try_convert('bold red') # => "\e[1;31m"
|
|
125
|
-
# Terminal::Ansi.try_convert('invalid')
|
|
125
|
+
# Terminal::Ansi.try_convert('invalid') # => nil
|
|
126
126
|
#
|
|
127
127
|
# @param attributes [#to_s] space-separated attribute names
|
|
128
128
|
# @param separator [String] delimiter for splitting
|
|
129
|
-
# @return [String, nil] ANSI escape sequence, or
|
|
129
|
+
# @return [String, nil] ANSI escape sequence, or `nil` if any
|
|
130
130
|
# attribute is invalid
|
|
131
131
|
def try_convert(attributes, separator: ' ')
|
|
132
132
|
return unless attributes
|
|
133
|
-
|
|
134
|
-
attributes.
|
|
135
|
-
|
|
133
|
+
cache = @convert_cache[separator]
|
|
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?
|
|
137
|
+
# attributes.uniq!
|
|
138
|
+
codes = attributes.map { @attr_str_map[it] || break }
|
|
139
|
+
"\e[#{codes.join(';')}m" if codes
|
|
140
|
+
end
|
|
136
141
|
end
|
|
137
142
|
|
|
138
143
|
# Generate rainbow-colored text using true color (24-bit) ANSI
|
|
@@ -168,9 +173,9 @@ module Terminal
|
|
|
168
173
|
|
|
169
174
|
# Convert BBCode markup to ANSI escape codes.
|
|
170
175
|
#
|
|
171
|
-
# Tags like
|
|
172
|
-
#
|
|
173
|
-
# Escape a tag with a backslash:
|
|
176
|
+
# Tags like `[bold]` are converted to their ANSI equivalents;
|
|
177
|
+
# `[/bold]` or `[/]` resets. Unknown tags are left unchanged.
|
|
178
|
+
# Escape a tag with a backslash: `[\\bold]` renders as `[bold]`.
|
|
174
179
|
#
|
|
175
180
|
# @see .unbbcode
|
|
176
181
|
# @see .plain
|
|
@@ -213,7 +218,7 @@ module Terminal
|
|
|
213
218
|
next "[#{match}]"
|
|
214
219
|
end
|
|
215
220
|
next match_str if (match = match.split).empty?
|
|
216
|
-
next if match.all? { @
|
|
221
|
+
next if match.all? { @attr_str_map[it] }
|
|
217
222
|
match_str
|
|
218
223
|
end
|
|
219
224
|
end
|
|
@@ -237,7 +242,7 @@ module Terminal
|
|
|
237
242
|
next match_str if fc == '\\' || fc == ']'
|
|
238
243
|
match = Regexp.last_match(1) or next match_str
|
|
239
244
|
next match_str if (parts = match.split).empty?
|
|
240
|
-
next "[\\#{match}]" if parts.all? { @
|
|
245
|
+
next "[\\#{match}]" if parts.all? { @attr_str_map[it] }
|
|
241
246
|
match_str
|
|
242
247
|
end
|
|
243
248
|
end
|
|
@@ -271,9 +276,9 @@ module Terminal
|
|
|
271
276
|
attributes.all? do |arg|
|
|
272
277
|
case arg
|
|
273
278
|
when String
|
|
274
|
-
@
|
|
279
|
+
@attr_str_map[arg]
|
|
275
280
|
when Symbol
|
|
276
|
-
@
|
|
281
|
+
@attr_sym_map[arg]
|
|
277
282
|
when (0..767)
|
|
278
283
|
true
|
|
279
284
|
end
|
|
@@ -361,7 +366,7 @@ module Terminal
|
|
|
361
366
|
# Terminal::Ansi.cursor_pos(1, 1) # home position
|
|
362
367
|
# Terminal::Ansi.cursor_pos(10, 5) # row 10, column 5
|
|
363
368
|
#
|
|
364
|
-
# @param row [Integer, nil] row number;
|
|
369
|
+
# @param row [Integer, nil] row number; `nil` moves to home
|
|
365
370
|
# @param column [Integer, nil] column number
|
|
366
371
|
# @return (see .[])
|
|
367
372
|
def cursor_pos(row, column = nil)
|
|
@@ -396,7 +401,7 @@ module Terminal
|
|
|
396
401
|
# Erase part of the screen.
|
|
397
402
|
#
|
|
398
403
|
# @param part [Symbol] area to erase:
|
|
399
|
-
#
|
|
404
|
+
# `:all`, `:below`, `:above`, or `:scrollback`
|
|
400
405
|
# @return (see .[])
|
|
401
406
|
def screen_erase(part = :all) = "\e[#{@screen_erase[part]}J"
|
|
402
407
|
|
|
@@ -449,7 +454,7 @@ module Terminal
|
|
|
449
454
|
# Erase part of the current line.
|
|
450
455
|
#
|
|
451
456
|
# @param part [Symbol] area to erase:
|
|
452
|
-
#
|
|
457
|
+
# `:all`, `:to_end`, or `:to_start`
|
|
453
458
|
# @return (see .[])
|
|
454
459
|
def line_erase(part = :all) = "\e[#{@line_erase[part]}K"
|
|
455
460
|
|
|
@@ -472,7 +477,7 @@ module Terminal
|
|
|
472
477
|
# @see .link_end
|
|
473
478
|
#
|
|
474
479
|
# @param url [#to_s] the link URL
|
|
475
|
-
# @param params [Hash] optional link parameters (e.g.,
|
|
480
|
+
# @param params [Hash] optional link parameters (e.g., `id:`)
|
|
476
481
|
# @return (see .[])
|
|
477
482
|
def link_start(url, **params)
|
|
478
483
|
"\e]8;#{params.map { it.join('=') }.join(':')};#{url}\a"
|
|
@@ -520,12 +525,12 @@ module Terminal
|
|
|
520
525
|
# Terminal.raw_write(Terminal::Ansi.progress(:error)) # error state
|
|
521
526
|
# Terminal.raw_write(Terminal::Ansi.progress(nil)) # hide
|
|
522
527
|
#
|
|
523
|
-
# @param state [Symbol, Numeric,
|
|
524
|
-
# -
|
|
525
|
-
#
|
|
528
|
+
# @param state [Symbol, Numeric, true, false] progress state:
|
|
529
|
+
# - `:show` or `true` to show, `:err`/`:error`, `:warn`/`:warning`,
|
|
530
|
+
# `:indeterminate`, a `Numeric` (0-100) to set percentage, or
|
|
526
531
|
# any other value to hide
|
|
527
|
-
# @param percent [
|
|
528
|
-
#
|
|
532
|
+
# @param percent [#to_i] progress percentage (0-100, used with
|
|
533
|
+
# `:show` state)
|
|
529
534
|
# @return (see .[])
|
|
530
535
|
def progress(state, percent = 0)
|
|
531
536
|
case state
|
|
@@ -563,9 +568,9 @@ module Terminal
|
|
|
563
568
|
# @param fracd [Integer, nil] fractional denominator
|
|
564
569
|
# (must be > fracn, max 15)
|
|
565
570
|
# @param vertical [Symbol, Integer, nil] vertical alignment:
|
|
566
|
-
#
|
|
571
|
+
# `:top`, `:bottom`, `:middle`
|
|
567
572
|
# @param horizontal [Symbol, Integer, nil] horizontal alignment:
|
|
568
|
-
#
|
|
573
|
+
# `:left`, `:right`, `:center`
|
|
569
574
|
# @return (see .[])
|
|
570
575
|
def scale(
|
|
571
576
|
text,
|
|
@@ -625,7 +630,7 @@ module Terminal
|
|
|
625
630
|
|
|
626
631
|
@re_bbcode = /(?:\[((?~[\[\]]))\])/
|
|
627
632
|
|
|
628
|
-
|
|
633
|
+
color_map = {
|
|
629
634
|
# foreground
|
|
630
635
|
'black' => '30',
|
|
631
636
|
'red' => '31',
|
|
@@ -684,9 +689,9 @@ module Terminal
|
|
|
684
689
|
'on_bright_white' => '107'
|
|
685
690
|
}
|
|
686
691
|
|
|
687
|
-
@colors =
|
|
692
|
+
@colors = color_map.keys.map!(&:to_sym).sort!.freeze
|
|
688
693
|
|
|
689
|
-
|
|
694
|
+
@attr_base_map = {
|
|
690
695
|
'reset' => '',
|
|
691
696
|
'bold' => '1',
|
|
692
697
|
'faint' => '2',
|
|
@@ -743,7 +748,7 @@ module Terminal
|
|
|
743
748
|
'dashed_underline_off' => '4:0'
|
|
744
749
|
}
|
|
745
750
|
|
|
746
|
-
attr_alias = ->(t, s) {
|
|
751
|
+
attr_alias = ->(t, s) { @attr_base_map[t] = @attr_base_map[s] }
|
|
747
752
|
|
|
748
753
|
# aliases:
|
|
749
754
|
attr_alias['off', 'reset']
|
|
@@ -768,18 +773,18 @@ module Terminal
|
|
|
768
773
|
attr_alias['sup', 'superscript']
|
|
769
774
|
attr_alias['sub', 'subscript']
|
|
770
775
|
|
|
771
|
-
@attributes =
|
|
776
|
+
@attributes = @attr_base_map.keys.map!(&:to_sym).sort!.freeze
|
|
772
777
|
|
|
773
778
|
# shortcuts disable:
|
|
774
|
-
|
|
779
|
+
@attr_base_map.keys.each do |n|
|
|
775
780
|
attr_alias["/#{n.delete_suffix('_off')}", n] if n.end_with?('_off')
|
|
776
781
|
end
|
|
777
782
|
|
|
778
783
|
# additional shortcuts disable:
|
|
779
784
|
attr_alias['/', 'reset']
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
785
|
+
@attr_base_map['/bg'] = color_map['on_default']
|
|
786
|
+
@attr_base_map['/fg'] = color_map['default']
|
|
787
|
+
@attr_base_map['/ul'] = color_map['ul_default']
|
|
783
788
|
|
|
784
789
|
attr_alias['/b', 'bold_off']
|
|
785
790
|
attr_alias['/d', 'dim_off']
|
|
@@ -800,15 +805,20 @@ module Terminal
|
|
|
800
805
|
@cbase.default = '38'
|
|
801
806
|
@cbase.freeze
|
|
802
807
|
|
|
803
|
-
@
|
|
804
|
-
|
|
805
|
-
|
|
808
|
+
@attr_base_map.merge!(color_map).freeze
|
|
809
|
+
|
|
810
|
+
@attr_str_map = @attr_base_map.dup
|
|
811
|
+
@attr_str_map.default_proc =
|
|
812
|
+
proc do |h, s|
|
|
813
|
+
if h.size > 1024 # cache limit
|
|
814
|
+
h.keep_if { |key, _| @attr_base_map.key?(key) }
|
|
815
|
+
end
|
|
816
|
+
b, v = /\A(on|ul)?_?#?([[:xdigit:]]{1,6})\z/.match(s)&.captures
|
|
806
817
|
unless v
|
|
807
|
-
b = /\A(on|ul)?_?([a-z]{3,}[0-9]{0,3})\z/.match(
|
|
808
|
-
v = NAMED_COLORS[b[2]]
|
|
809
|
-
next "#{@cbase[b[1]]};#{v}"
|
|
818
|
+
b = /\A(on|ul)?_?([a-z]{3,}[0-9]{0,3})\z/.match(s) or next h[s] = nil
|
|
819
|
+
next h[s] = (v = NAMED_COLORS[b[2]]) ? "#{@cbase[b[1]]};#{v}" : nil
|
|
810
820
|
end
|
|
811
|
-
case v.size
|
|
821
|
+
h[s] = case v.size
|
|
812
822
|
when 1, 2
|
|
813
823
|
"#{@cbase[b]};5;#{v.hex}"
|
|
814
824
|
when 3
|
|
@@ -817,12 +827,12 @@ module Terminal
|
|
|
817
827
|
"#{@cbase[b]};2;#{v[0, 2].hex};#{v[2, 2].hex};#{v[4, 2].hex}"
|
|
818
828
|
end
|
|
819
829
|
end
|
|
820
|
-
attr_map.merge!(clr_map).keys.sort!.each { @attr_map[it] = attr_map[it] }
|
|
821
|
-
@attr_map.freeze
|
|
822
830
|
|
|
823
|
-
@
|
|
824
|
-
@
|
|
825
|
-
@
|
|
831
|
+
@attr_sym_map = @attr_base_map.transform_keys(&:to_sym)
|
|
832
|
+
@attr_sym_map.compare_by_identity
|
|
833
|
+
@attr_sym_map.default_proc = proc { |h, s| h[s] = @attr_str_map[s.to_s] }
|
|
834
|
+
|
|
835
|
+
@convert_cache = Hash.new { |h, k| h[k] = {} }
|
|
826
836
|
|
|
827
837
|
@screen_erase = { below: nil, above: '1', scrollback: '3' }
|
|
828
838
|
@screen_erase.default = '2'
|
data/lib/terminal/input/ansi.rb
CHANGED
|
@@ -6,14 +6,13 @@ module Terminal
|
|
|
6
6
|
attr_reader :input_mode
|
|
7
7
|
|
|
8
8
|
def on_key_event(mouse: false, mouse_move: false, focus: false)
|
|
9
|
-
raise('already reading key events') if (@in_recursion += 1) != 1
|
|
10
9
|
return unless block_given?
|
|
11
|
-
opts =
|
|
10
|
+
opts = nil
|
|
12
11
|
STDIN.noecho do
|
|
13
|
-
opts
|
|
12
|
+
opts = __input_options(mouse, mouse_move, focus)
|
|
14
13
|
while (raw = @in.getch)
|
|
15
14
|
next yield(KeyEvent[raw]) if raw != "\e"
|
|
16
|
-
while
|
|
15
|
+
while (nc = @in.read_nonblock(1, exception: false)).is_a?(String)
|
|
17
16
|
raw << nc
|
|
18
17
|
end
|
|
19
18
|
next yield(KeyEvent[raw]) if raw.rindex("\e") < 2
|
|
@@ -25,13 +24,15 @@ module Terminal
|
|
|
25
24
|
__input_error
|
|
26
25
|
false
|
|
27
26
|
ensure
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if opts
|
|
28
|
+
@in_opts.pop
|
|
29
|
+
@in&.syswrite(opts)
|
|
30
|
+
end
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
private
|
|
33
34
|
|
|
34
|
-
def
|
|
35
|
+
def __input_options(mouse, mouse_move, focus)
|
|
35
36
|
opts = (mouse ? ['1000'] : [])
|
|
36
37
|
# highlight: '1001'
|
|
37
38
|
# drag: '1002'
|
|
@@ -41,7 +42,19 @@ module Terminal
|
|
|
41
42
|
# sgr: '1006'
|
|
42
43
|
# urxvt: '1015'
|
|
43
44
|
# pixel: '1016'
|
|
44
|
-
|
|
45
|
+
if (last = @in_opts[-1])
|
|
46
|
+
if opts.empty?
|
|
47
|
+
@in.syswrite("#{last}l")
|
|
48
|
+
return "#{last}h"
|
|
49
|
+
end
|
|
50
|
+
@in_opts << (opts = "\e[?#{opts.join(';')};1006")
|
|
51
|
+
@in.syswrite("#{last}l#{opts}h")
|
|
52
|
+
return "#{opts}l#{last}h"
|
|
53
|
+
end
|
|
54
|
+
return if opts.empty?
|
|
55
|
+
@in_opts << (opts = "\e[?#{opts.join(';')};1006")
|
|
56
|
+
@in.syswrite("#{opts}h")
|
|
57
|
+
"#{opts}l"
|
|
45
58
|
end
|
|
46
59
|
end
|
|
47
60
|
|
data/lib/terminal/input/dumb.rb
CHANGED
|
@@ -5,8 +5,7 @@ module Terminal
|
|
|
5
5
|
module DumpInput
|
|
6
6
|
def input_mode = :dumb
|
|
7
7
|
|
|
8
|
-
def on_key_event(*
|
|
9
|
-
raise('already reading key events') if (@in_recursion += 1) != 1
|
|
8
|
+
def on_key_event(*)
|
|
10
9
|
return unless block_given?
|
|
11
10
|
while (raw = STDIN.getc)
|
|
12
11
|
opts = dumb_keys[raw.ord] if raw.size == 1
|
|
@@ -17,8 +16,6 @@ module Terminal
|
|
|
17
16
|
rescue IOError, SystemCallError
|
|
18
17
|
__input_error
|
|
19
18
|
false
|
|
20
|
-
ensure
|
|
21
|
-
@in_recursion -= 1
|
|
22
19
|
end
|
|
23
20
|
|
|
24
21
|
private
|
|
@@ -23,8 +23,8 @@ module Terminal
|
|
|
23
23
|
class KeyEvent
|
|
24
24
|
# The key identifier.
|
|
25
25
|
#
|
|
26
|
-
# @return [Symbol, String, nil] a named key like
|
|
27
|
-
#
|
|
26
|
+
# @return [Symbol, String, nil] a named key like `:Enter`, `:Tab`,
|
|
27
|
+
# `:MBLeft` (mouse), or a character string like `"a"`; `nil` for
|
|
28
28
|
# unknown sequences
|
|
29
29
|
attr_reader :key
|
|
30
30
|
|
|
@@ -61,8 +61,8 @@ module Terminal
|
|
|
61
61
|
|
|
62
62
|
# Mouse position when this is a mouse event.
|
|
63
63
|
#
|
|
64
|
-
# @return [Array<Integer, Integer>, nil]
|
|
65
|
-
# events,
|
|
64
|
+
# @return [Array<Integer, Integer>, nil] `[row, column]` for mouse
|
|
65
|
+
# events, `nil` for keyboard events
|
|
66
66
|
attr_reader :position
|
|
67
67
|
|
|
68
68
|
# The raw input sequence as received from the terminal.
|
|
@@ -85,8 +85,8 @@ module Terminal
|
|
|
85
85
|
# @private
|
|
86
86
|
# Destructuring-friendly array representation.
|
|
87
87
|
#
|
|
88
|
-
# @return [Array<String>]
|
|
89
|
-
# @return [Array<String, String>]
|
|
88
|
+
# @return [Array<String>] `[raw]` if {#simple?}
|
|
89
|
+
# @return [Array<String, String>] `[raw, name]` otherwise
|
|
90
90
|
def to_ary = simple? ? [@raw] : [@raw, @name]
|
|
91
91
|
|
|
92
92
|
# @return [String] the event {#name}
|
|
@@ -140,7 +140,7 @@ module Terminal
|
|
|
140
140
|
@names ||=
|
|
141
141
|
(@csiu.values + @csi.values + @ss3.values)
|
|
142
142
|
.uniq
|
|
143
|
-
.keep_if { Symbol
|
|
143
|
+
.keep_if { it.is_a?(Symbol) }
|
|
144
144
|
.sort!
|
|
145
145
|
).dup
|
|
146
146
|
end
|
data/lib/terminal/input.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Terminal
|
|
|
15
15
|
# Terminal.input_mode # => :csi_u
|
|
16
16
|
#
|
|
17
17
|
# @attribute [r] input_mode
|
|
18
|
-
# @return [Symbol]
|
|
18
|
+
# @return [Symbol] `:csi_u`, `:legacy`, `:dumb`, or `:error`
|
|
19
19
|
|
|
20
20
|
#
|
|
21
21
|
# @!endgroup
|
|
@@ -42,8 +42,7 @@ module Terminal
|
|
|
42
42
|
# @param focus [true, false] enable terminal focus/unfocus events
|
|
43
43
|
# @yield [event] called for each input event
|
|
44
44
|
# @yieldparam event [KeyEvent] the parsed key event
|
|
45
|
-
# @return [nil, false]
|
|
46
|
-
# @raise [RuntimeError] if already inside a key event loop
|
|
45
|
+
# @return [nil, false] `false` on I/O error
|
|
47
46
|
|
|
48
47
|
# @!method self.read_key_event
|
|
49
48
|
#
|
|
@@ -53,7 +52,7 @@ module Terminal
|
|
|
53
52
|
# event = Terminal.read_key_event
|
|
54
53
|
# puts "You pressed: #{event.name}"
|
|
55
54
|
#
|
|
56
|
-
# @return [KeyEvent, false] the next key event, or
|
|
55
|
+
# @return [KeyEvent, false] the next key event, or `false` on error
|
|
57
56
|
|
|
58
57
|
#
|
|
59
58
|
# @!endgroup
|
|
@@ -62,10 +61,10 @@ module Terminal
|
|
|
62
61
|
# @private
|
|
63
62
|
module Input
|
|
64
63
|
def input_mode
|
|
65
|
-
@in_recursion = 0
|
|
66
64
|
case @input_mode = __find_input_mode
|
|
67
65
|
when :legacy, :csi_u
|
|
68
66
|
@in = IO.console
|
|
67
|
+
@in_opts = []
|
|
69
68
|
require_relative('input/ansi')
|
|
70
69
|
extend AnsiInput
|
|
71
70
|
when :dumb
|
|
@@ -105,7 +104,7 @@ module Terminal
|
|
|
105
104
|
con = IO.console
|
|
106
105
|
STDIN.noecho do
|
|
107
106
|
return :legacy if con.syswrite("\e[>1u\e[?u\e[c") != 12
|
|
108
|
-
inp =
|
|
107
|
+
inp = ''.b
|
|
109
108
|
inp << con.getch until inp.rindex('c')
|
|
110
109
|
return :legacy unless inp.include?("\e[?1u")
|
|
111
110
|
end
|
data/lib/terminal/output/ansi.rb
CHANGED
|
@@ -43,6 +43,8 @@ module Terminal
|
|
|
43
43
|
|
|
44
44
|
def raw_write(object)
|
|
45
45
|
@out.syswrite(object)
|
|
46
|
+
rescue Errno::EINTR
|
|
47
|
+
retry # fixes rare condition found on MacOS Terminal.app
|
|
46
48
|
rescue IOError, SystemCallError
|
|
47
49
|
__output_error(nil)
|
|
48
50
|
end
|
|
@@ -84,6 +86,7 @@ module Terminal
|
|
|
84
86
|
suffix: nil
|
|
85
87
|
)
|
|
86
88
|
objects.flatten!
|
|
89
|
+
return @out.puts if objects.empty?
|
|
87
90
|
@out.puts(
|
|
88
91
|
Text::Formatter.format(
|
|
89
92
|
*objects,
|
data/lib/terminal/output/dumb.rb
CHANGED
|
@@ -67,13 +67,14 @@ module Terminal
|
|
|
67
67
|
suffix: nil
|
|
68
68
|
)
|
|
69
69
|
objects.flatten!
|
|
70
|
+
return @out.puts if objects.empty?
|
|
70
71
|
@out.puts(
|
|
71
72
|
Text::Formatter.format(
|
|
72
73
|
*objects,
|
|
73
74
|
ansi: false,
|
|
74
75
|
bbcode:,
|
|
75
76
|
spaces:,
|
|
76
|
-
eol
|
|
77
|
+
eol:,
|
|
77
78
|
align:,
|
|
78
79
|
width:,
|
|
79
80
|
height:,
|
data/lib/terminal/output.rb
CHANGED
|
@@ -66,7 +66,7 @@ module Terminal
|
|
|
66
66
|
#
|
|
67
67
|
# The terminal size.
|
|
68
68
|
#
|
|
69
|
-
# @return [Array<Integer, Integer>]
|
|
69
|
+
# @return [Array<Integer, Integer>] `[rows, columns]`
|
|
70
70
|
|
|
71
71
|
# Terminal width in columns.
|
|
72
72
|
#
|
|
@@ -89,7 +89,7 @@ module Terminal
|
|
|
89
89
|
# Current cursor position.
|
|
90
90
|
# This is only available when ANSI is supported ({ansi?} return true).
|
|
91
91
|
#
|
|
92
|
-
# @return [Array<Integer, Integer>, nil]
|
|
92
|
+
# @return [Array<Integer, Integer>, nil] `[row, column]` or `nil` if
|
|
93
93
|
# unavailable
|
|
94
94
|
|
|
95
95
|
# Whether the terminal has been resized since the last size query.
|
|
@@ -113,7 +113,7 @@ module Terminal
|
|
|
113
113
|
# @example
|
|
114
114
|
# Terminal << '[bold]Hello[/bold] World'
|
|
115
115
|
#
|
|
116
|
-
# @param object [#to_s, nil] the object to output;
|
|
116
|
+
# @param object [#to_s, nil] the object to output; `nil` is ignored
|
|
117
117
|
# @return [self]
|
|
118
118
|
|
|
119
119
|
# @!method print(*objects, bbcode: true)
|
|
@@ -162,7 +162,7 @@ module Terminal
|
|
|
162
162
|
# processing.
|
|
163
163
|
#
|
|
164
164
|
# @param object [#to_s] object to write
|
|
165
|
-
# @return [Integer, nil] bytes written, or
|
|
165
|
+
# @return [Integer, nil] bytes written, or `nil` on error
|
|
166
166
|
|
|
167
167
|
# @!method hide_cursor
|
|
168
168
|
#
|
|
@@ -4,9 +4,9 @@ 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
|
|
8
|
-
#
|
|
9
|
-
# input is read from the
|
|
7
|
+
# Stubs all Terminal I/O methods and provides `stdout`, `stdin`, and
|
|
8
|
+
# `stdoutstr` test helpers. Output is captured into the `stdout` array;
|
|
9
|
+
# input is read from the `stdin` array.
|
|
10
10
|
#
|
|
11
11
|
# @example Basic usage
|
|
12
12
|
# RSpec.describe MyClass do
|
|
@@ -22,15 +22,15 @@ require 'stringio'
|
|
|
22
22
|
# include_context 'with Terminal.rb', ansi: false, colors: 8,
|
|
23
23
|
# size: [40, 120]
|
|
24
24
|
#
|
|
25
|
-
# @param ansi [true, false] mock ANSI support (default:
|
|
25
|
+
# @param ansi [true, false] mock ANSI support (default: `true`)
|
|
26
26
|
# @param application [Symbol] mock terminal application
|
|
27
|
-
# (default:
|
|
28
|
-
# @param colors [Integer, Symbol] mock color depth; use
|
|
29
|
-
# for 16777216 (default:
|
|
27
|
+
# (default: `:kitty`)
|
|
28
|
+
# @param colors [Integer, Symbol] mock color depth; use `:true_color`
|
|
29
|
+
# for 16777216 (default: `256`)
|
|
30
30
|
# @param size [Array<Integer, Integer>] mock terminal size as
|
|
31
|
-
#
|
|
31
|
+
# `[rows, columns]` (default: `[25, 80]`)
|
|
32
32
|
# @param pos [Array<Integer, Integer>] mock cursor position as
|
|
33
|
-
#
|
|
33
|
+
# `[row, column]` (default: `[1, 1]`)
|
|
34
34
|
RSpec.shared_context 'with Terminal.rb' do |ansi: true, application: :kitty, colors: 256, size: [25, 80], pos: [1, 1]|
|
|
35
35
|
let(:stdout) { StringIO.new }
|
|
36
36
|
let(:stdout_lines) { stdout.string.lines(chomp: true) }
|
data/lib/terminal/shell.rb
CHANGED
|
@@ -77,7 +77,7 @@ module Terminal
|
|
|
77
77
|
return unless obj
|
|
78
78
|
return copy_writer(obj) if obj.respond_to?(:readpartial)
|
|
79
79
|
return copy_writer(obj.to_io) if obj.respond_to?(:to_io)
|
|
80
|
-
return array_writer(obj) if Array
|
|
80
|
+
return array_writer(obj) if obj.is_a?(Array)
|
|
81
81
|
if obj.respond_to?(:each)
|
|
82
82
|
return enum_writer(obj.enum_for(:each)) if obj.respond_to?(:enum_for)
|
|
83
83
|
return enum_writer(Enumerator.new { |y| obj.each { y << it } })
|