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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 756d1e0e016472a063f9d64650f4731ef6e8fe272d6a84c4e001119f050af3ea
4
- data.tar.gz: 74c24ba64e08e6dc79605c17b0ded59ecb8784a45f95dbe5be7f231c889fdab7
3
+ metadata.gz: 107b7b65dc5f58b1178e01fc86adad127df49fd175e56f7e36d2bfb873fc17a9
4
+ data.tar.gz: dfbf4797df22ac2803034a85a00fce2c480e4530840abd0d2a900bfb98993db5
5
5
  SHA512:
6
- metadata.gz: 68bb81c425cb5df44650acc351a28cbbb3eccaea53b405167f1207ba3dc528093de6322bac087fad9301da7470711b4cd399f120233637e9b92f1727e9e2d4c9
7
- data.tar.gz: 9e855ec0401d22c615fd6ff25e2adae7717ecbf3578ea40425e5ef580b9520ee23130c8d49814034f682590166d2319070c17c18407962fc8782b18c1fffe4fa
6
+ metadata.gz: 115235b382d02c4c5242925e1bf1ff3b9928702b30682dd4a2964282b11df6339118da582e0eebc096806005f781a537688741eab2e94984db391abaf6b4ec72
7
+ data.tar.gz: 2718bf0147fcb7c3d84374f59c4f8ea4862c58dfd6982057196a78bd1f4308eb9c319a55ba3c41339a45a6fa994b1449b2cedb2e00ce6de5548dd3eaa5b98fa0
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Terminal.rb ![version](https://img.shields.io/gem/v/terminal_rb?label=)
2
2
 
3
- Terminal access with super fast ANSI control codes support and [BBCode-like](https://en.wikipedia.org/wiki/BBCode) embedded text attribute syntax.
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/Terminal)
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] part screen part to erase
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] part line part to erase
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] title text
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] url URL to link to
485
- # @param [#to_s] text text to display for the link
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] text text to scale
501
- # @param [Integer, nil] scale
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] width
504
+ # @param width [Integer, nil]
504
505
  # with in cells, range 0..7
505
- # @param [Integer, nil] fracn
506
+ # @param fracn [Integer, nil]
506
507
  # numerator for the fractional scale, range: 0..15
507
- # @param [Integer, nil] fracd
508
+ # @param fracd [Integer, nil]
508
509
  # denominator for the fractional scale, range: 0..15, > fracn
509
- # @param [:top, :bottom, :centered, nil] vertical
510
+ # @param vertical [:top, :bottom, :centered, nil]
510
511
  # vertical alignment to use for fractionally scaled text
511
- # @param [:left, :right, :centered, nil] horizontal
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
- # @!visibility private
791
+ # @private
791
792
  RESET = self[:reset].freeze
792
793
 
793
- # @!visibility private
794
+ # @private
794
795
  FULL_RESET = "\ec"
795
796
 
796
- # @!visibility private
797
+ # @private
797
798
  CURSOR_HOME = cursor_pos(nil, nil).freeze
798
- # @!visibility private
799
+ # @private
799
800
  CURSOR_FIRST_ROW = cursor_pos(1).freeze
800
- # @!visibility private
801
+ # @private
801
802
  CURSOR_FIRST_COLUMN = cursor_column(1).freeze
802
803
 
803
- # @!visibility private
804
+ # @private
804
805
  CURSOR_SHOW = "\e[?25h"
805
- # @!visibility private
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
- # @!visibility private
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
- # @!visibility private
816
+ # @private
816
817
  CURSOR_POS_RESTORE = "\e8"
817
818
 
818
- # @!visibility private
819
+ # @private
819
820
  SCREEN_ERASE = screen_erase.freeze
820
- # @!visibility private
821
+ # @private
821
822
  SCREEN_ERASE_BELOW = screen_erase(:below).freeze
822
- # @!visibility private
823
+ # @private
823
824
  SCREEN_ERASE_ABOVE = screen_erase(:above).freeze
824
- # @!visibility private
825
+ # @private
825
826
  SCREEN_ERASE_SCROLLBACK = screen_erase(:scrollback).freeze
826
827
 
827
- # @!visibility private
828
+ # @private
828
829
  SCREEN_SAVE = "\e[?47h"
829
- # @!visibility private
830
+ # @private
830
831
  SCREEN_RESTORE = "\e[?47l"
831
832
 
832
- # @!visibility private
833
+ # @private
833
834
  # @comment at least Kitty requires CURSOR_HOME too
834
835
  SCREEN_ALTERNATE = "\e[?1049h#{CURSOR_HOME}".freeze
835
- # @!visibility private
836
+ # @private
836
837
  SCREEN_ALTERNATE_OFF = "\e[?1049l"
837
838
 
838
- # @!visibility private
839
+ # @private
839
840
  SCREEN_REVERSE_MODE_ON = "\e[?5h"
840
- # @!visibility private
841
+ # @private
841
842
  SCREEN_REVERSE_MODE_OFF = "\e[?5l"
842
843
 
843
- # @!visibility private
844
+ # @private
844
845
  LINE_ERASE = line_erase.freeze
845
- # @!visibility private
846
+ # @private
846
847
  LINE_ERASE_TO_END = line_erase(:to_end).freeze
847
- # @!visibility private
848
+ # @private
848
849
  LINE_ERASE_TO_START = line_erase(:to_start).freeze
849
- # @!visibility private
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 KeyCodes should be cached
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] raw keyboard input
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
- # @!visibility private
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
- # @!visibility private
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
- # @!visibility private
191
+ # @private
192
192
  def to_ary = simple? ? [@raw] : [@raw, @name]
193
193
 
194
- # @!visibility private
194
+ # @private
195
195
  def to_s = @name.dup
196
196
 
197
- # @!visibility private
197
+ # @private
198
198
  def inspect = "<#{self.class.name} #{to_ary.map(&:inspect).join(' ')}>"
199
199
 
200
- # @!visibility private
200
+ # @private
201
201
  def freeze
202
202
  @raw.freeze
203
203
  @key.freeze
@@ -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] mode modifies the result
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? && _write("\e[>1u\e[?u\e[c") && csi_u?
68
- at_exit { _write("\e[<u") }
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 = _write("\e[?#{opts}h") ? "\e[?#{opts}l" : nil
102
+ opts = raw_write("\e[?#{opts}h") ? "\e[?#{opts}l" : nil
103
103
  read_tty
104
104
  ensure
105
- _write(opts) if opts
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(:_write) do |object|
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 v16.0.0
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
- 0x1ace,
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
- 0x10efb,
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
- 0x16ff1,
1110
+ 0x16ff6,
1103
1111
  0x16fff,
1104
- 0x187f7,
1105
- 0x187ff,
1106
1112
  0x18cd5,
1107
1113
  0x18cfe,
1108
- 0x18d08,
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
- 0x1f6d7,
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
- 0x1fa89,
1275
- 0x1fa8e,
1290
+ 0x1fa8a,
1291
+ 0x1fa8d,
1276
1292
  0x1fac6,
1277
- 0x1facd,
1293
+ 0x1fac7,
1294
+ 0x1fac8,
1295
+ 0x1facc,
1278
1296
  0x1fadc,
1279
1297
  0x1fade,
1280
- 0x1fae9,
1281
- 0x1faef,
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 object to process
28
- # @param [true|false] bbcode whether to interpret embedded BBCode
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, ...] text
48
+ # @param text [#to_s, ...]
49
49
  # text objects to process
50
- # @param [#to_i, nil] limit
50
+ # @param limit [#to_i, nil]
51
51
  # optionally limit line size
52
- # @param [true, false] bbcode
52
+ # @param bbcode [true, false]
53
53
  # whether to interprete embedded BBCode (see {Ansi.bbcode})
54
- # @param [true, false] ansi
54
+ # @param ansi [true, false]
55
55
  # whether to keep embedded ANSI control codes
56
- # @param [true, false] ignore_newline
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
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Terminal
4
4
  # The version number of the gem.
5
- VERSION = '0.13.0'
5
+ VERSION = '0.14.0'
6
6
  end
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 formatting (see {Text.each_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 `2` and all
22
- # output methods ({<<}, {print}, {puts}) will not forward ANSI control
23
- # codes to the terminal, {read_key_event} will not support CSIu.
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
- # - `:alacritty`,
36
- # - `:amiga`,
37
- # - `:code_edit`,
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
- _write(Ansi::CURSOR_HIDE) if ansi? && (@cc += 1) == 1
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
- _write(Ansi::CURSOR_SHOW) if @cc > 0 && (@cc -= 1).zero?
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 object to write
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>] objects any number of objects to write
198
- # @param [true|false] bbcode whether to interpret embedded BBCode
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
- private
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
- def _write(str)
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
- private_constant :Detect
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.13.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 access with super fast ANSI control codes support, modern CSIu
13
- input, word-wise line break, BBCode-like embedded text attribute syntax. '
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.7.1
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: []