terminal_rb 0.9.7 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +1 -1
- data/lib/terminal/ansi.rb +36 -15
- data/lib/terminal/input/csiu_keys.rb +75 -79
- data/lib/terminal/input/dumb_keys.rb +3 -3
- data/lib/terminal/input/legacy_keys.rb +85 -63
- data/lib/terminal/input.rb +64 -49
- data/lib/terminal/text/char_width.rb +11 -13
- data/lib/terminal/text.rb +9 -8
- data/lib/terminal/version.rb +1 -1
- data/lib/terminal.rb +47 -29
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 82d95e0e2e8423c8bbb464019e8282bb589c5bc8fb02298ec529ff58e9ee67bb
|
4
|
+
data.tar.gz: bbb98fffca9d97e21a5440644a1ddc0b91dc60e945af287da60dbab07b6ae63b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44d16eba18775b6bbd208d19288bd44265a30301607136cafb959e985e59248820829383dd714c62f235880d30c46f4ce0a86a46d46dd8b6f05e042f73068c20
|
7
|
+
data.tar.gz: 80b0d17ece194d7fa8b36080314c21683bb1d7739344529bbf47c7da8afbc27c5c910a860393bf348edaef2a27e590d2b2505ab7d7d8934ae349eed152653144
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Terminal.rb 
|
2
2
|
|
3
|
-
Terminal access with
|
3
|
+
Terminal access with super fast ANSI control codes support and [BBCode-like](https://en.wikipedia.org/wiki/BBCode) embedded text attribute syntax.
|
4
4
|
|
5
5
|
- Gem: [rubygems.org](https://rubygems.org/gems/terminal_rb)
|
6
6
|
- Source: [codeberg.org](https://codeberg.org/mblumtritt/Terminal.rb)
|
data/lib/terminal/ansi.rb
CHANGED
@@ -226,7 +226,7 @@ module Terminal
|
|
226
226
|
str = str.to_s
|
227
227
|
return str.dup unless str.index('[')
|
228
228
|
str.gsub(BBCODE) do |match_str|
|
229
|
-
|
229
|
+
match = Regexp.last_match(1) or next match_str
|
230
230
|
next "[#{match[1..]}]" if match[0] == '\\'
|
231
231
|
try_convert(match) || match_str
|
232
232
|
end
|
@@ -246,7 +246,7 @@ module Terminal
|
|
246
246
|
str = str.to_s
|
247
247
|
return str.dup unless str.index('[')
|
248
248
|
str.gsub(BBCODE) do |match_str|
|
249
|
-
|
249
|
+
match = Regexp.last_match(1) or next match_str
|
250
250
|
next "[#{match[1..]}]" if match[0] == '\\'
|
251
251
|
next match_str if (match = match.split).empty?
|
252
252
|
next if match.all? { ATTRIBUTES[_1] || COLORS[_1] || _color(_1) }
|
@@ -274,7 +274,7 @@ module Terminal
|
|
274
274
|
end
|
275
275
|
str =
|
276
276
|
str.gsub(BBCODE) do |match_str|
|
277
|
-
|
277
|
+
match = Regexp.last_match(1) or next match_str
|
278
278
|
next "[#{match[1..]}]" if match[0] == '\\'
|
279
279
|
next match_str if (match = match.split).empty?
|
280
280
|
next if match.all? { ATTRIBUTES[_1] || COLORS[_1] || _color(_1) }
|
@@ -462,14 +462,7 @@ module Terminal
|
|
462
462
|
}K"
|
463
463
|
end
|
464
464
|
|
465
|
-
# Set
|
466
|
-
# This is not widely supported.
|
467
|
-
#
|
468
|
-
# @param [#to_s] title text
|
469
|
-
# @return (see cursor_up)
|
470
|
-
def title(title) = "\e]2;#{title}\a"
|
471
|
-
|
472
|
-
# Set tab title.
|
465
|
+
# Set (tab) title.
|
473
466
|
# This is not widely supported; works for
|
474
467
|
# Hyper,
|
475
468
|
# iTerm2,
|
@@ -478,9 +471,9 @@ module Terminal
|
|
478
471
|
# Tabby,
|
479
472
|
# WezTerm.
|
480
473
|
#
|
481
|
-
# @param
|
474
|
+
# @param [#to_s] title text
|
482
475
|
# @return (see cursor_up)
|
483
|
-
def
|
476
|
+
def title(title) = "\e]0;#{title}\a"
|
484
477
|
|
485
478
|
# Create a hyperlink.
|
486
479
|
# This is not widely supported; works for
|
@@ -677,15 +670,26 @@ module Terminal
|
|
677
670
|
LINE_ERASE_PREV = "#{cursor_prev_line(nil)}#{LINE_ERASE}".freeze
|
678
671
|
|
679
672
|
# @comment seems not widely supported:
|
680
|
-
# doubled
|
681
|
-
# doubled
|
673
|
+
# doubled: def cursor_column(column = 1) = "\e[#{column}`"
|
674
|
+
# doubled: def cursor_row(row = 1) = "\e[#{row}d"
|
675
|
+
# doubled: def cursor_pos(row, col) = "\e[#{row};#{col}f"
|
676
|
+
#
|
682
677
|
# def cursor_column_rel(columns = 1) = "\e[#{columns}a"
|
683
678
|
# def cursor_row_rel(rows = 1) = "\e[#{rows}e"
|
684
679
|
# def cursor_tab(count = 1) = "\e[#{column}I"
|
685
680
|
# def cursor_reverse_tab(count = 1) = "\e[#{count}Z"
|
681
|
+
#
|
682
|
+
# def lines_insert(count = 1) = "\e[#{count}L"
|
683
|
+
# def lines_delete(count = 1) = "\e[#{count}M"
|
684
|
+
#
|
685
|
+
# def chars_insert_spaces(count = 1) = "\e[#{count}@"
|
686
686
|
# def chars_delete(count = 1) = "\e[#{count}P"
|
687
687
|
# def chars_erase(count = 1) = "\e[#{count}X"
|
688
|
+
#
|
689
|
+
# def chars_repeat_last(count = 1) = "\e[#{count}b"
|
690
|
+
#
|
688
691
|
# def notify(title) = "\e]9;#{title}\a"
|
692
|
+
#
|
689
693
|
# def set_scroll_region(top = nil, bottom = nil) = "\e[#{top};#{bottom}r"
|
690
694
|
|
691
695
|
# @comment TODO:
|
@@ -695,5 +699,22 @@ module Terminal
|
|
695
699
|
# https://sw.kovidgoyal.net/kitty/color-stack
|
696
700
|
# https://sw.kovidgoyal.net/kitty/deccara
|
697
701
|
# https://sw.kovidgoyal.net/kitty/clipboard
|
702
|
+
|
703
|
+
# @comment other:
|
704
|
+
# "\eE" same as "\r\n"
|
705
|
+
# "\eD" same as "\n" but preserves X coord
|
706
|
+
# "\eM" reverse "\n"
|
707
|
+
# "\e[6n" report Cursor Position as "ESC \[ row ; col R"
|
708
|
+
# "\e[21t report window’s title as ESC ] l title ESC \"
|
709
|
+
# "\e[?Xl" set mouse mode "X":
|
710
|
+
# 9: X10_MOUSE
|
711
|
+
# 1000: VT200_MOUSE
|
712
|
+
# 1002: BTN_EVENT_MOUSE
|
713
|
+
# 1003: ANY_EVENT_MOUSE
|
714
|
+
# 1004: FOCUS_EVENT_MOUSE
|
715
|
+
# 1005: Xterm’s UTF8
|
716
|
+
# 1006: Xterm’s CSI-style
|
717
|
+
# 1015: Urxvt’s CSI-style
|
718
|
+
# "\e[?Xl" reset mouse mode "X"
|
698
719
|
end
|
699
720
|
end
|
@@ -3,83 +3,50 @@
|
|
3
3
|
module Terminal
|
4
4
|
module CSIuKeys
|
5
5
|
class << self
|
6
|
-
def
|
7
|
-
|
8
|
-
return if key[0] != "\e"
|
9
|
-
return C0_LEGACY[key[1].ord] if key.size == 2 # ESC ?
|
10
|
-
return csi(key) if key[1] == '['
|
11
|
-
SS3[key[2].ord] if key[1] == 'O' && key.size == 3 # ESC O ?
|
6
|
+
def key_name(key)
|
7
|
+
@key_name[key.ord] if key.size == 1
|
12
8
|
end
|
13
9
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
return ord == 0x5a ? 'Shift+Tab' : SS3[ord]
|
23
|
-
end
|
24
|
-
return if key.size < 5
|
25
|
-
return unless key.start_with?("\e[1;")
|
26
|
-
# ESC [ 1 ; <modifier> ?
|
27
|
-
name = SS3[type.ord] or return
|
28
|
-
mod = key[4..-2]
|
29
|
-
mod.empty? ? name : modifier(name, mod.to_i - 1)
|
30
|
-
end
|
31
|
-
|
32
|
-
def unicode(key)
|
33
|
-
with_modifier(key) do |ord|
|
34
|
-
ORD[ord] || UNICODE_ORD[ord] || ord.chr(Encoding::UTF_8)
|
10
|
+
def translate(esc)
|
11
|
+
return if esc[0] != '[' # ESC [ ...
|
12
|
+
return @csi1[esc[1].ord] if esc.size == 2 # ESC [ ?
|
13
|
+
return csi1(esc) if esc[1] == '1' && esc[2] == ';'
|
14
|
+
if esc[-1] == '~'
|
15
|
+
# ESC [ <code> ~
|
16
|
+
# ESC [ <code> ; <modifier> ~
|
17
|
+
return with_modifier(esc) { @csi[_1] }
|
35
18
|
end
|
19
|
+
return if esc[-1] != 'u'
|
20
|
+
# ESC [ <code> u
|
21
|
+
# ESC [ <code> ; <modifier> u
|
22
|
+
with_modifier(esc) { @csiu[_1] || _1.chr(Encoding::UTF_8) }
|
36
23
|
end
|
37
24
|
|
38
|
-
|
39
|
-
with_modifier(key) do |ord|
|
40
|
-
return 'F3' if ord == 13
|
41
|
-
ord == 57_427 ? 'KpBegin' : ORD[ord]
|
42
|
-
end
|
43
|
-
end
|
25
|
+
private
|
44
26
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
|
27
|
+
def csi1(esc)
|
28
|
+
# ESC [ 1 ; <modifier> [~ABCDEFHPQRS]
|
29
|
+
return if esc.size < 5
|
30
|
+
key = @csi1[esc[-1].ord] or return
|
31
|
+
return key if (mod = esc[3..-2].join.to_i - 1) < 1 # no mod
|
32
|
+
(@mods.filter_map { |b, n| n if mod.allbits?(b) } << key).join('+')
|
49
33
|
end
|
50
34
|
|
51
|
-
def
|
52
|
-
(
|
35
|
+
def with_modifier(esc)
|
36
|
+
return yield(esc[1..-2].join.to_i) if (idx = esc.index(';')).nil? # no mod
|
37
|
+
key = yield(esc[1..(idx - 1)].join.to_i) or return
|
38
|
+
return key if (mod = esc[(idx + 1)..-2].join.to_i - 1) < 1 # no mod
|
39
|
+
(@mods.filter_map { |b, n| n if mod.allbits?(b) } << key).join('+')
|
53
40
|
end
|
54
41
|
end
|
55
42
|
|
56
|
-
|
43
|
+
@csiu = {
|
57
44
|
0x02 => 'Ins',
|
58
|
-
0x03 => 'Del',
|
59
|
-
0x04 => 'Del',
|
60
|
-
0x05 => 'PageUp',
|
61
|
-
0x06 => 'PageDown',
|
62
|
-
0x07 => 'Home',
|
63
|
-
0x08 => 'End',
|
64
45
|
0x09 => 'Tab',
|
65
|
-
0x0b => 'F1',
|
66
|
-
0x0c => 'F2',
|
67
46
|
0x0d => 'Enter',
|
68
|
-
0x0e => 'F4',
|
69
|
-
0x0f => 'F5',
|
70
|
-
0x11 => 'F6',
|
71
|
-
0x12 => 'F7',
|
72
|
-
0x13 => 'F8',
|
73
|
-
0x14 => 'F9',
|
74
|
-
0x15 => 'F10',
|
75
|
-
0x17 => 'F11',
|
76
|
-
0x18 => 'F12',
|
77
47
|
0x1b => 'Esc',
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
# ESC [ ? u
|
82
|
-
UNICODE_ORD = {
|
48
|
+
0x20 => 'Space',
|
49
|
+
0x7f => 'Back',
|
83
50
|
57_376 => 'F13',
|
84
51
|
57_377 => 'F14',
|
85
52
|
57_378 => 'F15',
|
@@ -118,7 +85,7 @@ module Terminal
|
|
118
85
|
57_411 => 'KpMultiply',
|
119
86
|
57_412 => 'KpSubtract',
|
120
87
|
57_413 => 'KpAdd',
|
121
|
-
57_414 => 'KpEnter'
|
88
|
+
57_414 => 'Return', # 'KpEnter'
|
122
89
|
57_415 => 'KpEqual',
|
123
90
|
57_416 => 'KpSeparator',
|
124
91
|
57_417 => 'KpLeft',
|
@@ -160,21 +127,7 @@ module Terminal
|
|
160
127
|
57_454 => 'IsoLevel5Shift'
|
161
128
|
}.compare_by_identity.freeze
|
162
129
|
|
163
|
-
|
164
|
-
C0_LEGACY = {
|
165
|
-
0x00 => 'Ctrl+Space',
|
166
|
-
0x08 => 'Ctrl+Back',
|
167
|
-
0x09 => 'Alt+Tab',
|
168
|
-
0x0d => 'Alt+Enter',
|
169
|
-
0x1b => 'Alt+Esc',
|
170
|
-
0x20 => 'Alt+Space',
|
171
|
-
0x64 => 'Alt+Del',
|
172
|
-
0x7f => 'Alt+Back'
|
173
|
-
}.compare_by_identity.freeze
|
174
|
-
|
175
|
-
# ESC [ ?
|
176
|
-
# ESC [ O ?
|
177
|
-
SS3 = {
|
130
|
+
@csi1 = {
|
178
131
|
0x41 => 'Up', # A
|
179
132
|
0x42 => 'Down', # B
|
180
133
|
0x43 => 'Right', # C
|
@@ -187,7 +140,50 @@ module Terminal
|
|
187
140
|
0x53 => 'F4' # S
|
188
141
|
}.compare_by_identity.freeze
|
189
142
|
|
190
|
-
|
143
|
+
@csi = {
|
144
|
+
0x02 => 'Ins',
|
145
|
+
0x03 => 'Del',
|
146
|
+
0x05 => 'PageUp',
|
147
|
+
0x06 => 'PageDown',
|
148
|
+
0x07 => 'Home',
|
149
|
+
0x08 => 'End',
|
150
|
+
0x09 => 'Tab',
|
151
|
+
0x0b => 'F1',
|
152
|
+
0x0c => 'F2',
|
153
|
+
0x0d => 'F3',
|
154
|
+
0x0e => 'F4',
|
155
|
+
0x0f => 'F5',
|
156
|
+
0x11 => 'F6',
|
157
|
+
0x12 => 'F7',
|
158
|
+
0x13 => 'F8',
|
159
|
+
0x14 => 'F9',
|
160
|
+
0x15 => 'F10',
|
161
|
+
0x17 => 'F11',
|
162
|
+
0x18 => 'F12',
|
163
|
+
0x19 => 'F13',
|
164
|
+
0x1a => 'F14',
|
165
|
+
0x1b => 'F15',
|
166
|
+
0x1c => 'F16',
|
167
|
+
0x1d => 'F17',
|
168
|
+
0x1e => 'F18',
|
169
|
+
0x1f => 'F19',
|
170
|
+
0x20 => 'F20',
|
171
|
+
0x21 => 'F21',
|
172
|
+
0x22 => 'F22',
|
173
|
+
0x23 => 'F23',
|
174
|
+
0x24 => 'F24'
|
175
|
+
}.compare_by_identity.freeze
|
176
|
+
|
177
|
+
@key_name = {
|
178
|
+
0x04 => 'Del',
|
179
|
+
0x09 => 'Tab',
|
180
|
+
0x0d => 'Enter',
|
181
|
+
0x1b => 'Esc',
|
182
|
+
0x20 => 'Space',
|
183
|
+
0x7f => 'Back'
|
184
|
+
}.compare_by_identity.freeze
|
185
|
+
|
186
|
+
@mods = {
|
191
187
|
1 => 'Shift',
|
192
188
|
2 => 'Alt',
|
193
189
|
4 => 'Ctrl',
|
@@ -3,55 +3,102 @@
|
|
3
3
|
module Terminal
|
4
4
|
module LegacyKeys
|
5
5
|
class << self
|
6
|
-
def
|
7
|
-
|
8
|
-
return if key[0] != "\e"
|
9
|
-
return csi(key) if key[1] == '['
|
10
|
-
SS3[key[2].ord] if key[1] == 'O' && key.size == 3 # ESC O ?
|
6
|
+
def key_name(key)
|
7
|
+
@key_name[key.ord] if key.size == 1
|
11
8
|
end
|
12
9
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
if key.size == 3 # ESC [ ?
|
18
|
-
ord = key[2].ord
|
19
|
-
return ord == 0x5a ? 'Shift+Tab' : SS3[ord]
|
10
|
+
def translate(esc)
|
11
|
+
return "Alt+#{@esc1[esc[0].ord] || esc[0]}" if esc.size == 1 # ESC ?
|
12
|
+
if esc.size == 2 && (esc[0] == 'O' || esc[0] == '[')
|
13
|
+
return @ss3[esc[1].ord] # ESC O ? || ESC [ ?
|
20
14
|
end
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
15
|
+
if esc[0] == "\e" # ESC ESC ...
|
16
|
+
return "Alt+#{@esc1[esc[1].ord] || esc[1]}" if esc.size == 1
|
17
|
+
esc.shift
|
18
|
+
return (name = translate(esc)) ? "Alt+#{name}" : nil
|
19
|
+
end
|
20
|
+
return if esc[0] != '[' # ESC [ ...
|
21
|
+
return csi1(esc) if esc[1] == '1' && esc[2] == ';'
|
22
|
+
csi(esc) if esc[-1] == '~'
|
28
23
|
end
|
29
24
|
|
30
|
-
|
31
|
-
with_modifier(key) { LEGACY[_1] }
|
32
|
-
end
|
25
|
+
private
|
33
26
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
27
|
+
def csi(esc)
|
28
|
+
# ESC [ <code> ~
|
29
|
+
# ESC [ <code> ; <modifier> ~
|
30
|
+
return @csi[esc[1..-2].join.to_i] if (idx = esc.index(';')).nil? # no mod
|
31
|
+
key = @csi[esc[1..(idx - 1)].join.to_i] or return
|
32
|
+
return key if (mod = esc[(idx + 1)..-2].join.to_i - 1) < 1 # no mod
|
33
|
+
(@mods.filter_map { |b, n| n if mod.allbits?(b) } << key).join('+')
|
38
34
|
end
|
39
35
|
|
40
|
-
def
|
41
|
-
|
36
|
+
def csi1(esc)
|
37
|
+
# ESC [ 1 ; <modifier> [~ABCDEFHPQRS]
|
38
|
+
return if esc.size < 5
|
39
|
+
key = @ss3[esc[-1].ord] or return
|
40
|
+
return key if (mod = esc[3..-2].join.to_i - 1) < 1 # no mod
|
41
|
+
(@mods.filter_map { |b, n| n if mod.allbits?(b) } << key).join('+')
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
|
-
|
45
|
+
@csi = {
|
46
|
+
0x02 => 'Ins',
|
47
|
+
0x03 => 'Del',
|
48
|
+
0x05 => 'PageUp',
|
49
|
+
0x06 => 'PageDown',
|
50
|
+
0x07 => 'Home',
|
51
|
+
0x08 => 'End',
|
52
|
+
0x09 => 'Tab',
|
53
|
+
0x0b => 'F1',
|
54
|
+
0x0c => 'F2',
|
55
|
+
0x0d => 'F3',
|
56
|
+
0x0e => 'F4',
|
57
|
+
0x0f => 'F5',
|
58
|
+
0x11 => 'F6',
|
59
|
+
0x12 => 'F7',
|
60
|
+
0x13 => 'F8',
|
61
|
+
0x14 => 'F9',
|
62
|
+
0x15 => 'F10',
|
63
|
+
0x17 => 'F11',
|
64
|
+
0x18 => 'F12',
|
65
|
+
0x1b => 'Enter'
|
66
|
+
}.compare_by_identity.freeze
|
67
|
+
|
68
|
+
@ss3 = {
|
69
|
+
0x41 => 'Up', # A
|
70
|
+
0x42 => 'Down', # B
|
71
|
+
0x43 => 'Right', # C
|
72
|
+
0x44 => 'Left', # D
|
73
|
+
0x46 => 'End', # F
|
74
|
+
0x48 => 'Home', # H
|
75
|
+
0x50 => 'F1', # P
|
76
|
+
0x51 => 'F2', # Q
|
77
|
+
0x52 => 'F3', # R
|
78
|
+
0x53 => 'F4', # S
|
79
|
+
0x5a => 'Shift+Tab' # Z - not widely used
|
80
|
+
}.compare_by_identity.freeze
|
81
|
+
|
82
|
+
@esc1 = {
|
83
|
+
0x08 => 'Back',
|
84
|
+
0x09 => 'Tab',
|
85
|
+
0x10 => 'Ctrl+?',
|
86
|
+
0x0d => 'Enter',
|
87
|
+
0x1b => 'Esc',
|
88
|
+
0x20 => 'Space',
|
89
|
+
0x7f => 'Back'
|
90
|
+
}.compare_by_identity.freeze
|
91
|
+
|
92
|
+
@key_name = {
|
93
|
+
0x00 => 'Ctrl+Space',
|
47
94
|
0x01 => 'Ctrl+a',
|
48
95
|
0x02 => 'Ctrl+b',
|
49
96
|
0x03 => 'Ctrl+c',
|
50
|
-
0x04 => '
|
97
|
+
0x04 => 'Ctrl+d',
|
51
98
|
0x05 => 'Ctrl+e',
|
52
99
|
0x06 => 'Ctrl+f',
|
53
100
|
0x07 => 'Ctrl+g',
|
54
|
-
0x08 => 'Ctrl+
|
101
|
+
0x08 => 'Ctrl+Back',
|
55
102
|
0x09 => 'Tab',
|
56
103
|
0x0a => 'Ctrl+j',
|
57
104
|
0x0b => 'Ctrl+k',
|
@@ -70,41 +117,16 @@ module Terminal
|
|
70
117
|
0x18 => 'Ctrl+x',
|
71
118
|
0x19 => 'Ctrl+y',
|
72
119
|
0x1a => 'Ctrl+z',
|
73
|
-
0x1d => 'Ctrl+ü', # FIND AM
|
74
|
-
0x1e => 'Ctrl+^', # FIND AM
|
75
|
-
0x1f => 'Ctrl+-',
|
76
120
|
0x1b => 'Esc',
|
121
|
+
0x1c => 'Ctrl+4',
|
122
|
+
0x1d => 'Ctrl+5',
|
123
|
+
0x1e => 'Ctrl+6',
|
124
|
+
0x1f => 'Ctrl+7',
|
125
|
+
0x20 => 'Space',
|
77
126
|
0x7f => 'Back'
|
78
127
|
}.compare_by_identity.freeze
|
79
128
|
|
80
|
-
|
81
|
-
# ESC [ O ?
|
82
|
-
SS3 = {
|
83
|
-
0x41 => 'Up', # A
|
84
|
-
0x42 => 'Down', # B
|
85
|
-
0x43 => 'Right', # C
|
86
|
-
0x44 => 'Left', # D
|
87
|
-
0x46 => 'End', # F
|
88
|
-
0x48 => 'Home', # H
|
89
|
-
0x50 => 'F1', # P
|
90
|
-
0x51 => 'F2', # Q
|
91
|
-
0x52 => 'F3', # R
|
92
|
-
0x53 => 'F4' # S
|
93
|
-
}.compare_by_identity.freeze
|
94
|
-
|
95
|
-
LEGACY = {
|
96
|
-
0x03 => 'Del',
|
97
|
-
0x0f => 'F5',
|
98
|
-
0x11 => 'F6',
|
99
|
-
0x12 => 'F7',
|
100
|
-
0x13 => 'F8',
|
101
|
-
0x14 => 'F9',
|
102
|
-
0x15 => 'F10',
|
103
|
-
0x17 => 'F11',
|
104
|
-
0x18 => 'F12'
|
105
|
-
}.compare_by_identity.freeze
|
106
|
-
|
107
|
-
MODS = {
|
129
|
+
@mods = {
|
108
130
|
1 => 'Shift',
|
109
131
|
2 => 'Alt',
|
110
132
|
4 => 'Ctrl',
|
data/lib/terminal/input.rb
CHANGED
@@ -30,66 +30,63 @@ module Terminal
|
|
30
30
|
# @return [String] key name in `:named` mode
|
31
31
|
# @return [[String, String]] key code and key name in `:both` mode
|
32
32
|
def read_key(mode: :named)
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
read_csi_u(mode)
|
40
|
-
end
|
33
|
+
return if input_mode == :error
|
34
|
+
return read_dumb(mode == :both) if @input_mode == :dumb
|
35
|
+
return read_tty&.join if mode == :raw
|
36
|
+
key, name = @input_mode == :csi_u ? read_csiu : read_legacy
|
37
|
+
return unless key
|
38
|
+
mode == :both ? [key, name] : name || key
|
41
39
|
end
|
42
40
|
|
43
41
|
private
|
44
42
|
|
45
|
-
def read_dumb(
|
46
|
-
|
43
|
+
def read_dumb(both)
|
44
|
+
inp = STDIN.getc
|
45
|
+
name = DumbKeys.key_name(inp)
|
46
|
+
both ? [inp, name] : name || inp
|
47
47
|
rescue Interrupt
|
48
|
-
|
49
|
-
rescue IOError, SystemCallError
|
50
|
-
@input_mode = :error
|
51
|
-
nil
|
48
|
+
DumbKeys.key_name("\x05")
|
52
49
|
end
|
53
50
|
|
54
|
-
def read_legacy
|
55
|
-
|
56
|
-
def read_csi_u(mode)
|
57
|
-
if _write("\e[>1u")
|
58
|
-
key = read_tty
|
59
|
-
_write("\e[<u")
|
60
|
-
translate_key(key, mode, CSIuKeys)
|
61
|
-
else
|
62
|
-
@input_mode = :legacy
|
63
|
-
read_legacy(mode)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
def translate_key(key, mode, translator)
|
51
|
+
def read_legacy
|
52
|
+
key, *esc = read_tty
|
68
53
|
return unless key
|
69
|
-
return key if
|
70
|
-
|
71
|
-
mode == :both ? [key, name] : name || key
|
54
|
+
return key, LegacyKeys.key_name(key) if esc.empty?
|
55
|
+
[key + esc.join, LegacyKeys.translate(esc)]
|
72
56
|
end
|
73
57
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
|
58
|
+
def read_csiu
|
59
|
+
unless @csiu_set
|
60
|
+
if _write("\e[>1u").nil?
|
61
|
+
@input_mode = :legacy
|
62
|
+
return read_legacy
|
63
|
+
end
|
64
|
+
@csiu_set = true
|
65
|
+
at_exit { _write("\e[<u") if @csiu_set }
|
78
66
|
end
|
79
|
-
key
|
80
|
-
|
81
|
-
key
|
82
|
-
|
83
|
-
@input_mode = :error
|
84
|
-
nil
|
67
|
+
key, *esc = read_tty
|
68
|
+
return unless key
|
69
|
+
return key, CSIuKeys.key_name(key) if esc.empty?
|
70
|
+
[key + esc.join, CSIuKeys.translate(esc)]
|
85
71
|
end
|
86
72
|
|
87
73
|
def find_input_mode
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
74
|
+
case ENV['INPUT_MODE']
|
75
|
+
when 'dumb', 'DUMB'
|
76
|
+
:dumb
|
77
|
+
when 'legacy', 'LEGACY'
|
78
|
+
:legacy
|
79
|
+
when 'csi_u', 'CSI_U', 'csiu', 'CSIU'
|
80
|
+
:csi_u
|
81
|
+
else
|
82
|
+
return :dumb unless STDIN.tty?
|
83
|
+
STDIN.sync = true
|
84
|
+
return :legacy if !ansi? || _write("\e[>1u\e[?u\e[c").nil? || !csi_u?
|
85
|
+
_write("\e[<u")
|
86
|
+
:csi_u
|
87
|
+
end
|
88
|
+
rescue Interrupt
|
89
|
+
:legacy
|
93
90
|
rescue IOError, SystemCallError
|
94
91
|
:error
|
95
92
|
end
|
@@ -98,12 +95,30 @@ module Terminal
|
|
98
95
|
# iTerm2 returns result in two chunks :/
|
99
96
|
ret = false
|
100
97
|
while true
|
101
|
-
|
102
|
-
|
103
|
-
|
98
|
+
break if (str = STDIN.getch.dup) != "\e"
|
99
|
+
while (nc = STDIN.read_nonblock(1, exception: false))
|
100
|
+
String === nc ? str << nc : break
|
101
|
+
end
|
102
|
+
ret = true if str.include?("\e[?1u")
|
103
|
+
break if str[-1] == 'c'
|
104
104
|
end
|
105
105
|
ret
|
106
106
|
end
|
107
|
+
|
108
|
+
# nil, nil => error; key, [] => simple key; "\e", [] => esc sequence
|
109
|
+
def read_tty
|
110
|
+
key = [STDIN.getch]
|
111
|
+
return key if key[0] != "\e"
|
112
|
+
while (nc = STDIN.read_nonblock(1, exception: false))
|
113
|
+
String === nc ? key << nc : break
|
114
|
+
end
|
115
|
+
key
|
116
|
+
rescue Interrupt
|
117
|
+
key
|
118
|
+
rescue IOError, SystemCallError
|
119
|
+
@input_mode = :error
|
120
|
+
nil
|
121
|
+
end
|
107
122
|
end
|
108
123
|
|
109
124
|
dir = __dir__
|
@@ -132,7 +132,7 @@ module Terminal
|
|
132
132
|
0x450,
|
133
133
|
0x451,
|
134
134
|
0x482,
|
135
|
-
|
135
|
+
0x489,
|
136
136
|
0x590,
|
137
137
|
0x5bd,
|
138
138
|
0x5be,
|
@@ -359,6 +359,7 @@ module Terminal
|
|
359
359
|
0x109d,
|
360
360
|
0x10ff,
|
361
361
|
0x115f,
|
362
|
+
0x11ff,
|
362
363
|
0x135c,
|
363
364
|
0x135f,
|
364
365
|
0x1711,
|
@@ -414,8 +415,6 @@ module Terminal
|
|
414
415
|
0x1a7e,
|
415
416
|
0x1a7f,
|
416
417
|
0x1aaf,
|
417
|
-
0x1abd,
|
418
|
-
0x1abe,
|
419
418
|
0x1ace,
|
420
419
|
0x1aff,
|
421
420
|
0x1b03,
|
@@ -494,10 +493,6 @@ module Terminal
|
|
494
493
|
0x20ab,
|
495
494
|
0x20ac,
|
496
495
|
0x20cf,
|
497
|
-
0x20dc,
|
498
|
-
0x20e0,
|
499
|
-
0x20e1,
|
500
|
-
0x20e4,
|
501
496
|
0x20f0,
|
502
497
|
0x2102,
|
503
498
|
0x2103,
|
@@ -770,7 +765,7 @@ module Terminal
|
|
770
765
|
0xa48f,
|
771
766
|
0xa4c6,
|
772
767
|
0xa66e,
|
773
|
-
|
768
|
+
0xa672,
|
774
769
|
0xa673,
|
775
770
|
0xa67d,
|
776
771
|
0xa69d,
|
@@ -843,6 +838,10 @@ module Terminal
|
|
843
838
|
0xabed,
|
844
839
|
0xabff,
|
845
840
|
0xd7a3,
|
841
|
+
0xd7af,
|
842
|
+
0xd7c6,
|
843
|
+
0xd7ca,
|
844
|
+
0xd7fb,
|
846
845
|
0xdfff,
|
847
846
|
0xf8ff,
|
848
847
|
0xfaff,
|
@@ -1645,7 +1644,6 @@ module Terminal
|
|
1645
1644
|
0,
|
1646
1645
|
1,
|
1647
1646
|
2,
|
1648
|
-
1,
|
1649
1647
|
0,
|
1650
1648
|
1,
|
1651
1649
|
0,
|
@@ -1782,10 +1780,6 @@ module Terminal
|
|
1782
1780
|
1,
|
1783
1781
|
0,
|
1784
1782
|
1,
|
1785
|
-
0,
|
1786
|
-
1,
|
1787
|
-
0,
|
1788
|
-
1,
|
1789
1783
|
-1,
|
1790
1784
|
1,
|
1791
1785
|
-1,
|
@@ -2130,6 +2124,10 @@ module Terminal
|
|
2130
2124
|
1,
|
2131
2125
|
2,
|
2132
2126
|
1,
|
2127
|
+
0,
|
2128
|
+
1,
|
2129
|
+
0,
|
2130
|
+
1,
|
2133
2131
|
-1,
|
2134
2132
|
2,
|
2135
2133
|
1,
|
data/lib/terminal/text.rb
CHANGED
@@ -114,12 +114,13 @@ module Terminal
|
|
114
114
|
def char_width(char)
|
115
115
|
ord = char.ord
|
116
116
|
return CONTROL_CHAR_WIDTH[ord] || 2 if ord < 0x20
|
117
|
-
return 1 if ord < 0xa1
|
118
|
-
|
119
|
-
return
|
120
|
-
sco = char[1].ord
|
121
|
-
|
122
|
-
|
117
|
+
return 1 if char.size < 2 && ord < 0xa1
|
118
|
+
width = CharWidth[ord]
|
119
|
+
return @ambiguous_char_width if width == -1
|
120
|
+
sco = char[-1].ord
|
121
|
+
return width if sco != 0xff9e && sco != 0xff9f
|
122
|
+
# handle halfwidth dakuten/handakuten
|
123
|
+
char.size < 2 ? 1 : char.each_char.sum { char_width(_1) }
|
123
124
|
end
|
124
125
|
|
125
126
|
def lim_pairs(snippeds, limit)
|
@@ -452,7 +453,7 @@ module Terminal
|
|
452
453
|
SCAN_EXPR =
|
453
454
|
/\G(?:
|
454
455
|
(\r?\n)
|
455
|
-
| (\e\[[\d;:\?]*[
|
456
|
+
| (\e\[[\d;:\?]*[ABCDEFGHJKSTfhilmnsu])
|
456
457
|
| (\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
|
457
458
|
| (\s+)
|
458
459
|
| (\X)
|
@@ -460,7 +461,7 @@ module Terminal
|
|
460
461
|
|
461
462
|
WIDTH_SCANNER =
|
462
463
|
/\G(?:
|
463
|
-
(?:\e\[[\d;:\?]*[
|
464
|
+
(?:\e\[[\d;:\?]*[ABCDEFGHJKSTfhilmnsu])
|
464
465
|
| (?:\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
|
465
466
|
| (\s+)
|
466
467
|
| (\X)
|
data/lib/terminal/version.rb
CHANGED
data/lib/terminal.rb
CHANGED
@@ -182,7 +182,7 @@ module Terminal
|
|
182
182
|
# @param [#to_s] object object to write
|
183
183
|
# @return [Terminal] itself
|
184
184
|
def <<(object)
|
185
|
-
@out
|
185
|
+
@out.write(Ansi.bbcode(object)) if @out && object != nil
|
186
186
|
self
|
187
187
|
rescue IOError
|
188
188
|
@out = nil
|
@@ -197,17 +197,15 @@ module Terminal
|
|
197
197
|
# @return [nil]
|
198
198
|
def print(*objects, bbcode: true)
|
199
199
|
return unless @out
|
200
|
-
objects
|
201
|
-
|
202
|
-
return if (line = (bbcode ? @bbcode : @nobbcode)[line]).empty?
|
203
|
-
@out.write(line)
|
200
|
+
return @out.print(*objects) unless bbcode
|
201
|
+
objects.each { @out.write(Ansi.bbcode(_1)) }
|
204
202
|
nil
|
205
203
|
rescue IOError
|
206
204
|
@out = nil
|
207
205
|
end
|
208
206
|
|
209
207
|
# Writes the given objects to the terminal.
|
210
|
-
# Writes a newline after each
|
208
|
+
# Writes a newline after each object that does not already end with a
|
211
209
|
# newline sequence in it's String represenation.
|
212
210
|
# If called without any arguments, writes a newline only.
|
213
211
|
#
|
@@ -217,18 +215,10 @@ module Terminal
|
|
217
215
|
# @return (see print)
|
218
216
|
def puts(*objects, bbcode: true)
|
219
217
|
return unless @out
|
218
|
+
return @out.puts if objects.empty?
|
219
|
+
return @out.puts(objects) unless bbcode
|
220
220
|
objects.flatten!
|
221
|
-
|
222
|
-
@out.write("\n")
|
223
|
-
return
|
224
|
-
end
|
225
|
-
bbcode = bbcode ? @bbcode : @nobbcode
|
226
|
-
objects.each do |s|
|
227
|
-
next @out.write("\n") if s.nil?
|
228
|
-
@out.write(s = bbcode[s])
|
229
|
-
@out.write("\n") if s[-1] != "\n"
|
230
|
-
end
|
231
|
-
nil
|
221
|
+
objects.empty? ? @out.puts : @out.puts(objects.map! { Ansi.bbcode(_1) })
|
232
222
|
rescue IOError
|
233
223
|
@out = nil
|
234
224
|
end
|
@@ -261,20 +251,48 @@ module Terminal
|
|
261
251
|
@cc = 0
|
262
252
|
tty, @ansi = _determine_modes
|
263
253
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
254
|
+
unless tty.nil?
|
255
|
+
@out = STDOUT
|
256
|
+
@out.sync = true
|
257
|
+
if tty
|
258
|
+
@inf = STDOUT
|
259
|
+
@con = IO.console
|
260
|
+
Signal.trap('WINCH') { @size = nil } if Signal.list.key?('WINCH')
|
261
|
+
end
|
270
262
|
end
|
271
263
|
|
272
|
-
|
273
|
-
@
|
274
|
-
@
|
275
|
-
|
276
|
-
|
277
|
-
|
264
|
+
unless @ansi
|
265
|
+
@plain = ->(s) { Ansi.plain(s) }
|
266
|
+
@undecorate = ->(s) { Ansi.undecorate(s) }
|
267
|
+
|
268
|
+
class << self
|
269
|
+
alias << <<
|
270
|
+
def <<(object)
|
271
|
+
@out.write(Ansi.plain(object)) if @out && object != nil
|
272
|
+
self
|
273
|
+
rescue IOError
|
274
|
+
@out = nil
|
275
|
+
self
|
276
|
+
end
|
277
|
+
|
278
|
+
alias print print
|
279
|
+
def print(*objects, bbcode: true)
|
280
|
+
return if @out.nil? || objects.empty?
|
281
|
+
@out.print(*objects.map!(&(bbcode ? @plain : @undecorate)))
|
282
|
+
rescue IOError
|
283
|
+
@out = nil
|
284
|
+
end
|
285
|
+
|
286
|
+
alias puts puts
|
287
|
+
def puts(*objects, bbcode: true)
|
288
|
+
return unless @out
|
289
|
+
objects.flatten!
|
290
|
+
return @out.puts if objects.empty?
|
291
|
+
@out.puts(objects.map!(&(bbcode ? @plain : @undecorate)))
|
292
|
+
rescue IOError
|
293
|
+
@out = nil
|
294
|
+
end
|
295
|
+
end
|
278
296
|
end
|
279
297
|
|
280
298
|
dir = "#{__dir__}/terminal"
|
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: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Blumtritt
|
@@ -10,7 +10,7 @@ cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies: []
|
12
12
|
description: |
|
13
|
-
Terminal access with
|
13
|
+
Terminal access with super fast ANSI control codes support and
|
14
14
|
BBCode-like embedded text attribute syntax.
|
15
15
|
executables:
|
16
16
|
- bbcode
|
@@ -49,7 +49,7 @@ metadata:
|
|
49
49
|
rubygems_mfa_required: 'true'
|
50
50
|
source_code_uri: https://codeberg.org/mblumtritt/Terminal.rb
|
51
51
|
bug_tracker_uri: https://codeberg.org/mblumtritt/Terminal.rb/issues
|
52
|
-
documentation_uri: https://rubydoc.info/gems/terminal_rb/0.
|
52
|
+
documentation_uri: https://rubydoc.info/gems/terminal_rb/0.11.0/Terminal
|
53
53
|
rdoc_options: []
|
54
54
|
require_paths:
|
55
55
|
- lib
|
@@ -64,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
64
|
- !ruby/object:Gem::Version
|
65
65
|
version: '0'
|
66
66
|
requirements: []
|
67
|
-
rubygems_version: 3.
|
67
|
+
rubygems_version: 3.7.1
|
68
68
|
specification_version: 4
|
69
|
-
summary:
|
69
|
+
summary: Fast terminal access with ANSI and BBCode support.
|
70
70
|
test_files: []
|