Skip to content

Commit 1f66761

Browse files
authored
Fix color of queries ending before the query ends (silva96#32)
1 parent 06f2acd commit 1f66761

1 file changed

Lines changed: 74 additions & 53 deletions

File tree

‎lib/log_bench/app/renderer/ansi.rb‎

Lines changed: 74 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -39,73 +39,60 @@ def wrap_ansi_text(text, max_width)
3939
[text]
4040
else
4141
chunks = []
42-
remaining = text
43-
active_colors = []
4442

45-
# Extract initial color state
46-
text.scan(/\e\[[0-9;]*m/) do |ansi_code|
47-
if /\e\[0m/.match?(ansi_code)
48-
active_colors.clear
49-
else
50-
active_colors << ansi_code
51-
end
52-
end
43+
# Parse the text to extract segments with their colors
44+
segments = parse_ansi_segments(text)
5345

54-
while remaining.length > 0
55-
clean_remaining = remaining.gsub(/\e\[[0-9;]*m/, "")
46+
current_chunk = ""
47+
current_chunk_length = 0
48+
active_color_state = ""
5649

57-
if clean_remaining.length <= max_width
58-
# Last chunk
59-
chunks << if active_colors.any? && !remaining.start_with?(*active_colors)
60-
active_colors.join("") + remaining
50+
segments.each do |segment|
51+
if segment[:type] == :ansi
52+
# Track color state
53+
active_color_state = if segment[:text] == "\e[0m"
54+
""
6155
else
62-
remaining
56+
segment[:text]
6357
end
64-
break
58+
current_chunk += segment[:text]
6559
else
66-
# Find break point and preserve color state
67-
break_point = max_width
68-
original_pos = 0
69-
clean_pos = 0
70-
chunk_colors = active_colors.dup
71-
72-
remaining.each_char.with_index do |char, idx|
73-
if /^\e\[[0-9;]*m/.match?(remaining[idx..])
74-
# Found ANSI sequence
75-
ansi_match = remaining[idx..].match(/^(\e\[[0-9;]*m)/)
76-
ansi_code = ansi_match[1]
77-
78-
if /\e\[0m/.match?(ansi_code)
79-
chunk_colors.clear
80-
active_colors.clear
81-
else
82-
chunk_colors << ansi_code unless chunk_colors.include?(ansi_code)
83-
active_colors << ansi_code unless active_colors.include?(ansi_code)
84-
end
60+
# Text segment - check if it fits
61+
text_content = segment[:text]
8562

86-
original_pos += ansi_code.length
87-
idx + ansi_code.length - 1
88-
else
89-
clean_pos += 1
90-
original_pos += 1
63+
while text_content.length > 0
64+
remaining_space = max_width - current_chunk_length
9165

92-
if clean_pos >= break_point
93-
break
66+
if text_content.length <= remaining_space
67+
# Entire text fits in current chunk
68+
current_chunk += text_content
69+
current_chunk_length += text_content.length
70+
break
71+
else
72+
# Need to split the text
73+
if remaining_space > 0
74+
# Take what fits in current chunk
75+
chunk_part = text_content[0...remaining_space]
76+
current_chunk += chunk_part
77+
text_content = text_content[remaining_space..]
9478
end
95-
end
96-
end
9779

98-
chunk_text = remaining[0...original_pos]
99-
chunks << if active_colors.any? && !chunk_text.start_with?(*active_colors)
100-
active_colors.join("") + chunk_text
101-
else
102-
chunk_text
103-
end
80+
# Finish current chunk
81+
chunks << current_chunk
10482

105-
remaining = remaining[original_pos..]
83+
# Start new chunk with color state
84+
current_chunk = active_color_state
85+
current_chunk_length = 0
86+
end
87+
end
10688
end
10789
end
10890

91+
# Add final chunk if it has content
92+
if current_chunk.length > 0
93+
chunks << current_chunk
94+
end
95+
10996
chunks
11097
end
11198
end
@@ -143,6 +130,40 @@ def wrap_plain_text(text, max_width)
143130

144131
attr_accessor :screen
145132

133+
def parse_ansi_segments(text)
134+
segments = []
135+
remaining = text
136+
137+
while remaining.length > 0
138+
# Look for next ANSI sequence
139+
ansi_match = remaining.match(/^(\e\[[0-9;]*m)/)
140+
141+
if ansi_match
142+
# Found ANSI sequence at start
143+
segments << {type: :ansi, text: ansi_match[1]}
144+
remaining = remaining[ansi_match[1].length..]
145+
else
146+
# Look for ANSI sequence anywhere in remaining text
147+
next_ansi = remaining.match(/(\e\[[0-9;]*m)/)
148+
149+
if next_ansi
150+
# Text before ANSI sequence
151+
text_before = remaining[0...next_ansi.begin(1)]
152+
if text_before.length > 0
153+
segments << {type: :text, text: text_before}
154+
end
155+
remaining = remaining[next_ansi.begin(1)..]
156+
else
157+
# No more ANSI sequences, rest is text
158+
segments << {type: :text, text: remaining}
159+
break
160+
end
161+
end
162+
end
163+
164+
segments
165+
end
166+
146167
def ansi_to_curses_color(codes)
147168
# Convert ANSI color codes to curses color pairs
148169
return nil if codes.empty? || codes == [0]

0 commit comments

Comments
 (0)