terminal_rb 0.13.0 → 0.15.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/examples/info.rb +1 -0
- data/examples/text.rb +32 -0
- data/lib/terminal/ansi.rb +48 -36
- 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 +75 -76
- data/lib/terminal/version.rb +1 -1
- data/lib/terminal.rb +107 -42
- data/terminal_rb.gemspec +33 -0
- metadata +10 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 23d41a32cd285a8ec05dde44ac26131e207f567b4137ff4c1b144171abb72dd6
|
|
4
|
+
data.tar.gz: 28325ab102a8ecd1ec6e9f2b9196aa85f8bf8d59898b6105a26d6afa2cc1a065
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ff88211e6ac2b869db965ef8861ddf312699c4cef971deea0f2b75bfac0f77df8f23936aa2a6870adfd60167391a720e76a089918b1e8b2bb35842d6fd32cb0a
|
|
7
|
+
data.tar.gz: '091504b7dfebe143f8df736f52db891a08a27d4bac9763706af06def5a9c2e413b620300c4188bd700c529b2446cb1ecb351a4425772bdd051b46332b96ce4bd'
|
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/examples/info.rb
CHANGED
|
@@ -12,5 +12,6 @@ Terminal.puts <<~TEXT
|
|
|
12
12
|
Terminal Size: [b]#{Terminal.size.join(' x ')}[/]
|
|
13
13
|
Cursor Position: [b]#{Terminal.pos&.join(', ')}[/]
|
|
14
14
|
Input Mode: [b]#{Terminal.input_mode}[/]
|
|
15
|
+
Link Support: [b]#{Terminal::Ansi.link('https://codeberg.org/mblumtritt/Terminal.rb', 'Link')}[/]
|
|
15
16
|
|
|
16
17
|
TEXT
|
data/examples/text.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../lib/terminal'
|
|
4
|
+
|
|
5
|
+
LOREM = <<~IPSUM
|
|
6
|
+
This example outputs [i bright_white]Lorem ipsum[/] text so that it takes up 75% of the screen width and fits the height of your screen.
|
|
7
|
+
|
|
8
|
+
[i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet.
|
|
9
|
+
|
|
10
|
+
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla [red]facilisis[/fg] at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. [i bright_white]Lorem ipsum[/] dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore [b]magna[/b] aliquam erat volutpat.
|
|
11
|
+
|
|
12
|
+
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla [red]facilisis[/fg] at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
|
|
13
|
+
|
|
14
|
+
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. [i bright_white]Lorem ipsum[/] dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore [b]magna[/b] aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
|
|
15
|
+
|
|
16
|
+
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla [red]facilisis[/fg].
|
|
17
|
+
|
|
18
|
+
At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, At accusam aliquyam diam diam dolore dolores duo eirmod eos erat, et nonumy sed tempor et et invidunt justo labore Stet clita ea et gubergren, kasd [b]magna[/b] no rebum. [yellow]sanctus[/fg] sea sed takimata ut vero voluptua. est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat.
|
|
19
|
+
|
|
20
|
+
Consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg].
|
|
21
|
+
|
|
22
|
+
[i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet. [i bright_white]Lorem ipsum[/] dolor sit amet, [green]consetetur[/fg] sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore [b]magna[/b] aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata [yellow]sanctus[/fg] est [i bright_white]Lorem ipsum[/] dolor sit amet.
|
|
23
|
+
|
|
24
|
+
Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla [red]facilisis[/fg] at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. [i bright_white]Lorem ipsum[/] dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore [b]magna[/b] aliquam erat volutpat.
|
|
25
|
+
|
|
26
|
+
Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla [red]facilisis[/fg] at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi.
|
|
27
|
+
|
|
28
|
+
Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. [i bright_white]Lorem ipsum[/] dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore [b]magna[/b] aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat.
|
|
29
|
+
IPSUM
|
|
30
|
+
|
|
31
|
+
rows, cols = Terminal.size
|
|
32
|
+
puts Terminal::Text.each(LOREM, limit: cols * 0.75).take(rows - 1)
|
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,11 +481,22 @@ 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
|
|
|
489
|
+
# Show a simple notification.
|
|
490
|
+
# This is not widely supported; works for
|
|
491
|
+
# Ghosty,
|
|
492
|
+
# iTerm2,
|
|
493
|
+
# Kitty,
|
|
494
|
+
# WezTerm.
|
|
495
|
+
#
|
|
496
|
+
# @param text [#to_s] text to display
|
|
497
|
+
# @return (see cursor_up)
|
|
498
|
+
def notify(text) = "\e]9;#{text}\a"
|
|
499
|
+
|
|
489
500
|
# Create scaled text.
|
|
490
501
|
# It uses the
|
|
491
502
|
# [text sizing protocol](https://sw.kovidgoyal.net/kitty/text-sizing-protocol).
|
|
@@ -497,18 +508,19 @@ module Terminal
|
|
|
497
508
|
# @example Half-height Greeting
|
|
498
509
|
# Terminal::Ansi.scale('Hello Ruby!', fracn: 1, fracd: 2, vertical: :centered)
|
|
499
510
|
#
|
|
500
|
-
# @param [#to_s]
|
|
501
|
-
#
|
|
511
|
+
# @param text [#to_s]
|
|
512
|
+
# text to scale
|
|
513
|
+
# @param scale [Integer, nil]
|
|
502
514
|
# overall scale size, range 1..7
|
|
503
|
-
# @param [Integer, nil]
|
|
515
|
+
# @param width [Integer, nil]
|
|
504
516
|
# with in cells, range 0..7
|
|
505
|
-
# @param [Integer, nil]
|
|
517
|
+
# @param fracn [Integer, nil]
|
|
506
518
|
# numerator for the fractional scale, range: 0..15
|
|
507
|
-
# @param [Integer, nil]
|
|
519
|
+
# @param fracd [Integer, nil]
|
|
508
520
|
# denominator for the fractional scale, range: 0..15, > fracn
|
|
509
|
-
# @param [:top, :bottom, :centered, nil]
|
|
521
|
+
# @param vertical [:top, :bottom, :centered, nil]
|
|
510
522
|
# vertical alignment to use for fractionally scaled text
|
|
511
|
-
# @param [:left, :right, :centered, nil]
|
|
523
|
+
# @param horizontal [:left, :right, :centered, nil]
|
|
512
524
|
# horizontal alignment to use for fractionally scaled text
|
|
513
525
|
# @return (see cursor_up)
|
|
514
526
|
def scale(
|
|
@@ -587,7 +599,7 @@ module Terminal
|
|
|
587
599
|
|
|
588
600
|
@re_test =
|
|
589
601
|
/
|
|
590
|
-
(?:\e\[[\d
|
|
602
|
+
(?:\e\[[\d;:?]*[ABCDEFGHJKSTfminsuhl])
|
|
591
603
|
|
|
|
592
604
|
(?:\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
|
|
593
605
|
/x
|
|
@@ -787,66 +799,66 @@ module Terminal
|
|
|
787
799
|
autoload :NAMED_COLORS, "#{__dir__}/ansi/named_colors.rb"
|
|
788
800
|
private_constant :NAMED_COLORS
|
|
789
801
|
|
|
790
|
-
#
|
|
802
|
+
# @private
|
|
791
803
|
RESET = self[:reset].freeze
|
|
792
804
|
|
|
793
|
-
#
|
|
805
|
+
# @private
|
|
794
806
|
FULL_RESET = "\ec"
|
|
795
807
|
|
|
796
|
-
#
|
|
808
|
+
# @private
|
|
797
809
|
CURSOR_HOME = cursor_pos(nil, nil).freeze
|
|
798
|
-
#
|
|
810
|
+
# @private
|
|
799
811
|
CURSOR_FIRST_ROW = cursor_pos(1).freeze
|
|
800
|
-
#
|
|
812
|
+
# @private
|
|
801
813
|
CURSOR_FIRST_COLUMN = cursor_column(1).freeze
|
|
802
814
|
|
|
803
|
-
#
|
|
815
|
+
# @private
|
|
804
816
|
CURSOR_SHOW = "\e[?25h"
|
|
805
|
-
#
|
|
817
|
+
# @private
|
|
806
818
|
CURSOR_HIDE = "\e[?25l"
|
|
807
819
|
|
|
808
820
|
# @comment CURSOR_POS_SAVE_SCO = "\e[s"
|
|
809
821
|
# @comment CURSOR_POS_SAVE_DEC = "\e7"
|
|
810
|
-
#
|
|
822
|
+
# @private
|
|
811
823
|
CURSOR_POS_SAVE = "\e7"
|
|
812
824
|
|
|
813
825
|
# @comment CURSOR_POS_RESTORE_SCO = "\e[u"
|
|
814
826
|
# @comment CURSOR_POS_RESTORE_DEC = "\e8"
|
|
815
|
-
#
|
|
827
|
+
# @private
|
|
816
828
|
CURSOR_POS_RESTORE = "\e8"
|
|
817
829
|
|
|
818
|
-
#
|
|
830
|
+
# @private
|
|
819
831
|
SCREEN_ERASE = screen_erase.freeze
|
|
820
|
-
#
|
|
832
|
+
# @private
|
|
821
833
|
SCREEN_ERASE_BELOW = screen_erase(:below).freeze
|
|
822
|
-
#
|
|
834
|
+
# @private
|
|
823
835
|
SCREEN_ERASE_ABOVE = screen_erase(:above).freeze
|
|
824
|
-
#
|
|
836
|
+
# @private
|
|
825
837
|
SCREEN_ERASE_SCROLLBACK = screen_erase(:scrollback).freeze
|
|
826
838
|
|
|
827
|
-
#
|
|
839
|
+
# @private
|
|
828
840
|
SCREEN_SAVE = "\e[?47h"
|
|
829
|
-
#
|
|
841
|
+
# @private
|
|
830
842
|
SCREEN_RESTORE = "\e[?47l"
|
|
831
843
|
|
|
832
|
-
#
|
|
844
|
+
# @private
|
|
833
845
|
# @comment at least Kitty requires CURSOR_HOME too
|
|
834
846
|
SCREEN_ALTERNATE = "\e[?1049h#{CURSOR_HOME}".freeze
|
|
835
|
-
#
|
|
847
|
+
# @private
|
|
836
848
|
SCREEN_ALTERNATE_OFF = "\e[?1049l"
|
|
837
849
|
|
|
838
|
-
#
|
|
850
|
+
# @private
|
|
839
851
|
SCREEN_REVERSE_MODE_ON = "\e[?5h"
|
|
840
|
-
#
|
|
852
|
+
# @private
|
|
841
853
|
SCREEN_REVERSE_MODE_OFF = "\e[?5l"
|
|
842
854
|
|
|
843
|
-
#
|
|
855
|
+
# @private
|
|
844
856
|
LINE_ERASE = line_erase.freeze
|
|
845
|
-
#
|
|
857
|
+
# @private
|
|
846
858
|
LINE_ERASE_TO_END = line_erase(:to_end).freeze
|
|
847
|
-
#
|
|
859
|
+
# @private
|
|
848
860
|
LINE_ERASE_TO_START = line_erase(:to_start).freeze
|
|
849
|
-
#
|
|
861
|
+
# @private
|
|
850
862
|
LINE_ERASE_PREV = "#{cursor_prev_line(nil)}#{LINE_ERASE}".freeze
|
|
851
863
|
|
|
852
864
|
# @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,12 +24,11 @@ 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
|
-
str = bbcode ? Ansi.unbbcode(str) : str.to_s
|
|
32
|
-
return 0 if str.empty?
|
|
31
|
+
return 0 if (str = bbcode ? Ansi.unbbcode(str) : str.to_s).empty?
|
|
33
32
|
str = str.encode(@encoding) if str.encoding != @encoding
|
|
34
33
|
width = 0
|
|
35
34
|
str.scan(WIDTH_SCANNER) do |sp, gc|
|
|
@@ -45,15 +44,15 @@ module Terminal
|
|
|
45
44
|
# Terminal::Text.each_line('This is a simple test 😀', limit: 6).to_a
|
|
46
45
|
# # => ["This", "is a", "simple", "test", "😀"]
|
|
47
46
|
#
|
|
48
|
-
# @param [#to_s, ...]
|
|
47
|
+
# @param text [#to_s, ...]
|
|
49
48
|
# text objects to process
|
|
50
|
-
# @param [#to_i, nil]
|
|
49
|
+
# @param limit [#to_i, nil]
|
|
51
50
|
# optionally limit line size
|
|
52
|
-
# @param [true, false]
|
|
51
|
+
# @param bbcode [true, false]
|
|
53
52
|
# whether to interprete embedded BBCode (see {Ansi.bbcode})
|
|
54
|
-
# @param [true, false]
|
|
53
|
+
# @param ansi [true, false]
|
|
55
54
|
# whether to keep embedded ANSI control codes
|
|
56
|
-
# @param [true, false]
|
|
55
|
+
# @param ignore_newline [true, false]
|
|
57
56
|
# wheter to ignore embedded line breaks (`"\r\n"` or `"\n"`)
|
|
58
57
|
# @yield [String] text line
|
|
59
58
|
# @return [Enumerator] when no block given
|
|
@@ -68,14 +67,14 @@ module Terminal
|
|
|
68
67
|
&block
|
|
69
68
|
)
|
|
70
69
|
unless limit
|
|
71
|
-
|
|
72
|
-
return block ? lines(
|
|
70
|
+
snippets = as_snippets(text, bbcode, ansi, ignore_newline, Word)
|
|
71
|
+
return block ? lines(snippets, &block) : to_enum(:lines, snippets)
|
|
73
72
|
end
|
|
74
73
|
limit = limit.to_i
|
|
75
74
|
raise(ArgumentError, "invalid limit - #{limit}") if limit < 1
|
|
76
|
-
|
|
77
|
-
return lim_lines(
|
|
78
|
-
to_enum(:lim_lines,
|
|
75
|
+
snippets = as_snippets(text, bbcode, ansi, ignore_newline, WordEx)
|
|
76
|
+
return lim_lines(snippets, limit, &block) if block
|
|
77
|
+
to_enum(:lim_lines, snippets, limit)
|
|
79
78
|
end
|
|
80
79
|
alias each each_line
|
|
81
80
|
|
|
@@ -98,14 +97,14 @@ module Terminal
|
|
|
98
97
|
&block
|
|
99
98
|
)
|
|
100
99
|
unless limit
|
|
101
|
-
|
|
102
|
-
return block ? pairs(
|
|
100
|
+
snippets = as_snippets(text, bbcode, ansi, ignore_newline, Word)
|
|
101
|
+
return block ? pairs(snippets, &block) : to_enum(:pairs, snippets)
|
|
103
102
|
end
|
|
104
103
|
limit = limit.to_i
|
|
105
104
|
raise(ArgumentError, "invalid limit - #{limit}") if limit < 1
|
|
106
|
-
|
|
107
|
-
return lim_pairs(
|
|
108
|
-
to_enum(:lim_pairs,
|
|
105
|
+
snippets = as_snippets(text, bbcode, ansi, ignore_newline, WordEx)
|
|
106
|
+
return lim_pairs(snippets, limit, &block) if block
|
|
107
|
+
to_enum(:lim_pairs, snippets, limit)
|
|
109
108
|
end
|
|
110
109
|
alias each_with_size each_line_with_size
|
|
111
110
|
|
|
@@ -123,12 +122,12 @@ module Terminal
|
|
|
123
122
|
char.size < 2 ? 1 : char.each_char.sum { char_width(_1) }
|
|
124
123
|
end
|
|
125
124
|
|
|
126
|
-
def lim_pairs(
|
|
125
|
+
def lim_pairs(snippets, limit)
|
|
127
126
|
line = @empty.dup
|
|
128
127
|
size = 0
|
|
129
128
|
csi = nil
|
|
130
|
-
|
|
131
|
-
if
|
|
129
|
+
snippets.each do |snippet|
|
|
130
|
+
if snippet == :space
|
|
132
131
|
next if size == 0
|
|
133
132
|
next line << ' ' if (size += 1) <= limit
|
|
134
133
|
yield(line, size - 1)
|
|
@@ -136,31 +135,31 @@ module Terminal
|
|
|
136
135
|
next size = 0
|
|
137
136
|
end
|
|
138
137
|
|
|
139
|
-
if
|
|
138
|
+
if snippet == :nl
|
|
140
139
|
line[-1] == ' ' ? yield(line.chop, size - 1) : yield(line, size)
|
|
141
140
|
line = "#{csi}"
|
|
142
141
|
next size = 0
|
|
143
142
|
end
|
|
144
143
|
|
|
145
|
-
if
|
|
144
|
+
if snippet == :hard_nl
|
|
146
145
|
line[-1] == ' ' ? yield(line.chop, size - 1) : yield(line, size)
|
|
147
146
|
line = @empty.dup
|
|
148
147
|
csi = nil
|
|
149
148
|
next size = 0
|
|
150
149
|
end
|
|
151
150
|
|
|
152
|
-
if
|
|
151
|
+
if snippet == CsiEnd
|
|
153
152
|
line << CsiEnd if csi
|
|
154
153
|
next csi = nil
|
|
155
154
|
end
|
|
156
155
|
|
|
157
|
-
next line << (csi =
|
|
158
|
-
next line <<
|
|
156
|
+
next line << (csi = snippet) if snippet.is_a?(Csi)
|
|
157
|
+
next line << snippet if snippet.is_a?(Osc)
|
|
159
158
|
|
|
160
159
|
# Word:
|
|
161
160
|
|
|
162
|
-
if (ns = size +
|
|
163
|
-
line <<
|
|
161
|
+
if (ns = size + snippet.size) <= limit
|
|
162
|
+
line << snippet
|
|
164
163
|
next size = ns
|
|
165
164
|
end
|
|
166
165
|
|
|
@@ -170,16 +169,16 @@ module Terminal
|
|
|
170
169
|
end
|
|
171
170
|
yield(line, size) if size != 0
|
|
172
171
|
|
|
173
|
-
if
|
|
174
|
-
line = "#{csi}#{
|
|
175
|
-
next size =
|
|
172
|
+
if snippet.size <= limit
|
|
173
|
+
line = "#{csi}#{snippet}"
|
|
174
|
+
next size = snippet.size
|
|
176
175
|
end
|
|
177
176
|
|
|
178
|
-
words =
|
|
177
|
+
words = snippet.split(limit)
|
|
179
178
|
if words[-1].size <= limit
|
|
180
|
-
|
|
181
|
-
line = "#{csi}#{
|
|
182
|
-
size =
|
|
179
|
+
snippet = words.pop
|
|
180
|
+
line = "#{csi}#{snippet}"
|
|
181
|
+
size = snippet.size
|
|
183
182
|
else
|
|
184
183
|
line = "#{csi}"
|
|
185
184
|
size = 0
|
|
@@ -189,51 +188,51 @@ module Terminal
|
|
|
189
188
|
nil
|
|
190
189
|
end
|
|
191
190
|
|
|
192
|
-
def pairs(
|
|
191
|
+
def pairs(snippets)
|
|
193
192
|
line = @empty.dup
|
|
194
193
|
size = 0
|
|
195
194
|
csi = nil
|
|
196
|
-
|
|
197
|
-
if
|
|
195
|
+
snippets.each do |snippet|
|
|
196
|
+
if snippet == :space
|
|
198
197
|
next if size == 0
|
|
199
198
|
line << ' '
|
|
200
199
|
next size += 1
|
|
201
200
|
end
|
|
202
201
|
|
|
203
|
-
if
|
|
202
|
+
if snippet == :nl
|
|
204
203
|
line[-1] == ' ' ? yield(line.chop, size - 1) : yield(line, size)
|
|
205
204
|
line = "#{csi}"
|
|
206
205
|
next size = 0
|
|
207
206
|
end
|
|
208
207
|
|
|
209
|
-
if
|
|
208
|
+
if snippet == :hard_nl
|
|
210
209
|
line[-1] == ' ' ? yield(line.chop, size - 1) : yield(line, size)
|
|
211
210
|
line = @empty.dup
|
|
212
211
|
csi = nil
|
|
213
212
|
next size = 0
|
|
214
213
|
end
|
|
215
214
|
|
|
216
|
-
if
|
|
215
|
+
if snippet == CsiEnd
|
|
217
216
|
line << CsiEnd if csi
|
|
218
217
|
next csi = nil
|
|
219
218
|
end
|
|
220
219
|
|
|
221
|
-
next line << (csi =
|
|
222
|
-
next line <<
|
|
220
|
+
next line << (csi = snippet) if snippet.is_a?(Csi)
|
|
221
|
+
next line << snippet if snippet.is_a?(Osc)
|
|
223
222
|
|
|
224
223
|
# Word:
|
|
225
|
-
line <<
|
|
226
|
-
size +=
|
|
224
|
+
line << snippet
|
|
225
|
+
size += snippet.size
|
|
227
226
|
end
|
|
228
227
|
nil
|
|
229
228
|
end
|
|
230
229
|
|
|
231
|
-
def lim_lines(
|
|
230
|
+
def lim_lines(snippets, limit)
|
|
232
231
|
line = @empty.dup
|
|
233
232
|
size = 0
|
|
234
233
|
csi = nil
|
|
235
|
-
|
|
236
|
-
if
|
|
234
|
+
snippets.each do |snippet|
|
|
235
|
+
if snippet == :space
|
|
237
236
|
next if size == 0
|
|
238
237
|
next line << ' ' if (size += 1) <= limit
|
|
239
238
|
yield(line)
|
|
@@ -241,31 +240,31 @@ module Terminal
|
|
|
241
240
|
next size = 0
|
|
242
241
|
end
|
|
243
242
|
|
|
244
|
-
if
|
|
243
|
+
if snippet == :nl
|
|
245
244
|
yield(line[-1] == ' ' ? line.chop : line)
|
|
246
245
|
line = "#{csi}"
|
|
247
246
|
next size = 0
|
|
248
247
|
end
|
|
249
248
|
|
|
250
|
-
if
|
|
249
|
+
if snippet == :hard_nl
|
|
251
250
|
yield(line[-1] == ' ' ? line.chop : line)
|
|
252
251
|
line = @empty.dup
|
|
253
252
|
csi = nil
|
|
254
253
|
next size = 0
|
|
255
254
|
end
|
|
256
255
|
|
|
257
|
-
if
|
|
256
|
+
if snippet == CsiEnd
|
|
258
257
|
line << CsiEnd if csi
|
|
259
258
|
next csi = nil
|
|
260
259
|
end
|
|
261
260
|
|
|
262
|
-
next line << (csi =
|
|
263
|
-
next line <<
|
|
261
|
+
next line << (csi = snippet) if snippet.is_a?(Csi)
|
|
262
|
+
next line << snippet if snippet.is_a?(Osc)
|
|
264
263
|
|
|
265
264
|
# Word:
|
|
266
265
|
|
|
267
|
-
if (ns = size +
|
|
268
|
-
line <<
|
|
266
|
+
if (ns = size + snippet.size) <= limit
|
|
267
|
+
line << snippet
|
|
269
268
|
next size = ns
|
|
270
269
|
end
|
|
271
270
|
|
|
@@ -275,16 +274,16 @@ module Terminal
|
|
|
275
274
|
end
|
|
276
275
|
yield(line) if size != 0
|
|
277
276
|
|
|
278
|
-
if
|
|
279
|
-
line = "#{csi}#{
|
|
280
|
-
next size =
|
|
277
|
+
if snippet.size <= limit
|
|
278
|
+
line = "#{csi}#{snippet}"
|
|
279
|
+
next size = snippet.size
|
|
281
280
|
end
|
|
282
281
|
|
|
283
|
-
words =
|
|
282
|
+
words = snippet.split(limit)
|
|
284
283
|
if words[-1].size <= limit
|
|
285
|
-
|
|
286
|
-
line = "#{csi}#{
|
|
287
|
-
size =
|
|
284
|
+
snippet = words.pop
|
|
285
|
+
line = "#{csi}#{snippet}"
|
|
286
|
+
size = snippet.size
|
|
288
287
|
else
|
|
289
288
|
line = "#{csi}"
|
|
290
289
|
size = 0
|
|
@@ -294,46 +293,46 @@ module Terminal
|
|
|
294
293
|
nil
|
|
295
294
|
end
|
|
296
295
|
|
|
297
|
-
def lines(
|
|
296
|
+
def lines(snippets)
|
|
298
297
|
line = @empty.dup
|
|
299
298
|
size = 0
|
|
300
299
|
csi = nil
|
|
301
|
-
|
|
302
|
-
if
|
|
300
|
+
snippets.each do |snippet|
|
|
301
|
+
if snippet == :space
|
|
303
302
|
next if size == 0
|
|
304
303
|
line << ' '
|
|
305
304
|
next size += 1
|
|
306
305
|
end
|
|
307
306
|
|
|
308
|
-
if
|
|
307
|
+
if snippet == :nl
|
|
309
308
|
yield(line[-1] == ' ' ? line.chop : line)
|
|
310
309
|
line = "#{csi}"
|
|
311
310
|
next size = 0
|
|
312
311
|
end
|
|
313
312
|
|
|
314
|
-
if
|
|
313
|
+
if snippet == :hard_nl
|
|
315
314
|
yield(line[-1] == ' ' ? line.chop : line)
|
|
316
315
|
line = @empty.dup
|
|
317
316
|
csi = nil
|
|
318
317
|
next size = 0
|
|
319
318
|
end
|
|
320
319
|
|
|
321
|
-
if
|
|
320
|
+
if snippet == CsiEnd
|
|
322
321
|
line << CsiEnd if csi
|
|
323
322
|
next csi = nil
|
|
324
323
|
end
|
|
325
324
|
|
|
326
|
-
next line << (csi =
|
|
327
|
-
next line <<
|
|
325
|
+
next line << (csi = snippet) if snippet.is_a?(Csi)
|
|
326
|
+
next line << snippet if snippet.is_a?(Osc)
|
|
328
327
|
|
|
329
328
|
# Word:
|
|
330
|
-
line <<
|
|
331
|
-
size +=
|
|
329
|
+
line << snippet
|
|
330
|
+
size += snippet.size
|
|
332
331
|
end
|
|
333
332
|
nil
|
|
334
333
|
end
|
|
335
334
|
|
|
336
|
-
def
|
|
335
|
+
def as_snippets(text, bbcode, ansi, ignore_newline, word_class)
|
|
337
336
|
ret = []
|
|
338
337
|
last = nil
|
|
339
338
|
text.each do |txt|
|
|
@@ -451,7 +450,7 @@ module Terminal
|
|
|
451
450
|
SCAN_EXPR =
|
|
452
451
|
/\G(?:
|
|
453
452
|
(\r?\n)
|
|
454
|
-
| (\e\[[\d
|
|
453
|
+
| (\e\[[\d;:?]*[ABCDEFGHJKSTfhilmnsu])
|
|
455
454
|
| (\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
|
|
456
455
|
| (\s+)
|
|
457
456
|
| (\X)
|
|
@@ -459,7 +458,7 @@ module Terminal
|
|
|
459
458
|
|
|
460
459
|
WIDTH_SCANNER =
|
|
461
460
|
/\G(?:
|
|
462
|
-
(?:\e\[[\d
|
|
461
|
+
(?:\e\[[\d;:?]*[ABCDEFGHJKSTfhilmnsu])
|
|
463
462
|
| (?:\e\]\d+(?:;[^\a\e]+)*(?:\a|\e\\))
|
|
464
463
|
| (\s+)
|
|
465
464
|
| (\X)
|
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,38 @@ 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?
|
|
154
|
+
self
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Show the alternate screen.
|
|
158
|
+
# Will not send the control code if the alternate screen is already used.
|
|
159
|
+
#
|
|
160
|
+
# When you called {show_alt_screen} n-times you need to call
|
|
161
|
+
# {hide_alt_screen} n-times to show the default screen again.
|
|
162
|
+
#
|
|
163
|
+
# @return [Terminal] itself
|
|
164
|
+
def show_alt_screen
|
|
165
|
+
raw_write(Ansi::SCREEN_ALTERNATE) if ansi? && (@as += 1) == 1
|
|
166
|
+
self
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Hide the alternate screen.
|
|
170
|
+
# Will not send the control code if the alternate screen is not used.
|
|
171
|
+
#
|
|
172
|
+
# When you called {show_alt_screen} n-times you need to call
|
|
173
|
+
# {hide_alt_screen} n-times to show the default screen again.
|
|
174
|
+
#
|
|
175
|
+
# @return [Terminal] itself
|
|
176
|
+
def hide_alt_screen
|
|
177
|
+
raw_write(Ansi::SCREEN_ALTERNATE_OFF) if @as > 0 && (@as -= 1).zero?
|
|
178
178
|
self
|
|
179
179
|
end
|
|
180
180
|
|
|
181
181
|
# Writes the given object to the terminal.
|
|
182
182
|
# Interprets embedded BBCode.
|
|
183
183
|
#
|
|
184
|
-
# @param [#to_s] object
|
|
184
|
+
# @param object [#to_s] object to write
|
|
185
185
|
# @return [Terminal] itself
|
|
186
186
|
def <<(object)
|
|
187
187
|
@out.write(Ansi.bbcode(object)) if @out && object != nil
|
|
@@ -194,8 +194,8 @@ module Terminal
|
|
|
194
194
|
# Writes the given objects to the terminal.
|
|
195
195
|
# Optionally interprets embedded BBCode.
|
|
196
196
|
#
|
|
197
|
-
# @param [Array<#to_s>]
|
|
198
|
-
# @param [true|false]
|
|
197
|
+
# @param objects [Array<#to_s>] any number of objects to write
|
|
198
|
+
# @param bbcode [true|false] whether to interpret embedded BBCode
|
|
199
199
|
# @return [nil]
|
|
200
200
|
def print(*objects, bbcode: true)
|
|
201
201
|
return unless @out
|
|
@@ -225,14 +225,78 @@ module Terminal
|
|
|
225
225
|
@out = nil
|
|
226
226
|
end
|
|
227
227
|
|
|
228
|
-
|
|
228
|
+
# Execute a command and report command output line by line
|
|
229
|
+
# or capture the output.
|
|
230
|
+
#
|
|
231
|
+
# @example Execute a command wih arguments
|
|
232
|
+
# ret, out, = Terminal.sh('curl', '--version')
|
|
233
|
+
# raise('command not found - curl') unless ret
|
|
234
|
+
# puts out
|
|
235
|
+
#
|
|
236
|
+
# @example Execute shell commands and print error
|
|
237
|
+
# ret, _, err = Terminal.sh("mkdir '/test' && cd '/test'")
|
|
238
|
+
# raise('unable to execute') unless ret
|
|
239
|
+
# puts("error #{ret.exitstatus}: #{err.join}") unless ret.success?
|
|
240
|
+
#
|
|
241
|
+
# @example Copy a file content to clipboard
|
|
242
|
+
# command = ENV.fetch('CLIP_COPY', 'pbcopy')
|
|
243
|
+
# File.open(__FILE__) do |file|
|
|
244
|
+
# ret, = Terminal.sh(*command, input: file)
|
|
245
|
+
# raise("command not found - #{command}") unless ret
|
|
246
|
+
# end
|
|
247
|
+
#
|
|
248
|
+
# @overload sh(*cmd, **options)
|
|
249
|
+
# @return [[Process::Status, output, error]]
|
|
250
|
+
# the status of the process successfully executed,
|
|
251
|
+
# the captured output,
|
|
252
|
+
# the captured error output,
|
|
253
|
+
# @return [nil]
|
|
254
|
+
# when the command was not executable
|
|
255
|
+
#
|
|
256
|
+
# @overload sh(*cmd, **options)
|
|
257
|
+
# @yieldparam line [String]
|
|
258
|
+
# next received line from the process
|
|
259
|
+
# @yieldparam type [:output, :error]
|
|
260
|
+
# whether the received line is standard output or error
|
|
261
|
+
# @return [Process::Status]
|
|
262
|
+
# the status of the process successfully executed
|
|
263
|
+
# @return [nil]
|
|
264
|
+
# when the command was not executable
|
|
265
|
+
#
|
|
266
|
+
# @param cmd [Array<#to_s>]
|
|
267
|
+
# command line or command with arguments
|
|
268
|
+
# @param options [Hash]
|
|
269
|
+
# options how to execute the new process;
|
|
270
|
+
# see `Process.spawn` for default execution options provided by Ruby
|
|
271
|
+
# @option options [Hash<String,String>] :env
|
|
272
|
+
# key/value pairs added to the ENV of the process;
|
|
273
|
+
# with option `:unsetenv_others` set to `true` it replaces the ENV
|
|
274
|
+
# @option options [true, false] :shell
|
|
275
|
+
# whether to create a seperate shell or not
|
|
276
|
+
# @option options [#readpartial, #to_io, Array, #each, #to_a, #to_s] :input
|
|
277
|
+
# piped to the command as input
|
|
278
|
+
def sh(*cmd, **options, &block)
|
|
279
|
+
return Shell.exec(cmd, **options, &block) if block_given?
|
|
280
|
+
out = []
|
|
281
|
+
err = []
|
|
282
|
+
[
|
|
283
|
+
Shell.exec(cmd, **options) do |line, type|
|
|
284
|
+
(type == :error ? err : out) << line
|
|
285
|
+
end,
|
|
286
|
+
out,
|
|
287
|
+
err
|
|
288
|
+
]
|
|
289
|
+
end
|
|
229
290
|
|
|
230
|
-
|
|
291
|
+
# @private
|
|
292
|
+
def raw_write(str)
|
|
231
293
|
@out&.write(str)
|
|
232
294
|
rescue IOError
|
|
233
295
|
@out = nil
|
|
234
296
|
end
|
|
235
297
|
|
|
298
|
+
private
|
|
299
|
+
|
|
236
300
|
def _default_size
|
|
237
301
|
rows = ENV['LINES'].to_i
|
|
238
302
|
columns = ENV['COLUMNS'].to_i
|
|
@@ -250,7 +314,7 @@ module Terminal
|
|
|
250
314
|
end
|
|
251
315
|
end
|
|
252
316
|
|
|
253
|
-
@cc = 0
|
|
317
|
+
@cc = @as = 0
|
|
254
318
|
tty, @ansi = _determine_modes
|
|
255
319
|
|
|
256
320
|
unless tty.nil?
|
|
@@ -300,5 +364,6 @@ module Terminal
|
|
|
300
364
|
dir = "#{__dir__}/terminal"
|
|
301
365
|
autoload :Text, "#{dir}/text.rb"
|
|
302
366
|
autoload :Detect, "#{dir}/detect.rb"
|
|
303
|
-
|
|
367
|
+
autoload :Shell, "#{dir}/shell.rb"
|
|
368
|
+
private_constant :Detect, :Shell
|
|
304
369
|
end
|
data/terminal_rb.gemspec
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lib/terminal/version'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = 'terminal_rb'
|
|
7
|
+
spec.version = Terminal::VERSION
|
|
8
|
+
spec.summary =
|
|
9
|
+
'Fast terminal access with ANSI, CSIu, BBCode, word-wise line break support.'
|
|
10
|
+
spec.description = <<~DESCRIPTION.tr("\n", ' ')
|
|
11
|
+
Terminal.rb supports you with input and output on your terminal.
|
|
12
|
+
Simple BBCode-like markup for attributes and coloring, word-wise line
|
|
13
|
+
breaks, and correct special key recognition enable you to implement your
|
|
14
|
+
CLI app quickly and easily.
|
|
15
|
+
DESCRIPTION
|
|
16
|
+
|
|
17
|
+
spec.author = 'Mike Blumtritt'
|
|
18
|
+
spec.licenses = %w[MIT Ruby]
|
|
19
|
+
spec.homepage = 'https://codeberg.org/mblumtritt/Terminal.rb'
|
|
20
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
|
21
|
+
spec.metadata['bug_tracker_uri'] = "#{spec.homepage}/issues"
|
|
22
|
+
spec.metadata['documentation_uri'] = 'https://rubydoc.info/gems/terminal_rb'
|
|
23
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
24
|
+
spec.metadata['yard.run'] = 'yard'
|
|
25
|
+
|
|
26
|
+
spec.required_ruby_version = '> 3.0'
|
|
27
|
+
|
|
28
|
+
spec.files = Dir['lib/**/*.rb']
|
|
29
|
+
spec.files += Dir['examples/*.rb']
|
|
30
|
+
spec.files += %w[terminal_rb.gemspec .yardopts]
|
|
31
|
+
spec.executables = %w[bbcode]
|
|
32
|
+
spec.extra_rdoc_files = %w[README.md]
|
|
33
|
+
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.15.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: []
|
|
@@ -27,6 +28,7 @@ files:
|
|
|
27
28
|
- examples/bbcode.rb
|
|
28
29
|
- examples/info.rb
|
|
29
30
|
- examples/key-codes.rb
|
|
31
|
+
- examples/text.rb
|
|
30
32
|
- lib/terminal.rb
|
|
31
33
|
- lib/terminal/ansi.rb
|
|
32
34
|
- lib/terminal/ansi/named_colors.rb
|
|
@@ -34,13 +36,16 @@ files:
|
|
|
34
36
|
- lib/terminal/input.rb
|
|
35
37
|
- lib/terminal/input/key_event.rb
|
|
36
38
|
- lib/terminal/rspec/helper.rb
|
|
39
|
+
- lib/terminal/shell.rb
|
|
37
40
|
- lib/terminal/text.rb
|
|
38
41
|
- lib/terminal/text/char_width.rb
|
|
39
42
|
- lib/terminal/version.rb
|
|
40
43
|
- lib/terminal_rb.rb
|
|
44
|
+
- terminal_rb.gemspec
|
|
41
45
|
homepage: https://codeberg.org/mblumtritt/Terminal.rb
|
|
42
46
|
licenses:
|
|
43
47
|
- MIT
|
|
48
|
+
- Ruby
|
|
44
49
|
metadata:
|
|
45
50
|
source_code_uri: https://codeberg.org/mblumtritt/Terminal.rb
|
|
46
51
|
bug_tracker_uri: https://codeberg.org/mblumtritt/Terminal.rb/issues
|
|
@@ -52,7 +57,7 @@ require_paths:
|
|
|
52
57
|
- lib
|
|
53
58
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
54
59
|
requirements:
|
|
55
|
-
- - "
|
|
60
|
+
- - ">"
|
|
56
61
|
- !ruby/object:Gem::Version
|
|
57
62
|
version: '3.0'
|
|
58
63
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
@@ -61,7 +66,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
61
66
|
- !ruby/object:Gem::Version
|
|
62
67
|
version: '0'
|
|
63
68
|
requirements: []
|
|
64
|
-
rubygems_version: 3.7.
|
|
69
|
+
rubygems_version: 3.7.2
|
|
65
70
|
specification_version: 4
|
|
66
71
|
summary: Fast terminal access with ANSI, CSIu, BBCode, word-wise line break support.
|
|
67
72
|
test_files: []
|