rcurses 2.10 → 3.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/lib/rcurses/pane.rb +174 -203
- data/lib/rcurses.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 706550c12ee9965c33b958c6ce4ec5cb0aa12fb37196b4dd623cea43b982528f
|
4
|
+
data.tar.gz: 17e20e12a50a34f2d36069041b3f1dfbedbf27ab7dddf8f3681fd8b289e6fe00
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68fe3d1c80b12fae7ad09b49efdec40a889bf36f6d269e68ab09971bf9f057874ac122cff4d1c08c9b0673e13e65857c98e9e888977138a1c51a385544520901
|
7
|
+
data.tar.gz: aa6d784ef29afbb0b5e09ada56b2caadf2b474e0bf909eaa92dc161f88d6d1e6c33424d3c1ed2c407148d9ce502bd5563bf1eb1118e3b2a7a15444df18494a89
|
data/lib/rcurses/pane.rb
CHANGED
@@ -15,9 +15,9 @@ module Rcurses
|
|
15
15
|
@fg, @bg = fg, bg
|
16
16
|
@text = "" # Initialize text variable
|
17
17
|
@align = "l" # Default alignment
|
18
|
-
@scroll = true #
|
19
|
-
@prompt = "" #
|
20
|
-
@ix = 0 #
|
18
|
+
@scroll = true # Enable scroll indicators
|
19
|
+
@prompt = "" # Prompt for editline
|
20
|
+
@ix = 0 # Starting text line index
|
21
21
|
@max_h, @max_w = IO.console.winsize
|
22
22
|
end
|
23
23
|
|
@@ -74,108 +74,12 @@ module Rcurses
|
|
74
74
|
refresh
|
75
75
|
end
|
76
76
|
|
77
|
+
# Optimized refresh using double buffering to minimize flicker.
|
77
78
|
def refresh(cont = @text)
|
78
79
|
@max_h, @max_w = IO.console.winsize
|
79
80
|
|
80
|
-
#
|
81
|
-
|
82
|
-
# Define opening and closing sequences
|
83
|
-
open_sequences = {
|
84
|
-
"\e[1m" => "\e[22m",
|
85
|
-
"\e[3m" => "\e[23m",
|
86
|
-
"\e[4m" => "\e[24m",
|
87
|
-
"\e[5m" => "\e[25m",
|
88
|
-
"\e[7m" => "\e[27m" }
|
89
|
-
# All known closing sequences
|
90
|
-
close_sequences = open_sequences.values + ["\e[0m"]
|
91
|
-
# Regex to match ANSI escape sequences
|
92
|
-
ansi_regex = /\e\[[0-9;]*m/
|
93
|
-
result = []
|
94
|
-
# Tokenize the line into ANSI sequences and plain text
|
95
|
-
tokens = line.scan(/(\e\[[0-9;]*m|[^\e]+)/).flatten.compact
|
96
|
-
current_line = ''
|
97
|
-
current_line_length = 0
|
98
|
-
active_sequences = []
|
99
|
-
tokens.each do |token|
|
100
|
-
if token.match?(ansi_regex)
|
101
|
-
# It's an ANSI sequence
|
102
|
-
current_line << token
|
103
|
-
if close_sequences.include?(token)
|
104
|
-
# It's a closing sequence
|
105
|
-
if token == "\e[0m"
|
106
|
-
# Reset all sequences
|
107
|
-
active_sequences.clear
|
108
|
-
else
|
109
|
-
# Remove the corresponding opening sequence
|
110
|
-
corresponding_open = open_sequences.key(token)
|
111
|
-
active_sequences.delete(corresponding_open)
|
112
|
-
end
|
113
|
-
else
|
114
|
-
# It's an opening sequence (or any other ANSI sequence)
|
115
|
-
active_sequences << token
|
116
|
-
end
|
117
|
-
else
|
118
|
-
# It's plain text, split into words and spaces
|
119
|
-
words = token.scan(/\S+\s*/)
|
120
|
-
words.each do |word|
|
121
|
-
word_length = word.gsub(ansi_regex, '').length
|
122
|
-
if current_line_length + word_length <= w
|
123
|
-
# Append word to current line
|
124
|
-
current_line << word
|
125
|
-
current_line_length += word_length
|
126
|
-
else
|
127
|
-
# Word doesn't fit in the current line
|
128
|
-
if current_line_length > 0
|
129
|
-
# Finish the current line and start a new one
|
130
|
-
result << current_line
|
131
|
-
# Start new line with active ANSI sequences
|
132
|
-
current_line = active_sequences.join
|
133
|
-
current_line_length = 0
|
134
|
-
end
|
135
|
-
# Handle long words that might need splitting
|
136
|
-
while word_length > w
|
137
|
-
# Split the word
|
138
|
-
part = word[0, w]
|
139
|
-
current_line << part
|
140
|
-
result << current_line
|
141
|
-
# Update word and lengths
|
142
|
-
word = word[w..-1]
|
143
|
-
word_length = word.gsub(ansi_regex, '').length
|
144
|
-
# Start new line
|
145
|
-
current_line = active_sequences.join
|
146
|
-
current_line_length = 0
|
147
|
-
end
|
148
|
-
# Append any remaining part of the word
|
149
|
-
if word_length > 0
|
150
|
-
current_line << word
|
151
|
-
current_line_length += word_length
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
end
|
156
|
-
end
|
157
|
-
# Append any remaining text in the current line
|
158
|
-
result << current_line unless current_line.empty?
|
159
|
-
result
|
160
|
-
end
|
161
|
-
|
162
|
-
# Define the main textformat function
|
163
|
-
def textformat(cont)
|
164
|
-
# Split the content by '\n'
|
165
|
-
lines = cont.split("\n")
|
166
|
-
result = []
|
167
|
-
lines.each do |line|
|
168
|
-
split_lines = split_line_with_ansi(line, @w)
|
169
|
-
result.concat(split_lines)
|
170
|
-
end
|
171
|
-
result
|
172
|
-
end
|
173
|
-
|
174
|
-
# Start the actual refresh
|
175
|
-
o_row, o_col = pos
|
176
|
-
|
177
|
-
# Adjust pane dimensions and positions
|
178
|
-
if @border # Keep panes inside screen
|
81
|
+
# Adjust pane dimensions (unchanged logic)
|
82
|
+
if @border
|
179
83
|
@w = @max_w - 2 if @w > @max_w - 2
|
180
84
|
@h = @max_h - 2 if @h > @max_h - 2
|
181
85
|
@x = 2 if @x < 2; @x = @max_w - @w if @x + @w > @max_w
|
@@ -187,75 +91,81 @@ module Rcurses
|
|
187
91
|
@y = 1 if @y < 1; @y = @max_h - @h + 1 if @y + @h > @max_h + 1
|
188
92
|
end
|
189
93
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
94
|
+
# Save current cursor position.
|
95
|
+
o_row, o_col = pos
|
96
|
+
|
97
|
+
# Hide cursor and switch to alternate screen buffer (if desired). Hide it any case and use Curses.show to bring it back.
|
98
|
+
# Note: The alternate screen buffer means your program’s output won’t mix with the normal terminal content.
|
99
|
+
STDOUT.print "\e[?25l" # hide cursor
|
100
|
+
# Uncomment the next line to use the alternate screen buffer.
|
101
|
+
#STDOUT.print "\e[?1049h"
|
102
|
+
|
103
|
+
buf = ""
|
104
|
+
buf << "\e[#{@y};#{@x}H" # Move cursor to start of pane.
|
105
|
+
fmt = [@fg, @bg].compact.join(',')
|
106
|
+
@txt = cont.split("\n")
|
107
|
+
@txt = textformat(cont) if @txt.any? { |line| line.pure.length >= @w }
|
108
|
+
@ix = @txt.length - 1 if @ix > @txt.length - 1
|
109
|
+
@ix = 0 if @ix < 0
|
195
110
|
|
196
|
-
@h.times do |i|
|
197
|
-
l = @ix + i
|
198
|
-
if @txt[l].to_s != ""
|
199
|
-
# Get padding width and half width
|
111
|
+
@h.times do |i|
|
112
|
+
l = @ix + i
|
113
|
+
if @txt[l].to_s != ""
|
200
114
|
pl = @w - @txt[l].pure.length
|
201
115
|
pl = 0 if pl < 0
|
202
116
|
hl = pl / 2
|
203
117
|
case @align
|
204
118
|
when "l"
|
205
|
-
|
119
|
+
buf << @txt[l].c(fmt) << " ".c(fmt) * pl
|
206
120
|
when "r"
|
207
|
-
|
121
|
+
buf << " ".c(fmt) * pl << @txt[l].c(fmt)
|
208
122
|
when "c"
|
209
|
-
|
123
|
+
buf << " ".c(fmt) * hl << @txt[l].c(fmt) << " ".c(fmt) * (pl - hl)
|
210
124
|
end
|
211
125
|
else
|
212
|
-
|
126
|
+
buf << " ".c(fmt) * @w
|
213
127
|
end
|
214
|
-
|
215
|
-
row(@y + i + 1)
|
128
|
+
buf << "\e[#{@y + i + 1};#{@x}H" # Next line.
|
216
129
|
end
|
217
130
|
|
218
|
-
|
219
|
-
|
220
|
-
|
131
|
+
# Draw scroll markers.
|
132
|
+
if @ix > 0 and @scroll
|
133
|
+
buf << "\e[#{@y};#{@x + @w - 1}H" << "∆".c(fmt)
|
221
134
|
@moreup = true
|
222
135
|
else
|
223
136
|
@moreup = false
|
224
137
|
end
|
225
138
|
|
226
|
-
if @txt.length - @ix > @h and @scroll
|
227
|
-
|
228
|
-
print "∇".c(fmt)
|
139
|
+
if @txt.length - @ix > @h and @scroll
|
140
|
+
buf << "\e[#{@y + @h - 1};#{@x + @w - 1}H" << "∇".c(fmt)
|
229
141
|
@moredown = true
|
230
142
|
else
|
231
143
|
@moredown = false
|
232
144
|
end
|
233
145
|
|
234
|
-
|
235
|
-
|
236
|
-
|
146
|
+
# Draw border if enabled.
|
147
|
+
if @border
|
148
|
+
buf << "\e[#{@y - 1};#{@x - 1}H" << ("┌" + "─" * @w + "┐").c(fmt)
|
237
149
|
@h.times do |i|
|
238
|
-
|
239
|
-
|
240
|
-
col(@x + @w)
|
241
|
-
print "│".c(fmt)
|
150
|
+
buf << "\e[#{@y + i};#{@x - 1}H" << "│".c(fmt)
|
151
|
+
buf << "\e[#{@y + i};#{@x + @w}H" << "│".c(fmt)
|
242
152
|
end
|
243
|
-
|
244
|
-
print ("└" + "─" * @w + "┘").c(fmt)
|
153
|
+
buf << "\e[#{@y + @h};#{@x - 1}H" << ("└" + "─" * @w + "┘").c(fmt)
|
245
154
|
end
|
246
155
|
|
247
|
-
|
248
|
-
|
156
|
+
# Restore original cursor position.
|
157
|
+
buf << "\e[#{o_row};#{o_col}H"
|
158
|
+
|
159
|
+
# Print the whole buffer at once.
|
160
|
+
print buf
|
161
|
+
|
162
|
+
# Uncomment the next line to switched to the alternate screen buffer.
|
163
|
+
#STDOUT.print "\e[?1049l"
|
164
|
+
|
249
165
|
@txt
|
250
166
|
end
|
251
167
|
|
252
|
-
def puts(txt)
|
253
|
-
@text = txt
|
254
|
-
refresh
|
255
|
-
end
|
256
|
-
|
257
168
|
def textformat(cont)
|
258
|
-
# Split the content by '\n'
|
259
169
|
lines = cont.split("\n")
|
260
170
|
result = []
|
261
171
|
lines.each do |line|
|
@@ -265,6 +175,11 @@ module Rcurses
|
|
265
175
|
result
|
266
176
|
end
|
267
177
|
|
178
|
+
def puts(txt)
|
179
|
+
@text = txt
|
180
|
+
refresh
|
181
|
+
end
|
182
|
+
|
268
183
|
def right
|
269
184
|
if @pos < @txt[@ix + @line].length
|
270
185
|
@pos += 1
|
@@ -286,6 +201,7 @@ module Rcurses
|
|
286
201
|
end
|
287
202
|
end
|
288
203
|
end
|
204
|
+
|
289
205
|
def left
|
290
206
|
if @pos == 0
|
291
207
|
if @line == 0
|
@@ -301,6 +217,7 @@ module Rcurses
|
|
301
217
|
@pos -= 1
|
302
218
|
end
|
303
219
|
end
|
220
|
+
|
304
221
|
def up
|
305
222
|
if @line == 0
|
306
223
|
@ix -= 1 unless @ix == 0
|
@@ -312,6 +229,7 @@ module Rcurses
|
|
312
229
|
rescue
|
313
230
|
end
|
314
231
|
end
|
232
|
+
|
315
233
|
def down
|
316
234
|
if @line == @h - 1
|
317
235
|
@ix += 1 unless @ix + @line >= @txt.length - 1
|
@@ -325,10 +243,10 @@ module Rcurses
|
|
325
243
|
end
|
326
244
|
|
327
245
|
def parse(cont)
|
328
|
-
cont.gsub!(/\*(.+?)\*/,
|
329
|
-
cont.gsub!(/\/(.+?)\//,
|
330
|
-
cont.gsub!(/_(.+?)_/,
|
331
|
-
cont.gsub!(/#(.+?)#/,
|
246
|
+
cont.gsub!(/\*(.+?)\*/, '\1'.b)
|
247
|
+
cont.gsub!(/\/(.+?)\//, '\1'.i)
|
248
|
+
cont.gsub!(/_(.+?)_/, '\1'.u)
|
249
|
+
cont.gsub!(/#(.+?)#/, '\1'.r)
|
332
250
|
cont.gsub!(/<([^|]+)\|([^>]+)>/) do
|
333
251
|
text = $2; codes = $1
|
334
252
|
text.c(codes)
|
@@ -338,92 +256,85 @@ module Rcurses
|
|
338
256
|
|
339
257
|
def edit
|
340
258
|
begin
|
341
|
-
# Switch to raw mode without echoing input
|
342
259
|
STDIN.raw!
|
343
260
|
Rcurses::Cursor.show
|
344
|
-
# Prepare content for editing, replacing newlines with a placeholder
|
345
261
|
content = @text.pure.gsub("\n", "¬\n")
|
346
|
-
|
347
|
-
@
|
348
|
-
@
|
349
|
-
@
|
350
|
-
@txt = refresh(content)
|
262
|
+
@ix = 0
|
263
|
+
@line = 0
|
264
|
+
@pos = 0
|
265
|
+
@txt = refresh(content)
|
351
266
|
input_char = ''
|
352
267
|
|
353
|
-
while input_char != 'ESC'
|
354
|
-
row(@y + @line)
|
355
|
-
col(@x + @pos)
|
356
|
-
|
357
|
-
input_char = getchr # Read user input
|
268
|
+
while input_char != 'ESC'
|
269
|
+
row(@y + @line)
|
270
|
+
col(@x + @pos)
|
271
|
+
input_char = getchr
|
358
272
|
case input_char
|
359
|
-
when 'C-L'
|
273
|
+
when 'C-L'
|
360
274
|
@align = 'l'
|
361
|
-
when 'C-R'
|
275
|
+
when 'C-R'
|
362
276
|
@align = 'r'
|
363
|
-
when 'C-C'
|
277
|
+
when 'C-C'
|
364
278
|
@align = 'c'
|
365
|
-
when 'C-Y'
|
279
|
+
when 'C-Y'
|
366
280
|
Clipboard.copy(@text.pure)
|
367
|
-
when 'C-S'
|
281
|
+
when 'C-S'
|
368
282
|
content = content.gsub('¬', "\n")
|
369
283
|
content = parse(content)
|
370
284
|
@text = content
|
371
285
|
input_char = 'ESC'
|
372
|
-
when 'DEL'
|
286
|
+
when 'DEL'
|
373
287
|
posx = calculate_posx
|
374
288
|
content.slice!(posx)
|
375
|
-
when 'BACK'
|
289
|
+
when 'BACK'
|
376
290
|
if @pos > 0
|
377
291
|
left
|
378
292
|
posx = calculate_posx
|
379
293
|
content.slice!(posx)
|
380
294
|
end
|
381
|
-
when 'WBACK'
|
295
|
+
when 'WBACK'
|
382
296
|
while @pos > 0 && content[calculate_posx - 1] != ' '
|
383
297
|
left
|
384
298
|
posx = calculate_posx
|
385
299
|
content.slice!(posx)
|
386
300
|
end
|
387
|
-
when 'C-K'
|
301
|
+
when 'C-K'
|
388
302
|
line_start_pos = calculate_line_start_pos
|
389
303
|
line_length = @txt[@ix + @line]&.length || 0
|
390
304
|
content.slice!(line_start_pos + @pos, line_length - @pos)
|
391
|
-
when 'UP'
|
305
|
+
when 'UP'
|
392
306
|
up
|
393
|
-
when 'DOWN'
|
307
|
+
when 'DOWN'
|
394
308
|
down
|
395
|
-
when 'RIGHT'
|
309
|
+
when 'RIGHT'
|
396
310
|
right
|
397
|
-
when 'LEFT'
|
311
|
+
when 'LEFT'
|
398
312
|
left
|
399
|
-
when 'HOME'
|
313
|
+
when 'HOME'
|
400
314
|
@pos = 0
|
401
|
-
when 'END'
|
315
|
+
when 'END'
|
402
316
|
current_line_length = @txt[@ix + @line]&.length || 0
|
403
317
|
@pos = current_line_length
|
404
|
-
when 'C-HOME'
|
318
|
+
when 'C-HOME'
|
405
319
|
@ix = 0
|
406
320
|
@line = 0
|
407
321
|
@pos = 0
|
408
|
-
when 'C-END'
|
322
|
+
when 'C-END'
|
409
323
|
total_lines = @txt.length
|
410
324
|
@ix = [total_lines - @h, 0].max
|
411
325
|
@line = [@h - 1, total_lines - @ix - 1].min
|
412
326
|
current_line_length = @txt[@ix + @line]&.length || 0
|
413
327
|
@pos = current_line_length
|
414
|
-
when 'ENTER'
|
328
|
+
when 'ENTER'
|
415
329
|
posx = calculate_posx
|
416
330
|
content.insert(posx, "¬\n")
|
417
331
|
right
|
418
|
-
when /^.$/
|
332
|
+
when /^.$/
|
419
333
|
posx = calculate_posx
|
420
334
|
content.insert(posx, input_char)
|
421
335
|
right
|
422
|
-
else
|
423
|
-
# Handle unrecognized input if necessary
|
424
336
|
end
|
425
337
|
|
426
|
-
# Handle pasted input (additional characters in the buffer)
|
427
338
|
while IO.select([$stdin], nil, nil, 0)
|
428
339
|
input_char = $stdin.read_nonblock(1) rescue nil
|
429
340
|
break unless input_char
|
@@ -432,10 +343,9 @@ module Rcurses
|
|
432
343
|
right
|
433
344
|
end
|
434
345
|
|
435
|
-
@txt = refresh(content)
|
346
|
+
@txt = refresh(content)
|
436
347
|
end
|
437
348
|
ensure
|
438
|
-
# Restore terminal mode
|
439
349
|
STDIN.cooked!
|
440
350
|
end
|
441
351
|
Rcurses::Cursor.hide
|
@@ -443,36 +353,28 @@ module Rcurses
|
|
443
353
|
|
444
354
|
def editline
|
445
355
|
begin
|
446
|
-
# Switch to raw mode without echo
|
447
356
|
STDIN.raw!
|
448
357
|
Rcurses::Cursor.show
|
449
|
-
|
450
|
-
# Initialize position and dimensions
|
451
|
-
# Ensure pane is within screen bounds
|
452
358
|
@x = [[@x, 1].max, @max_w - @w + 1].min
|
453
359
|
@y = [[@y, 1].max, @max_h - @h + 1].min
|
454
|
-
|
455
360
|
@scroll = false
|
456
|
-
@ix
|
361
|
+
@ix = 0
|
457
362
|
row(@y)
|
458
|
-
|
459
363
|
fmt = [@fg, @bg].compact.join(',')
|
460
364
|
col(@x)
|
461
|
-
print @prompt.c(fmt)
|
462
|
-
|
365
|
+
print @prompt.c(fmt)
|
463
366
|
prompt_len = @prompt.pure.length
|
464
367
|
content_len = @w - prompt_len
|
465
368
|
cont = @text.pure.slice(0, content_len)
|
466
|
-
@pos = cont.length
|
369
|
+
@pos = cont.length
|
467
370
|
chr = ''
|
468
371
|
|
469
|
-
while chr != 'ESC'
|
470
|
-
col(@x + prompt_len)
|
471
|
-
cont = cont.slice(0, content_len)
|
472
|
-
print cont.ljust(content_len).c(fmt)
|
473
|
-
col(@x + prompt_len + @pos)
|
474
|
-
|
475
|
-
chr = getchr # Read user input
|
372
|
+
while chr != 'ESC'
|
373
|
+
col(@x + prompt_len)
|
374
|
+
cont = cont.slice(0, content_len)
|
375
|
+
print cont.ljust(content_len).c(fmt)
|
376
|
+
col(@x + prompt_len + @pos)
|
377
|
+
chr = getchr
|
476
378
|
case chr
|
477
379
|
when 'LEFT'
|
478
380
|
@pos -= 1 if @pos > 0
|
@@ -507,7 +409,6 @@ module Rcurses
|
|
507
409
|
end
|
508
410
|
end
|
509
411
|
|
510
|
-
# Handle pasted input
|
511
412
|
while IO.select([$stdin], nil, nil, 0)
|
512
413
|
chr = $stdin.read_nonblock(1) rescue nil
|
513
414
|
break unless chr
|
@@ -518,7 +419,6 @@ module Rcurses
|
|
518
419
|
end
|
519
420
|
end
|
520
421
|
ensure
|
521
|
-
# Restore terminal mode
|
522
422
|
STDIN.cooked!
|
523
423
|
end
|
524
424
|
Rcurses::Cursor.hide
|
@@ -526,23 +426,94 @@ module Rcurses
|
|
526
426
|
|
527
427
|
private
|
528
428
|
|
529
|
-
|
429
|
+
def flush_stdin
|
430
|
+
while IO.select([$stdin], nil, nil, 0.005)
|
431
|
+
begin
|
432
|
+
$stdin.read_nonblock(1024)
|
433
|
+
rescue IO::WaitReadable, EOFError
|
434
|
+
break
|
435
|
+
end
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
530
439
|
def calculate_posx
|
531
440
|
total_length = 0
|
532
441
|
(@ix + @line).times do |i|
|
533
|
-
total_length += @txt[i].pure.length + 1 # +1 for the newline
|
442
|
+
total_length += @txt[i].pure.length + 1 # +1 for the newline
|
534
443
|
end
|
535
444
|
total_length += @pos
|
536
445
|
total_length
|
537
446
|
end
|
538
|
-
|
447
|
+
|
539
448
|
def calculate_line_start_pos
|
540
449
|
total_length = 0
|
541
450
|
(@ix + @line).times do |i|
|
542
|
-
total_length += @txt[i].pure.length + 1
|
451
|
+
total_length += @txt[i].pure.length + 1
|
543
452
|
end
|
544
453
|
total_length
|
545
454
|
end
|
455
|
+
|
456
|
+
def split_line_with_ansi(line, w)
|
457
|
+
open_sequences = {
|
458
|
+
"\e[1m" => "\e[22m",
|
459
|
+
"\e[3m" => "\e[23m",
|
460
|
+
"\e[4m" => "\e[24m",
|
461
|
+
"\e[5m" => "\e[25m",
|
462
|
+
"\e[7m" => "\e[27m"
|
463
|
+
}
|
464
|
+
close_sequences = open_sequences.values + ["\e[0m"]
|
465
|
+
ansi_regex = /\e\[[0-9;]*m/
|
466
|
+
result = []
|
467
|
+
tokens = line.scan(/(\e\[[0-9;]*m|[^\e]+)/).flatten.compact
|
468
|
+
current_line = ''
|
469
|
+
current_line_length = 0
|
470
|
+
active_sequences = []
|
471
|
+
tokens.each do |token|
|
472
|
+
if token.match?(ansi_regex)
|
473
|
+
current_line << token
|
474
|
+
if close_sequences.include?(token)
|
475
|
+
if token == "\e[0m"
|
476
|
+
active_sequences.clear
|
477
|
+
else
|
478
|
+
corresponding_open = open_sequences.key(token)
|
479
|
+
active_sequences.delete(corresponding_open)
|
480
|
+
end
|
481
|
+
else
|
482
|
+
active_sequences << token
|
483
|
+
end
|
484
|
+
else
|
485
|
+
words = token.scan(/\s+|\S+/)
|
486
|
+
words.each do |word|
|
487
|
+
word_length = word.gsub(ansi_regex, '').length
|
488
|
+
if current_line_length + word_length <= w
|
489
|
+
current_line << word
|
490
|
+
current_line_length += word_length
|
491
|
+
else
|
492
|
+
if current_line_length > 0
|
493
|
+
result << current_line
|
494
|
+
current_line = active_sequences.join
|
495
|
+
current_line_length = 0
|
496
|
+
end
|
497
|
+
while word_length > w
|
498
|
+
part = word[0, w]
|
499
|
+
current_line << part
|
500
|
+
result << current_line
|
501
|
+
word = word[w..-1]
|
502
|
+
word_length = word.gsub(ansi_regex, '').length
|
503
|
+
current_line = active_sequences.join
|
504
|
+
current_line_length = 0
|
505
|
+
end
|
506
|
+
if word_length > 0
|
507
|
+
current_line << word
|
508
|
+
current_line_length += word_length
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
end
|
513
|
+
end
|
514
|
+
result << current_line unless current_line.empty?
|
515
|
+
result
|
516
|
+
end
|
546
517
|
end
|
547
518
|
end
|
548
519
|
|
data/lib/rcurses.rb
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
# Web_site: http://isene.com/
|
6
6
|
# Github: https://github.com/isene/rcurses
|
7
7
|
# License: Public domain
|
8
|
-
# Version:
|
8
|
+
# Version: 3.0: Flicker reduction by double screen buffer
|
9
9
|
|
10
10
|
require 'io/console' # Basic gem for rcurses
|
11
11
|
require 'io/wait' # stdin handling
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rcurses
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '
|
4
|
+
version: '3.0'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Geir Isene
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-04-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clipboard
|
@@ -29,8 +29,8 @@ description: 'Create curses applications for the terminal easier than ever. Crea
|
|
29
29
|
up text (in panes or anywhere in the terminal) in bold, italic, underline, reverse
|
30
30
|
color, blink and in any 256 terminal colors for foreground and background. Use a
|
31
31
|
simple editor to let users edit text in panes. Left, right or center align text
|
32
|
-
in panes. Cursor movement around the terminal. New in
|
33
|
-
|
32
|
+
in panes. Cursor movement around the terminal. New in 3.0: Flicker reduction by
|
33
|
+
double screen buffer.'
|
34
34
|
email: g@isene.com
|
35
35
|
executables: []
|
36
36
|
extensions: []
|