rcurses 4.8.3 → 4.9.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/cursor.rb +12 -7
- data/lib/rcurses/input.rb +7 -2
- data/lib/rcurses/pane.rb +131 -28
- data/lib/rcurses.rb +46 -1
- data/lib/string_extensions.rb +16 -5
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 998bc4ac36e7e3b51fc910a59bf1ffdf933870a8975ef205b572d4f3c1fe377a
|
4
|
+
data.tar.gz: 7bd9a0993fda8a06545969b7a4bea36f80e782e7dcefd477d11389f6764c0dff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99f1957fafce2eaaa495c7fa9230e5724643ec82e3f772420846ce1ead3d461d0682dc9df57066bc24286f32f63861a631e370d00a8ff805bac033e6ad81a94d
|
7
|
+
data.tar.gz: b4a41c371b9e8555b4f8f76fb6a45c5b50a43a7ada8e992aba9340f22ba6f84ee48513fce45294291eb0396b86b8540cc10eda37a9bd93aeb3fff1e3e79141cf
|
data/lib/rcurses/cursor.rb
CHANGED
@@ -8,15 +8,20 @@ module Rcurses
|
|
8
8
|
def restore; print(Gem.win_platform? ? CSI + 'u' : ESC + '8'); end # Restore cursor position
|
9
9
|
def pos # Query cursor current position
|
10
10
|
res = ''
|
11
|
-
|
12
|
-
$
|
13
|
-
|
14
|
-
|
15
|
-
|
11
|
+
begin
|
12
|
+
$stdin.raw do |stdin|
|
13
|
+
$stdout << CSI + '6n' # The actual ANSI get-position
|
14
|
+
$stdout.flush
|
15
|
+
while (c = stdin.getc) != 'R'
|
16
|
+
res << c if c
|
17
|
+
end
|
16
18
|
end
|
19
|
+
m = res.match(/(?<row>\d+);(?<col>\d+)/)
|
20
|
+
return m[:row].to_i, m[:col].to_i
|
21
|
+
rescue Errno::EIO, Errno::ENOTTY, IOError
|
22
|
+
# Return safe defaults if terminal state is compromised
|
23
|
+
return 1, 1
|
17
24
|
end
|
18
|
-
m = res.match(/(?<row>\d+);(?<col>\d+)/)
|
19
|
-
return m[:row].to_i, m[:col].to_i
|
20
25
|
end
|
21
26
|
def rowget
|
22
27
|
row, _col = pos
|
data/lib/rcurses/input.rb
CHANGED
@@ -7,6 +7,9 @@ module Rcurses
|
|
7
7
|
c = t ? Timeout.timeout(t) { $stdin.getch } : $stdin.getch
|
8
8
|
rescue Timeout::Error
|
9
9
|
return nil
|
10
|
+
rescue Errno::EIO, Errno::ENOTTY, IOError => e
|
11
|
+
# Handle terminal focus loss or disconnection gracefully
|
12
|
+
return "ESC"
|
10
13
|
end
|
11
14
|
|
12
15
|
# 2) If it's ESC, grab any quick trailing bytes
|
@@ -15,7 +18,8 @@ module Rcurses
|
|
15
18
|
if IO.select([$stdin], nil, nil, 0.05)
|
16
19
|
begin
|
17
20
|
seq << $stdin.read_nonblock(16)
|
18
|
-
rescue IO::WaitReadable, EOFError
|
21
|
+
rescue IO::WaitReadable, EOFError, Errno::EIO, Errno::ENOTTY
|
22
|
+
# Handle various terminal state errors gracefully
|
19
23
|
end
|
20
24
|
end
|
21
25
|
end
|
@@ -115,7 +119,8 @@ module Rcurses
|
|
115
119
|
while IO.select([$stdin], nil, nil, 0)
|
116
120
|
begin
|
117
121
|
$stdin.read_nonblock(4096)
|
118
|
-
rescue IO::WaitReadable, EOFError
|
122
|
+
rescue IO::WaitReadable, EOFError, Errno::EIO, Errno::ENOTTY
|
123
|
+
# Handle terminal state errors during flush
|
119
124
|
break
|
120
125
|
end
|
121
126
|
end
|
data/lib/rcurses/pane.rb
CHANGED
@@ -1,25 +1,68 @@
|
|
1
1
|
module Rcurses
|
2
|
-
#
|
3
|
-
# This is a simplified version that may need adjustments for full Unicode support.
|
2
|
+
# Enhanced display_width function with better Unicode support
|
4
3
|
def self.display_width(str)
|
5
4
|
width = 0
|
6
5
|
str.each_char do |char|
|
7
6
|
cp = char.ord
|
7
|
+
|
8
|
+
# Handle NUL and control characters
|
8
9
|
if cp == 0
|
9
10
|
# NUL – no width
|
11
|
+
next
|
10
12
|
elsif cp < 32 || (cp >= 0x7F && cp < 0xA0)
|
11
13
|
# Control characters: no width
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
14
|
+
next
|
15
|
+
end
|
16
|
+
|
17
|
+
# Handle combining characters (zero width)
|
18
|
+
if (cp >= 0x0300 && cp <= 0x036F) || # Combining Diacritical Marks
|
19
|
+
(cp >= 0x1AB0 && cp <= 0x1AFF) || # Combining Diacritical Marks Extended
|
20
|
+
(cp >= 0x1DC0 && cp <= 0x1DFF) || # Combining Diacritical Marks Supplement
|
21
|
+
(cp >= 0x20D0 && cp <= 0x20FF) || # Combining Diacritical Marks for Symbols
|
22
|
+
(cp >= 0xFE20 && cp <= 0xFE2F) # Combining Half Marks
|
23
|
+
next
|
24
|
+
end
|
25
|
+
|
26
|
+
# Handle wide characters (East Asian width)
|
27
|
+
if (cp >= 0x1100 && cp <= 0x115F) || # Hangul Jamo
|
28
|
+
(cp >= 0x2329 && cp <= 0x232A) || # Left/Right-Pointing Angle Bracket
|
29
|
+
(cp >= 0x2E80 && cp <= 0x2EFF) || # CJK Radicals Supplement
|
30
|
+
(cp >= 0x2F00 && cp <= 0x2FDF) || # Kangxi Radicals
|
31
|
+
(cp >= 0x2FF0 && cp <= 0x2FFF) || # Ideographic Description Characters
|
32
|
+
(cp >= 0x3000 && cp <= 0x303E) || # CJK Symbols and Punctuation
|
33
|
+
(cp >= 0x3041 && cp <= 0x3096) || # Hiragana
|
34
|
+
(cp >= 0x30A1 && cp <= 0x30FA) || # Katakana
|
35
|
+
(cp >= 0x3105 && cp <= 0x312D) || # Bopomofo
|
36
|
+
(cp >= 0x3131 && cp <= 0x318E) || # Hangul Compatibility Jamo
|
37
|
+
(cp >= 0x3190 && cp <= 0x31BA) || # Kanbun
|
38
|
+
(cp >= 0x31C0 && cp <= 0x31E3) || # CJK Strokes
|
39
|
+
(cp >= 0x31F0 && cp <= 0x31FF) || # Katakana Phonetic Extensions
|
40
|
+
(cp >= 0x3200 && cp <= 0x32FF) || # Enclosed CJK Letters and Months
|
41
|
+
(cp >= 0x3300 && cp <= 0x33FF) || # CJK Compatibility
|
42
|
+
(cp >= 0x3400 && cp <= 0x4DBF) || # CJK Unified Ideographs Extension A
|
43
|
+
(cp >= 0x4E00 && cp <= 0x9FFF) || # CJK Unified Ideographs
|
44
|
+
(cp >= 0xA960 && cp <= 0xA97F) || # Hangul Jamo Extended-A
|
45
|
+
(cp >= 0xAC00 && cp <= 0xD7A3) || # Hangul Syllables
|
46
|
+
(cp >= 0xD7B0 && cp <= 0xD7FF) || # Hangul Jamo Extended-B
|
47
|
+
(cp >= 0xF900 && cp <= 0xFAFF) || # CJK Compatibility Ideographs
|
48
|
+
(cp >= 0xFE10 && cp <= 0xFE19) || # Vertical Forms
|
49
|
+
(cp >= 0xFE30 && cp <= 0xFE6F) || # CJK Compatibility Forms
|
50
|
+
(cp >= 0xFF00 && cp <= 0xFF60) || # Fullwidth Forms
|
51
|
+
(cp >= 0xFFE0 && cp <= 0xFFE6) || # Fullwidth Forms
|
52
|
+
(cp >= 0x1F000 && cp <= 0x1F02F) || # Mahjong Tiles
|
53
|
+
(cp >= 0x1F030 && cp <= 0x1F09F) || # Domino Tiles
|
54
|
+
(cp >= 0x1F100 && cp <= 0x1F1FF) || # Enclosed Alphanumeric Supplement
|
55
|
+
(cp >= 0x1F200 && cp <= 0x1F2FF) || # Enclosed Ideographic Supplement
|
56
|
+
(cp >= 0x1F300 && cp <= 0x1F5FF) || # Miscellaneous Symbols and Pictographs
|
57
|
+
(cp >= 0x1F600 && cp <= 0x1F64F) || # Emoticons
|
58
|
+
(cp >= 0x1F650 && cp <= 0x1F67F) || # Ornamental Dingbats
|
59
|
+
(cp >= 0x1F680 && cp <= 0x1F6FF) || # Transport and Map Symbols
|
60
|
+
(cp >= 0x1F700 && cp <= 0x1F77F) || # Alchemical Symbols
|
61
|
+
(cp >= 0x1F780 && cp <= 0x1F7FF) || # Geometric Shapes Extended
|
62
|
+
(cp >= 0x1F800 && cp <= 0x1F8FF) || # Supplemental Arrows-C
|
63
|
+
(cp >= 0x1F900 && cp <= 0x1F9FF) || # Supplemental Symbols and Pictographs
|
64
|
+
(cp >= 0x20000 && cp <= 0x2FFFF) || # CJK Unified Ideographs Extension B-F
|
65
|
+
(cp >= 0x30000 && cp <= 0x3FFFF) # CJK Unified Ideographs Extension G
|
23
66
|
width += 2
|
24
67
|
else
|
25
68
|
width += 1
|
@@ -32,13 +75,19 @@ module Rcurses
|
|
32
75
|
require 'clipboard' # Ensure the 'clipboard' gem is installed
|
33
76
|
include Cursor
|
34
77
|
include Input
|
78
|
+
|
79
|
+
# Compiled regex patterns for performance
|
80
|
+
ANSI_REGEX = /\e\[[0-9;]*m/.freeze
|
81
|
+
SGR_REGEX = /\e\[\d+(?:;\d+)*m/.freeze
|
35
82
|
attr_accessor :x, :y, :w, :h, :fg, :bg
|
36
83
|
attr_accessor :border, :scroll, :text, :ix, :index, :align, :prompt
|
37
84
|
attr_accessor :moreup, :moredown
|
38
85
|
attr_accessor :record, :history
|
86
|
+
attr_accessor :updates_suspended
|
39
87
|
|
40
88
|
def initialize(x = 1, y = 1, w = 1, h = 1, fg = nil, bg = nil)
|
41
|
-
@
|
89
|
+
@terminal_size_cache = nil
|
90
|
+
@terminal_size_time = nil
|
42
91
|
@x = x
|
43
92
|
@y = y
|
44
93
|
@w = w
|
@@ -54,6 +103,8 @@ module Rcurses
|
|
54
103
|
@pos = 0 # For cursor tracking during editing:
|
55
104
|
@record = false # Don't record history unless explicitly set to true
|
56
105
|
@history = [] # History array
|
106
|
+
@updates_suspended = false
|
107
|
+
@lazy_cache_limit = 1000 # Limit for lazy text cache
|
57
108
|
end
|
58
109
|
|
59
110
|
def text=(new_text)
|
@@ -82,6 +133,24 @@ module Rcurses
|
|
82
133
|
full_refresh
|
83
134
|
end
|
84
135
|
|
136
|
+
def suspend_updates
|
137
|
+
@updates_suspended = true
|
138
|
+
end
|
139
|
+
|
140
|
+
def resume_updates
|
141
|
+
@updates_suspended = false
|
142
|
+
refresh
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_terminal_size
|
146
|
+
now = Time.now
|
147
|
+
if @terminal_size_cache.nil? || @terminal_size_time.nil? || (now - @terminal_size_time) > 0.5
|
148
|
+
@terminal_size_cache = IO.console.winsize
|
149
|
+
@terminal_size_time = now
|
150
|
+
end
|
151
|
+
@terminal_size_cache
|
152
|
+
end
|
153
|
+
|
85
154
|
def move(dx, dy)
|
86
155
|
@x += dx
|
87
156
|
@y += dy
|
@@ -90,7 +159,8 @@ module Rcurses
|
|
90
159
|
|
91
160
|
def linedown
|
92
161
|
@ix += 1
|
93
|
-
|
162
|
+
text_lines = @text.split("\n")
|
163
|
+
@ix = text_lines.length if @ix > text_lines.length - 1
|
94
164
|
refresh
|
95
165
|
end
|
96
166
|
|
@@ -102,7 +172,8 @@ module Rcurses
|
|
102
172
|
|
103
173
|
def pagedown
|
104
174
|
@ix = @ix + @h - 1
|
105
|
-
|
175
|
+
text_lines = @text.split("\n")
|
176
|
+
@ix = text_lines.length - @h if @ix > text_lines.length - @h
|
106
177
|
refresh
|
107
178
|
end
|
108
179
|
|
@@ -113,7 +184,8 @@ module Rcurses
|
|
113
184
|
end
|
114
185
|
|
115
186
|
def bottom
|
116
|
-
|
187
|
+
text_lines = @text.split("\n")
|
188
|
+
@ix = text_lines.length - @h
|
117
189
|
refresh
|
118
190
|
end
|
119
191
|
|
@@ -160,7 +232,15 @@ module Rcurses
|
|
160
232
|
# Diff-based refresh that minimizes flicker.
|
161
233
|
# In this updated version we lazily process only the raw lines required to fill the pane.
|
162
234
|
def refresh(cont = @text)
|
163
|
-
|
235
|
+
return if @updates_suspended
|
236
|
+
|
237
|
+
# Check if we're in batch mode and suspend updates accordingly
|
238
|
+
if Rcurses.batch_mode?
|
239
|
+
Rcurses.add_to_batch(self)
|
240
|
+
return
|
241
|
+
end
|
242
|
+
|
243
|
+
@max_h, @max_w = get_terminal_size
|
164
244
|
|
165
245
|
if @border
|
166
246
|
@w = @max_w - 2 if @w > @max_w - 2
|
@@ -184,11 +264,17 @@ module Rcurses
|
|
184
264
|
|
185
265
|
# Lazy evaluation: If the content or pane width has changed, reinitialize the lazy cache.
|
186
266
|
if !defined?(@cached_text) || @cached_text != cont || @cached_w != @w
|
187
|
-
@raw_txt = cont.split("\n")
|
267
|
+
@raw_txt = cont.split("\n")
|
188
268
|
@lazy_txt = [] # This will hold the processed (wrapped) lines as needed.
|
189
269
|
@lazy_index = 0 # Pointer to the next raw line to process.
|
190
270
|
@cached_text = cont.dup
|
191
271
|
@cached_w = @w
|
272
|
+
|
273
|
+
# Clear cache if it gets too large to prevent memory leaks
|
274
|
+
if @lazy_txt.size > @lazy_cache_limit
|
275
|
+
@lazy_txt.clear
|
276
|
+
@lazy_index = 0
|
277
|
+
end
|
192
278
|
end
|
193
279
|
|
194
280
|
content_rows = @h
|
@@ -221,11 +307,11 @@ module Rcurses
|
|
221
307
|
hl = pl / 2
|
222
308
|
case @align
|
223
309
|
when "l"
|
224
|
-
line_str = @txt[l].c(fmt) + " ".c(fmt) * pl
|
310
|
+
line_str = @txt[l].pure.c(fmt) + " ".c(fmt) * pl
|
225
311
|
when "r"
|
226
|
-
line_str = " ".c(fmt) * pl + @txt[l].c(fmt)
|
312
|
+
line_str = " ".c(fmt) * pl + @txt[l].pure.c(fmt)
|
227
313
|
when "c"
|
228
|
-
line_str = " ".c(fmt) * hl + @txt[l].c(fmt) + " ".c(fmt) * (pl - hl)
|
314
|
+
line_str = " ".c(fmt) * hl + @txt[l].pure.c(fmt) + " ".c(fmt) * (pl - hl)
|
229
315
|
end
|
230
316
|
else
|
231
317
|
line_str = " ".c(fmt) * @w
|
@@ -605,14 +691,13 @@ module Rcurses
|
|
605
691
|
"\e[7m" => "\e[27m"
|
606
692
|
}
|
607
693
|
close_sequences = open_sequences.values + ["\e[0m"]
|
608
|
-
ansi_regex = /\e\[[0-9;]*m/
|
609
694
|
result = []
|
610
695
|
tokens = line.scan(/(\e\[[0-9;]*m|[^\e]+)/).flatten.compact
|
611
696
|
current_line = ''
|
612
697
|
current_line_length = 0
|
613
698
|
active_sequences = []
|
614
699
|
tokens.each do |token|
|
615
|
-
if token.match?(
|
700
|
+
if token.match?(ANSI_REGEX)
|
616
701
|
current_line << token
|
617
702
|
if close_sequences.include?(token)
|
618
703
|
if token == "\e[0m"
|
@@ -627,7 +712,7 @@ module Rcurses
|
|
627
712
|
else
|
628
713
|
words = token.scan(/\s+|\S+/)
|
629
714
|
words.each do |word|
|
630
|
-
word_length = Rcurses.display_width(word.gsub(
|
715
|
+
word_length = Rcurses.display_width(word.gsub(ANSI_REGEX, ''))
|
631
716
|
if current_line_length + word_length <= w
|
632
717
|
current_line << word
|
633
718
|
current_line_length += word_length
|
@@ -638,11 +723,12 @@ module Rcurses
|
|
638
723
|
current_line_length = 0
|
639
724
|
end
|
640
725
|
while word_length > w
|
641
|
-
|
726
|
+
# Split safely respecting UTF-8 boundaries and display width
|
727
|
+
part = safe_substring_by_width(word, w)
|
642
728
|
current_line << part
|
643
729
|
result << current_line
|
644
|
-
word = word[
|
645
|
-
word_length = Rcurses.display_width(word.gsub(
|
730
|
+
word = word[part.length..-1]
|
731
|
+
word_length = Rcurses.display_width(word.gsub(ANSI_REGEX, ''))
|
646
732
|
current_line = active_sequences.join
|
647
733
|
current_line_length = 0
|
648
734
|
end
|
@@ -657,6 +743,23 @@ module Rcurses
|
|
657
743
|
result << current_line unless current_line.empty?
|
658
744
|
result
|
659
745
|
end
|
746
|
+
|
747
|
+
# Helper method to safely split strings by display width while respecting UTF-8 boundaries
|
748
|
+
def safe_substring_by_width(str, max_width)
|
749
|
+
return str if Rcurses.display_width(str) <= max_width
|
750
|
+
|
751
|
+
result = ''
|
752
|
+
current_width = 0
|
753
|
+
|
754
|
+
str.each_char do |char|
|
755
|
+
char_width = Rcurses.display_width(char)
|
756
|
+
break if current_width + char_width > max_width
|
757
|
+
result += char
|
758
|
+
current_width += char_width
|
759
|
+
end
|
760
|
+
|
761
|
+
result
|
762
|
+
end
|
660
763
|
end
|
661
764
|
end
|
662
765
|
|
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: 4.
|
8
|
+
# Version: 4.9.0: Major performance improvements - memory leak fixes, terminal dimension caching, batch updates, better Unicode support, enhanced error handling
|
9
9
|
|
10
10
|
require 'io/console' # Basic gem for rcurses
|
11
11
|
require 'io/wait' # stdin handling
|
@@ -52,6 +52,51 @@ module Rcurses
|
|
52
52
|
|
53
53
|
@cleaned_up = true
|
54
54
|
end
|
55
|
+
|
56
|
+
# Public: Batch multiple pane updates to prevent flickering
|
57
|
+
def batch_updates
|
58
|
+
@batched_panes = []
|
59
|
+
yield
|
60
|
+
@batched_panes.each(&:resume_updates)
|
61
|
+
@batched_panes = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
# Internal: Track panes that need updating in batch mode
|
65
|
+
def add_to_batch(pane)
|
66
|
+
if @batched_panes
|
67
|
+
pane.suspend_updates unless @batched_panes.include?(pane)
|
68
|
+
@batched_panes << pane unless @batched_panes.include?(pane)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Public: Check if we're in batch mode
|
73
|
+
def batch_mode?
|
74
|
+
!@batched_panes.nil?
|
75
|
+
end
|
76
|
+
|
77
|
+
# Content caching system for improved performance
|
78
|
+
def self.cache_get(key)
|
79
|
+
@content_cache ||= {}
|
80
|
+
@content_cache[key]
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.cache_set(key, value)
|
84
|
+
@content_cache ||= {}
|
85
|
+
@content_cache_limit ||= 100
|
86
|
+
|
87
|
+
# Simple LRU eviction when cache gets too large
|
88
|
+
if @content_cache.size >= @content_cache_limit
|
89
|
+
# Remove oldest entries (simplified LRU)
|
90
|
+
keys_to_remove = @content_cache.keys.first(@content_cache.size - @content_cache_limit + 10)
|
91
|
+
keys_to_remove.each { |k| @content_cache.delete(k) }
|
92
|
+
end
|
93
|
+
|
94
|
+
@content_cache[key] = value
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.cache_clear
|
98
|
+
@content_cache = {}
|
99
|
+
end
|
55
100
|
end
|
56
101
|
|
57
102
|
# Kick off initialization as soon as the library is required.
|
data/lib/string_extensions.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# string_extensions.rb
|
2
2
|
|
3
3
|
class String
|
4
|
+
# Compiled regex patterns for performance
|
5
|
+
ANSI_SGR_REGEX = /\e\[\d+(?:;\d+)*m/.freeze
|
6
|
+
ANSI_SEQUENCE_REGEX = /\e\[\d+(?:;\d+)*m/.freeze
|
4
7
|
# 256-color or truecolor RGB foregroundbreset only the fg (SGR 39)
|
5
8
|
def fg(color)
|
6
9
|
sp, ep = if color.to_s =~ /\A[0-9A-Fa-f]{6}\z/
|
@@ -93,12 +96,20 @@ class String
|
|
93
96
|
|
94
97
|
# Strip all ANSI SGR sequences
|
95
98
|
def pure
|
96
|
-
gsub(
|
99
|
+
gsub(ANSI_SGR_REGEX, '')
|
97
100
|
end
|
98
101
|
|
99
102
|
# Remove stray leading/trailing reset if the string has no other styling
|
100
103
|
def clean_ansi
|
101
|
-
|
104
|
+
# If we have opening ANSI codes without proper closing, just use pure
|
105
|
+
# to avoid unbalanced sequences that can corrupt terminal display
|
106
|
+
temp = gsub(/\A(?:\e\[0m)+/, '').gsub(/\e\[0m\z/, '')
|
107
|
+
# Check if we have unbalanced ANSI sequences (opening codes without closing)
|
108
|
+
if temp =~ /\e\[[\d;]+m/ && temp !~ /\e\[0m\z/
|
109
|
+
pure
|
110
|
+
else
|
111
|
+
temp
|
112
|
+
end
|
102
113
|
end
|
103
114
|
|
104
115
|
# Truncate the *visible* length to n, but preserve embedded ANSI
|
@@ -108,7 +119,7 @@ class String
|
|
108
119
|
i = 0
|
109
120
|
|
110
121
|
while i < length && count < n
|
111
|
-
if self[i] == "\e" && (m = self[i..-1].match(/\A(
|
122
|
+
if self[i] == "\e" && (m = self[i..-1].match(/\A(#{ANSI_SEQUENCE_REGEX.source})/))
|
112
123
|
out << m[1]
|
113
124
|
i += m[1].length
|
114
125
|
else
|
@@ -131,7 +142,7 @@ class String
|
|
131
142
|
count, out, i, injected = 0, '', 0, false
|
132
143
|
|
133
144
|
while i < length
|
134
|
-
if self[i] == "\e" && (m = self[i..-1].match(/\A(
|
145
|
+
if self[i] == "\e" && (m = self[i..-1].match(/\A(#{ANSI_SEQUENCE_REGEX.source})/))
|
135
146
|
out << m[1]
|
136
147
|
i += m[1].length
|
137
148
|
else
|
@@ -146,7 +157,7 @@ class String
|
|
146
157
|
end
|
147
158
|
|
148
159
|
unless injected
|
149
|
-
if out =~ /(
|
160
|
+
if out =~ /(#{ANSI_SEQUENCE_REGEX.source})\z/
|
150
161
|
trailing = $1
|
151
162
|
out = out[0...-trailing.length] + insertion + trailing
|
152
163
|
else
|
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.
|
4
|
+
version: 4.9.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-07-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: clipboard
|
@@ -29,9 +29,9 @@ 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.
|
33
|
-
|
34
|
-
|
32
|
+
in panes. Cursor movement around the terminal. 4.9.0: Major performance improvements
|
33
|
+
- memory leak fixes, terminal dimension caching, batch updates, better Unicode support,
|
34
|
+
enhanced error handling.'
|
35
35
|
email: g@isene.com
|
36
36
|
executables: []
|
37
37
|
extensions: []
|