terminal_rb 0.13.0 → 0.14.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/ansi.rb +36 -35
- data/lib/terminal/input/key_event.rb +8 -8
- data/lib/terminal/input.rb +5 -5
- data/lib/terminal/rspec/helper.rb +1 -1
- data/lib/terminal/shell.rb +131 -0
- data/lib/terminal/text/char_width.rb +49 -13
- data/lib/terminal/text.rb +7 -7
- data/lib/terminal/version.rb +1 -1
- data/lib/terminal.rb +82 -41
- metadata +7 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 107b7b65dc5f58b1178e01fc86adad127df49fd175e56f7e36d2bfb873fc17a9
|
|
4
|
+
data.tar.gz: dfbf4797df22ac2803034a85a00fce2c480e4530840abd0d2a900bfb98993db5
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 115235b382d02c4c5242925e1bf1ff3b9928702b30682dd4a2964282b11df6339118da582e0eebc096806005f781a537688741eab2e94984db391abaf6b4ec72
|
|
7
|
+
data.tar.gz: 2718bf0147fcb7c3d84374f59c4f8ea4862c58dfd6982057196a78bd1f4308eb9c319a55ba3c41339a45a6fa994b1449b2cedb2e00ce6de5548dd3eaa5b98fa0
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# Terminal.rb 
|
|
2
2
|
|
|
3
|
-
Terminal
|
|
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, and correct special key recognition 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/
|
|
7
|
+
- Help: [rubydoc.info](https://rubydoc.info/gems/terminal_rb/index)
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
data/lib/terminal/ansi.rb
CHANGED
|
@@ -387,7 +387,7 @@ module Terminal
|
|
|
387
387
|
|
|
388
388
|
# Erase screen part.
|
|
389
389
|
#
|
|
390
|
-
# @param [:below, :above, :all, :scrollback]
|
|
390
|
+
# @param part [:below, :above, :all, :scrollback] screen part to erase
|
|
391
391
|
# @return (see cursor_up)
|
|
392
392
|
def screen_erase(part = :all)
|
|
393
393
|
"\e[#{
|
|
@@ -444,7 +444,7 @@ module Terminal
|
|
|
444
444
|
|
|
445
445
|
# Erase part of line.
|
|
446
446
|
#
|
|
447
|
-
# @param [:to_end, :to_start, :all]
|
|
447
|
+
# @param part [:to_end, :to_start, :all] line part to erase
|
|
448
448
|
# @return (see cursor_up)
|
|
449
449
|
def line_erase(part = :all)
|
|
450
450
|
"\e[#{
|
|
@@ -468,7 +468,7 @@ module Terminal
|
|
|
468
468
|
# Tabby,
|
|
469
469
|
# WezTerm.
|
|
470
470
|
#
|
|
471
|
-
# @param [#to_s]
|
|
471
|
+
# @param title [#to_s] text
|
|
472
472
|
# @return (see cursor_up)
|
|
473
473
|
def title(title) = "\e]0;#{title}\a"
|
|
474
474
|
|
|
@@ -481,8 +481,8 @@ module Terminal
|
|
|
481
481
|
# Tabby,
|
|
482
482
|
# WezTerm.
|
|
483
483
|
#
|
|
484
|
-
# @param [#to_s]
|
|
485
|
-
# @param [#to_s] text
|
|
484
|
+
# @param url [#to_s] URL to link to
|
|
485
|
+
# @param text [#to_s] text to display for the link
|
|
486
486
|
# @return (see cursor_up)
|
|
487
487
|
def link(url, text) = "\e]8;;#{url}\a#{text}\e]8;;\a"
|
|
488
488
|
|
|
@@ -497,18 +497,19 @@ module Terminal
|
|
|
497
497
|
# @example Half-height Greeting
|
|
498
498
|
# Terminal::Ansi.scale('Hello Ruby!', fracn: 1, fracd: 2, vertical: :centered)
|
|
499
499
|
#
|
|
500
|
-
# @param [#to_s]
|
|
501
|
-
#
|
|
500
|
+
# @param text [#to_s]
|
|
501
|
+
# text to scale
|
|
502
|
+
# @param scale [Integer, nil]
|
|
502
503
|
# overall scale size, range 1..7
|
|
503
|
-
# @param [Integer, nil]
|
|
504
|
+
# @param width [Integer, nil]
|
|
504
505
|
# with in cells, range 0..7
|
|
505
|
-
# @param [Integer, nil]
|
|
506
|
+
# @param fracn [Integer, nil]
|
|
506
507
|
# numerator for the fractional scale, range: 0..15
|
|
507
|
-
# @param [Integer, nil]
|
|
508
|
+
# @param fracd [Integer, nil]
|
|
508
509
|
# denominator for the fractional scale, range: 0..15, > fracn
|
|
509
|
-
# @param [:top, :bottom, :centered, nil]
|
|
510
|
+
# @param vertical [:top, :bottom, :centered, nil]
|
|
510
511
|
# vertical alignment to use for fractionally scaled text
|
|
511
|
-
# @param [:left, :right, :centered, nil]
|
|
512
|
+
# @param horizontal [:left, :right, :centered, nil]
|
|
512
513
|
# horizontal alignment to use for fractionally scaled text
|
|
513
514
|
# @return (see cursor_up)
|
|
514
515
|
def scale(
|
|
@@ -787,66 +788,66 @@ module Terminal
|
|
|
787
788
|
autoload :NAMED_COLORS, "#{__dir__}/ansi/named_colors.rb"
|
|
788
789
|
private_constant :NAMED_COLORS
|
|
789
790
|
|
|
790
|
-
#
|
|
791
|
+
# @private
|
|
791
792
|
RESET = self[:reset].freeze
|
|
792
793
|
|
|
793
|
-
#
|
|
794
|
+
# @private
|
|
794
795
|
FULL_RESET = "\ec"
|
|
795
796
|
|
|
796
|
-
#
|
|
797
|
+
# @private
|
|
797
798
|
CURSOR_HOME = cursor_pos(nil, nil).freeze
|
|
798
|
-
#
|
|
799
|
+
# @private
|
|
799
800
|
CURSOR_FIRST_ROW = cursor_pos(1).freeze
|
|
800
|
-
#
|
|
801
|
+
# @private
|
|
801
802
|
CURSOR_FIRST_COLUMN = cursor_column(1).freeze
|
|
802
803
|
|
|
803
|
-
#
|
|
804
|
+
# @private
|
|
804
805
|
CURSOR_SHOW = "\e[?25h"
|
|
805
|
-
#
|
|
806
|
+
# @private
|
|
806
807
|
CURSOR_HIDE = "\e[?25l"
|
|
807
808
|
|
|
808
809
|
# @comment CURSOR_POS_SAVE_SCO = "\e[s"
|
|
809
810
|
# @comment CURSOR_POS_SAVE_DEC = "\e7"
|
|
810
|
-
#
|
|
811
|
+
# @private
|
|
811
812
|
CURSOR_POS_SAVE = "\e7"
|
|
812
813
|
|
|
813
814
|
# @comment CURSOR_POS_RESTORE_SCO = "\e[u"
|
|
814
815
|
# @comment CURSOR_POS_RESTORE_DEC = "\e8"
|
|
815
|
-
#
|
|
816
|
+
# @private
|
|
816
817
|
CURSOR_POS_RESTORE = "\e8"
|
|
817
818
|
|
|
818
|
-
#
|
|
819
|
+
# @private
|
|
819
820
|
SCREEN_ERASE = screen_erase.freeze
|
|
820
|
-
#
|
|
821
|
+
# @private
|
|
821
822
|
SCREEN_ERASE_BELOW = screen_erase(:below).freeze
|
|
822
|
-
#
|
|
823
|
+
# @private
|
|
823
824
|
SCREEN_ERASE_ABOVE = screen_erase(:above).freeze
|
|
824
|
-
#
|
|
825
|
+
# @private
|
|
825
826
|
SCREEN_ERASE_SCROLLBACK = screen_erase(:scrollback).freeze
|
|
826
827
|
|
|
827
|
-
#
|
|
828
|
+
# @private
|
|
828
829
|
SCREEN_SAVE = "\e[?47h"
|
|
829
|
-
#
|
|
830
|
+
# @private
|
|
830
831
|
SCREEN_RESTORE = "\e[?47l"
|
|
831
832
|
|
|
832
|
-
#
|
|
833
|
+
# @private
|
|
833
834
|
# @comment at least Kitty requires CURSOR_HOME too
|
|
834
835
|
SCREEN_ALTERNATE = "\e[?1049h#{CURSOR_HOME}".freeze
|
|
835
|
-
#
|
|
836
|
+
# @private
|
|
836
837
|
SCREEN_ALTERNATE_OFF = "\e[?1049l"
|
|
837
838
|
|
|
838
|
-
#
|
|
839
|
+
# @private
|
|
839
840
|
SCREEN_REVERSE_MODE_ON = "\e[?5h"
|
|
840
|
-
#
|
|
841
|
+
# @private
|
|
841
842
|
SCREEN_REVERSE_MODE_OFF = "\e[?5l"
|
|
842
843
|
|
|
843
|
-
#
|
|
844
|
+
# @private
|
|
844
845
|
LINE_ERASE = line_erase.freeze
|
|
845
|
-
#
|
|
846
|
+
# @private
|
|
846
847
|
LINE_ERASE_TO_END = line_erase(:to_end).freeze
|
|
847
|
-
#
|
|
848
|
+
# @private
|
|
848
849
|
LINE_ERASE_TO_START = line_erase(:to_start).freeze
|
|
849
|
-
#
|
|
850
|
+
# @private
|
|
850
851
|
LINE_ERASE_PREV = "#{cursor_prev_line(nil)}#{LINE_ERASE}".freeze
|
|
851
852
|
|
|
852
853
|
# @comment seems not widely supported:
|
|
@@ -7,7 +7,7 @@ module Terminal
|
|
|
7
7
|
class KeyEvent
|
|
8
8
|
class << self
|
|
9
9
|
# @attribute [w] caching
|
|
10
|
-
# @return [true, false] whether
|
|
10
|
+
# @return [true, false] whether {KeyEvent}s should be cached
|
|
11
11
|
def caching = !!@cache
|
|
12
12
|
|
|
13
13
|
# @attribute [w] caching
|
|
@@ -21,7 +21,7 @@ module Terminal
|
|
|
21
21
|
|
|
22
22
|
# Translate a keyboard input string into a related KeyEvent
|
|
23
23
|
#
|
|
24
|
-
# @param [String]
|
|
24
|
+
# @param raw [String] keyboard input
|
|
25
25
|
# @return [KeyEvent] related key event
|
|
26
26
|
def [](raw)
|
|
27
27
|
return new(raw, *@single_key[raw.ord]) if raw.size == 1
|
|
@@ -52,7 +52,7 @@ module Terminal
|
|
|
52
52
|
unknown(raw)
|
|
53
53
|
end
|
|
54
54
|
|
|
55
|
-
#
|
|
55
|
+
# @private
|
|
56
56
|
def new(raw, key = raw, modifier = 0, extra = nil)
|
|
57
57
|
@cache ? (@cache[raw] ||= super.freeze) : super.freeze
|
|
58
58
|
end
|
|
@@ -164,7 +164,7 @@ module Terminal
|
|
|
164
164
|
attr_reader :modifier
|
|
165
165
|
|
|
166
166
|
# @comment for mouse events
|
|
167
|
-
#
|
|
167
|
+
# @private
|
|
168
168
|
attr_reader :extra
|
|
169
169
|
|
|
170
170
|
# Name of the key event.
|
|
@@ -188,16 +188,16 @@ module Terminal
|
|
|
188
188
|
# @return [Array<Symbol, String>] all pressed keys
|
|
189
189
|
def to_a = @ary.dup
|
|
190
190
|
|
|
191
|
-
#
|
|
191
|
+
# @private
|
|
192
192
|
def to_ary = simple? ? [@raw] : [@raw, @name]
|
|
193
193
|
|
|
194
|
-
#
|
|
194
|
+
# @private
|
|
195
195
|
def to_s = @name.dup
|
|
196
196
|
|
|
197
|
-
#
|
|
197
|
+
# @private
|
|
198
198
|
def inspect = "<#{self.class.name} #{to_ary.map(&:inspect).join(' ')}>"
|
|
199
199
|
|
|
200
|
-
#
|
|
200
|
+
# @private
|
|
201
201
|
def freeze
|
|
202
202
|
@raw.freeze
|
|
203
203
|
@key.freeze
|
data/lib/terminal/input.rb
CHANGED
|
@@ -28,7 +28,7 @@ module Terminal
|
|
|
28
28
|
#
|
|
29
29
|
# @deprecated Use rather {read_key_event} for better input handling.
|
|
30
30
|
#
|
|
31
|
-
# @param [:named, :raw, :both]
|
|
31
|
+
# @param mode [:named, :raw, :both] modifies the result
|
|
32
32
|
# @return [String] key code ("as is") in `:raw` mode
|
|
33
33
|
# @return [String] key name in `:named` mode
|
|
34
34
|
# @return [[String, String]] key code and key name in `:both` mode
|
|
@@ -64,8 +64,8 @@ module Terminal
|
|
|
64
64
|
return :dumb if im == 'dumb'
|
|
65
65
|
return :legacy if im == 'legacy'
|
|
66
66
|
return :dumb unless STDIN.tty?
|
|
67
|
-
if ansi? &&
|
|
68
|
-
at_exit {
|
|
67
|
+
if ansi? && raw_write("\e[>1u\e[?u\e[c") && csi_u?
|
|
68
|
+
at_exit { raw_write("\e[<u") }
|
|
69
69
|
return :csi_u
|
|
70
70
|
end
|
|
71
71
|
:legacy
|
|
@@ -99,10 +99,10 @@ module Terminal
|
|
|
99
99
|
# urxvt: '1015'
|
|
100
100
|
# pixel: '1016'
|
|
101
101
|
opts = each_move ? '1000;1003;1006;1015' : '1000;1006;1015'
|
|
102
|
-
opts =
|
|
102
|
+
opts = raw_write("\e[?#{opts}h") ? "\e[?#{opts}l" : nil
|
|
103
103
|
read_tty
|
|
104
104
|
ensure
|
|
105
|
-
|
|
105
|
+
raw_write(opts) if opts
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
def read_tty
|
|
@@ -53,7 +53,7 @@ RSpec.shared_context 'with Terminal.rb' do |ansi: true, application: :kitty, col
|
|
|
53
53
|
nil
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
allow(Terminal).to receive(:
|
|
56
|
+
allow(Terminal).to receive(:raw_write) do |object|
|
|
57
57
|
stdout.push(object = object.to_s)
|
|
58
58
|
object.bytesize
|
|
59
59
|
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Terminal
|
|
4
|
+
module Shell
|
|
5
|
+
class << self
|
|
6
|
+
def exec(cmd, env: {}, shell: false, input: nil, **options)
|
|
7
|
+
options = options.except(:in, :out, :err)
|
|
8
|
+
if shell
|
|
9
|
+
cmd = cmd.map! { _escape(_1) }.join(' ')
|
|
10
|
+
elsif cmd.size == 1 && cmd[0].include?(' ')
|
|
11
|
+
cmd = cmd[0]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
input = Input.for(input)
|
|
15
|
+
ret = nil
|
|
16
|
+
with_io(options, input) do |cio, out_r, err_r, in_w|
|
|
17
|
+
thread = Process.detach(Process.spawn(env, *cmd, options))
|
|
18
|
+
begin
|
|
19
|
+
cio.each(&:close)
|
|
20
|
+
read = [out_r, err_r]
|
|
21
|
+
write = [in_w] if in_w
|
|
22
|
+
while !read.empty? || write
|
|
23
|
+
rr, wr, = IO.select(read, write)
|
|
24
|
+
if rr.include?(out_r)
|
|
25
|
+
begin
|
|
26
|
+
yield(out_r.readline(chomp: true), :output)
|
|
27
|
+
rescue SystemCallError, IOError
|
|
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
|
|
37
|
+
end
|
|
38
|
+
next if wr.empty?
|
|
39
|
+
begin
|
|
40
|
+
next if input.call(in_w)
|
|
41
|
+
in_w.close
|
|
42
|
+
write = nil
|
|
43
|
+
rescue SystemCallError, IOError
|
|
44
|
+
write = nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
ensure
|
|
48
|
+
ret = thread.join.value
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
ret
|
|
52
|
+
rescue SystemCallError, IOError
|
|
53
|
+
nil
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
def with_io(options, input)
|
|
59
|
+
IO.pipe do |out_r, out_w|
|
|
60
|
+
IO.pipe do |err_r, err_w|
|
|
61
|
+
cio = [options[:out] = out_w, options[:err] = err_w]
|
|
62
|
+
return yield(cio, out_r, err_r) unless input
|
|
63
|
+
IO.pipe do |in_r, in_w|
|
|
64
|
+
cio << (options[:in] = in_r)
|
|
65
|
+
in_w.sync = true
|
|
66
|
+
yield(cio, out_r, err_r, in_w)
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def _escape(str)
|
|
73
|
+
return +"''" if str.empty?
|
|
74
|
+
str = str.dup
|
|
75
|
+
str.gsub!(%r{[^A-Za-z0-9_\-.,:+/@\n]}, "\\\\\\&")
|
|
76
|
+
str.gsub!("\n", "'\n'")
|
|
77
|
+
str
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
module Input
|
|
82
|
+
def self.for(obj)
|
|
83
|
+
return unless obj
|
|
84
|
+
return CopyWriter.new(obj) if obj.respond_to?(:readpartial)
|
|
85
|
+
return CopyWriter.new(obj.to_io) if obj.respond_to?(:to_io)
|
|
86
|
+
return ArrayWriter.new(obj) if obj.is_a?(Array)
|
|
87
|
+
return EnumerableWriter.new(obj) if obj.respond_to?(:each)
|
|
88
|
+
return ArrayWriter.new(obj.to_a) if obj.respond_to?(:to_a)
|
|
89
|
+
Writer.new(obj)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
class Writer
|
|
93
|
+
def call(io) = (io.write(_next) if @obj)
|
|
94
|
+
def initialize(obj) = (@obj = obj)
|
|
95
|
+
def _next = (_, @obj = @obj, nil).first
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class CopyWriter < Writer
|
|
99
|
+
def call(io) = (IO.copy_stream(_next, io) if @obj)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
class ArrayWriter
|
|
103
|
+
def call(io) = io.write(@ry[@idx += 1] || return)
|
|
104
|
+
|
|
105
|
+
def initialize(ary)
|
|
106
|
+
@ry = ary
|
|
107
|
+
@idx = -1
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
class EnumerableWriter
|
|
112
|
+
def call(io)
|
|
113
|
+
io.write(@enum.next)
|
|
114
|
+
rescue StopIteration
|
|
115
|
+
false
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def initialize(enum)
|
|
119
|
+
@enum =
|
|
120
|
+
if enum.respond_to?(:enum_for)
|
|
121
|
+
enum.enum_for(:each)
|
|
122
|
+
else
|
|
123
|
+
Enumerator.new { |y| enum.each { y << _1 } }
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
private_constant :Shell
|
|
131
|
+
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Terminal
|
|
4
4
|
module Text
|
|
5
5
|
#
|
|
6
|
-
# Generated file; based on Unicode
|
|
6
|
+
# Generated file; based on Unicode v17.0.0
|
|
7
7
|
#
|
|
8
8
|
module CharWidth
|
|
9
9
|
def self.[](ord) = @width[@last.bsearch_index { ord <= _1 }]
|
|
@@ -415,7 +415,9 @@ module Terminal
|
|
|
415
415
|
0x1a7e,
|
|
416
416
|
0x1a7f,
|
|
417
417
|
0x1aaf,
|
|
418
|
-
|
|
418
|
+
0x1add,
|
|
419
|
+
0x1adf,
|
|
420
|
+
0x1aeb,
|
|
419
421
|
0x1aff,
|
|
420
422
|
0x1b03,
|
|
421
423
|
0x1b33,
|
|
@@ -887,7 +889,7 @@ module Terminal
|
|
|
887
889
|
0x10d6d,
|
|
888
890
|
0x10eaa,
|
|
889
891
|
0x10eac,
|
|
890
|
-
|
|
892
|
+
0x10ef9,
|
|
891
893
|
0x10eff,
|
|
892
894
|
0x10f45,
|
|
893
895
|
0x10f50,
|
|
@@ -1037,6 +1039,12 @@ module Terminal
|
|
|
1037
1039
|
0x11a96,
|
|
1038
1040
|
0x11a97,
|
|
1039
1041
|
0x11a99,
|
|
1042
|
+
0x11b5f,
|
|
1043
|
+
0x11b60,
|
|
1044
|
+
0x11b61,
|
|
1045
|
+
0x11b64,
|
|
1046
|
+
0x11b65,
|
|
1047
|
+
0x11b66,
|
|
1040
1048
|
0x11c2f,
|
|
1041
1049
|
0x11c36,
|
|
1042
1050
|
0x11c37,
|
|
@@ -1099,13 +1107,13 @@ module Terminal
|
|
|
1099
1107
|
0x16fe3,
|
|
1100
1108
|
0x16fe4,
|
|
1101
1109
|
0x16fef,
|
|
1102
|
-
|
|
1110
|
+
0x16ff6,
|
|
1103
1111
|
0x16fff,
|
|
1104
|
-
0x187f7,
|
|
1105
|
-
0x187ff,
|
|
1106
1112
|
0x18cd5,
|
|
1107
1113
|
0x18cfe,
|
|
1108
|
-
|
|
1114
|
+
0x18d1e,
|
|
1115
|
+
0x18d7f,
|
|
1116
|
+
0x18df2,
|
|
1109
1117
|
0x1afef,
|
|
1110
1118
|
0x1aff3,
|
|
1111
1119
|
0x1aff4,
|
|
@@ -1178,6 +1186,14 @@ module Terminal
|
|
|
1178
1186
|
0x1e4ef,
|
|
1179
1187
|
0x1e5ed,
|
|
1180
1188
|
0x1e5ef,
|
|
1189
|
+
0x1e6e2,
|
|
1190
|
+
0x1e6e3,
|
|
1191
|
+
0x1e6e5,
|
|
1192
|
+
0x1e6e6,
|
|
1193
|
+
0x1e6ed,
|
|
1194
|
+
0x1e6ef,
|
|
1195
|
+
0x1e6f4,
|
|
1196
|
+
0x1e6f5,
|
|
1181
1197
|
0x1e8cf,
|
|
1182
1198
|
0x1e8d6,
|
|
1183
1199
|
0x1e943,
|
|
@@ -1251,7 +1267,7 @@ module Terminal
|
|
|
1251
1267
|
0x1f6cf,
|
|
1252
1268
|
0x1f6d2,
|
|
1253
1269
|
0x1f6d4,
|
|
1254
|
-
|
|
1270
|
+
0x1f6d8,
|
|
1255
1271
|
0x1f6db,
|
|
1256
1272
|
0x1f6df,
|
|
1257
1273
|
0x1f6ea,
|
|
@@ -1271,14 +1287,16 @@ module Terminal
|
|
|
1271
1287
|
0x1fa6f,
|
|
1272
1288
|
0x1fa7c,
|
|
1273
1289
|
0x1fa7f,
|
|
1274
|
-
|
|
1275
|
-
|
|
1290
|
+
0x1fa8a,
|
|
1291
|
+
0x1fa8d,
|
|
1276
1292
|
0x1fac6,
|
|
1277
|
-
|
|
1293
|
+
0x1fac7,
|
|
1294
|
+
0x1fac8,
|
|
1295
|
+
0x1facc,
|
|
1278
1296
|
0x1fadc,
|
|
1279
1297
|
0x1fade,
|
|
1280
|
-
|
|
1281
|
-
|
|
1298
|
+
0x1faea,
|
|
1299
|
+
0x1faee,
|
|
1282
1300
|
0x1faf8,
|
|
1283
1301
|
0x1ffff,
|
|
1284
1302
|
0x2fffd,
|
|
@@ -1748,6 +1766,8 @@ module Terminal
|
|
|
1748
1766
|
1,
|
|
1749
1767
|
0,
|
|
1750
1768
|
1,
|
|
1769
|
+
0,
|
|
1770
|
+
1,
|
|
1751
1771
|
-1,
|
|
1752
1772
|
1,
|
|
1753
1773
|
-1,
|
|
@@ -2381,6 +2401,12 @@ module Terminal
|
|
|
2381
2401
|
1,
|
|
2382
2402
|
0,
|
|
2383
2403
|
1,
|
|
2404
|
+
0,
|
|
2405
|
+
1,
|
|
2406
|
+
0,
|
|
2407
|
+
1,
|
|
2408
|
+
0,
|
|
2409
|
+
1,
|
|
2384
2410
|
2,
|
|
2385
2411
|
0,
|
|
2386
2412
|
1,
|
|
@@ -2468,6 +2494,14 @@ module Terminal
|
|
|
2468
2494
|
1,
|
|
2469
2495
|
0,
|
|
2470
2496
|
1,
|
|
2497
|
+
0,
|
|
2498
|
+
1,
|
|
2499
|
+
0,
|
|
2500
|
+
1,
|
|
2501
|
+
0,
|
|
2502
|
+
1,
|
|
2503
|
+
0,
|
|
2504
|
+
1,
|
|
2471
2505
|
2,
|
|
2472
2506
|
1,
|
|
2473
2507
|
2,
|
|
@@ -2570,6 +2604,8 @@ module Terminal
|
|
|
2570
2604
|
1,
|
|
2571
2605
|
2,
|
|
2572
2606
|
1,
|
|
2607
|
+
2,
|
|
2608
|
+
1,
|
|
2573
2609
|
0,
|
|
2574
2610
|
1,
|
|
2575
2611
|
-1,
|
data/lib/terminal/text.rb
CHANGED
|
@@ -24,8 +24,8 @@ module Terminal
|
|
|
24
24
|
# some are ambiguous. The function uses {ambiguous_char_width} for each of
|
|
25
25
|
# these characters.
|
|
26
26
|
#
|
|
27
|
-
# @param [#to_s] str
|
|
28
|
-
# @param [true|false]
|
|
27
|
+
# @param object [#to_s] str to process
|
|
28
|
+
# @param bbcode [true|false] whether to interpret embedded BBCode
|
|
29
29
|
# @return [Integer] display width
|
|
30
30
|
def width(str, bbcode: true)
|
|
31
31
|
str = bbcode ? Ansi.unbbcode(str) : str.to_s
|
|
@@ -45,15 +45,15 @@ module Terminal
|
|
|
45
45
|
# Terminal::Text.each_line('This is a simple test 😀', limit: 6).to_a
|
|
46
46
|
# # => ["This", "is a", "simple", "test", "😀"]
|
|
47
47
|
#
|
|
48
|
-
# @param [#to_s, ...]
|
|
48
|
+
# @param text [#to_s, ...]
|
|
49
49
|
# text objects to process
|
|
50
|
-
# @param [#to_i, nil]
|
|
50
|
+
# @param limit [#to_i, nil]
|
|
51
51
|
# optionally limit line size
|
|
52
|
-
# @param [true, false]
|
|
52
|
+
# @param bbcode [true, false]
|
|
53
53
|
# whether to interprete embedded BBCode (see {Ansi.bbcode})
|
|
54
|
-
# @param [true, false]
|
|
54
|
+
# @param ansi [true, false]
|
|
55
55
|
# whether to keep embedded ANSI control codes
|
|
56
|
-
# @param [true, false]
|
|
56
|
+
# @param ignore_newline [true, false]
|
|
57
57
|
# wheter to ignore embedded line breaks (`"\r\n"` or `"\n"`)
|
|
58
58
|
# @yield [String] text line
|
|
59
59
|
# @return [Enumerator] when no block given
|
data/lib/terminal/version.rb
CHANGED
data/lib/terminal.rb
CHANGED
|
@@ -7,20 +7,20 @@ require_relative 'terminal/input'
|
|
|
7
7
|
# Terminal access with support for ANSI control codes and
|
|
8
8
|
# [BBCode-like](https://en.wikipedia.org/wiki/BBCode) embedded text attribute
|
|
9
9
|
# syntax (see {Ansi.bbcode}).
|
|
10
|
-
#
|
|
11
10
|
# It automagically detects whether your terminal supports ANSI features, like
|
|
12
11
|
# coloring (see {colors}) or the
|
|
13
12
|
# [CSIu protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol) support
|
|
14
13
|
# (see {read_key_event} and {input_mode}).
|
|
15
14
|
# It calculates the display width for Unicode chars (see {Text.width}) and help
|
|
16
|
-
# you to display text with line
|
|
15
|
+
# you to display text with word-wise line breaks (see {Text.each_line}).
|
|
17
16
|
#
|
|
18
17
|
module Terminal
|
|
19
18
|
class << self
|
|
20
19
|
# Return true if the current terminal supports ANSI control codes.
|
|
21
|
-
# When the terminal does not support it, {colors} will return
|
|
22
|
-
# output methods ({<<}, {print}, {puts}) will not forward ANSI control
|
|
23
|
-
# codes to the terminal, {read_key_event} will not support
|
|
20
|
+
# When the terminal does not support it, {colors} will return 2 (two) and
|
|
21
|
+
# all output methods ({<<}, {print}, {puts}) will not forward ANSI control
|
|
22
|
+
# codes to the terminal, {read_key_event} will not support extended key
|
|
23
|
+
# codes.
|
|
24
24
|
#
|
|
25
25
|
# @attribute [r] ansi?
|
|
26
26
|
# @return [Boolean] whether ANSI control codes are supported
|
|
@@ -31,34 +31,10 @@ module Terminal
|
|
|
31
31
|
# The detection supports a wide range of terminal emulators but may return
|
|
32
32
|
# `nil` if an unsupported terminal was found. These are the supported
|
|
33
33
|
# application IDs:
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
# - `:dg_unix`,
|
|
39
|
-
# - `:fluent`,
|
|
40
|
-
# - `:ghostty`,
|
|
41
|
-
# - `:hpterm`,
|
|
42
|
-
# - `:hyper`,
|
|
43
|
-
# - `:iterm`,
|
|
44
|
-
# - `:macos`,
|
|
45
|
-
# - `:mintty`,
|
|
46
|
-
# - `:ms_terminal`,
|
|
47
|
-
# - `:ncr260`,
|
|
48
|
-
# - `:nsterm`,
|
|
49
|
-
# - `:rio`,
|
|
50
|
-
# - `:tabby`,
|
|
51
|
-
# - `:terminator`,
|
|
52
|
-
# - `:terminology`,
|
|
53
|
-
# - `:terminus`,
|
|
54
|
-
# - `:termite`,
|
|
55
|
-
# - `:tmux`,
|
|
56
|
-
# - `:vscode`,
|
|
57
|
-
# - `:vt100`,
|
|
58
|
-
# - `:warp`,
|
|
59
|
-
# - `:wezterm`,
|
|
60
|
-
# - `:wyse`,
|
|
61
|
-
# - `:xnuppc`
|
|
34
|
+
# `:alacritty`, `:amiga`, `:code_edit`, `:dg_unix`, `:docker`, `:fluent`,
|
|
35
|
+
# `:hpterm`, `:hyper`, `:iterm`, `:macos`, `:mintty`, `:ms_terminal`,
|
|
36
|
+
# `:ncr260`, `:nsterm`, `:terminator`, `:terminology`, `:termite`,
|
|
37
|
+
# `:vt100`, `:warp`, `:wezterm`, `:wyse`, `:xnuppc`
|
|
62
38
|
#
|
|
63
39
|
# @attribute [r] application
|
|
64
40
|
# @return [Symbol, nil] the application identifier
|
|
@@ -162,7 +138,7 @@ module Terminal
|
|
|
162
138
|
#
|
|
163
139
|
# @return [Terminal] itself
|
|
164
140
|
def hide_cursor
|
|
165
|
-
|
|
141
|
+
raw_write(Ansi::CURSOR_HIDE) if ansi? && (@cc += 1) == 1
|
|
166
142
|
self
|
|
167
143
|
end
|
|
168
144
|
|
|
@@ -174,14 +150,14 @@ module Terminal
|
|
|
174
150
|
#
|
|
175
151
|
# @return [Terminal] itself
|
|
176
152
|
def show_cursor
|
|
177
|
-
|
|
153
|
+
raw_write(Ansi::CURSOR_SHOW) if @cc > 0 && (@cc -= 1).zero?
|
|
178
154
|
self
|
|
179
155
|
end
|
|
180
156
|
|
|
181
157
|
# Writes the given object to the terminal.
|
|
182
158
|
# Interprets embedded BBCode.
|
|
183
159
|
#
|
|
184
|
-
# @param [#to_s] object
|
|
160
|
+
# @param object [#to_s] object to write
|
|
185
161
|
# @return [Terminal] itself
|
|
186
162
|
def <<(object)
|
|
187
163
|
@out.write(Ansi.bbcode(object)) if @out && object != nil
|
|
@@ -194,8 +170,8 @@ module Terminal
|
|
|
194
170
|
# Writes the given objects to the terminal.
|
|
195
171
|
# Optionally interprets embedded BBCode.
|
|
196
172
|
#
|
|
197
|
-
# @param [Array<#to_s>]
|
|
198
|
-
# @param [true|false]
|
|
173
|
+
# @param objects [Array<#to_s>] any number of objects to write
|
|
174
|
+
# @param bbcode [true|false] whether to interpret embedded BBCode
|
|
199
175
|
# @return [nil]
|
|
200
176
|
def print(*objects, bbcode: true)
|
|
201
177
|
return unless @out
|
|
@@ -225,14 +201,78 @@ module Terminal
|
|
|
225
201
|
@out = nil
|
|
226
202
|
end
|
|
227
203
|
|
|
228
|
-
|
|
204
|
+
# Execute a command and report command output line by line
|
|
205
|
+
# or capture the output.
|
|
206
|
+
#
|
|
207
|
+
# @example Execute a command wih arguments
|
|
208
|
+
# ret, out, = Terminal.sh('curl', '--version')
|
|
209
|
+
# raise('command not found - curl') unless ret
|
|
210
|
+
# puts out
|
|
211
|
+
#
|
|
212
|
+
# @example Execute shell commands and print error
|
|
213
|
+
# ret, _, err = Terminal.sh("mkdir '/test' && cd '/test'")
|
|
214
|
+
# raise('unable to execute') unless ret
|
|
215
|
+
# puts("error #{ret.exitstatus}: #{err.join}") unless ret.success?
|
|
216
|
+
#
|
|
217
|
+
# @example Copy a file content to clipboard
|
|
218
|
+
# command = ENV.fetch('CLIP_COPY', 'pbcopy')
|
|
219
|
+
# File.open(__FILE__) do |file|
|
|
220
|
+
# ret, = Terminal.sh(*command, input: file)
|
|
221
|
+
# raise("command not found - #{command}") unless ret
|
|
222
|
+
# end
|
|
223
|
+
#
|
|
224
|
+
# @overload sh(*cmd, **options)
|
|
225
|
+
# @return [[Process::Status, output, error]]
|
|
226
|
+
# the status of the process successfully executed,
|
|
227
|
+
# the captured output,
|
|
228
|
+
# the captured error output,
|
|
229
|
+
# @return [nil]
|
|
230
|
+
# when the command was not executable
|
|
231
|
+
#
|
|
232
|
+
# @overload sh(*cmd, **options)
|
|
233
|
+
# @yieldparam line [String]
|
|
234
|
+
# next received line from the process
|
|
235
|
+
# @yieldparam type [:output, :error]
|
|
236
|
+
# whether the received line is standard output or error
|
|
237
|
+
# @return [Process::Status]
|
|
238
|
+
# the status of the process successfully executed
|
|
239
|
+
# @return [nil]
|
|
240
|
+
# when the command was not executable
|
|
241
|
+
#
|
|
242
|
+
# @param cmd [Array<#to_s>]
|
|
243
|
+
# command line or command with arguments
|
|
244
|
+
# @param options [Hash]
|
|
245
|
+
# options how to execute the new process;
|
|
246
|
+
# see `Process.spawn` for default execution options provided by Ruby
|
|
247
|
+
# @option options [Hash<String,String>] :env
|
|
248
|
+
# key/value pairs added to the ENV of the process;
|
|
249
|
+
# with option `:unsetenv_others` set to `true` it replaces the ENV
|
|
250
|
+
# @option options [true, false] :shell
|
|
251
|
+
# whether to create a seperate shell or not
|
|
252
|
+
# @option options [#readpartial, #to_io, Array, #each, #to_a, #to_s] :input
|
|
253
|
+
# piped to the command as input
|
|
254
|
+
def sh(*cmd, **options, &block)
|
|
255
|
+
return Shell.exec(cmd, **options, &block) if block_given?
|
|
256
|
+
out = []
|
|
257
|
+
err = []
|
|
258
|
+
[
|
|
259
|
+
Shell.exec(cmd, **options) do |line, type|
|
|
260
|
+
(type == :error ? err : out) << line
|
|
261
|
+
end,
|
|
262
|
+
out,
|
|
263
|
+
err
|
|
264
|
+
]
|
|
265
|
+
end
|
|
229
266
|
|
|
230
|
-
|
|
267
|
+
# @private
|
|
268
|
+
def raw_write(str)
|
|
231
269
|
@out&.write(str)
|
|
232
270
|
rescue IOError
|
|
233
271
|
@out = nil
|
|
234
272
|
end
|
|
235
273
|
|
|
274
|
+
private
|
|
275
|
+
|
|
236
276
|
def _default_size
|
|
237
277
|
rows = ENV['LINES'].to_i
|
|
238
278
|
columns = ENV['COLUMNS'].to_i
|
|
@@ -300,5 +340,6 @@ module Terminal
|
|
|
300
340
|
dir = "#{__dir__}/terminal"
|
|
301
341
|
autoload :Text, "#{dir}/text.rb"
|
|
302
342
|
autoload :Detect, "#{dir}/detect.rb"
|
|
303
|
-
|
|
343
|
+
autoload :Shell, "#{dir}/shell.rb"
|
|
344
|
+
private_constant :Detect, :Shell
|
|
304
345
|
end
|
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.14.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Blumtritt
|
|
@@ -9,8 +9,9 @@ bindir: bin
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies: []
|
|
12
|
-
description: 'Terminal
|
|
13
|
-
|
|
12
|
+
description: 'Terminal.rb supports you with input and output on your terminal. Simple
|
|
13
|
+
BBCode-like markup for attributes and coloring, word-wise line breaks, and correct
|
|
14
|
+
special key recognition enable you to implement your CLI app quickly and easily. '
|
|
14
15
|
executables:
|
|
15
16
|
- bbcode
|
|
16
17
|
extensions: []
|
|
@@ -34,6 +35,7 @@ files:
|
|
|
34
35
|
- lib/terminal/input.rb
|
|
35
36
|
- lib/terminal/input/key_event.rb
|
|
36
37
|
- lib/terminal/rspec/helper.rb
|
|
38
|
+
- lib/terminal/shell.rb
|
|
37
39
|
- lib/terminal/text.rb
|
|
38
40
|
- lib/terminal/text/char_width.rb
|
|
39
41
|
- lib/terminal/version.rb
|
|
@@ -52,7 +54,7 @@ require_paths:
|
|
|
52
54
|
- lib
|
|
53
55
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
56
|
requirements:
|
|
55
|
-
- - "
|
|
57
|
+
- - ">"
|
|
56
58
|
- !ruby/object:Gem::Version
|
|
57
59
|
version: '3.0'
|
|
58
60
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
@@ -61,7 +63,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
61
63
|
- !ruby/object:Gem::Version
|
|
62
64
|
version: '0'
|
|
63
65
|
requirements: []
|
|
64
|
-
rubygems_version: 3.
|
|
66
|
+
rubygems_version: 3.6.9
|
|
65
67
|
specification_version: 4
|
|
66
68
|
summary: Fast terminal access with ANSI, CSIu, BBCode, word-wise line break support.
|
|
67
69
|
test_files: []
|