terminal_rb 0.20.0 → 1.0.4
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 +5 -4
- data/bin/bbcode +25 -21
- data/examples/24bit-colors.rb +3 -3
- data/examples/3bit-colors.rb +9 -9
- data/examples/8bit-colors.rb +18 -18
- data/examples/attributes.rb +24 -10
- data/examples/bbcode.rb +23 -19
- data/examples/info.rb +7 -7
- data/examples/key-codes.rb +4 -4
- data/examples/screen_viewer.rb +82 -0
- data/examples/text.rb +12 -28
- data/lib/terminal/ansi/named_colors.rb +1 -0
- data/lib/terminal/ansi/screen_viewer.rb +224 -0
- data/lib/terminal/ansi.rb +502 -480
- data/lib/terminal/detect.rb +1 -0
- data/lib/terminal/input/ansi.rb +9 -7
- data/lib/terminal/input/dumb.rb +5 -8
- data/lib/terminal/input/key_event.rb +131 -75
- data/lib/terminal/input.rb +49 -40
- data/lib/terminal/output/ansi.rb +39 -5
- data/lib/terminal/output/dumb.rb +33 -0
- data/lib/terminal/output.rb +139 -125
- data/lib/terminal/rspec/helper.rb +30 -1
- data/lib/terminal/shell.rb +10 -6
- data/lib/terminal/text/char_width.rb +178 -176
- data/lib/terminal/text/formatter.rb +614 -0
- data/lib/terminal/text.rb +168 -444
- data/lib/terminal/version.rb +1 -1
- data/lib/terminal.rb +79 -75
- metadata +9 -7
- data/terminal_rb.gemspec +0 -36
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Terminal
|
|
4
|
+
module Ansi
|
|
5
|
+
# A scrollable text viewer for the terminal.
|
|
6
|
+
#
|
|
7
|
+
# Renders word-wrapped content and supports paging, line-by-line
|
|
8
|
+
# scrolling, and terminal resize. Intended for use with the alternate
|
|
9
|
+
# screen buffer.
|
|
10
|
+
#
|
|
11
|
+
# @example Basic pager
|
|
12
|
+
# viewer = Terminal::Ansi::ScreenViewer.new(lines, footer_rows: 1)
|
|
13
|
+
# Terminal.show_alt_screen
|
|
14
|
+
# viewer.draw
|
|
15
|
+
# Terminal.on_key_event do |event|
|
|
16
|
+
# case event.key
|
|
17
|
+
# when :Up then viewer.up
|
|
18
|
+
# when :Down then viewer.down
|
|
19
|
+
# when :PageUp then viewer.page_up
|
|
20
|
+
# when :PageDown then viewer.page_down
|
|
21
|
+
# when 'q' then break
|
|
22
|
+
# end
|
|
23
|
+
# end
|
|
24
|
+
# Terminal.hide_alt_screen
|
|
25
|
+
#
|
|
26
|
+
class ScreenViewer
|
|
27
|
+
# Total number of terminal rows.
|
|
28
|
+
#
|
|
29
|
+
# @return [Integer]
|
|
30
|
+
attr_reader :rows
|
|
31
|
+
|
|
32
|
+
# @attribute [w] rows
|
|
33
|
+
def rows=(value)
|
|
34
|
+
resize(value, @columns)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Total number of terminal columns.
|
|
38
|
+
#
|
|
39
|
+
# @return [Integer]
|
|
40
|
+
attr_reader :columns
|
|
41
|
+
|
|
42
|
+
# @attribute [w] columns
|
|
43
|
+
def columns=(value)
|
|
44
|
+
resize(@rows, value)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Number of rows available for content (rows minus footer).
|
|
48
|
+
#
|
|
49
|
+
# @return [Integer]
|
|
50
|
+
attr_reader :rows_visible
|
|
51
|
+
|
|
52
|
+
# Total number of lines.
|
|
53
|
+
#
|
|
54
|
+
# @attribute [r] line_count
|
|
55
|
+
# @return [Integer, nil] +nil+ when not already calculated
|
|
56
|
+
def line_count = @buf&.size
|
|
57
|
+
|
|
58
|
+
# Index of the topmost visible line.
|
|
59
|
+
#
|
|
60
|
+
# @return [Integer]
|
|
61
|
+
attr_reader :line_top
|
|
62
|
+
|
|
63
|
+
# Index of the line just below the last visible line.
|
|
64
|
+
#
|
|
65
|
+
# @attribute [r] line_bottom
|
|
66
|
+
# @return [Integer]
|
|
67
|
+
def line_bottom = (@line_top + @rows_visible if @line_top)
|
|
68
|
+
|
|
69
|
+
# Resize to the current terminal dimensions and redraw.
|
|
70
|
+
#
|
|
71
|
+
# @return [self, nil] +nil+ when resize is not required
|
|
72
|
+
def resize_to_screen = _resize(*Terminal.size)
|
|
73
|
+
|
|
74
|
+
# Resize to specific dimensions (clamped to terminal bounds)
|
|
75
|
+
# and redraw.
|
|
76
|
+
#
|
|
77
|
+
# @param rows [Integer] new row count
|
|
78
|
+
# @param columns [Integer] new column count
|
|
79
|
+
# @return [self, nil] +nil+ when resize is not required
|
|
80
|
+
def resize(rows, columns)
|
|
81
|
+
_resize(
|
|
82
|
+
rows.clamp(1, Terminal.rows),
|
|
83
|
+
columns.clamp(1, Terminal.columns)
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Render the current viewport to the terminal.
|
|
88
|
+
#
|
|
89
|
+
# @return [self]
|
|
90
|
+
def draw
|
|
91
|
+
recalculate unless @buf
|
|
92
|
+
if @buf.empty?
|
|
93
|
+
Terminal.raw_write(SCREEN_ERASE)
|
|
94
|
+
return self
|
|
95
|
+
end
|
|
96
|
+
Terminal.raw_write(CURSOR_HOME)
|
|
97
|
+
_draw(@line_top, @rows_visible)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Scroll to the beginning of the content.
|
|
101
|
+
#
|
|
102
|
+
# @return [self, nil] +nil+ when already on begin
|
|
103
|
+
def begin
|
|
104
|
+
return draw unless @buf
|
|
105
|
+
return if @line_top == 0
|
|
106
|
+
@line_top = 0
|
|
107
|
+
draw
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Scroll to the end of the content.
|
|
111
|
+
#
|
|
112
|
+
# @return [self, nil] +nil+ when already on end
|
|
113
|
+
def end
|
|
114
|
+
return draw unless @buf
|
|
115
|
+
idx = @buf.size - @rows_visible
|
|
116
|
+
return if idx < 0 || @line_top == idx
|
|
117
|
+
@line_top = idx
|
|
118
|
+
draw
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Scroll up by the given number of lines.
|
|
122
|
+
#
|
|
123
|
+
# @param count [Integer] number of lines to scroll
|
|
124
|
+
# @return (see #begin)
|
|
125
|
+
def up(count = 1)
|
|
126
|
+
return draw unless @buf
|
|
127
|
+
return if @line_top == 0
|
|
128
|
+
if (nidx = @line_top - count) < 0
|
|
129
|
+
count = @line_top
|
|
130
|
+
@line_top = 0
|
|
131
|
+
else
|
|
132
|
+
@line_top = nidx
|
|
133
|
+
end
|
|
134
|
+
Terminal.raw_write(Ansi.screen_scroll_down(count))
|
|
135
|
+
Terminal.raw_write(CURSOR_HOME)
|
|
136
|
+
_draw(@line_top, count)
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Scroll down by the given number of lines.
|
|
140
|
+
#
|
|
141
|
+
# @param count [Integer] number of lines to scroll
|
|
142
|
+
# @return (see #end)
|
|
143
|
+
def down(count = 1)
|
|
144
|
+
return draw unless @buf
|
|
145
|
+
lidx = @line_top + @rows_visible
|
|
146
|
+
return if (count = [count, @buf.size - lidx].min) <= 0
|
|
147
|
+
@line_top += count
|
|
148
|
+
Terminal.raw_write(Ansi.screen_scroll_up(count))
|
|
149
|
+
Terminal.raw_write(Ansi.cursor_pos(@rows_visible - count + 1))
|
|
150
|
+
_draw(lidx, count)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Scroll up by one full page.
|
|
154
|
+
#
|
|
155
|
+
# @return (see #begin)
|
|
156
|
+
def page_up = up(@rows_visible)
|
|
157
|
+
|
|
158
|
+
# Scroll up by half a page.
|
|
159
|
+
#
|
|
160
|
+
# @return (see #begin)
|
|
161
|
+
def half_up = up(@rows_visible / 2)
|
|
162
|
+
|
|
163
|
+
# Scroll down by one full page.
|
|
164
|
+
#
|
|
165
|
+
# @return (see #end)
|
|
166
|
+
def page_down = down(@rows_visible)
|
|
167
|
+
|
|
168
|
+
# Scroll down by half a page.
|
|
169
|
+
#
|
|
170
|
+
# @return (see #end)
|
|
171
|
+
def half_down = down(@rows_visible / 2)
|
|
172
|
+
|
|
173
|
+
# Create a new screen viewer.
|
|
174
|
+
#
|
|
175
|
+
# @param lines [Array<#to_s>] content lines to display
|
|
176
|
+
# @param footer_rows [Integer] number of rows reserved at the bottom
|
|
177
|
+
# (not scrolled with content)
|
|
178
|
+
# @param bbcode [true, false] whether to process BBCode markup
|
|
179
|
+
def initialize(lines, footer_rows: 0, bbcode: true)
|
|
180
|
+
@footer_rows = footer_rows.to_i
|
|
181
|
+
@rows, @columns = Terminal.size
|
|
182
|
+
@rows_visible = @rows - @footer_rows
|
|
183
|
+
@fmt =
|
|
184
|
+
Text::Formatter.new(
|
|
185
|
+
*lines,
|
|
186
|
+
bbcode: bbcode,
|
|
187
|
+
ansi: true,
|
|
188
|
+
eol: true,
|
|
189
|
+
spaces: true
|
|
190
|
+
)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
private
|
|
194
|
+
|
|
195
|
+
def _resize(rows, columns)
|
|
196
|
+
return if @rows == rows && @columns == columns
|
|
197
|
+
@rows = rows
|
|
198
|
+
@columns = columns
|
|
199
|
+
@rows_visible = rows - @footer_rows
|
|
200
|
+
@buf = nil
|
|
201
|
+
draw
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
def _draw(idx, count)
|
|
205
|
+
idx -= 1
|
|
206
|
+
(count - 1).times do
|
|
207
|
+
line = @buf[idx += 1] and Terminal.raw_write(line)
|
|
208
|
+
Terminal.raw_write(NEXT_LINE)
|
|
209
|
+
end
|
|
210
|
+
line = @buf[idx + 1] and Terminal.raw_write(line)
|
|
211
|
+
Terminal.raw_write(LINE_ERASE_TO_END)
|
|
212
|
+
self
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
NEXT_LINE = -"#{LINE_ERASE_TO_END}#{CURSOR_NEXT_LINE}"
|
|
216
|
+
private_constant :NEXT_LINE
|
|
217
|
+
|
|
218
|
+
def recalculate
|
|
219
|
+
@buf = @fmt.lines(width: @columns)
|
|
220
|
+
@line_top = 0
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
end
|
|
224
|
+
end
|