terminal_rb 0.17.1 → 0.18.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 +2 -2
- data/lib/terminal/input/ansi.rb +47 -0
- data/lib/terminal/input/dumb.rb +42 -0
- data/lib/terminal/input.rb +88 -179
- data/lib/terminal/output/ansi.rb +114 -0
- data/lib/terminal/output/dumb.rb +76 -0
- data/lib/terminal/output.rb +238 -0
- data/lib/terminal/rspec/helper.rb +7 -3
- data/lib/terminal/version.rb +1 -1
- data/lib/terminal.rb +6 -290
- data/terminal_rb.gemspec +3 -1
- metadata +8 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a718915d07704084ceeb6a395a4a40d97feb52c17048f8024b3493afb0d8a1c5
|
|
4
|
+
data.tar.gz: 252bb2965c7b5d33e58fb0752e1bb171cbe259de315494f5e547803554c843a7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9d006c4e463e3522b3e3c63bd343d2806cf4dd11bdaa6e9cf82b73ee7fe84d40296b9d919d3b0ea7664edca354e28e99302e0e01b87f04e5fa0dd5ccecd4a6b2
|
|
7
|
+
data.tar.gz: bb3a2b01dbd455bd66c2dbddcbd29dfca325d134b40b34b1de14168cb8c0c20be001f0bd20e78cd64f18a56bde7fe17a73673e7d7c249cb446cafeaef1c27f80
|
data/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Terminal.rb supports you with input and output on your terminal. Simple [BBCode]
|
|
|
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/
|
|
7
|
+
- Help: [rubydoc.info](https://rubydoc.info/gems/terminal_rb/0.18.0/Terminal)
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -60,7 +60,7 @@ TEXT
|
|
|
60
60
|
# => "attribute syntax."]
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
Have a look at the [examples](
|
|
63
|
+
Have a look at the [examples](https://codeberg.org/mblumtritt/Terminal.rb/src/branch/main/examples) directory to learn from code.
|
|
64
64
|
|
|
65
65
|
## Installation
|
|
66
66
|
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Terminal
|
|
4
|
+
module AnsiInput
|
|
5
|
+
attr_reader :input_mode
|
|
6
|
+
|
|
7
|
+
def on_key_event(mouse: false, mouse_move: false, focus: false)
|
|
8
|
+
raise('already reading key events') if (@in_recursion += 1) != 1
|
|
9
|
+
return unless block_given?
|
|
10
|
+
opts = __in_option(mouse, mouse_move, focus)
|
|
11
|
+
opts &&= @in.syswrite("#{opts}h") ? "#{opts}l" : nil
|
|
12
|
+
while (raw = @in.getch)
|
|
13
|
+
(yield(KeyEvent[raw]) ? next : break) if raw != "\e"
|
|
14
|
+
lesci = 0
|
|
15
|
+
while String === (nc = @in.read_nonblock(1, exception: false))
|
|
16
|
+
lesci = raw.size if nc == "\e"
|
|
17
|
+
raw << nc
|
|
18
|
+
end
|
|
19
|
+
(yield(KeyEvent[raw]) ? next : break) if lesci < 2
|
|
20
|
+
break unless raw[1..].split("\e").all? { yield(KeyEvent["\e#{_1}"]) }
|
|
21
|
+
end
|
|
22
|
+
rescue IOError, SystemCallError
|
|
23
|
+
__input_error
|
|
24
|
+
false
|
|
25
|
+
ensure
|
|
26
|
+
@in_recursion -= 1
|
|
27
|
+
@in&.syswrite(opts) if opts
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def __in_option(mouse, mouse_move, focus)
|
|
33
|
+
opts = +(mouse ? '1000' : '')
|
|
34
|
+
# highlight: '1001'
|
|
35
|
+
# drag: '1002'
|
|
36
|
+
opts << ';1003' if mouse_move
|
|
37
|
+
opts << ';1004' if focus
|
|
38
|
+
# ext: '1005'
|
|
39
|
+
# sgr: '1006'
|
|
40
|
+
# urxvt: '1015'
|
|
41
|
+
# pixel: '1016'
|
|
42
|
+
"\e[?#{opts};1006" unless opts.empty?
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private_constant :AnsiInput
|
|
47
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Terminal
|
|
4
|
+
module DumpInput
|
|
5
|
+
def input_mode = :dumb
|
|
6
|
+
|
|
7
|
+
def on_key_event(*_)
|
|
8
|
+
raise('already reading key events') if (@in_recursion += 1) != 1
|
|
9
|
+
return unless block_given?
|
|
10
|
+
while (raw = __getc)
|
|
11
|
+
opts = dumb_keys[raw.ord] if raw.size == 1
|
|
12
|
+
return unless yield(KeyEvent.new(raw, *opts))
|
|
13
|
+
end
|
|
14
|
+
rescue IOError, SystemCallError
|
|
15
|
+
__input_error
|
|
16
|
+
false
|
|
17
|
+
ensure
|
|
18
|
+
@in_recursion -= 1
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def dumb_keys
|
|
24
|
+
@dumb_keys ||= {
|
|
25
|
+
0x05 => ['c', 4],
|
|
26
|
+
0x08 => :Back,
|
|
27
|
+
0x09 => :Tab,
|
|
28
|
+
0x0a => :Enter,
|
|
29
|
+
0x0d => :Return,
|
|
30
|
+
0x1b => :Esc
|
|
31
|
+
}.compare_by_identity.freeze
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def __getc
|
|
35
|
+
STDIN.getc
|
|
36
|
+
rescue Interrupt
|
|
37
|
+
"\x05"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private_constant :DumpInput
|
|
42
|
+
end
|
data/lib/terminal/input.rb
CHANGED
|
@@ -3,208 +3,117 @@
|
|
|
3
3
|
require 'io/console'
|
|
4
4
|
|
|
5
5
|
module Terminal
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
# @!group Attributes
|
|
7
|
+
|
|
8
|
+
# @attribute [r] self.input_mode
|
|
9
|
+
#
|
|
10
|
+
# Supported input mode.
|
|
11
|
+
#
|
|
12
|
+
# @return [:csi_u]
|
|
13
|
+
# when [CSIu protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol)
|
|
14
|
+
# supported
|
|
15
|
+
# @return [:legacy]
|
|
16
|
+
# for standard terminal
|
|
17
|
+
# @return [:dumb]
|
|
18
|
+
# for non-interactive input (pipes etc.)
|
|
19
|
+
# @return [:error]
|
|
20
|
+
# when input device is not avail (closed)
|
|
21
|
+
|
|
22
|
+
# @!endgroup
|
|
23
|
+
|
|
24
|
+
# @!group Input methods
|
|
25
|
+
|
|
26
|
+
# @!method self.read_key_event
|
|
27
|
+
# Read next {KeyEvent} from standard input.
|
|
28
|
+
#
|
|
29
|
+
# @return [KeyEvent] next event
|
|
30
|
+
# @return [nil] in error case
|
|
31
|
+
|
|
32
|
+
# @!method self.on_key_event(mouse: false, mouse_move: false, focus: false, &block)
|
|
33
|
+
# Event loop for key and mouse events.
|
|
34
|
+
#
|
|
35
|
+
# @param mouse [true, false]
|
|
36
|
+
# whether mouse buttons should be reported
|
|
37
|
+
# @param mouse_move [true, false]
|
|
38
|
+
# whether mouse movement should be reported
|
|
39
|
+
# @param focus [true, false]
|
|
40
|
+
# whether focus/unfocus of terminal window should be reported
|
|
41
|
+
# @yieldparam event [KeyEvent]
|
|
42
|
+
# next event
|
|
43
|
+
# @yieldreturn [true, false]
|
|
44
|
+
# whether the loop should be continued
|
|
45
|
+
# @return [true]
|
|
46
|
+
# when the loop was started
|
|
47
|
+
# @return [false]
|
|
48
|
+
# when the current input device is not available
|
|
49
|
+
# @return [nil]
|
|
50
|
+
# when no block was given
|
|
51
|
+
|
|
52
|
+
# @!endgroup
|
|
53
|
+
|
|
54
|
+
module Input
|
|
20
55
|
def input_mode
|
|
21
|
-
@
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
# @return [String] key code ("as is") in `:raw` mode
|
|
33
|
-
# @return [String] key name in `:named` mode
|
|
34
|
-
# @return [[String, String]] key code and key name in `:both` mode
|
|
35
|
-
# @return [nil] in error case
|
|
36
|
-
def read_key(mode: :named)
|
|
37
|
-
warn(
|
|
38
|
-
'Terminal.read_key is deprecaded; use Terminal.read_key_event instead.',
|
|
39
|
-
uplevel: 1
|
|
40
|
-
)
|
|
41
|
-
event = read_key_event or return
|
|
42
|
-
return event.raw if mode == :raw
|
|
43
|
-
key, name = event
|
|
44
|
-
mode == :both ? [key, name] : name || key
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Read next {KeyEvent} from standard input.
|
|
48
|
-
#
|
|
49
|
-
# @return [KeyEvent] next event
|
|
50
|
-
# @return [nil] in error case
|
|
51
|
-
def read_key_event
|
|
52
|
-
prevent_input_recursion do
|
|
53
|
-
case input_mode
|
|
54
|
-
when :dumb
|
|
55
|
-
raw = read_dumb or return
|
|
56
|
-
opts = @dumb_keys[raw.ord] if raw.size == 1
|
|
57
|
-
KeyEvent.new(raw, *opts)
|
|
58
|
-
when :csi_u, :legacy
|
|
59
|
-
raw = read_tty or return
|
|
60
|
-
KeyEvent[raw]
|
|
61
|
-
end
|
|
56
|
+
@in_recursion = 0
|
|
57
|
+
case @input_mode = __find_input_mode
|
|
58
|
+
when :legacy, :csi_u
|
|
59
|
+
@in = IO.console
|
|
60
|
+
require_relative 'input/ansi'
|
|
61
|
+
extend AnsiInput
|
|
62
|
+
when :dumb
|
|
63
|
+
require_relative 'input/dumb'
|
|
64
|
+
extend DumpInput
|
|
65
|
+
else
|
|
66
|
+
__input_error
|
|
62
67
|
end
|
|
68
|
+
@input_mode
|
|
63
69
|
end
|
|
64
70
|
|
|
65
|
-
# Event loop for key and mouse events.
|
|
66
|
-
#
|
|
67
|
-
# @param mouse [true, false]
|
|
68
|
-
# whether mouse buttons should be reported
|
|
69
|
-
# @param mouse_move [true, false]
|
|
70
|
-
# whether mouse movement should be reported
|
|
71
|
-
# @param focus [true, false]
|
|
72
|
-
# whether focus/unfocus of terminal window should be reported
|
|
73
|
-
# @yieldparam event [KeyEvent]
|
|
74
|
-
# next event
|
|
75
|
-
# @yieldreturn [true, false]
|
|
76
|
-
# whether the loop should be continued
|
|
77
|
-
# @return [true]
|
|
78
|
-
# when the loop was started
|
|
79
|
-
# @return [false]
|
|
80
|
-
# when the current input device is not available
|
|
81
|
-
# @return [nil]
|
|
82
|
-
# when no block was given
|
|
83
71
|
def on_key_event(mouse: false, mouse_move: false, focus: false, &block)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
case input_mode
|
|
87
|
-
when :dumb
|
|
88
|
-
on_bumb_key_event(&block)
|
|
89
|
-
true
|
|
90
|
-
when :csi_u, :legacy
|
|
91
|
-
on_tty_key_event(mouse_option(mouse, mouse_move, focus), &block)
|
|
92
|
-
true
|
|
93
|
-
else
|
|
94
|
-
false
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
private
|
|
100
|
-
|
|
101
|
-
def prevent_input_recursion
|
|
102
|
-
if (@key_event += 1) != 1
|
|
103
|
-
raise(RuntimeError, 'already reading key events', caller(2))
|
|
104
|
-
end
|
|
105
|
-
begin
|
|
106
|
-
yield
|
|
107
|
-
ensure
|
|
108
|
-
@key_event -= 1
|
|
109
|
-
end
|
|
72
|
+
input_mode
|
|
73
|
+
on_key_event(mouse: mouse, mouse_move: mouse_move, focus: focus, &block)
|
|
110
74
|
end
|
|
111
75
|
|
|
112
|
-
def
|
|
113
|
-
opts = +(mouse ? '1000' : '')
|
|
114
|
-
# highlight: '1001'
|
|
115
|
-
# drag: '1002'
|
|
116
|
-
opts << ';1003' if mouse_move
|
|
117
|
-
opts << ';1004' if focus
|
|
118
|
-
# ext: '1005'
|
|
119
|
-
# sgr: '1006'
|
|
120
|
-
# urxvt: '1015'
|
|
121
|
-
# pixel: '1016'
|
|
122
|
-
"\e[?#{opts};1006" unless opts.empty?
|
|
123
|
-
end
|
|
76
|
+
def read_key_event = on_key_event { return _1 }
|
|
124
77
|
|
|
125
|
-
|
|
126
|
-
STDIN.noecho do |stdin|
|
|
127
|
-
opts &&= raw_write("#{opts}h") ? "#{opts}l" : nil
|
|
128
|
-
while (raw = stdin.getch)
|
|
129
|
-
(yield(KeyEvent[raw]) ? next : break) if raw != "\e"
|
|
130
|
-
lesci = 0
|
|
131
|
-
while String === (nc = stdin.read_nonblock(1, exception: false))
|
|
132
|
-
lesci = raw.size if nc == "\e"
|
|
133
|
-
raw << nc
|
|
134
|
-
end
|
|
135
|
-
(yield(KeyEvent[raw]) ? next : break) if lesci < 2
|
|
136
|
-
break unless raw[1..].split("\e").all? { yield(KeyEvent["\e#{_1}"]) }
|
|
137
|
-
end
|
|
138
|
-
end
|
|
139
|
-
rescue Interrupt
|
|
140
|
-
# nop
|
|
141
|
-
rescue IOError, SystemCallError
|
|
142
|
-
@input_mode = :error
|
|
143
|
-
ensure
|
|
144
|
-
raw_write(opts) if opts
|
|
145
|
-
end
|
|
78
|
+
private
|
|
146
79
|
|
|
147
|
-
def
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
80
|
+
def __input_error
|
|
81
|
+
class << self
|
|
82
|
+
alias input_mode input_mode
|
|
83
|
+
def input_mode = :error
|
|
84
|
+
alias read_key_event read_key_event
|
|
85
|
+
def read_key_event = false
|
|
86
|
+
alias on_key_event on_key_event
|
|
87
|
+
def on_key_event(*_) = false
|
|
151
88
|
end
|
|
89
|
+
@in = nil
|
|
152
90
|
end
|
|
153
91
|
|
|
154
|
-
def
|
|
92
|
+
def __find_input_mode
|
|
155
93
|
im = ENV['INPUT_MODE']
|
|
156
94
|
return :legacy if im == 'legacy'
|
|
157
95
|
return :dumb if im == 'dumb' || !STDIN.tty?
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
96
|
+
con = IO.console
|
|
97
|
+
return :legacy if con.syswrite("\e[>1u\e[?u\e[c") != 12
|
|
98
|
+
inp = +''
|
|
99
|
+
inp << con.getch until inp.rindex('c')
|
|
100
|
+
return :legacy unless inp.include?("\e[?1u")
|
|
101
|
+
at_exit do
|
|
102
|
+
IO.console.syswrite("\e[<u")
|
|
103
|
+
rescue StandardError
|
|
104
|
+
# nop
|
|
165
105
|
end
|
|
106
|
+
:csi_u
|
|
166
107
|
rescue Interrupt
|
|
167
108
|
:legacy
|
|
168
109
|
rescue IOError, SystemCallError
|
|
110
|
+
__input_error
|
|
169
111
|
:error
|
|
170
112
|
end
|
|
171
|
-
|
|
172
|
-
def read_dumb
|
|
173
|
-
STDIN.getc
|
|
174
|
-
rescue Interrupt
|
|
175
|
-
"\x05"
|
|
176
|
-
rescue IOError, SystemCallError
|
|
177
|
-
@input_mode = :error
|
|
178
|
-
nil
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def read_tty
|
|
182
|
-
STDIN.noecho do |stdin|
|
|
183
|
-
if (key = stdin.getch) == "\e"
|
|
184
|
-
while (nc = stdin.read_nonblock(1, exception: false))
|
|
185
|
-
String === nc ? key << nc : break
|
|
186
|
-
end
|
|
187
|
-
end
|
|
188
|
-
key
|
|
189
|
-
end
|
|
190
|
-
rescue Interrupt
|
|
191
|
-
nil
|
|
192
|
-
rescue IOError, SystemCallError
|
|
193
|
-
@input_mode = :error
|
|
194
|
-
nil
|
|
195
|
-
end
|
|
196
113
|
end
|
|
197
114
|
|
|
198
|
-
|
|
199
|
-
0x05 => ['c', 4],
|
|
200
|
-
0x08 => :Back,
|
|
201
|
-
0x09 => :Tab,
|
|
202
|
-
0x0a => :Enter,
|
|
203
|
-
0x0d => :Return,
|
|
204
|
-
0x1b => :Esc
|
|
205
|
-
}.compare_by_identity.freeze
|
|
206
|
-
|
|
207
|
-
@key_event = 0
|
|
115
|
+
extend Input
|
|
208
116
|
|
|
117
|
+
private_constant :Input
|
|
209
118
|
autoload :KeyEvent, "#{__dir__}/input/key_event.rb"
|
|
210
119
|
end
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Terminal
|
|
4
|
+
module AnsiOutput
|
|
5
|
+
def output_mode = :ansi
|
|
6
|
+
def ansi? = true
|
|
7
|
+
def tui? = %i[csi_u legacy].include?(input_mode)
|
|
8
|
+
def colors = (@colors ||= Detect.colors)
|
|
9
|
+
|
|
10
|
+
def columns=(value)
|
|
11
|
+
self.size = [rows, value]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def pos
|
|
15
|
+
@con&.cursor
|
|
16
|
+
rescue IOError, SystemCallError
|
|
17
|
+
@con = nil
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def pos=(pos)
|
|
21
|
+
@con&.cursor = pos
|
|
22
|
+
rescue IOError, SystemCallError
|
|
23
|
+
@con = nil
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def rows=(value)
|
|
27
|
+
self.size = [value, columns]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def size
|
|
31
|
+
@size ||= @con&.winsize || __default_size
|
|
32
|
+
rescue IOError, SystemCallError
|
|
33
|
+
@con = nil
|
|
34
|
+
@size = __default_size
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def size=(size)
|
|
38
|
+
@con&.winsize = size
|
|
39
|
+
rescue IOError, SystemCallError
|
|
40
|
+
@con = nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def raw_write(str)
|
|
44
|
+
@out.syswrite(str)
|
|
45
|
+
rescue IOError, SystemCallError
|
|
46
|
+
__output_error(nil)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def <<(object)
|
|
50
|
+
@out.write(Ansi.bbcode(object)) if object != nil
|
|
51
|
+
self
|
|
52
|
+
rescue IOError, SystemCallError
|
|
53
|
+
__output_error(self)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def print(*objects, bbcode: true)
|
|
57
|
+
return if objects.empty?
|
|
58
|
+
return @out.print(*objects) unless bbcode
|
|
59
|
+
@out.print(*objects.map! { Ansi.bbcode(_1) })
|
|
60
|
+
rescue IOError, SystemCallError
|
|
61
|
+
__output_error(nil)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def puts(*objects, bbcode: true)
|
|
65
|
+
return @out.puts(objects.empty? ? nil : objects) unless bbcode
|
|
66
|
+
objects.flatten!
|
|
67
|
+
@out.puts(objects.empty? ? nil : objects.map! { Ansi.bbcode(_1) })
|
|
68
|
+
rescue IOError, SystemCallError
|
|
69
|
+
__output_error(nil)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def hide_cursor
|
|
73
|
+
raw_write(Ansi::CURSOR_HIDE) if (@cc += 1) == 1
|
|
74
|
+
self
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def show_cursor
|
|
78
|
+
raw_write(Ansi::CURSOR_SHOW) if @cc > 0 && (@cc -= 1).zero?
|
|
79
|
+
self
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def show_alt_screen
|
|
83
|
+
raw_write(Ansi::SCREEN_ALTERNATE) if (@as += 1) == 1
|
|
84
|
+
self
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def hide_alt_screen
|
|
88
|
+
raw_write(Ansi::SCREEN_ALTERNATE_OFF) if @as > 0 && (@as -= 1).zero?
|
|
89
|
+
self
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
private
|
|
93
|
+
|
|
94
|
+
def __output_error(ret)
|
|
95
|
+
require_relative 'dumb'
|
|
96
|
+
extend DumbOutput
|
|
97
|
+
@size = @cc = @as = nil
|
|
98
|
+
__output_error(ret)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def __init_tty
|
|
102
|
+
require('io/console')
|
|
103
|
+
@con = IO.console
|
|
104
|
+
Signal.trap('WINCH') { @size = nil } if Signal.list.key?('WINCH')
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private_class_method def self.extended(mod)
|
|
108
|
+
mod.instance_variable_set(:@cc, 0)
|
|
109
|
+
mod.instance_variable_set(:@as, 0)
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
private_constant :AnsiOutput
|
|
114
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Terminal
|
|
4
|
+
module DumbOutput
|
|
5
|
+
def output_mode = :dumb
|
|
6
|
+
def ansi? = false
|
|
7
|
+
def tui? = false
|
|
8
|
+
def colors = 2
|
|
9
|
+
def pos = nil
|
|
10
|
+
def size = (@size ||= __default_size)
|
|
11
|
+
def hide_cursor = self
|
|
12
|
+
def show_cursor = self
|
|
13
|
+
def show_alt_screen = self
|
|
14
|
+
def hide_alt_screen = self
|
|
15
|
+
|
|
16
|
+
def columns=(_)
|
|
17
|
+
# nop
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def pos=(_)
|
|
21
|
+
# nop
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def rows=(_)
|
|
25
|
+
# nop
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def size=(_)
|
|
29
|
+
# nop
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def raw_write(_) = nil
|
|
33
|
+
|
|
34
|
+
def <<(object)
|
|
35
|
+
@out.write(Ansi.plain(object)) if object != nil
|
|
36
|
+
self
|
|
37
|
+
rescue IOError, SystemCallError
|
|
38
|
+
__output_error(self)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def print(*objects, bbcode: true)
|
|
42
|
+
return if objects.empty?
|
|
43
|
+
@out.print(*objects.map!(&(bbcode ? @plain : @undecorate)))
|
|
44
|
+
rescue IOError, SystemCallError
|
|
45
|
+
__output_error(nil)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def puts(*objects, bbcode: true)
|
|
49
|
+
objects.flatten!
|
|
50
|
+
return @out.puts if objects.empty?
|
|
51
|
+
@out.puts(*objects.map!(&(bbcode ? @plain : @undecorate)))
|
|
52
|
+
rescue IOError, SystemCallError
|
|
53
|
+
__output_error(nil)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def __output_error(ret)
|
|
59
|
+
class << self
|
|
60
|
+
def output_mode = :error
|
|
61
|
+
def <<(_) = self
|
|
62
|
+
def print(*_) = nil
|
|
63
|
+
def puts(*_) = nil
|
|
64
|
+
end
|
|
65
|
+
@out = @plain = @undecorate = nil
|
|
66
|
+
ret
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
private_class_method def self.extended(mod)
|
|
70
|
+
mod.instance_variable_set(:@plain, ->(s) { Ansi.plain(s) })
|
|
71
|
+
mod.instance_variable_set(:@undecorate, ->(s) { Ansi.undecorate(s) })
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private_constant :DumbOutput
|
|
76
|
+
end
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Terminal
|
|
4
|
+
class << self
|
|
5
|
+
# @!group Attributes
|
|
6
|
+
|
|
7
|
+
# @attribute [r] tui?
|
|
8
|
+
#
|
|
9
|
+
# Return `true` if the current terminal supports ANSI control codes for
|
|
10
|
+
# input _and_ output.
|
|
11
|
+
# In this case not only all output methods ({<<}, {print}, {puts}) will
|
|
12
|
+
# forward ANSI control codes to the terminal and translate BBCode
|
|
13
|
+
# (see {Ansi.bbcode}). But also the input methods {read_key_event} and
|
|
14
|
+
# {on_key_event} will support extended key codes, mouse and focus events.
|
|
15
|
+
#
|
|
16
|
+
# @see output_mode
|
|
17
|
+
# @see ansi?
|
|
18
|
+
#
|
|
19
|
+
# @return [true, false]
|
|
20
|
+
# whether ANSI control codes are supported for input and output
|
|
21
|
+
|
|
22
|
+
# @attribute [r] output_mode
|
|
23
|
+
#
|
|
24
|
+
# Supported output mode.
|
|
25
|
+
#
|
|
26
|
+
# @return [:ansi]
|
|
27
|
+
# All output methods ({<<}, {print}, {puts}) will forward ANSI control
|
|
28
|
+
# codes to the terminal and translate BBCode (see {Ansi.bbcode}).
|
|
29
|
+
# @return [:dumb]
|
|
30
|
+
# All output methods will not forward ANSI control codes and BBCodes will
|
|
31
|
+
# be removed.
|
|
32
|
+
# The {colors} method will just return 2 (two).
|
|
33
|
+
# @return [:error]
|
|
34
|
+
# When the output device signaled an error or was closed, nothing will
|
|
35
|
+
# be written to the terminal.
|
|
36
|
+
|
|
37
|
+
# @!endgroup
|
|
38
|
+
|
|
39
|
+
# @!group Output attributes
|
|
40
|
+
|
|
41
|
+
# @attribute [r] ansi?
|
|
42
|
+
#
|
|
43
|
+
# @see output_mode
|
|
44
|
+
# @see tui?
|
|
45
|
+
#
|
|
46
|
+
# @return [true, false]
|
|
47
|
+
# whether ANSI control codes are supported for output
|
|
48
|
+
|
|
49
|
+
# @attribute [r] colors
|
|
50
|
+
#
|
|
51
|
+
# Number of supported colors.
|
|
52
|
+
# The detection checks various conditions to find the correct value. The
|
|
53
|
+
# most common values are
|
|
54
|
+
#
|
|
55
|
+
# - `16_777_216` for 24-bit encoding ({true_color?} return true)
|
|
56
|
+
# - `256` for 8-Bit encoding
|
|
57
|
+
# - `52` for some Unix terminals with an extended color palette
|
|
58
|
+
# - `8` for 3-/4-bit encoding
|
|
59
|
+
# - `2` if ANSI is not supported in general ({ansi?} return false)
|
|
60
|
+
#
|
|
61
|
+
# @return [Integer]
|
|
62
|
+
# number of supported colors
|
|
63
|
+
|
|
64
|
+
# @attribute [r] true_color?
|
|
65
|
+
#
|
|
66
|
+
# @see colors
|
|
67
|
+
#
|
|
68
|
+
# @return [true, false]
|
|
69
|
+
# whether true colors are supported
|
|
70
|
+
def true_color? = (colors == 16_777_216)
|
|
71
|
+
|
|
72
|
+
# @attribute [r] columns
|
|
73
|
+
#
|
|
74
|
+
# Screen column count.
|
|
75
|
+
# See {size} for support and detection details.
|
|
76
|
+
#
|
|
77
|
+
# @return [Integer]
|
|
78
|
+
# number of available columns
|
|
79
|
+
def columns = size[1]
|
|
80
|
+
#
|
|
81
|
+
# @attribute [w] columns
|
|
82
|
+
|
|
83
|
+
# @attribute [r] rows
|
|
84
|
+
#
|
|
85
|
+
# Screen row count.
|
|
86
|
+
# See {size} for support and detection details.
|
|
87
|
+
#
|
|
88
|
+
# @return [Integer]
|
|
89
|
+
# number of available rows
|
|
90
|
+
def rows = size[0]
|
|
91
|
+
#
|
|
92
|
+
# @attribute [w] rows
|
|
93
|
+
|
|
94
|
+
# @attribute [r] pos
|
|
95
|
+
#
|
|
96
|
+
# Current cursor position.
|
|
97
|
+
# This is only available when ANSI is supported ({ansi?} return true).
|
|
98
|
+
#
|
|
99
|
+
# @return [[Integer, Integer]]
|
|
100
|
+
# cursor position as rows and columns
|
|
101
|
+
# @return [nil]
|
|
102
|
+
# for incompatible terminals
|
|
103
|
+
#
|
|
104
|
+
# @attribute [w] pos
|
|
105
|
+
|
|
106
|
+
# @attribute [r] size
|
|
107
|
+
#
|
|
108
|
+
# Screen size as a tuple of {rows} and {columns}.
|
|
109
|
+
#
|
|
110
|
+
# If the terminal does not support the report of it's dimension or ANSI
|
|
111
|
+
# is not supported in general then environment variables `COLUMNS` and
|
|
112
|
+
# `LINES` will be used.
|
|
113
|
+
# If this failed `[25, 80]` will be returned as default.
|
|
114
|
+
#
|
|
115
|
+
# Setting the terminal size is not widely supported.
|
|
116
|
+
#
|
|
117
|
+
# @see rows
|
|
118
|
+
# @see columns
|
|
119
|
+
#
|
|
120
|
+
# @return [[Integer, Integer]]
|
|
121
|
+
# available screen size as rows and columns
|
|
122
|
+
#
|
|
123
|
+
# @attribute [w] size
|
|
124
|
+
|
|
125
|
+
# @!endgroup
|
|
126
|
+
|
|
127
|
+
# @!group Output methods
|
|
128
|
+
|
|
129
|
+
# @!method <<(object)
|
|
130
|
+
#
|
|
131
|
+
# Writes the given object to the terminal.
|
|
132
|
+
# Interprets embedded BBCode (see {Ansi.bbcode}).
|
|
133
|
+
#
|
|
134
|
+
# @param object [#to_s] object to write
|
|
135
|
+
# @return [Terminal] itself
|
|
136
|
+
|
|
137
|
+
# @!method print(*objects, bbcode: true)
|
|
138
|
+
#
|
|
139
|
+
# Writes the given objects to the terminal.
|
|
140
|
+
# Optionally interprets embedded BBCode (see {Ansi.bbcode}).
|
|
141
|
+
#
|
|
142
|
+
# @param objects [Array<#to_s>] any number of objects to write
|
|
143
|
+
# @param bbcode [true|false] whether to interpret embedded BBCode
|
|
144
|
+
# @return [nil]
|
|
145
|
+
|
|
146
|
+
# @!method puts(*objects, bbcode: true)
|
|
147
|
+
#
|
|
148
|
+
# Writes the given objects to the terminal.
|
|
149
|
+
# Writes a newline after each object that does not already end with a
|
|
150
|
+
# newline sequence in it's String represenation.
|
|
151
|
+
# If called without any arguments, writes a newline only.
|
|
152
|
+
#
|
|
153
|
+
# Optionally interprets embedded BBCode (see {Ansi.bbcode}).
|
|
154
|
+
#
|
|
155
|
+
# @param objects [Array<#to_s>] any number of objects to write
|
|
156
|
+
# @param bbcode [true|false] whether to interpret embedded BBCode
|
|
157
|
+
# @return [nil]
|
|
158
|
+
|
|
159
|
+
# @!endgroup
|
|
160
|
+
|
|
161
|
+
# @!group Output helper methods
|
|
162
|
+
|
|
163
|
+
# @!method hide_cursor
|
|
164
|
+
#
|
|
165
|
+
# Hide the cursor.
|
|
166
|
+
# Will not send the control code if the cursor is already hidden.
|
|
167
|
+
#
|
|
168
|
+
# When you called {hide_cursor} n-times you need to call {show_cursor}
|
|
169
|
+
# n-times to show the cursor again.
|
|
170
|
+
#
|
|
171
|
+
# @return [Terminal] itself
|
|
172
|
+
|
|
173
|
+
# @!method show_cursor
|
|
174
|
+
#
|
|
175
|
+
# Show the cursor.
|
|
176
|
+
# Will not send the control code if the cursor is not hidden.
|
|
177
|
+
#
|
|
178
|
+
# When you called {hide_cursor} n-times you need to call {show_cursor}
|
|
179
|
+
# n-times to show the cursor again.
|
|
180
|
+
#
|
|
181
|
+
# @return [Terminal] itself
|
|
182
|
+
|
|
183
|
+
# @!method show_alt_screen
|
|
184
|
+
#
|
|
185
|
+
# Show the alternate screen.
|
|
186
|
+
# Will not send the control code if the alternate screen is already used.
|
|
187
|
+
#
|
|
188
|
+
# When you called {show_alt_screen} n-times you need to call
|
|
189
|
+
# {hide_alt_screen} n-times to show the default screen again.
|
|
190
|
+
#
|
|
191
|
+
# @return [Terminal] itself
|
|
192
|
+
|
|
193
|
+
# @!method hide_alt_screen
|
|
194
|
+
#
|
|
195
|
+
# Hide the alternate screen.
|
|
196
|
+
# Will not send the control code if the alternate screen is not used.
|
|
197
|
+
#
|
|
198
|
+
# When you called {show_alt_screen} n-times you need to call
|
|
199
|
+
# {hide_alt_screen} n-times to show the default screen again.
|
|
200
|
+
#
|
|
201
|
+
# @return [Terminal] itself
|
|
202
|
+
|
|
203
|
+
# @!endgroup
|
|
204
|
+
|
|
205
|
+
private
|
|
206
|
+
|
|
207
|
+
def __default_size
|
|
208
|
+
rows = ENV['LINES'].to_i
|
|
209
|
+
columns = ENV['COLUMNS'].to_i
|
|
210
|
+
[rows > 0 ? rows : 25, columns > 0 ? columns : 80]
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
def __output_modes
|
|
214
|
+
return false if ENV.key?('NO_COLOR') || ENV['TERM'] == 'dumb'
|
|
215
|
+
[tty = STDOUT.tty?, ENV['ANSI'] == 'force' || tty]
|
|
216
|
+
rescue IOError, SystemCallError
|
|
217
|
+
nil
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
tty, ansi = __output_modes
|
|
222
|
+
if tty.nil?
|
|
223
|
+
require_relative 'output/dumb'
|
|
224
|
+
extend DumbOutput
|
|
225
|
+
__output_error(nil)
|
|
226
|
+
else
|
|
227
|
+
@out = STDOUT
|
|
228
|
+
@out.sync = true if defined?(@out.sync)
|
|
229
|
+
if ansi
|
|
230
|
+
require_relative 'output/ansi'
|
|
231
|
+
extend AnsiOutput
|
|
232
|
+
__init_tty if tty
|
|
233
|
+
else
|
|
234
|
+
require_relative 'output/dumb'
|
|
235
|
+
extend DumbOutput
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
@@ -53,9 +53,13 @@ RSpec.shared_context 'with Terminal.rb' do |ansi: true, application: :kitty, col
|
|
|
53
53
|
nil
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
if ansi
|
|
57
|
+
allow(Terminal).to receive(:raw_write) do |object|
|
|
58
|
+
stdout.push(object = object.to_s)
|
|
59
|
+
object.bytesize
|
|
60
|
+
end
|
|
61
|
+
else
|
|
62
|
+
allow(Terminal).to receive(:raw_write).and_return(nil)
|
|
59
63
|
end
|
|
60
64
|
|
|
61
65
|
allow(Terminal).to receive(:read_key_event) do
|
data/lib/terminal/version.rb
CHANGED
data/lib/terminal.rb
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative 'terminal/
|
|
3
|
+
require_relative 'terminal/output'
|
|
4
4
|
require_relative 'terminal/input'
|
|
5
|
+
require_relative 'terminal/ansi'
|
|
5
6
|
|
|
6
7
|
#
|
|
7
8
|
# Terminal access with support for ANSI control codes and
|
|
@@ -16,41 +17,7 @@ require_relative 'terminal/input'
|
|
|
16
17
|
#
|
|
17
18
|
module Terminal
|
|
18
19
|
class << self
|
|
19
|
-
#
|
|
20
|
-
# output.
|
|
21
|
-
# In this case all output methods ({<<}, {print}, {puts}) will forward ANSI
|
|
22
|
-
# control codes to the terminal and translate BBCode (see {Ansi.bbcode}).
|
|
23
|
-
#
|
|
24
|
-
# When `false` is returned (ANSI is not supported) the output methods will
|
|
25
|
-
# not forward ANSI control codes and BBCodes will be removed.
|
|
26
|
-
# The {colors} method will just return 2 (two).
|
|
27
|
-
#
|
|
28
|
-
# @see tui?
|
|
29
|
-
#
|
|
30
|
-
# @attribute [r] ansi?
|
|
31
|
-
# @return [true, false] whether ANSI control codes are supported for output
|
|
32
|
-
def ansi? = @ansi
|
|
33
|
-
|
|
34
|
-
# Return `true` if the current terminal supports ANSI control codes for
|
|
35
|
-
# input _and_ output.
|
|
36
|
-
# In this case not only all output methods ({<<}, {print}, {puts}) will
|
|
37
|
-
# forward ANSI control codes to the terminal and translate BBCode
|
|
38
|
-
# (see {Ansi.bbcode}). But also the input methods {read_key_event} and
|
|
39
|
-
# {on_key_event} will support extended key codes, mouse and focus events.
|
|
40
|
-
#
|
|
41
|
-
# @see ansi?
|
|
42
|
-
#
|
|
43
|
-
# @attribute [r] tui?
|
|
44
|
-
# @return [true, false]
|
|
45
|
-
# whether ANSI control codes are supported for input and output
|
|
46
|
-
def tui?
|
|
47
|
-
case input_mode
|
|
48
|
-
when :csi_u, :legacy
|
|
49
|
-
@ansi
|
|
50
|
-
else
|
|
51
|
-
false
|
|
52
|
-
end
|
|
53
|
-
end
|
|
20
|
+
# @!group Attributes
|
|
54
21
|
|
|
55
22
|
# Terminal application identifier.
|
|
56
23
|
#
|
|
@@ -66,190 +33,9 @@ module Terminal
|
|
|
66
33
|
# @return [Symbol, nil] the application identifier
|
|
67
34
|
def application = (@application ||= Detect.application)
|
|
68
35
|
|
|
69
|
-
#
|
|
70
|
-
# The detection checks various conditions to find the correct value. The
|
|
71
|
-
# most common values are
|
|
72
|
-
#
|
|
73
|
-
# - `16_777_216` for 24-bit encoding ({true_color?} return true)
|
|
74
|
-
# - `256` for 8-Bit encoding
|
|
75
|
-
# - `52` for some Unix terminals with an extended color palette
|
|
76
|
-
# - `8` for 3-/4-bit encoding
|
|
77
|
-
# - `2` if ANSI is not supported in general ({ansi?} return false)
|
|
78
|
-
#
|
|
79
|
-
# @attribute [r] colors
|
|
80
|
-
# @return [Integer] number of supported colors
|
|
81
|
-
def colors = (@colors ||= ansi? ? Detect.colors : 2)
|
|
82
|
-
|
|
83
|
-
# Screen column count.
|
|
84
|
-
# See {size} for support and detection details.
|
|
85
|
-
#
|
|
86
|
-
# @attribute [r] columns
|
|
87
|
-
# @return [Integer] number of available columns
|
|
88
|
-
def columns = size[1]
|
|
89
|
-
|
|
90
|
-
# @attribute [w] columns
|
|
91
|
-
def columns=(value)
|
|
92
|
-
self.size = [rows, value]
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
# Current cursor position.
|
|
96
|
-
# This is only available when ANSI is supported ({ansi?} return true).
|
|
97
|
-
#
|
|
98
|
-
# @attribute [r] pos
|
|
99
|
-
# @return [[Integer, Integer]] cursor position as rows and columns
|
|
100
|
-
# @return [nil] for incompatible terminals
|
|
101
|
-
def pos
|
|
102
|
-
@con&.cursor
|
|
103
|
-
rescue IOError
|
|
104
|
-
@con = nil
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
# @attribute [w] pos
|
|
108
|
-
def pos=(pos)
|
|
109
|
-
@con&.cursor = pos
|
|
110
|
-
rescue IOError
|
|
111
|
-
@con = nil
|
|
112
|
-
end
|
|
113
|
-
|
|
114
|
-
# Screen row count.
|
|
115
|
-
# See {size} for support and detection details.
|
|
116
|
-
#
|
|
117
|
-
# @attribute [r] rows
|
|
118
|
-
# @return [Integer] number of available rows
|
|
119
|
-
def rows = size[0]
|
|
120
|
-
|
|
121
|
-
# @attribute [w] rows
|
|
122
|
-
def rows=(value)
|
|
123
|
-
self.size = [value, columns]
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Screen size as a tuple of {rows} and {columns}.
|
|
127
|
-
#
|
|
128
|
-
# If the terminal does not support the report of it's dimension or ANSI
|
|
129
|
-
# is not supported in general then environment variables `COLUMNS` and
|
|
130
|
-
# `LINES` will be used.
|
|
131
|
-
# If this failed `[25, 80]` will be returned as default.
|
|
132
|
-
#
|
|
133
|
-
# Setting the terminal size is not widely supported.
|
|
134
|
-
#
|
|
135
|
-
# @see rows
|
|
136
|
-
# @see columns
|
|
137
|
-
#
|
|
138
|
-
# @attribute [r] size
|
|
139
|
-
# @return [[Integer, Integer]] available screen size as rows and columns
|
|
140
|
-
def size
|
|
141
|
-
@size ||= @inf&.winsize || _default_size
|
|
142
|
-
rescue IOError
|
|
143
|
-
@inf = nil
|
|
144
|
-
@size = _default_size
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
# @attribute [w] size
|
|
148
|
-
def size=(size)
|
|
149
|
-
@inf&.winsize = size
|
|
150
|
-
rescue IOError
|
|
151
|
-
@inf = nil
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
# @see colors
|
|
155
|
-
# @attribute [r] true_color?
|
|
156
|
-
# @return [true, false] whether true colors are supported
|
|
157
|
-
def true_color? = (colors == 16_777_216)
|
|
158
|
-
|
|
159
|
-
# Hide the cursor.
|
|
160
|
-
# Will not send the control code if the cursor is already hidden.
|
|
161
|
-
#
|
|
162
|
-
# When you called {hide_cursor} n-times you need to call {show_cursor}
|
|
163
|
-
# n-times to show the cursor again.
|
|
164
|
-
#
|
|
165
|
-
# @return [Terminal] itself
|
|
166
|
-
def hide_cursor
|
|
167
|
-
raw_write(Ansi::CURSOR_HIDE) if ansi? && (@cc += 1) == 1
|
|
168
|
-
self
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
# Show the cursor.
|
|
172
|
-
# Will not send the control code if the cursor is not hidden.
|
|
173
|
-
#
|
|
174
|
-
# When you called {hide_cursor} n-times you need to call {show_cursor}
|
|
175
|
-
# n-times to show the cursor again.
|
|
176
|
-
#
|
|
177
|
-
# @return [Terminal] itself
|
|
178
|
-
def show_cursor
|
|
179
|
-
raw_write(Ansi::CURSOR_SHOW) if @cc > 0 && (@cc -= 1).zero?
|
|
180
|
-
self
|
|
181
|
-
end
|
|
36
|
+
# @!endgroup
|
|
182
37
|
|
|
183
|
-
#
|
|
184
|
-
# Will not send the control code if the alternate screen is already used.
|
|
185
|
-
#
|
|
186
|
-
# When you called {show_alt_screen} n-times you need to call
|
|
187
|
-
# {hide_alt_screen} n-times to show the default screen again.
|
|
188
|
-
#
|
|
189
|
-
# @return [Terminal] itself
|
|
190
|
-
def show_alt_screen
|
|
191
|
-
raw_write(Ansi::SCREEN_ALTERNATE) if ansi? && (@as += 1) == 1
|
|
192
|
-
self
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
# Hide the alternate screen.
|
|
196
|
-
# Will not send the control code if the alternate screen is not used.
|
|
197
|
-
#
|
|
198
|
-
# When you called {show_alt_screen} n-times you need to call
|
|
199
|
-
# {hide_alt_screen} n-times to show the default screen again.
|
|
200
|
-
#
|
|
201
|
-
# @return [Terminal] itself
|
|
202
|
-
def hide_alt_screen
|
|
203
|
-
raw_write(Ansi::SCREEN_ALTERNATE_OFF) if @as > 0 && (@as -= 1).zero?
|
|
204
|
-
self
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
# Writes the given object to the terminal.
|
|
208
|
-
# Interprets embedded BBCode.
|
|
209
|
-
#
|
|
210
|
-
# @param object [#to_s] object to write
|
|
211
|
-
# @return [Terminal] itself
|
|
212
|
-
def <<(object)
|
|
213
|
-
@out.write(Ansi.bbcode(object)) if @out && object != nil
|
|
214
|
-
self
|
|
215
|
-
rescue IOError
|
|
216
|
-
@out = nil
|
|
217
|
-
self
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
# Writes the given objects to the terminal.
|
|
221
|
-
# Optionally interprets embedded BBCode.
|
|
222
|
-
#
|
|
223
|
-
# @param objects [Array<#to_s>] any number of objects to write
|
|
224
|
-
# @param bbcode [true|false] whether to interpret embedded BBCode
|
|
225
|
-
# @return [nil]
|
|
226
|
-
def print(*objects, bbcode: true)
|
|
227
|
-
return unless @out
|
|
228
|
-
return @out.print(*objects) unless bbcode
|
|
229
|
-
objects.each { @out.write(Ansi.bbcode(_1)) }
|
|
230
|
-
nil
|
|
231
|
-
rescue IOError
|
|
232
|
-
@out = nil
|
|
233
|
-
end
|
|
234
|
-
|
|
235
|
-
# Writes the given objects to the terminal.
|
|
236
|
-
# Writes a newline after each object that does not already end with a
|
|
237
|
-
# newline sequence in it's String represenation.
|
|
238
|
-
# If called without any arguments, writes a newline only.
|
|
239
|
-
#
|
|
240
|
-
# Optionally interprets embedded BBCode.
|
|
241
|
-
#
|
|
242
|
-
# @param (see print)
|
|
243
|
-
# @return (see print)
|
|
244
|
-
def puts(*objects, bbcode: true)
|
|
245
|
-
return unless @out
|
|
246
|
-
return @out.puts if objects.empty?
|
|
247
|
-
return @out.puts(objects) unless bbcode
|
|
248
|
-
objects.flatten!
|
|
249
|
-
objects.empty? ? @out.puts : @out.puts(objects.map! { Ansi.bbcode(_1) })
|
|
250
|
-
rescue IOError
|
|
251
|
-
@out = nil
|
|
252
|
-
end
|
|
38
|
+
# @!group Tool methods
|
|
253
39
|
|
|
254
40
|
# Execute a command and report command output line by line
|
|
255
41
|
# or capture the output.
|
|
@@ -316,77 +102,7 @@ module Terminal
|
|
|
316
102
|
]
|
|
317
103
|
end
|
|
318
104
|
|
|
319
|
-
#
|
|
320
|
-
def raw_write(str)
|
|
321
|
-
@out&.syswrite(str)
|
|
322
|
-
rescue IOError
|
|
323
|
-
@out = nil
|
|
324
|
-
end
|
|
325
|
-
|
|
326
|
-
private
|
|
327
|
-
|
|
328
|
-
def _default_size
|
|
329
|
-
rows = ENV['LINES'].to_i
|
|
330
|
-
columns = ENV['COLUMNS'].to_i
|
|
331
|
-
[rows > 0 ? rows : 25, columns > 0 ? columns : 80]
|
|
332
|
-
end
|
|
333
|
-
|
|
334
|
-
def _determine_modes
|
|
335
|
-
# order is important!
|
|
336
|
-
tty = STDOUT.tty?
|
|
337
|
-
return tty, true if ENV['ANSI'] == 'force'
|
|
338
|
-
return false, false if ENV.key?('NO_COLOR') || ENV['TERM'] == 'dumb'
|
|
339
|
-
[tty, tty]
|
|
340
|
-
rescue IOError
|
|
341
|
-
[nil, false]
|
|
342
|
-
end
|
|
343
|
-
end
|
|
344
|
-
|
|
345
|
-
@cc = @as = 0
|
|
346
|
-
tty, @ansi = _determine_modes
|
|
347
|
-
|
|
348
|
-
unless tty.nil?
|
|
349
|
-
@out = STDOUT
|
|
350
|
-
@out.sync = true
|
|
351
|
-
if tty
|
|
352
|
-
@inf = STDOUT
|
|
353
|
-
@con = IO.console
|
|
354
|
-
Signal.trap('WINCH') { @size = nil } if Signal.list.key?('WINCH')
|
|
355
|
-
end
|
|
356
|
-
end
|
|
357
|
-
|
|
358
|
-
unless @ansi
|
|
359
|
-
@plain = ->(s) { Ansi.plain(s) }
|
|
360
|
-
@undecorate = ->(s) { Ansi.undecorate(s) }
|
|
361
|
-
|
|
362
|
-
class << self
|
|
363
|
-
alias << <<
|
|
364
|
-
def <<(object)
|
|
365
|
-
@out.write(Ansi.plain(object)) if @out && object != nil
|
|
366
|
-
self
|
|
367
|
-
rescue IOError
|
|
368
|
-
@out = nil
|
|
369
|
-
self
|
|
370
|
-
end
|
|
371
|
-
|
|
372
|
-
alias print print
|
|
373
|
-
def print(*objects, bbcode: true)
|
|
374
|
-
return if @out.nil? || objects.empty?
|
|
375
|
-
@out.print(*objects.map!(&(bbcode ? @plain : @undecorate)))
|
|
376
|
-
rescue IOError
|
|
377
|
-
@out = nil
|
|
378
|
-
end
|
|
379
|
-
|
|
380
|
-
alias puts puts
|
|
381
|
-
def puts(*objects, bbcode: true)
|
|
382
|
-
return unless @out
|
|
383
|
-
objects.flatten!
|
|
384
|
-
return @out.puts if objects.empty?
|
|
385
|
-
@out.puts(objects.map!(&(bbcode ? @plain : @undecorate)))
|
|
386
|
-
rescue IOError
|
|
387
|
-
@out = nil
|
|
388
|
-
end
|
|
389
|
-
end
|
|
105
|
+
# @!endgroup
|
|
390
106
|
end
|
|
391
107
|
|
|
392
108
|
dir = "#{__dir__}/terminal"
|
data/terminal_rb.gemspec
CHANGED
|
@@ -21,7 +21,9 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
spec.homepage = 'https://codeberg.org/mblumtritt/Terminal.rb'
|
|
22
22
|
spec.metadata['source_code_uri'] = spec.homepage
|
|
23
23
|
spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
|
|
24
|
-
spec.metadata['documentation_uri'] =
|
|
24
|
+
spec.metadata['documentation_uri'] = "https://rubydoc.info/gems/terminal_rb/#{
|
|
25
|
+
Terminal::VERSION
|
|
26
|
+
}"
|
|
25
27
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
26
28
|
spec.metadata['yard.run'] = 'yard'
|
|
27
29
|
|
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.18.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Blumtritt
|
|
@@ -35,7 +35,12 @@ files:
|
|
|
35
35
|
- lib/terminal/ansi/named_colors.rb
|
|
36
36
|
- lib/terminal/detect.rb
|
|
37
37
|
- lib/terminal/input.rb
|
|
38
|
+
- lib/terminal/input/ansi.rb
|
|
39
|
+
- lib/terminal/input/dumb.rb
|
|
38
40
|
- lib/terminal/input/key_event.rb
|
|
41
|
+
- lib/terminal/output.rb
|
|
42
|
+
- lib/terminal/output/ansi.rb
|
|
43
|
+
- lib/terminal/output/dumb.rb
|
|
39
44
|
- lib/terminal/rspec/helper.rb
|
|
40
45
|
- lib/terminal/shell.rb
|
|
41
46
|
- lib/terminal/text.rb
|
|
@@ -50,7 +55,7 @@ licenses:
|
|
|
50
55
|
metadata:
|
|
51
56
|
source_code_uri: https://codeberg.org/mblumtritt/Terminal.rb
|
|
52
57
|
bug_tracker_uri: https://codeberg.org/mblumtritt/Terminal.rb/issues
|
|
53
|
-
documentation_uri: https://rubydoc.info/gems/terminal_rb
|
|
58
|
+
documentation_uri: https://rubydoc.info/gems/terminal_rb/0.18.0
|
|
54
59
|
rubygems_mfa_required: 'true'
|
|
55
60
|
yard.run: yard
|
|
56
61
|
rdoc_options: []
|
|
@@ -67,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
67
72
|
- !ruby/object:Gem::Version
|
|
68
73
|
version: '0'
|
|
69
74
|
requirements: []
|
|
70
|
-
rubygems_version:
|
|
75
|
+
rubygems_version: 4.0.3
|
|
71
76
|
specification_version: 4
|
|
72
77
|
summary: Fast terminal access with ANSI, CSIu, mouse events, BBCode, word-wise line
|
|
73
78
|
break support and much more.
|