@@ -9,10 +9,45 @@ function ColoredPrinter.new()
99 self .current_fg = nil
1010 self .current_bg = nil
1111 self .current_styles = {}
12+ self .bright_color_bold = false
1213 self :setup_highlight_groups ()
1314 return self
1415end
1516
17+ function ColoredPrinter :get_256_color (index )
18+ -- Standard 16 colors (0-15)
19+ if index <= 15 then
20+ local colors = {
21+ " #000000" , " #800000" , " #008000" , " #808000" , " #000080" , " #800080" , " #008080" , " #c0c0c0" ,
22+ " #808080" , " #ff0000" , " #00ff00" , " #ffff00" , " #0000ff" , " #ff00ff" , " #00ffff" , " #ffffff"
23+ }
24+ return colors [index + 1 ]
25+ end
26+
27+ -- 216 color cube (16-231)
28+ if index <= 231 then
29+ local n = index - 16
30+ local r = math.floor (n / 36 )
31+ local g = math.floor ((n % 36 ) / 6 )
32+ local b = n % 6
33+
34+ local function color_value (c )
35+ if c == 0 then return 0 end
36+ return 55 + c * 40
37+ end
38+
39+ return string.format (" #%02x%02x%02x" , color_value (r ), color_value (g ), color_value (b ))
40+ end
41+
42+ -- Grayscale (232-255)
43+ if index <= 255 then
44+ local gray = 8 + (index - 232 ) * 10
45+ return string.format (" #%02x%02x%02x" , gray , gray , gray )
46+ end
47+
48+ return " #ffffff" -- fallback
49+ end
50+
1651function ColoredPrinter :setup_highlight_groups ()
1752 local basic_colors = {
1853 [" 30" ] = " Black" ,
@@ -40,11 +75,45 @@ function ColoredPrinter:setup_highlight_groups()
4075 self .color_groups [code ] = group_name
4176 end
4277
78+ -- Add background color groups (40-47, 100-107)
79+ local bg_colors = {
80+ [" 40" ] = " Black" ,
81+ [" 41" ] = " Red" ,
82+ [" 42" ] = " Green" ,
83+ [" 43" ] = " Yellow" ,
84+ [" 44" ] = " Blue" ,
85+ [" 45" ] = " Magenta" ,
86+ [" 46" ] = " Cyan" ,
87+ [" 47" ] = " White" ,
88+ [" 100" ] = " Grey" ,
89+ [" 101" ] = " Red" ,
90+ [" 102" ] = " Green" ,
91+ [" 103" ] = " Yellow" ,
92+ [" 104" ] = " Blue" ,
93+ [" 105" ] = " Magenta" ,
94+ [" 106" ] = " Cyan" ,
95+ [" 107" ] = " White" ,
96+ }
97+
98+ for code , color in pairs (bg_colors ) do
99+ local group_name = " QuicktestAnsiBgColor_" .. code
100+ vim .cmd (string.format (" highlight %s ctermbg=%s guibg=%s" , group_name , color :lower (), color ))
101+
102+ self .color_groups [code ] = group_name
103+ end
104+
105+ -- Use Normal highlight group directly to ensure proper default colors
106+ vim .cmd (" highlight default QuicktestAnsiColorDefault guifg=NONE guibg=NONE" )
43107 vim .cmd (" highlight default link QuicktestAnsiColorDefault Normal" )
44108 self .color_groups [" default" ] = " QuicktestAnsiColorDefault"
45109end
46110
47111function ColoredPrinter :get_or_create_color_group (fg , bg , styles )
112+ -- If no colors or styles are set, use the default group
113+ if not fg and not bg and (# styles == 0 ) then
114+ return self .color_groups [" default" ]
115+ end
116+
48117 local function sanitize (str )
49118 if str then
50119 -- Replace # with "hex" and any non-alphanumeric characters with their hex code
@@ -98,7 +167,7 @@ function ColoredPrinter:get_or_create_color_group(fg, bg, styles)
98167 if bg :match (" ^#" ) then
99168 cmd = cmd .. string.format (" guibg=%s" , bg )
100169 elseif self .color_groups [bg ] then
101- local bg_color = vim .fn .synIDattr (vim .fn .synIDtrans (vim .fn .hlID (self .color_groups [bg ])), " fg #" )
170+ local bg_color = vim .fn .synIDattr (vim .fn .synIDtrans (vim .fn .hlID (self .color_groups [bg ])), " bg #" )
102171 if bg_color and bg_color ~= " " then
103172 cmd = cmd .. " guibg=" .. bg_color
104173 end
@@ -110,7 +179,11 @@ function ColoredPrinter:get_or_create_color_group(fg, bg, styles)
110179 end
111180
112181 if start_cmd ~= cmd then
113- vim .cmd (cmd )
182+ local success , err = pcall (vim .cmd , cmd )
183+ if not success then
184+ -- Fallback to basic highlight if command fails
185+ vim .cmd (" highlight " .. group_name )
186+ end
114187 end
115188
116189 self .color_groups [color_key ] = group_name
@@ -142,28 +215,76 @@ function ColoredPrinter:parse_colors(line)
142215 local index = 1
143216 while index <= # codes do
144217 local code = tonumber (codes [index ])
218+ -- Skip empty or invalid codes
219+ if not code then
220+ index = index + 1
221+ goto continue
222+ end
145223 if code == 0 then
146224 self .current_fg , self .current_bg = nil , nil
147225 self .current_styles = {}
226+ self .bright_color_bold = false
148227 elseif code == 1 then
149228 table.insert (self .current_styles , " bold" )
229+ -- This is explicit bold, not from bright colors
230+ self .bright_color_bold = false
231+ elseif code == 2 then
232+ -- Dim/faint - implement by setting a gray foreground color
233+ self .current_fg = " #808080"
150234 elseif code == 3 then
151235 table.insert (self .current_styles , " italic" )
152236 elseif code == 4 then
153237 table.insert (self .current_styles , " underline" )
238+ elseif code == 5 or code == 6 then
239+ -- Blink - not supported in most terminals/Neovim, skip
240+ elseif code == 7 then
241+ table.insert (self .current_styles , " reverse" )
242+ elseif code == 8 then
243+ -- Conceal - not directly supported as gui attribute, skip
154244 elseif code == 9 then
155245 table.insert (self .current_styles , " strikethrough" )
246+ elseif code == 21 then
247+ table.insert (self .current_styles , " undercurl" )
248+ elseif code == 22 then
249+ self .current_styles = vim .tbl_filter (function (s ) return s ~= " bold" end , self .current_styles )
250+ -- Reset dim color if it was set
251+ if self .current_fg == " #808080" then
252+ self .current_fg = nil
253+ end
254+ elseif code == 23 then
255+ self .current_styles = vim .tbl_filter (function (s ) return s ~= " italic" end , self .current_styles )
256+ elseif code == 24 then
257+ self .current_styles = vim .tbl_filter (function (s ) return s ~= " underline" and s ~= " undercurl" end , self .current_styles )
258+ elseif code == 25 then
259+ -- Reset blink (not supported anyway)
260+ elseif code == 27 then
261+ self .current_styles = vim .tbl_filter (function (s ) return s ~= " reverse" end , self .current_styles )
262+ elseif code == 28 then
263+ -- Reset conceal (not supported anyway)
264+ elseif code == 29 then
265+ self .current_styles = vim .tbl_filter (function (s ) return s ~= " strikethrough" end , self .current_styles )
266+ elseif code == 39 then
267+ self .current_fg = nil -- Reset foreground to default
268+ -- If bold was auto-added by bright color, remove it
269+ if self .bright_color_bold then
270+ self .current_styles = vim .tbl_filter (function (s ) return s ~= " bold" end , self .current_styles )
271+ self .bright_color_bold = false
272+ end
273+ elseif code == 49 then
274+ self .current_bg = nil -- Reset background to default
156275 elseif code >= 30 and code <= 37 then
157276 self .current_fg = tostring (code )
158277 elseif code >= 40 and code <= 47 then
159- self .current_bg = tostring (code - 10 )
278+ self .current_bg = tostring (code )
160279 elseif code >= 90 and code <= 97 then
161280 self .current_fg = tostring (code )
162281 if not vim .tbl_contains (self .current_styles , " bold" ) then
163282 table.insert (self .current_styles , " bold" )
164283 end
284+ -- Mark that this bold was auto-added by bright color
285+ self .bright_color_bold = true
165286 elseif code >= 100 and code <= 107 then
166- self .current_bg = tostring (code - 10 )
287+ self .current_bg = tostring (code )
167288 elseif code == 38 or code == 48 then
168289 if codes [index + 1 ] == " 2" then
169290 if # codes >= index + 4 then
@@ -179,9 +300,24 @@ function ColoredPrinter:parse_colors(line)
179300 index = index + 4
180301 end
181302 end
303+ elseif codes [index + 1 ] == " 5" then
304+ if # codes >= index + 2 then
305+ local color_index = tonumber (codes [index + 2 ])
306+ if color_index and color_index >= 0 and color_index <= 255 then
307+ local color_hex = self :get_256_color (color_index )
308+ if code == 38 then
309+ self .current_fg = color_hex
310+ else
311+ self .current_bg = color_hex
312+ end
313+ index = index + 2
314+ end
315+ end
182316 end
183317 end
184318 index = index + 1
319+
320+ :: continue::
185321 end
186322
187323 i = j + 1
0 commit comments