terminal_rb 0.16.2 → 0.17.1
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/lib/terminal/ansi.rb +43 -2
- data/lib/terminal/input.rb +35 -18
- data/lib/terminal/shell.rb +55 -70
- data/lib/terminal/version.rb +1 -1
- data/lib/terminal.rb +2 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ccb3312146fca100bd13e4c079e5cf6f4a33e73ef9eb526765df1309ae5168e
|
|
4
|
+
data.tar.gz: ce4f5679183067ac7f7d84196db0ad9a06a22878fda79ec302fbacc8047699fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8a898bce2dc22f5b1d873ace6cef5ea53046318d68bc80062898ec7be43553acde5a7709c1b1043666533e91fbecec3e66dd0372c0adb1642803b4273f0c91d0
|
|
7
|
+
data.tar.gz: d9a06bd5ef76f97a0331bb2e05681fae5103dbe69a21c8e5ac0b97cb7342a6569d0e595b90b00744f22ed9253fc858fdbcd8e81e37ce595bfd1bb830c5cea2b9
|
data/lib/terminal/ansi.rb
CHANGED
|
@@ -467,7 +467,7 @@ module Terminal
|
|
|
467
467
|
|
|
468
468
|
# Create a hyperlink.
|
|
469
469
|
# This is not widely supported; works for
|
|
470
|
-
#
|
|
470
|
+
# Ghostty,
|
|
471
471
|
# iTerm2,
|
|
472
472
|
# Kitty,
|
|
473
473
|
# Rio,
|
|
@@ -481,7 +481,7 @@ module Terminal
|
|
|
481
481
|
|
|
482
482
|
# Show a simple notification.
|
|
483
483
|
# This is not widely supported; works for
|
|
484
|
-
#
|
|
484
|
+
# Ghostty,
|
|
485
485
|
# iTerm2,
|
|
486
486
|
# Kitty,
|
|
487
487
|
# WezTerm.
|
|
@@ -490,6 +490,42 @@ module Terminal
|
|
|
490
490
|
# @return (see cursor_up)
|
|
491
491
|
def notify(text) = "\e]9;#{text}\a"
|
|
492
492
|
|
|
493
|
+
# Set task progress state.
|
|
494
|
+
# This is not widely supported; works for
|
|
495
|
+
# Ghostty,
|
|
496
|
+
# iTerm2,
|
|
497
|
+
# Kitty.
|
|
498
|
+
#
|
|
499
|
+
# @param state [:show, :warning, :error, :indeterminate, :hide]
|
|
500
|
+
# - `:show`
|
|
501
|
+
# show progress indicator with given percent value in default mode
|
|
502
|
+
# - `:warning`
|
|
503
|
+
# show progress with given percent value in warning mode
|
|
504
|
+
# - `:error`
|
|
505
|
+
# show progress with given percent value in error mode
|
|
506
|
+
# - `:indeterminate`
|
|
507
|
+
# show progress in indeterminate state (percent value is ignored)
|
|
508
|
+
# - `:hide`
|
|
509
|
+
# hide progress indicator (percent value is ignored)
|
|
510
|
+
# @return (see cursor_up)
|
|
511
|
+
def progress(state, percent = 0)
|
|
512
|
+
case state
|
|
513
|
+
when :show, true
|
|
514
|
+
state = 1
|
|
515
|
+
when :err, :error
|
|
516
|
+
state = 2
|
|
517
|
+
when :warn, :warning
|
|
518
|
+
state = 4
|
|
519
|
+
when Numeric
|
|
520
|
+
percent, state = state, 1
|
|
521
|
+
when :indeterminate
|
|
522
|
+
return +PROGRESS_SHOW_INDETERMINATE
|
|
523
|
+
else
|
|
524
|
+
return +PROGRESS_HIDE
|
|
525
|
+
end
|
|
526
|
+
"\e]9;4;#{state};#{percent.to_i.clamp(0, 100)}\a"
|
|
527
|
+
end
|
|
528
|
+
|
|
493
529
|
# Create scaled text.
|
|
494
530
|
# It uses the
|
|
495
531
|
# [text sizing protocol](https://sw.kovidgoyal.net/kitty/text-sizing-protocol).
|
|
@@ -857,6 +893,11 @@ module Terminal
|
|
|
857
893
|
# @private
|
|
858
894
|
LINE_ERASE_PREV = -"#{cursor_prev_line(nil)}#{LINE_ERASE}"
|
|
859
895
|
|
|
896
|
+
# @private
|
|
897
|
+
PROGRESS_HIDE = "\e]9;4;0;0\a"
|
|
898
|
+
# @private
|
|
899
|
+
PROGRESS_SHOW_INDETERMINATE = "\e]9;4;3;0\a"
|
|
900
|
+
|
|
860
901
|
# @comment seems not widely supported:
|
|
861
902
|
# doubled: def cursor_column(column = 1) = "\e[#{column}`"
|
|
862
903
|
# doubled: def cursor_row(row = 1) = "\e[#{row}d"
|
data/lib/terminal/input.rb
CHANGED
|
@@ -49,14 +49,16 @@ module Terminal
|
|
|
49
49
|
# @return [KeyEvent] next event
|
|
50
50
|
# @return [nil] in error case
|
|
51
51
|
def read_key_event
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
|
60
62
|
end
|
|
61
63
|
end
|
|
62
64
|
|
|
@@ -80,20 +82,33 @@ module Terminal
|
|
|
80
82
|
# when no block was given
|
|
81
83
|
def on_key_event(mouse: false, mouse_move: false, focus: false, &block)
|
|
82
84
|
return unless block
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
85
|
+
prevent_input_recursion do
|
|
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
|
|
92
96
|
end
|
|
93
97
|
end
|
|
94
98
|
|
|
95
99
|
private
|
|
96
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
|
|
110
|
+
end
|
|
111
|
+
|
|
97
112
|
def mouse_option(mouse, mouse_move, focus)
|
|
98
113
|
opts = +(mouse ? '1000' : '')
|
|
99
114
|
# highlight: '1001'
|
|
@@ -109,7 +124,7 @@ module Terminal
|
|
|
109
124
|
|
|
110
125
|
def on_tty_key_event(opts)
|
|
111
126
|
STDIN.noecho do |stdin|
|
|
112
|
-
opts
|
|
127
|
+
opts &&= raw_write("#{opts}h") ? "#{opts}l" : nil
|
|
113
128
|
while (raw = stdin.getch)
|
|
114
129
|
(yield(KeyEvent[raw]) ? next : break) if raw != "\e"
|
|
115
130
|
lesci = 0
|
|
@@ -189,5 +204,7 @@ module Terminal
|
|
|
189
204
|
0x1b => :Esc
|
|
190
205
|
}.compare_by_identity.freeze
|
|
191
206
|
|
|
207
|
+
@key_event = 0
|
|
208
|
+
|
|
192
209
|
autoload :KeyEvent, "#{__dir__}/input/key_event.rb"
|
|
193
210
|
end
|
data/lib/terminal/shell.rb
CHANGED
|
@@ -5,50 +5,43 @@ module Terminal
|
|
|
5
5
|
class << self
|
|
6
6
|
def exec(cmd, env: {}, shell: false, input: nil, **options)
|
|
7
7
|
options = options.except(:in, :out, :err)
|
|
8
|
-
if shell
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
cmd = cmd[0]
|
|
12
|
-
end
|
|
8
|
+
cmd = cmd.map! { _escape(_1) }.join(' ') if shell
|
|
9
|
+
input = Input[input]
|
|
10
|
+
thread = nil
|
|
13
11
|
|
|
14
|
-
input = Input.for(input)
|
|
15
|
-
ret = nil
|
|
16
12
|
with_io(options, input) do |cio, out_r, err_r, in_w|
|
|
17
13
|
thread = Process.detach(Process.spawn(env, *cmd, options))
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
read.delete(out_r)
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
if rr.include?(err_r)
|
|
32
|
-
begin
|
|
33
|
-
yield(err_r.readline(chomp: true), :error)
|
|
34
|
-
rescue SystemCallError, IOError
|
|
35
|
-
read.delete(err_r)
|
|
36
|
-
end
|
|
14
|
+
cio.each(&:close)
|
|
15
|
+
read = [out_r, err_r]
|
|
16
|
+
write = [in_w] if in_w
|
|
17
|
+
until read.empty? && write.nil?
|
|
18
|
+
rr, wr, = IO.select(read, write)
|
|
19
|
+
if rr.include?(out_r)
|
|
20
|
+
begin
|
|
21
|
+
yield(out_r.readline(chomp: true), :output)
|
|
22
|
+
rescue SystemCallError, IOError
|
|
23
|
+
read.delete(out_r)
|
|
37
24
|
end
|
|
38
|
-
|
|
25
|
+
end
|
|
26
|
+
if rr.include?(err_r)
|
|
39
27
|
begin
|
|
40
|
-
|
|
41
|
-
in_w.close
|
|
42
|
-
write = nil
|
|
28
|
+
yield(err_r.readline(chomp: true), :error)
|
|
43
29
|
rescue SystemCallError, IOError
|
|
44
|
-
|
|
30
|
+
read.delete(err_r)
|
|
45
31
|
end
|
|
46
32
|
end
|
|
47
|
-
|
|
48
|
-
|
|
33
|
+
next if wr.empty?
|
|
34
|
+
begin
|
|
35
|
+
next if input.call(in_w)
|
|
36
|
+
in_w.close
|
|
37
|
+
write = nil
|
|
38
|
+
rescue SystemCallError, IOError
|
|
39
|
+
write = nil
|
|
40
|
+
end
|
|
49
41
|
end
|
|
50
42
|
end
|
|
51
|
-
|
|
43
|
+
|
|
44
|
+
thread.join.value if thread
|
|
52
45
|
rescue SystemCallError, IOError
|
|
53
46
|
nil
|
|
54
47
|
end
|
|
@@ -70,57 +63,49 @@ module Terminal
|
|
|
70
63
|
end
|
|
71
64
|
|
|
72
65
|
def _escape(str)
|
|
73
|
-
return
|
|
74
|
-
str
|
|
75
|
-
str.gsub!(%r{[^A-Za-z0-9_\-.,:+/@\n]}, "\\\\\\&")
|
|
76
|
-
str.gsub!("\n", "'\n'")
|
|
77
|
-
str
|
|
66
|
+
return "''" if str.empty?
|
|
67
|
+
str.gsub(%r{[^A-Za-z0-9_\-.,:+/@\n]}, "\\\\\\&").gsub("\n", "'\n'")
|
|
78
68
|
end
|
|
79
69
|
end
|
|
80
70
|
|
|
81
71
|
module Input
|
|
82
|
-
def self.
|
|
72
|
+
def self.[](obj)
|
|
83
73
|
return unless obj
|
|
84
|
-
return
|
|
85
|
-
return
|
|
86
|
-
return
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
74
|
+
return copy_writer(obj) if obj.respond_to?(:readpartial)
|
|
75
|
+
return copy_writer(obj.to_io) if obj.respond_to?(:to_io)
|
|
76
|
+
return array_writer(obj) if obj.is_a?(Array)
|
|
77
|
+
if obj.respond_to?(:each)
|
|
78
|
+
return enum_writer(obj.enum_for(:each)) if obj.respond_to?(:enum_for)
|
|
79
|
+
return enum_writer(Enumerator.new { |y| obj.each { y << _1 } })
|
|
80
|
+
end
|
|
81
|
+
return array_writer(obj.to_a) if obj.respond_to?(:to_a)
|
|
82
|
+
proc do |io|
|
|
83
|
+
io.write(obj)
|
|
84
|
+
nil
|
|
85
|
+
end
|
|
96
86
|
end
|
|
97
87
|
|
|
98
|
-
|
|
99
|
-
|
|
88
|
+
def self.copy_writer(src)
|
|
89
|
+
proc do |trg|
|
|
90
|
+
IO.copy_stream(src, trg)
|
|
91
|
+
nil
|
|
92
|
+
end
|
|
100
93
|
end
|
|
101
94
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
def initialize(ary)
|
|
106
|
-
@ry = ary
|
|
107
|
-
@idx = -1
|
|
108
|
-
end
|
|
95
|
+
def self.array_writer(array, idx = -1)
|
|
96
|
+
proc { _1.write(array[idx += 1] || next) }
|
|
109
97
|
end
|
|
110
98
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
io.write(
|
|
99
|
+
def self.enum_writer(enum)
|
|
100
|
+
proc do |io|
|
|
101
|
+
io.write(enum.next)
|
|
114
102
|
rescue StopIteration
|
|
115
|
-
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def initialize(enum)
|
|
119
|
-
return @enum = enum.enum_for(:each) if enum.respond_to?(:enum_for)
|
|
120
|
-
@enum = Enumerator.new { |y| enum.each { y << _1 } }
|
|
103
|
+
nil
|
|
121
104
|
end
|
|
122
105
|
end
|
|
123
106
|
end
|
|
107
|
+
|
|
108
|
+
private_constant :Input
|
|
124
109
|
end
|
|
125
110
|
|
|
126
111
|
private_constant :Shell
|
data/lib/terminal/version.rb
CHANGED
data/lib/terminal.rb
CHANGED
|
@@ -301,7 +301,9 @@ module Terminal
|
|
|
301
301
|
# whether to create a seperate shell or not
|
|
302
302
|
# @option options [#readpartial, #to_io, Array, #each, #to_a, #to_s] :input
|
|
303
303
|
# piped to the command as input
|
|
304
|
+
# @raise [ArgumentError] when command is missing
|
|
304
305
|
def sh(*cmd, **options, &block)
|
|
306
|
+
raise(ArgumentError, 'command expected') if cmd.empty?
|
|
305
307
|
return Shell.exec(cmd, **options, &block) if block_given?
|
|
306
308
|
out = []
|
|
307
309
|
err = []
|