terminal_rb 1.0.4 → 1.0.6
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/lorem_ipsum.ansi +39 -0
- data/lib/terminal/ansi/screen_viewer.rb +5 -5
- data/lib/terminal/ansi.rb +17 -17
- data/lib/terminal/input/ansi.rb +21 -8
- data/lib/terminal/input/dumb.rb +1 -4
- data/lib/terminal/input/key_event.rb +7 -7
- data/lib/terminal/input.rb +5 -6
- data/lib/terminal/output/ansi.rb +6 -4
- data/lib/terminal/output/dumb.rb +2 -1
- data/lib/terminal/output.rb +4 -4
- data/lib/terminal/rspec/helper.rb +48 -44
- data/lib/terminal/shell.rb +1 -1
- data/lib/terminal/text/formatter.rb +43 -33
- data/lib/terminal/text.rb +1 -1
- data/lib/terminal/version.rb +1 -1
- data/lib/terminal.rb +27 -27
- metadata +5 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8f2f93f672f13936c9415cf3a474533df0f5785da3272f3482a02842a0607917
|
|
4
|
+
data.tar.gz: 52a1498c80085ea7667b7641416f406ae8694e02505a0c3201efd5f150f8d986
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 41cfbe3e34fa3db0af51674dc0f70f32dd08fb20d2b3a1040eff2834c65440f917f2ecea021500037658fbcf613373148c8399fdb29b7ea987916e71c570a65d
|
|
7
|
+
data.tar.gz: b6485ac5e2a9ba17ec72ffadc94cad6f5348ab846f885d12e9a68a338c15baeaa0d564ce1e1a4d2f946505887782e02dfd7221fc03e60bd3e7bb40afb88b47a1
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# Terminal.rb v1.0.
|
|
1
|
+
# Terminal.rb v1.0.6
|
|
2
2
|
|
|
3
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, correct special key recognition and mouse event reporting 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/1.0.
|
|
7
|
+
- Help: [rubydoc.info](https://rubydoc.info/gems/terminal_rb/1.0.6/Terminal)
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
[b red]Lorem ipsum[/] dolor sit amet, [yellow]consectetur[/] adipiscing elit. Praesent odio orci, [black on_bright_yellow]congue[/] sit amet turpis a, posuere [inv]congue[/] purus. Proin et lectus eget mi vehicula [i green uu]pellentesque[/] nec eu sem. Quisque nunc lectus, [uu]pharetra[/] non justo nec, porttitor mollis enim. Aenean dapibus massa tortor. Curabitur iaculis consequat sapien, eget [i green uu]pellentesque[/] turpis tempor bibendum. Nam cursus at quam vel [yellow]sollicitudin[/]. Etiam luctus nisl nisl, at eleifend tortor blandit non. Duis non suscipit quam. Vestibulum commodo molestie [u]fermentum[/]. Vivamus gravida dui sed nisl ornare, in eleifend leo tristique. Pellentesque eget eleifend [i]augue[/].
|
|
2
|
+
|
|
3
|
+
Vestibulum ultrices, ipsum ut volutpat aliquet, lectus mauris varius ante, non faucibus massa nisi et magna. Nullam id lacus sit amet nisi luctus gravida ut ac odio. Suspendisse ligula erat, vehicula sit amet nulla eget, porta semper neque. Fusce eget ultrices massa. Sed quis mauris tempor, [black on_bright_yellow]congue[/] nibh sed, lacinia mauris. Pellentesque convallis viverra ultrices. Ut sit amet massa [yellow]sollicitudin[/] lectus [uu]pharetra[/] vehicula. Ut hendrerit risus sit amet consequat maximus. Aliquam at ligula at nulla bibendum laoreet ac vitae [i]augue[/]. Sed ullamcorper, orci vel fringilla imperdiet, nisl leo [yellow]consectetur[/] nunc, sit amet pulvinar tortor nulla quis ex.
|
|
4
|
+
|
|
5
|
+
Morbi vitae [i]augue[/] eu neque laoreet [black on_bright_yellow]congue[/]. Integer eu est libero. Quisque consequat felis ipsum, quis consequat dui eleifend eu. Quisque malesuada ipsum vel arcu [yellow]consectetur[/] fringilla. Integer eu eleifend enim. Nam feugiat rutrum justo, consequat ornare massa molestie quis. Aenean eget malesuada ex. Morbi [yellow]sollicitudin[/] commodo nisi, nec auctor ante porta vitae. Aenean tristique ligula sit amet purus ultricies auctor. Nam [cu]finibus[/], libero at vestibulum mattis, diam ipsum placerat massa, sit amet porta ligula libero et lorem. Nullam est tellus, elementum venenatis justo aliquam, pulvinar rutrum nulla. Phasellus nec nisl sem. Sed [i green uu]sagittis[/] nulla sed aliquet auctor. Quisque sit amet [yellow]sollicitudin[/] massa, ut porttitor lacus. Aliquam at sem nec lacus varius pretium rhoncus vel mi. Praesent et lacus sed nunc lacinia porttitor.
|
|
6
|
+
|
|
7
|
+
Nulla mauris [i]augue[/], lacinia sit amet porta et, efficitur in ex. Fusce a fringilla ex. Phasellus ut sem justo. Sed nec sapien orci. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Maecenas lectus dui, pretium quis massa vel, gravida commodo mi. Proin feugiat, felis eget bibendum dictum, urna velit cursus odio, ut accumsan ligula orci eu nibh. Donec cursus placerat est ac euismod.
|
|
8
|
+
|
|
9
|
+
Aliquam erat volutpat. Suspendisse iaculis leo porttitor quam pretium lacinia. Morbi euismod sit amet enim in condimentum. Morbi sed [uu]pharetra[/] purus. Vivamus quis libero sed neque [inv]congue[/] egestas. Mauris ullamcorper ipsum vel justo eleifend fringilla. Mauris facilisis quam convallis mauris [inv]congue[/] tempor. Fusce tortor purus, ultrices ut molestie sit amet, [u]fermentum[/] vel quam.
|
|
10
|
+
|
|
11
|
+
Duis placerat ultricies [i green uu]pellentesque[/]. Nam nec efficitur orci, in [yellow]sollicitudin[/] sapien. Nunc condimentum aliquet enim id efficitur. Nulla tempus urna ex. Ut sed dolor eget sapien faucibus imperdiet et tincidunt ex. Vestibulum feugiat vel quam id eleifend. Sed tristique a mauris eget [white on_red]lobortis[/]. Nulla mollis est ipsum, in [black on_bright_yellow]congue[/] nisi maximus eget. Phasellus laoreet rutrum facilisis. In suscipit elementum lorem, a blandit turpis.
|
|
12
|
+
|
|
13
|
+
Suspendisse suscipit in odio quis facilisis. Aliquam nec eros ultrices, hendrerit lacus sit amet, bibendum turpis. Duis ligula ligula, efficitur non tortor quis, ornare dictum urna. Nam blandit lorem blandit turpis laoreet malesuada. Duis et magna ut lorem tempus dignissim. Phasellus dui turpis, ultrices et aliquam nec, pulvinar sed [i]augue[/]. Morbi [uu]pharetra[/] laoreet ipsum non laoreet. Pellentesque a felis vitae sapien condimentum [yellow]sollicitudin[/]. Etiam in nisl eget odio luctus gravida. Donec [yellow]sollicitudin[/] risus porta, scelerisque est ut, pulvinar odio. Nulla at suscipit tortor. Curabitur euismod [inv]congue[/] rhoncus. Cras imperdiet enim arcu, non vehicula tortor tincidunt non. Vivamus tincidunt [u]fermentum[/] quam et dignissim. In lacinia venenatis nunc, non [inv]congue[/] ipsum [inv]congue[/] ut.
|
|
14
|
+
|
|
15
|
+
Donec vitae nulla id elit efficitur facilisis ut sit amet enim. Nam sed magna lorem. Sed scelerisque vel urna sed porta. Donec a orci vel est interdum ultricies ac eu mi. Quisque condimentum luctus venenatis. Proin laoreet [yellow]sollicitudin[/] magna. Proin at nisl a sem condimentum vehicula. Fusce interdum ipsum ac lectus [u]fermentum[/] rutrum. Fusce aliquam, eros at interdum semper, purus urna vulputate lacus, eu [u]fermentum[/] felis sem ut nisl. Pellentesque at viverra libero. Integer [u]fermentum[/] vestibulum tortor, eu aliquam tortor [black on_bright_yellow]congue[/] at. Vestibulum vel fringilla velit. Proin ultricies magna ac ipsum bibendum porttitor. Duis nec leo urna. Morbi rhoncus fringilla [cu]finibus[/].
|
|
16
|
+
|
|
17
|
+
Praesent metus mi, laoreet vel enim in, luctus aliquam odio. Maecenas ultrices metus quam, eget auctor ante [cu]finibus[/] ac. Etiam venenatis et tellus nec suscipit. Sed a nunc libero. In hac habitasse platea dictumst. Ut cursus efficitur dolor, at fringilla risus. Etiam id placerat tellus. Cras sit amet [white on_red]lobortis[/] turpis. Sed bibendum dolor tortor, a elementum nunc molestie cursus. Nullam nec lectus in [i]augue[/] commodo [i green uu]sagittis[/] sed eu tellus. Nam vitae mollis eros. Vivamus ut rhoncus risus. Phasellus [i]augue[/] risus, ultrices ac aliquet quis, suscipit at ipsum. Ut eu odio in felis facilisis [uu]pharetra[/] vitae ut neque. Nunc eget risus [white on_red]lobortis[/], placerat neque id, aliquet arcu. Etiam est tortor, ultricies sit amet eleifend eget, maximus nec ipsum.
|
|
18
|
+
|
|
19
|
+
Proin ex turpis, viverra a convallis at, tincidunt et mi. Ut venenatis tincidunt nisl ac rhoncus. Sed dapibus, metus sit amet [yellow]consectetur[/] mattis, nunc lectus [black on_bright_yellow]congue[/] metus, vitae ornare [i]augue[/] diam eu [i]augue[/]. Proin eget sapien a lorem laoreet [yellow]consectetur[/]. Morbi [i green uu]sagittis[/], dui at egestas malesuada, felis lectus aliquet ante, nec euismod tellus elit semper ante. Nulla vehicula non nibh a aliquet. Donec a massa eget justo tincidunt ullamcorper. Praesent elementum dictum orci et convallis. Sed non dui a [i]augue[/] accumsan vestibulum. Vestibulum id lacus [black on_bright_yellow]congue[/], hendrerit justo sit amet, suscipit magna. Mauris ultrices mattis diam a [white on_red]lobortis[/]. Ut interdum et sapien in [white on_red]lobortis[/].
|
|
20
|
+
|
|
21
|
+
Suspendisse placerat in ex et auctor. Nunc maximus metus quis [black on_bright_yellow]congue[/] suscipit. Vivamus lectus mi, efficitur vel turpis id, suscipit fringilla nunc. Fusce malesuada massa turpis. Aliquam dolor orci, facilisis eu lorem id, elementum placerat ipsum. Proin ac sem sem. Cras [cu]finibus[/] varius fringilla. Fusce non feugiat risus, ut egestas massa. Suspendisse vestibulum [i]augue[/] eu dapibus egestas. Nulla facilisi. Sed ut nulla eu massa fringilla commodo nec vitae neque.
|
|
22
|
+
|
|
23
|
+
Curabitur gravida posuere nunc ut placerat. Phasellus aliquam interdum [u]fermentum[/]. Ut ac mi erat. Morbi [yellow]consectetur[/] odio non nibh ultricies [u]fermentum[/]. Nam dolor enim, mollis at purus a, euismod facilisis ex. Pellentesque dui massa, [yellow]sollicitudin[/] vitae [white on_red]lobortis[/] nec, malesuada a eros. Donec [yellow]sollicitudin[/] tempor volutpat. Cras ac dignissim velit. Cras nec blandit lorem.
|
|
24
|
+
|
|
25
|
+
Phasellus [black on_bright_yellow]congue[/] eu urna non mollis. Suspendisse magna turpis, maximus eget ornare vel, [inv]congue[/] id nisl. Proin feugiat molestie odio quis semper. Pellentesque convallis viverra euismod. Mauris porttitor rhoncus eros, at egestas urna. Nam eget condimentum mi. Fusce ut purus metus. Sed sed ipsum nibh. Vestibulum vehicula tempor turpis, at rhoncus tellus porttitor sed.
|
|
26
|
+
|
|
27
|
+
Donec id dictum sem, ut vulputate nisl. Donec [black on_bright_yellow]congue[/] eleifend purus, eu pretium eros laoreet non. Duis commodo justo a quam laoreet egestas. Donec eu mauris ac metus pulvinar venenatis. Praesent dictum urna ac iaculis vehicula. Ut non felis viverra, semper odio ornare, [yellow]sollicitudin[/] purus. Sed nec metus vehicula, elementum sapien eu, ultrices massa. Quisque tristique semper luctus. Vestibulum sit amet diam sed purus [black on_bright_yellow]congue[/] efficitur et non justo. In massa magna, accumsan at condimentum sit amet, pretium ut orci. Nulla [uu]pharetra[/] magna a metus vehicula varius. Integer et lacus semper, suscipit [i]augue[/] non, [yellow]consectetur[/] nisi. In id tellus vel turpis tristique dignissim nec vel lectus. Sed interdum diam felis, at bibendum arcu feugiat sed.
|
|
28
|
+
|
|
29
|
+
Morbi tortor urna, suscipit non nulla nec, cursus interdum nisl. Nam ante nisi, suscipit a felis eu, faucibus tincidunt metus. Mauris gravida tellus in ipsum porttitor lacinia eu et ante. Maecenas blandit eleifend dui, eu viverra leo. Ut eu [i green uu]pellentesque[/] est. Cras id [cu]finibus[/] ipsum. Vestibulum eget mi ac ante dictum dictum scelerisque at nibh. Ut accumsan aliquam eros eget tincidunt. Nunc sit amet dignissim mauris. Praesent id [i]augue[/] consequat lectus [u]fermentum[/] accumsan quis ut elit. Proin venenatis eros lacus, non ullamcorper purus [u]fermentum[/] vitae. Fusce dolor libero, pretium ac mi nec, porta elementum mauris. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed viverra cursus ultricies.
|
|
30
|
+
|
|
31
|
+
Nam egestas ante a dui mollis rutrum. Phasellus a dapibus arcu. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse ac tortor in urna tristique bibendum. Aliquam ultrices, sem sit amet semper vehicula, [i]augue[/] erat porttitor [i]augue[/], a pretium nisi magna [i green uu]pellentesque[/] enim. Vestibulum nec nibh vitae nulla interdum [cu]finibus[/] ut viverra dolor. Pellentesque quis lacus enim. Donec et auctor mi. Curabitur eu [i green uu]pellentesque[/] risus. Aliquam [cu]finibus[/] dolor nec velit placerat [inv]congue[/]. Duis eget porttitor dolor. Vestibulum turpis nisi, porttitor ac pulvinar ac, vehicula viverra quam. In mollis, ante sit amet dictum [inv]congue[/], turpis dui luctus sem, eget convallis risus velit eget lorem.
|
|
32
|
+
|
|
33
|
+
Nam quis accumsan tellus, vitae dictum lectus. Donec varius porta metus et iaculis. Ut et consequat est, nec tincidunt neque. Sed ullamcorper lectus eu enim suscipit, sed tincidunt massa dictum. Sed venenatis elit metus, [inv]congue[/] aliquam lectus [i green uu]sagittis[/] at. Pellentesque ante arcu, pulvinar ut commodo at, consequat id sapien. Nunc mollis dolor eu ultricies mattis.
|
|
34
|
+
|
|
35
|
+
Phasellus et elementum lectus, vitae convallis nunc. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed dictum arcu [white on_red]lobortis[/] erat vulputate rhoncus. Ut id imperdiet justo. Donec vehicula in eros at lacinia. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Nam iaculis [black on_bright_yellow]congue[/] dignissim. Duis vel quam feugiat, gravida quam non, volutpat nisi. Integer eget auctor mauris, consequat dignissim nunc. Ut scelerisque [i]augue[/] aliquam erat rutrum, vitae aliquet est semper. Etiam nec velit lorem. Ut malesuada auctor metus sit amet posuere. Vivamus quis ipsum magna. Vivamus libero erat, dictum ut lorem auctor, tincidunt ultricies tortor. Suspendisse faucibus, ligula id auctor tincidunt, odio ligula tristique [i]augue[/], ac interdum ligula justo dictum magna.
|
|
36
|
+
|
|
37
|
+
Aliquam eros sapien, [inv]congue[/] ut metus eget, blandit iaculis ex. Donec id condimentum leo. Nunc [white on_red]lobortis[/] [yellow]consectetur[/] nunc nec posuere. Curabitur id iaculis diam. Nullam elementum luctus odio, et vehicula velit scelerisque at. Sed lorem nisl, malesuada quis massa sed, hendrerit [uu]pharetra[/] eros. Pellentesque leo mi, placerat id maximus ut, consequat sed turpis. Nulla nunc mi, convallis at iaculis non, ultricies vel tortor. Donec purus velit, molestie in [u]fermentum[/] ut, [black on_bright_yellow]congue[/] in dui. Morbi molestie, enim tempus malesuada consequat, orci lacus bibendum velit, non porta nisi ante a ipsum. Donec vitae nulla nulla.
|
|
38
|
+
|
|
39
|
+
Nulla varius dignissim turpis ac consequat. Fusce ut varius sapien. Cras [yellow]consectetur[/] est eleifend, imperdiet nibh a, dapibus justo. Etiam vehicula, est ac aliquam consequat, ante nibh porta ex, dictum suscipit diam justo et sapien. Sed porttitor ipsum nec diam [u]fermentum[/], vitae mattis diam gravida. Vivamus ac elementum ex, vitae maximus est. Maecenas tempor elementum pretium. Cras aliquam magna sit amet ipsum rutrum commodo. Aliquam cursus tincidunt sapien, et imperdiet sem [black on_bright_yellow]congue[/].
|
|
@@ -52,7 +52,7 @@ module Terminal
|
|
|
52
52
|
# Total number of lines.
|
|
53
53
|
#
|
|
54
54
|
# @attribute [r] line_count
|
|
55
|
-
# @return [Integer, nil]
|
|
55
|
+
# @return [Integer, nil] `nil` when not already calculated
|
|
56
56
|
def line_count = @buf&.size
|
|
57
57
|
|
|
58
58
|
# Index of the topmost visible line.
|
|
@@ -68,7 +68,7 @@ module Terminal
|
|
|
68
68
|
|
|
69
69
|
# Resize to the current terminal dimensions and redraw.
|
|
70
70
|
#
|
|
71
|
-
# @return [self, nil]
|
|
71
|
+
# @return [self, nil] `nil` when resize is not required
|
|
72
72
|
def resize_to_screen = _resize(*Terminal.size)
|
|
73
73
|
|
|
74
74
|
# Resize to specific dimensions (clamped to terminal bounds)
|
|
@@ -76,7 +76,7 @@ module Terminal
|
|
|
76
76
|
#
|
|
77
77
|
# @param rows [Integer] new row count
|
|
78
78
|
# @param columns [Integer] new column count
|
|
79
|
-
# @return [self, nil]
|
|
79
|
+
# @return [self, nil] `nil` when resize is not required
|
|
80
80
|
def resize(rows, columns)
|
|
81
81
|
_resize(
|
|
82
82
|
rows.clamp(1, Terminal.rows),
|
|
@@ -99,7 +99,7 @@ module Terminal
|
|
|
99
99
|
|
|
100
100
|
# Scroll to the beginning of the content.
|
|
101
101
|
#
|
|
102
|
-
# @return [self, nil]
|
|
102
|
+
# @return [self, nil] `nil` when already on begin
|
|
103
103
|
def begin
|
|
104
104
|
return draw unless @buf
|
|
105
105
|
return if @line_top == 0
|
|
@@ -109,7 +109,7 @@ module Terminal
|
|
|
109
109
|
|
|
110
110
|
# Scroll to the end of the content.
|
|
111
111
|
#
|
|
112
|
-
# @return [self, nil]
|
|
112
|
+
# @return [self, nil] `nil` when already on end
|
|
113
113
|
def end
|
|
114
114
|
return draw unless @buf
|
|
115
115
|
idx = @buf.size - @rows_visible
|
data/lib/terminal/ansi.rb
CHANGED
|
@@ -45,7 +45,7 @@ module Terminal
|
|
|
45
45
|
# Generate an ANSI escape sequence from attribute names or color
|
|
46
46
|
# indices.
|
|
47
47
|
#
|
|
48
|
-
# Accepts symbols (`:bold`,
|
|
48
|
+
# Accepts symbols (`:bold`, `:red`), strings (`"bold"`, `"#ff0000"`),
|
|
49
49
|
# or integers for 256-color palette (0-255 foreground, 256-511
|
|
50
50
|
# background, 512-767 underline color).
|
|
51
51
|
#
|
|
@@ -122,11 +122,11 @@ module Terminal
|
|
|
122
122
|
#
|
|
123
123
|
# @example
|
|
124
124
|
# Terminal::Ansi.try_convert('bold red') # => "\e[1;31m"
|
|
125
|
-
# Terminal::Ansi.try_convert('invalid')
|
|
125
|
+
# Terminal::Ansi.try_convert('invalid') # => nil
|
|
126
126
|
#
|
|
127
127
|
# @param attributes [#to_s] space-separated attribute names
|
|
128
128
|
# @param separator [String] delimiter for splitting
|
|
129
|
-
# @return [String, nil] ANSI escape sequence, or
|
|
129
|
+
# @return [String, nil] ANSI escape sequence, or `nil` if any
|
|
130
130
|
# attribute is invalid
|
|
131
131
|
def try_convert(attributes, separator: ' ')
|
|
132
132
|
return unless attributes
|
|
@@ -168,9 +168,9 @@ module Terminal
|
|
|
168
168
|
|
|
169
169
|
# Convert BBCode markup to ANSI escape codes.
|
|
170
170
|
#
|
|
171
|
-
# Tags like
|
|
172
|
-
#
|
|
173
|
-
# Escape a tag with a backslash:
|
|
171
|
+
# Tags like `[bold]` are converted to their ANSI equivalents;
|
|
172
|
+
# `[/bold]` or `[/]` resets. Unknown tags are left unchanged.
|
|
173
|
+
# Escape a tag with a backslash: `[\\bold]` renders as `[bold]`.
|
|
174
174
|
#
|
|
175
175
|
# @see .unbbcode
|
|
176
176
|
# @see .plain
|
|
@@ -361,7 +361,7 @@ module Terminal
|
|
|
361
361
|
# Terminal::Ansi.cursor_pos(1, 1) # home position
|
|
362
362
|
# Terminal::Ansi.cursor_pos(10, 5) # row 10, column 5
|
|
363
363
|
#
|
|
364
|
-
# @param row [Integer, nil] row number;
|
|
364
|
+
# @param row [Integer, nil] row number; `nil` moves to home
|
|
365
365
|
# @param column [Integer, nil] column number
|
|
366
366
|
# @return (see .[])
|
|
367
367
|
def cursor_pos(row, column = nil)
|
|
@@ -396,7 +396,7 @@ module Terminal
|
|
|
396
396
|
# Erase part of the screen.
|
|
397
397
|
#
|
|
398
398
|
# @param part [Symbol] area to erase:
|
|
399
|
-
#
|
|
399
|
+
# `:all`, `:below`, `:above`, or `:scrollback`
|
|
400
400
|
# @return (see .[])
|
|
401
401
|
def screen_erase(part = :all) = "\e[#{@screen_erase[part]}J"
|
|
402
402
|
|
|
@@ -449,7 +449,7 @@ module Terminal
|
|
|
449
449
|
# Erase part of the current line.
|
|
450
450
|
#
|
|
451
451
|
# @param part [Symbol] area to erase:
|
|
452
|
-
#
|
|
452
|
+
# `:all`, `:to_end`, or `:to_start`
|
|
453
453
|
# @return (see .[])
|
|
454
454
|
def line_erase(part = :all) = "\e[#{@line_erase[part]}K"
|
|
455
455
|
|
|
@@ -472,7 +472,7 @@ module Terminal
|
|
|
472
472
|
# @see .link_end
|
|
473
473
|
#
|
|
474
474
|
# @param url [#to_s] the link URL
|
|
475
|
-
# @param params [Hash] optional link parameters (e.g.,
|
|
475
|
+
# @param params [Hash] optional link parameters (e.g., `id:`)
|
|
476
476
|
# @return (see .[])
|
|
477
477
|
def link_start(url, **params)
|
|
478
478
|
"\e]8;#{params.map { it.join('=') }.join(':')};#{url}\a"
|
|
@@ -520,12 +520,12 @@ module Terminal
|
|
|
520
520
|
# Terminal.raw_write(Terminal::Ansi.progress(:error)) # error state
|
|
521
521
|
# Terminal.raw_write(Terminal::Ansi.progress(nil)) # hide
|
|
522
522
|
#
|
|
523
|
-
# @param state [Symbol, Numeric,
|
|
524
|
-
# -
|
|
525
|
-
#
|
|
523
|
+
# @param state [Symbol, Numeric, true, false] progress state:
|
|
524
|
+
# - `:show` or `true` to show, `:err`/`:error`, `:warn`/`:warning`,
|
|
525
|
+
# `:indeterminate`, a `Numeric` (0-100) to set percentage, or
|
|
526
526
|
# any other value to hide
|
|
527
|
-
# @param percent [
|
|
528
|
-
#
|
|
527
|
+
# @param percent [#to_i] progress percentage (0-100, used with
|
|
528
|
+
# `:show` state)
|
|
529
529
|
# @return (see .[])
|
|
530
530
|
def progress(state, percent = 0)
|
|
531
531
|
case state
|
|
@@ -563,9 +563,9 @@ module Terminal
|
|
|
563
563
|
# @param fracd [Integer, nil] fractional denominator
|
|
564
564
|
# (must be > fracn, max 15)
|
|
565
565
|
# @param vertical [Symbol, Integer, nil] vertical alignment:
|
|
566
|
-
#
|
|
566
|
+
# `:top`, `:bottom`, `:middle`
|
|
567
567
|
# @param horizontal [Symbol, Integer, nil] horizontal alignment:
|
|
568
|
-
#
|
|
568
|
+
# `:left`, `:right`, `:center`
|
|
569
569
|
# @return (see .[])
|
|
570
570
|
def scale(
|
|
571
571
|
text,
|
data/lib/terminal/input/ansi.rb
CHANGED
|
@@ -6,14 +6,13 @@ module Terminal
|
|
|
6
6
|
attr_reader :input_mode
|
|
7
7
|
|
|
8
8
|
def on_key_event(mouse: false, mouse_move: false, focus: false)
|
|
9
|
-
raise('already reading key events') if (@in_recursion += 1) != 1
|
|
10
9
|
return unless block_given?
|
|
11
|
-
opts =
|
|
10
|
+
opts = nil
|
|
12
11
|
STDIN.noecho do
|
|
13
|
-
opts
|
|
12
|
+
opts = __input_options(mouse, mouse_move, focus)
|
|
14
13
|
while (raw = @in.getch)
|
|
15
14
|
next yield(KeyEvent[raw]) if raw != "\e"
|
|
16
|
-
while
|
|
15
|
+
while (nc = @in.read_nonblock(1, exception: false)).is_a?(String)
|
|
17
16
|
raw << nc
|
|
18
17
|
end
|
|
19
18
|
next yield(KeyEvent[raw]) if raw.rindex("\e") < 2
|
|
@@ -25,13 +24,15 @@ module Terminal
|
|
|
25
24
|
__input_error
|
|
26
25
|
false
|
|
27
26
|
ensure
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
if opts
|
|
28
|
+
@in_opts.pop
|
|
29
|
+
@in&.syswrite(opts)
|
|
30
|
+
end
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
private
|
|
33
34
|
|
|
34
|
-
def
|
|
35
|
+
def __input_options(mouse, mouse_move, focus)
|
|
35
36
|
opts = (mouse ? ['1000'] : [])
|
|
36
37
|
# highlight: '1001'
|
|
37
38
|
# drag: '1002'
|
|
@@ -41,7 +42,19 @@ module Terminal
|
|
|
41
42
|
# sgr: '1006'
|
|
42
43
|
# urxvt: '1015'
|
|
43
44
|
# pixel: '1016'
|
|
44
|
-
|
|
45
|
+
if (last = @in_opts[-1])
|
|
46
|
+
if opts.empty?
|
|
47
|
+
@in.syswrite("#{last}l")
|
|
48
|
+
return "#{last}h"
|
|
49
|
+
end
|
|
50
|
+
@in_opts << (opts = "\e[?#{opts.join(';')};1006")
|
|
51
|
+
@in.syswrite("#{last}l#{opts}h")
|
|
52
|
+
return "#{opts}l#{last}h"
|
|
53
|
+
end
|
|
54
|
+
return if opts.empty?
|
|
55
|
+
@in_opts << (opts = "\e[?#{opts.join(';')};1006")
|
|
56
|
+
@in.syswrite("#{opts}h")
|
|
57
|
+
"#{opts}l"
|
|
45
58
|
end
|
|
46
59
|
end
|
|
47
60
|
|
data/lib/terminal/input/dumb.rb
CHANGED
|
@@ -5,8 +5,7 @@ module Terminal
|
|
|
5
5
|
module DumpInput
|
|
6
6
|
def input_mode = :dumb
|
|
7
7
|
|
|
8
|
-
def on_key_event(*
|
|
9
|
-
raise('already reading key events') if (@in_recursion += 1) != 1
|
|
8
|
+
def on_key_event(*)
|
|
10
9
|
return unless block_given?
|
|
11
10
|
while (raw = STDIN.getc)
|
|
12
11
|
opts = dumb_keys[raw.ord] if raw.size == 1
|
|
@@ -17,8 +16,6 @@ module Terminal
|
|
|
17
16
|
rescue IOError, SystemCallError
|
|
18
17
|
__input_error
|
|
19
18
|
false
|
|
20
|
-
ensure
|
|
21
|
-
@in_recursion -= 1
|
|
22
19
|
end
|
|
23
20
|
|
|
24
21
|
private
|
|
@@ -23,8 +23,8 @@ module Terminal
|
|
|
23
23
|
class KeyEvent
|
|
24
24
|
# The key identifier.
|
|
25
25
|
#
|
|
26
|
-
# @return [Symbol, String, nil] a named key like
|
|
27
|
-
#
|
|
26
|
+
# @return [Symbol, String, nil] a named key like `:Enter`, `:Tab`,
|
|
27
|
+
# `:MBLeft` (mouse), or a character string like `"a"`; `nil` for
|
|
28
28
|
# unknown sequences
|
|
29
29
|
attr_reader :key
|
|
30
30
|
|
|
@@ -61,8 +61,8 @@ module Terminal
|
|
|
61
61
|
|
|
62
62
|
# Mouse position when this is a mouse event.
|
|
63
63
|
#
|
|
64
|
-
# @return [Array<Integer, Integer>, nil]
|
|
65
|
-
# events,
|
|
64
|
+
# @return [Array<Integer, Integer>, nil] `[row, column]` for mouse
|
|
65
|
+
# events, `nil` for keyboard events
|
|
66
66
|
attr_reader :position
|
|
67
67
|
|
|
68
68
|
# The raw input sequence as received from the terminal.
|
|
@@ -85,8 +85,8 @@ module Terminal
|
|
|
85
85
|
# @private
|
|
86
86
|
# Destructuring-friendly array representation.
|
|
87
87
|
#
|
|
88
|
-
# @return [Array<String>]
|
|
89
|
-
# @return [Array<String, String>]
|
|
88
|
+
# @return [Array<String>] `[raw]` if {#simple?}
|
|
89
|
+
# @return [Array<String, String>] `[raw, name]` otherwise
|
|
90
90
|
def to_ary = simple? ? [@raw] : [@raw, @name]
|
|
91
91
|
|
|
92
92
|
# @return [String] the event {#name}
|
|
@@ -140,7 +140,7 @@ module Terminal
|
|
|
140
140
|
@names ||=
|
|
141
141
|
(@csiu.values + @csi.values + @ss3.values)
|
|
142
142
|
.uniq
|
|
143
|
-
.keep_if { Symbol
|
|
143
|
+
.keep_if { it.is_a?(Symbol) }
|
|
144
144
|
.sort!
|
|
145
145
|
).dup
|
|
146
146
|
end
|
data/lib/terminal/input.rb
CHANGED
|
@@ -15,7 +15,7 @@ module Terminal
|
|
|
15
15
|
# Terminal.input_mode # => :csi_u
|
|
16
16
|
#
|
|
17
17
|
# @attribute [r] input_mode
|
|
18
|
-
# @return [Symbol]
|
|
18
|
+
# @return [Symbol] `:csi_u`, `:legacy`, `:dumb`, or `:error`
|
|
19
19
|
|
|
20
20
|
#
|
|
21
21
|
# @!endgroup
|
|
@@ -42,8 +42,7 @@ module Terminal
|
|
|
42
42
|
# @param focus [true, false] enable terminal focus/unfocus events
|
|
43
43
|
# @yield [event] called for each input event
|
|
44
44
|
# @yieldparam event [KeyEvent] the parsed key event
|
|
45
|
-
# @return [nil, false]
|
|
46
|
-
# @raise [RuntimeError] if already inside a key event loop
|
|
45
|
+
# @return [nil, false] `false` on I/O error
|
|
47
46
|
|
|
48
47
|
# @!method self.read_key_event
|
|
49
48
|
#
|
|
@@ -53,7 +52,7 @@ module Terminal
|
|
|
53
52
|
# event = Terminal.read_key_event
|
|
54
53
|
# puts "You pressed: #{event.name}"
|
|
55
54
|
#
|
|
56
|
-
# @return [KeyEvent, false] the next key event, or
|
|
55
|
+
# @return [KeyEvent, false] the next key event, or `false` on error
|
|
57
56
|
|
|
58
57
|
#
|
|
59
58
|
# @!endgroup
|
|
@@ -62,10 +61,10 @@ module Terminal
|
|
|
62
61
|
# @private
|
|
63
62
|
module Input
|
|
64
63
|
def input_mode
|
|
65
|
-
@in_recursion = 0
|
|
66
64
|
case @input_mode = __find_input_mode
|
|
67
65
|
when :legacy, :csi_u
|
|
68
66
|
@in = IO.console
|
|
67
|
+
@in_opts = []
|
|
69
68
|
require_relative('input/ansi')
|
|
70
69
|
extend AnsiInput
|
|
71
70
|
when :dumb
|
|
@@ -105,7 +104,7 @@ module Terminal
|
|
|
105
104
|
con = IO.console
|
|
106
105
|
STDIN.noecho do
|
|
107
106
|
return :legacy if con.syswrite("\e[>1u\e[?u\e[c") != 12
|
|
108
|
-
inp =
|
|
107
|
+
inp = ''.b
|
|
109
108
|
inp << con.getch until inp.rindex('c')
|
|
110
109
|
return :legacy unless inp.include?("\e[?1u")
|
|
111
110
|
end
|
data/lib/terminal/output/ansi.rb
CHANGED
|
@@ -56,8 +56,8 @@ module Terminal
|
|
|
56
56
|
|
|
57
57
|
def print(*objects, bbcode: true)
|
|
58
58
|
return if objects.empty?
|
|
59
|
-
|
|
60
|
-
@out.print(*objects
|
|
59
|
+
objects.map! { Ansi.bbcode(it) } if bbcode
|
|
60
|
+
@out.print(*objects)
|
|
61
61
|
rescue IOError, SystemCallError
|
|
62
62
|
__output_error(nil)
|
|
63
63
|
end
|
|
@@ -65,8 +65,8 @@ module Terminal
|
|
|
65
65
|
def puts(*objects, bbcode: true)
|
|
66
66
|
objects.flatten!
|
|
67
67
|
return @out.puts if objects.empty?
|
|
68
|
-
|
|
69
|
-
@out.puts(objects
|
|
68
|
+
objects.map! { Ansi.bbcode(it) } if bbcode
|
|
69
|
+
@out.puts(*objects)
|
|
70
70
|
rescue IOError, SystemCallError
|
|
71
71
|
__output_error(nil)
|
|
72
72
|
end
|
|
@@ -84,6 +84,7 @@ module Terminal
|
|
|
84
84
|
suffix: nil
|
|
85
85
|
)
|
|
86
86
|
objects.flatten!
|
|
87
|
+
return @out.puts if objects.empty?
|
|
87
88
|
@out.puts(
|
|
88
89
|
Text::Formatter.format(
|
|
89
90
|
*objects,
|
|
@@ -145,6 +146,7 @@ module Terminal
|
|
|
145
146
|
private_class_method def self.extended(mod)
|
|
146
147
|
mod.instance_variable_set(:@cc, 0)
|
|
147
148
|
mod.instance_variable_set(:@as, 0)
|
|
149
|
+
# at_exit { raw_write("#{Ansi::CURSOR_SHOW}#{Ansi::SCREEN_ALTERNATE_OFF}") }
|
|
148
150
|
end
|
|
149
151
|
end
|
|
150
152
|
|
data/lib/terminal/output/dumb.rb
CHANGED
|
@@ -67,13 +67,14 @@ module Terminal
|
|
|
67
67
|
suffix: nil
|
|
68
68
|
)
|
|
69
69
|
objects.flatten!
|
|
70
|
+
return @out.puts if objects.empty?
|
|
70
71
|
@out.puts(
|
|
71
72
|
Text::Formatter.format(
|
|
72
73
|
*objects,
|
|
73
74
|
ansi: false,
|
|
74
75
|
bbcode:,
|
|
75
76
|
spaces:,
|
|
76
|
-
eol
|
|
77
|
+
eol:,
|
|
77
78
|
align:,
|
|
78
79
|
width:,
|
|
79
80
|
height:,
|
data/lib/terminal/output.rb
CHANGED
|
@@ -66,7 +66,7 @@ module Terminal
|
|
|
66
66
|
#
|
|
67
67
|
# The terminal size.
|
|
68
68
|
#
|
|
69
|
-
# @return [Array<Integer, Integer>]
|
|
69
|
+
# @return [Array<Integer, Integer>] `[rows, columns]`
|
|
70
70
|
|
|
71
71
|
# Terminal width in columns.
|
|
72
72
|
#
|
|
@@ -89,7 +89,7 @@ module Terminal
|
|
|
89
89
|
# Current cursor position.
|
|
90
90
|
# This is only available when ANSI is supported ({ansi?} return true).
|
|
91
91
|
#
|
|
92
|
-
# @return [Array<Integer, Integer>, nil]
|
|
92
|
+
# @return [Array<Integer, Integer>, nil] `[row, column]` or `nil` if
|
|
93
93
|
# unavailable
|
|
94
94
|
|
|
95
95
|
# Whether the terminal has been resized since the last size query.
|
|
@@ -113,7 +113,7 @@ module Terminal
|
|
|
113
113
|
# @example
|
|
114
114
|
# Terminal << '[bold]Hello[/bold] World'
|
|
115
115
|
#
|
|
116
|
-
# @param object [#to_s, nil] the object to output;
|
|
116
|
+
# @param object [#to_s, nil] the object to output; `nil` is ignored
|
|
117
117
|
# @return [self]
|
|
118
118
|
|
|
119
119
|
# @!method print(*objects, bbcode: true)
|
|
@@ -162,7 +162,7 @@ module Terminal
|
|
|
162
162
|
# processing.
|
|
163
163
|
#
|
|
164
164
|
# @param object [#to_s] object to write
|
|
165
|
-
# @return [Integer, nil] bytes written, or
|
|
165
|
+
# @return [Integer, nil] bytes written, or `nil` on error
|
|
166
166
|
|
|
167
167
|
# @!method hide_cursor
|
|
168
168
|
#
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'stringio'
|
|
4
|
+
|
|
3
5
|
# RSpec shared context for testing code that uses Terminal.rb.
|
|
4
6
|
#
|
|
5
|
-
# Stubs all Terminal I/O methods and provides
|
|
6
|
-
#
|
|
7
|
-
# input is read from the
|
|
7
|
+
# Stubs all Terminal I/O methods and provides `stdout`, `stdin`, and
|
|
8
|
+
# `stdoutstr` test helpers. Output is captured into the `stdout` array;
|
|
9
|
+
# input is read from the `stdin` array.
|
|
8
10
|
#
|
|
9
11
|
# @example Basic usage
|
|
10
12
|
# RSpec.describe MyClass do
|
|
@@ -20,19 +22,19 @@
|
|
|
20
22
|
# include_context 'with Terminal.rb', ansi: false, colors: 8,
|
|
21
23
|
# size: [40, 120]
|
|
22
24
|
#
|
|
23
|
-
# @param ansi [true, false] mock ANSI support (default:
|
|
25
|
+
# @param ansi [true, false] mock ANSI support (default: `true`)
|
|
24
26
|
# @param application [Symbol] mock terminal application
|
|
25
|
-
# (default:
|
|
26
|
-
# @param colors [Integer, Symbol] mock color depth; use
|
|
27
|
-
# for 16777216 (default:
|
|
27
|
+
# (default: `:kitty`)
|
|
28
|
+
# @param colors [Integer, Symbol] mock color depth; use `:true_color`
|
|
29
|
+
# for 16777216 (default: `256`)
|
|
28
30
|
# @param size [Array<Integer, Integer>] mock terminal size as
|
|
29
|
-
#
|
|
31
|
+
# `[rows, columns]` (default: `[25, 80]`)
|
|
30
32
|
# @param pos [Array<Integer, Integer>] mock cursor position as
|
|
31
|
-
#
|
|
33
|
+
# `[row, column]` (default: `[1, 1]`)
|
|
32
34
|
RSpec.shared_context 'with Terminal.rb' do |ansi: true, application: :kitty, colors: 256, size: [25, 80], pos: [1, 1]|
|
|
33
|
-
let(:stdout) {
|
|
35
|
+
let(:stdout) { StringIO.new }
|
|
36
|
+
let(:stdout_lines) { stdout.string.lines(chomp: true) }
|
|
34
37
|
let(:stdin) { [] }
|
|
35
|
-
let(:stdoutstr) { stdout.join }
|
|
36
38
|
|
|
37
39
|
before do
|
|
38
40
|
allow(Terminal).to receive(:ansi?).with(no_args).and_return(ansi)
|
|
@@ -48,46 +50,48 @@ RSpec.shared_context 'with Terminal.rb' do |ansi: true, application: :kitty, col
|
|
|
48
50
|
allow(Terminal).to receive(:size).with(no_args).and_return(size)
|
|
49
51
|
allow(Terminal).to receive(:pos).with(no_args).and_return(pos)
|
|
50
52
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
ansi ?
|
|
54
|
-
->(s) { Terminal::Ansi.bbcode(s) } :
|
|
55
|
-
->(s) { Terminal::Ansi.plain(s) },
|
|
56
|
-
false => ansi ? lambda(&:to_s) : ->(s) { Terminal::Ansi.undecorate(s) }
|
|
57
|
-
}.compare_by_identity
|
|
53
|
+
if ansi
|
|
54
|
+
bbcode_ = ->(s) { Terminal::Ansi.bbcode(s) }
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
56
|
+
allow(Terminal).to receive(:<<) do |object|
|
|
57
|
+
stdout.write(bbcode_[object]) if object != nil
|
|
58
|
+
Terminal
|
|
59
|
+
end
|
|
63
60
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
61
|
+
allow(Terminal).to receive(:print) do |*objects, bbcode: true|
|
|
62
|
+
next if objects.empty?
|
|
63
|
+
objects.map! { Ansi.bbcode(it) } if bbcode
|
|
64
|
+
stdout.print(*objects)
|
|
65
|
+
end
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
allow(Terminal).to receive(:puts) do |*objects, bbcode: true|
|
|
68
|
+
objects.flatten!
|
|
69
|
+
next @out.puts if objects.empty?
|
|
70
|
+
objects.map! { Ansi.bbcode(it) } if bbcode
|
|
71
|
+
stdout.puts(*objects)
|
|
75
72
|
end
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
73
|
+
|
|
74
|
+
allow(Terminal).to receive(:raw_write) { stdout.syswrite(it) }
|
|
75
|
+
else
|
|
76
|
+
plain_ = ->(s) { Terminal::Ansi.plain(s) }
|
|
77
|
+
undecorate_ = ->(s) { Terminal::Ansi.undecorate(s) }
|
|
78
|
+
|
|
79
|
+
allow(Terminal).to receive(:<<) do |object|
|
|
80
|
+
stdout.write(Terminal::Ansi.plain(object)) if object != nil
|
|
81
|
+
Terminal
|
|
81
82
|
end
|
|
82
|
-
nil
|
|
83
|
-
end
|
|
84
83
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
stdout.
|
|
88
|
-
object.bytesize
|
|
84
|
+
allow(Terminal).to receive(:print) do |*objects, bbcode: true|
|
|
85
|
+
next if objects.empty?
|
|
86
|
+
stdout.print(*objects.map!(&(bbcode ? plain_ : undecorate_)))
|
|
89
87
|
end
|
|
90
|
-
|
|
88
|
+
|
|
89
|
+
allow(Terminal).to receive(:puts) do |*objects, bbcode: true|
|
|
90
|
+
objects.flatten!
|
|
91
|
+
next stdout.puts if objects.empty?
|
|
92
|
+
stdout.puts(*objects.map!(&(bbcode ? plain_ : undecorate_)))
|
|
93
|
+
end
|
|
94
|
+
|
|
91
95
|
allow(Terminal).to receive(:raw_write).and_return(nil)
|
|
92
96
|
end
|
|
93
97
|
|
data/lib/terminal/shell.rb
CHANGED
|
@@ -77,7 +77,7 @@ module Terminal
|
|
|
77
77
|
return unless obj
|
|
78
78
|
return copy_writer(obj) if obj.respond_to?(:readpartial)
|
|
79
79
|
return copy_writer(obj.to_io) if obj.respond_to?(:to_io)
|
|
80
|
-
return array_writer(obj) if Array
|
|
80
|
+
return array_writer(obj) if obj.is_a?(Array)
|
|
81
81
|
if obj.respond_to?(:each)
|
|
82
82
|
return enum_writer(obj.enum_for(:each)) if obj.respond_to?(:enum_for)
|
|
83
83
|
return enum_writer(Enumerator.new { |y| obj.each { y << it } })
|
|
@@ -32,8 +32,8 @@ module Terminal
|
|
|
32
32
|
# # => [["Hello", 5]]
|
|
33
33
|
#
|
|
34
34
|
# @param (see #initialize)
|
|
35
|
-
# @param with_size [true, false] when
|
|
36
|
-
# return
|
|
35
|
+
# @param with_size [true, false] when `true`,
|
|
36
|
+
# return `[line, width]` pairs instead of plain strings
|
|
37
37
|
# @param (see #lines_with_size)
|
|
38
38
|
# @return (see #lines_with_size)
|
|
39
39
|
def self.[](
|
|
@@ -86,17 +86,23 @@ module Terminal
|
|
|
86
86
|
# @param ansi [true, false] recognize ANSI escape codes
|
|
87
87
|
# @param bbcode [true, false] process BBCode markup
|
|
88
88
|
# @param spaces [true, false] preserve whitespace;
|
|
89
|
-
# when
|
|
89
|
+
# when `false` leading/trailing spaces and multiple spaces are
|
|
90
90
|
# collapsed
|
|
91
91
|
# @param eol [true, false] preserve line endings;
|
|
92
|
-
# when
|
|
92
|
+
# when `false` newlines are treated as spaces
|
|
93
93
|
def initialize(*str, ansi: true, bbcode: true, spaces: true, eol: true)
|
|
94
94
|
@lex = []
|
|
95
95
|
return if str.empty?
|
|
96
96
|
ansi ? _generate_ansi(str, bbcode) : _generate(str, bbcode)
|
|
97
97
|
return if @lex.empty?
|
|
98
|
+
@keep_paragraph = true
|
|
98
99
|
@lex = _ignore_whitespace unless spaces
|
|
99
|
-
|
|
100
|
+
case eol
|
|
101
|
+
when false, nil
|
|
102
|
+
@lex = _ignore_newline
|
|
103
|
+
when :no_paragraph
|
|
104
|
+
@keep_paragraph = false
|
|
105
|
+
end
|
|
100
106
|
end
|
|
101
107
|
|
|
102
108
|
# Word-wrap and return lines with their display widths.
|
|
@@ -106,10 +112,10 @@ module Terminal
|
|
|
106
112
|
# fmt.lines_with_size(width: 5)
|
|
107
113
|
# # => [["Hello", 5], ["World", 5]]
|
|
108
114
|
#
|
|
109
|
-
# @param width [
|
|
110
|
-
#
|
|
115
|
+
# @param width [#to_i, nil] maximum line width in columns;
|
|
116
|
+
# `nil` returns unwrapped lines
|
|
111
117
|
# @return [Array<String, Integer>] pairs of
|
|
112
|
-
#
|
|
118
|
+
# `[line_text, display_width]`
|
|
113
119
|
# @raise (see #format)
|
|
114
120
|
def lines_with_size(width: nil)
|
|
115
121
|
return [] if @lex.empty?
|
|
@@ -125,9 +131,9 @@ module Terminal
|
|
|
125
131
|
# fmt.lines(width: 5)
|
|
126
132
|
# # => ["Hello", "World"]
|
|
127
133
|
#
|
|
128
|
-
# @param width [
|
|
134
|
+
# @param width [#to_i, nil] maximum line width in columns
|
|
129
135
|
# @return [Array<String>]
|
|
130
|
-
# @raise [ArgumentError] if
|
|
136
|
+
# @raise [ArgumentError] if `width` is zero or negative
|
|
131
137
|
def lines(width: nil) = lines_with_size(width:).map(&:first)
|
|
132
138
|
|
|
133
139
|
# Format lines with alignment, padding, and decorations.
|
|
@@ -138,11 +144,11 @@ module Terminal
|
|
|
138
144
|
# # => [" Hi "]
|
|
139
145
|
#
|
|
140
146
|
# @param align [Symbol, nil] text alignment:
|
|
141
|
-
#
|
|
142
|
-
# @param width [
|
|
143
|
-
# @param height [
|
|
147
|
+
# `:left`, `:right`, `:center`, or `nil` (no fill)
|
|
148
|
+
# @param width [#to_i, nil] output line width in columns
|
|
149
|
+
# @param height [#to_i, nil] number of output lines;
|
|
144
150
|
# negative values take lines from the end
|
|
145
|
-
# @param padding [
|
|
151
|
+
# @param padding [#to_i, Array, nil] CSS-style padding
|
|
146
152
|
# - 1 value: all sides;
|
|
147
153
|
# - 2 values: [vertical, horizontal];
|
|
148
154
|
# - 3 values: [top, horizontal, bottom];
|
|
@@ -150,7 +156,7 @@ module Terminal
|
|
|
150
156
|
# @param prefix [#to_s, nil] string prepended to each line
|
|
151
157
|
# @param suffix [#to_s, nil] string appended to each line
|
|
152
158
|
# @return [Array<String>] formatted output lines
|
|
153
|
-
# @raise [ArgumentError] if
|
|
159
|
+
# @raise [ArgumentError] if `width` is zero or negative
|
|
154
160
|
def format(
|
|
155
161
|
align: nil,
|
|
156
162
|
width: nil,
|
|
@@ -172,27 +178,29 @@ module Terminal
|
|
|
172
178
|
return [] if height < 1
|
|
173
179
|
end
|
|
174
180
|
|
|
181
|
+
space = SPACE_CACHE.dup
|
|
182
|
+
|
|
175
183
|
if width
|
|
176
184
|
w = (width = width.to_i) - left - right
|
|
177
185
|
raise(ArgumentError, "invalid width - #{width.inspect}") if w < 1
|
|
178
186
|
lws = _limited(w)
|
|
179
187
|
lws = tail ? lws.last(height) : lws.take(height) if height
|
|
180
|
-
empty = "#{prefix}#{
|
|
188
|
+
empty = "#{prefix}#{space[width]}#{suffix}" if top + bottom > 0
|
|
181
189
|
else
|
|
182
190
|
lws = @unlimited || _unlimited
|
|
183
191
|
lws = tail ? lws.last(height) : lws.take(height) if height
|
|
184
192
|
w = lws.empty? ? 0 : lws.max_by(&:last)[-1]
|
|
185
193
|
if top + bottom > 0
|
|
186
|
-
empty = "#{prefix}#{
|
|
194
|
+
empty = "#{prefix}#{space[w + left + right]}#{suffix}"
|
|
187
195
|
end
|
|
188
196
|
end
|
|
189
197
|
|
|
190
198
|
ret = []
|
|
191
199
|
ret.fill(empty, 0, top) if top > 0
|
|
192
|
-
left = left < 1 ? prefix.to_s : "#{prefix}#{
|
|
193
|
-
right = right < 1 ? suffix.to_s : "#{
|
|
200
|
+
left = left < 1 ? prefix.to_s : "#{prefix}#{space[left]}"
|
|
201
|
+
right = right < 1 ? suffix.to_s : "#{space[right]}#{suffix}"
|
|
194
202
|
|
|
195
|
-
|
|
203
|
+
# TODO: styling
|
|
196
204
|
|
|
197
205
|
case align
|
|
198
206
|
when :left
|
|
@@ -201,9 +209,9 @@ module Terminal
|
|
|
201
209
|
lws.each { |l, s| ret << "#{left}#{space[w - s]}#{l}#{right}" }
|
|
202
210
|
when :center
|
|
203
211
|
lws.each do |l, s|
|
|
204
|
-
|
|
205
|
-
next ret << "#{left}#{l}#{right}" if
|
|
206
|
-
ret << "#{left}#{space[
|
|
212
|
+
rs = ((s = w - s) / 2.0).round
|
|
213
|
+
next ret << "#{left}#{l}#{right}" if rs < 1
|
|
214
|
+
ret << "#{left}#{space[s - rs]}#{l}#{space[rs]}#{right}"
|
|
207
215
|
end
|
|
208
216
|
else
|
|
209
217
|
lws.each { |l, _| ret << "#{left}#{l}#{right}" }
|
|
@@ -224,7 +232,7 @@ module Terminal
|
|
|
224
232
|
# fmt = Terminal::Text::Formatter.new("short\na longer line")
|
|
225
233
|
# fmt.max_line_width # => 13
|
|
226
234
|
#
|
|
227
|
-
# @return [Integer] widest line in columns,
|
|
235
|
+
# @return [Integer] widest line in columns, `0` when empty
|
|
228
236
|
def max_line_width
|
|
229
237
|
@max_line_width ||=
|
|
230
238
|
(@lex.empty? ? 0 : (@unlimited || _unlimited).max_by(&:last)[-1])
|
|
@@ -237,7 +245,7 @@ module Terminal
|
|
|
237
245
|
# Terminal::Text::Formatter.new('Hello World').to_s(width: 5)
|
|
238
246
|
# # => "Hello\nWorld"
|
|
239
247
|
#
|
|
240
|
-
# @param width [
|
|
248
|
+
# @param width [#to_i, nil] maximum line width in columns
|
|
241
249
|
# @return [String]
|
|
242
250
|
def to_s(width: nil) = lines(width:).join("\n")
|
|
243
251
|
|
|
@@ -294,8 +302,9 @@ module Terminal
|
|
|
294
302
|
if (last = line[-1]).is_a?(Space) # ignore trailing space
|
|
295
303
|
line.pop
|
|
296
304
|
size -= last.size
|
|
305
|
+
next add_line.call
|
|
297
306
|
end
|
|
298
|
-
add_line.call
|
|
307
|
+
add_line.call if (current != EOP) || (@keep_paragraph || size > 0)
|
|
299
308
|
next
|
|
300
309
|
end
|
|
301
310
|
|
|
@@ -507,7 +516,7 @@ module Terminal
|
|
|
507
516
|
# @private
|
|
508
517
|
class Space
|
|
509
518
|
attr_accessor :size
|
|
510
|
-
def to_str = (' ' * @size)
|
|
519
|
+
def to_str = (@size == 1 ? ' ' : ' ' * @size)
|
|
511
520
|
def initialize = (@size = 1)
|
|
512
521
|
def inc = (@size += 1)
|
|
513
522
|
def inspect = @size == 1 ? '<Space>' : "<Space #{@size}>"
|
|
@@ -565,7 +574,7 @@ module Terminal
|
|
|
565
574
|
class << self
|
|
566
575
|
attr_reader :to_str, :size, :inspect
|
|
567
576
|
end
|
|
568
|
-
@to_str =
|
|
577
|
+
@to_str = Ansi::RESET
|
|
569
578
|
@size = 0
|
|
570
579
|
@inspect = '<CsiEnd>'
|
|
571
580
|
end
|
|
@@ -581,11 +590,12 @@ module Terminal
|
|
|
581
590
|
|
|
582
591
|
ENCODING = Encoding::UTF_8
|
|
583
592
|
|
|
584
|
-
|
|
585
|
-
Hash
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
593
|
+
space_cache =
|
|
594
|
+
Hash.new { |h, s| h[s] = s < 1 ? '' : ' ' * s }.compare_by_identity
|
|
595
|
+
space_cache[0]
|
|
596
|
+
space_cache[1]
|
|
597
|
+
space_cache[2]
|
|
598
|
+
SPACE_CACHE = space_cache.freeze
|
|
589
599
|
|
|
590
600
|
SCAN_REGEX =
|
|
591
601
|
/\G(?:
|
data/lib/terminal/text.rb
CHANGED
|
@@ -28,7 +28,7 @@ module Terminal
|
|
|
28
28
|
# Terminal::Text.ambiguous_char_width # => 1
|
|
29
29
|
# Terminal::Text.ambiguous_char_width = 2 # for CJK terminals
|
|
30
30
|
#
|
|
31
|
-
# @return [Integer] default:
|
|
31
|
+
# @return [Integer] default: `1`
|
|
32
32
|
attr_accessor :ambiguous_char_width
|
|
33
33
|
|
|
34
34
|
# Calculate the display width of a string in terminal columns.
|
data/lib/terminal/version.rb
CHANGED
data/lib/terminal.rb
CHANGED
|
@@ -30,32 +30,32 @@ module Terminal
|
|
|
30
30
|
# Detect the terminal emulator application.
|
|
31
31
|
#
|
|
32
32
|
# Known applications:
|
|
33
|
-
#
|
|
34
|
-
#
|
|
35
|
-
#
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
#
|
|
41
|
-
#
|
|
42
|
-
#
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
#
|
|
33
|
+
# `:alacritty`,
|
|
34
|
+
# `:amiga`,
|
|
35
|
+
# `:code_edit`,
|
|
36
|
+
# `:dg_unix`,
|
|
37
|
+
# `:docker`,
|
|
38
|
+
# `:fluent`,
|
|
39
|
+
# `:hpterm`,
|
|
40
|
+
# `:hyper`,
|
|
41
|
+
# `:iterm`,
|
|
42
|
+
# `:kitty`,
|
|
43
|
+
# `:macos`,
|
|
44
|
+
# `:mintty`,
|
|
45
|
+
# `:ms_terminal`,
|
|
46
|
+
# `:ncr260`,
|
|
47
|
+
# `:nsterm`,
|
|
48
|
+
# `:terminator`,
|
|
49
|
+
# `:terminology`,
|
|
50
|
+
# `:termite`,
|
|
51
|
+
# `:vscode`,
|
|
52
|
+
# `:vt100`,
|
|
53
|
+
# `:warp`,
|
|
54
|
+
# `:wyse`,
|
|
55
|
+
# `:xnuppc`
|
|
56
56
|
#
|
|
57
|
-
# Any unrecognized
|
|
58
|
-
# symbol. Returns
|
|
57
|
+
# Any unrecognized `TERM_PROGRAM` value is converted to a sanitized
|
|
58
|
+
# symbol. Returns `nil` when no terminal can be identified.
|
|
59
59
|
#
|
|
60
60
|
# @example
|
|
61
61
|
# Terminal.application # => :kitty
|
|
@@ -77,7 +77,7 @@ module Terminal
|
|
|
77
77
|
# @param cmd [Array<String>] command and arguments
|
|
78
78
|
# @param options [Hash] options passed to the shell runner
|
|
79
79
|
# @return [Array<Process::Status, Array<String>, Array<String>>]
|
|
80
|
-
#
|
|
80
|
+
# `[status, stdout_lines, stderr_lines]`
|
|
81
81
|
# @example
|
|
82
82
|
# status, output, error = Terminal.sh('echo', 'hello')
|
|
83
83
|
# output # => ["hello\n"]
|
|
@@ -88,7 +88,7 @@ module Terminal
|
|
|
88
88
|
# @param options [Hash] options passed to the shell runner
|
|
89
89
|
# @yield [line, type] called for each output line
|
|
90
90
|
# @yieldparam line [String] the output line
|
|
91
|
-
# @yieldparam type [Symbol]
|
|
91
|
+
# @yieldparam type [Symbol] `:output` or `:error`
|
|
92
92
|
# @return [Process::Status] the exit status
|
|
93
93
|
# @example
|
|
94
94
|
# Terminal.sh('make') do |line, type|
|
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: 1.0.
|
|
4
|
+
version: 1.0.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Mike Blumtritt
|
|
@@ -29,6 +29,7 @@ files:
|
|
|
29
29
|
- examples/bbcode.rb
|
|
30
30
|
- examples/info.rb
|
|
31
31
|
- examples/key-codes.rb
|
|
32
|
+
- examples/lorem_ipsum.ansi
|
|
32
33
|
- examples/screen_viewer.rb
|
|
33
34
|
- examples/text.rb
|
|
34
35
|
- lib/terminal.rb
|
|
@@ -59,13 +60,13 @@ metadata:
|
|
|
59
60
|
yard.run: yard
|
|
60
61
|
source_code_uri: https://codeberg.org/mblumtritt/Terminal.rb
|
|
61
62
|
bug_tracker_uri: https://codeberg.org/mblumtritt/Terminal.rb/issues
|
|
62
|
-
documentation_uri: https://rubydoc.info/gems/terminal_rb/1.0.
|
|
63
|
+
documentation_uri: https://rubydoc.info/gems/terminal_rb/1.0.6
|
|
63
64
|
rdoc_options: []
|
|
64
65
|
require_paths:
|
|
65
66
|
- lib
|
|
66
67
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
68
|
requirements:
|
|
68
|
-
- - "
|
|
69
|
+
- - ">="
|
|
69
70
|
- !ruby/object:Gem::Version
|
|
70
71
|
version: '3.4'
|
|
71
72
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
@@ -74,7 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
74
75
|
- !ruby/object:Gem::Version
|
|
75
76
|
version: '0'
|
|
76
77
|
requirements: []
|
|
77
|
-
rubygems_version: 4.0.
|
|
78
|
+
rubygems_version: 4.0.15
|
|
78
79
|
specification_version: 4
|
|
79
80
|
summary: Fast terminal access with ANSI, CSIu, mouse events, BBCode, word-wise line
|
|
80
81
|
break support and much more.
|