@@ -59,7 +59,6 @@ local function create_highlight(rgb_hex, mode)
5959 if mode == " foreground" then
6060 vim .api .nvim_set_hl (0 , highlight_name , { fg = " #" .. rgb_hex })
6161 else
62- -- TODO: 2025-01-11 - Should this check for background or virtualtext
6362 local rr , gg , bb = rgb_hex :sub (1 , 2 ), rgb_hex :sub (3 , 4 ), rgb_hex :sub (5 , 6 )
6463 local r , g , b = tonumber (rr , 16 ), tonumber (gg , 16 ), tonumber (bb , 16 )
6564 local fg_color = color .is_bright (r , g , b ) and " Black" or " White"
@@ -77,6 +76,59 @@ local function slice_line(bufnr, line, start_col, end_col)
7776 return string.sub (lines [1 ], start_col + 1 , end_col )
7877end
7978
79+ --- Add low priority highlights. Trims highlight ranges to avoid collisions.
80+ --- @param bufnr number : Buffer number
81+ --- @param extmarks table : List of low priority extmarks to reapply
82+ --- @param priority_ns_id number : Namespace id for priority highlights
83+ --- @param linenr number : Line number
84+ local function add_low_priority_highlights (bufnr , extmarks , priority_ns_id , linenr )
85+ local priority_marks = vim .api .nvim_buf_get_extmarks (
86+ bufnr ,
87+ priority_ns_id ,
88+ { linenr , 0 },
89+ { linenr + 1 , 0 },
90+ { details = true }
91+ )
92+ for _ , default_mark in ipairs (extmarks ) do
93+ local default_start = default_mark [3 ] -- Start column
94+ local default_end = default_mark [4 ].end_col
95+ local hl_group = default_mark [4 ].hl_group
96+ local non_overlapping_ranges = { { default_start , default_end } }
97+ for _ , lsp_mark in ipairs (priority_marks ) do
98+ local lsp_start = lsp_mark [3 ]
99+ local lsp_end = lsp_mark [4 ].end_col
100+ -- Adjust ranges to avoid collisions
101+ local new_ranges = {}
102+ for _ , range in ipairs (non_overlapping_ranges ) do
103+ local start , end_ = range [1 ], range [2 ]
104+ if lsp_start <= end_ and lsp_end >= start then
105+ -- Collision detected, split range
106+ if start < lsp_start then
107+ table.insert (new_ranges , { start , lsp_start })
108+ end
109+ if lsp_end < end_ then
110+ table.insert (new_ranges , { lsp_end , end_ })
111+ end
112+ else
113+ -- No collision, keep range
114+ table.insert (new_ranges , { start , end_ })
115+ end
116+ end
117+ non_overlapping_ranges = new_ranges
118+ end
119+ for _ , range in ipairs (non_overlapping_ranges ) do
120+ vim .api .nvim_buf_add_highlight (
121+ bufnr ,
122+ default_mark [4 ].ns_id , -- Original namespace
123+ hl_group ,
124+ linenr ,
125+ range [1 ],
126+ range [2 ]
127+ )
128+ end
129+ end
130+ end
131+
80132--- Create highlight and set highlights
81133--- @param bufnr number : Buffer number (0 for current )
82134--- @param ns_id number : Namespace id for which to create highlights
@@ -93,37 +145,41 @@ function M.add_highlight(bufnr, ns_id, line_start, line_end, data, ud_opts, hl_o
93145 hl_opts = hl_opts or {}
94146 vim .api .nvim_buf_clear_namespace (bufnr , ns_id , line_start , line_end )
95147 if ud_opts .mode == " background" or ud_opts .mode == " foreground" then
148+ local tw_both = ud_opts .tailwind == " both" and hl_opts .tailwind_lsp
96149 for linenr , hls in pairs (data ) do
150+ local marks
151+ if tw_both then
152+ marks = vim .api .nvim_buf_get_extmarks (
153+ bufnr ,
154+ const .namespace .default ,
155+ { linenr , 0 },
156+ { linenr + 1 , 0 },
157+ { details = true }
158+ )
159+ -- clear default namespace to apply LSP highlights, then rehighlight non-overlapping default highlights
160+ -- Fixes: https://github.com/catgoose/nvim-colorizer.lua/issues/61
161+ vim .api .nvim_buf_clear_namespace (bufnr , const .namespace .default , linenr , linenr + 1 )
162+ end
97163 for _ , hl in ipairs (hls ) do
98- if ud_opts .tailwind == " both" and hl_opts .tailwind_lsp then
99- vim .api .nvim_buf_clear_namespace (
100- bufnr ,
101- const .namespace .tailwind_names ,
102- linenr ,
103- linenr + 1
104- )
105- if ud_opts .tailwind_opts .update_names then
106- local txt = slice_line (bufnr , linenr , hl .range [1 ], hl .range [2 ])
107- if txt and not hl_state .updated_colors [txt ] then
108- hl_state .updated_colors [txt ] = true
109- names .update_color (txt , hl .rgb_hex )
110- end
164+ if tw_both and ud_opts .tailwind_opts .update_names then
165+ local txt = slice_line (bufnr , linenr , hl .range [1 ], hl .range [2 ])
166+ if txt and not hl_state .updated_colors [txt ] then
167+ hl_state .updated_colors [txt ] = true
168+ names .update_color (txt , hl .rgb_hex )
111169 end
112170 end
113171 local hlname = create_highlight (hl .rgb_hex , ud_opts .mode )
114172 vim .api .nvim_buf_add_highlight (bufnr , ns_id , hlname , linenr , hl .range [1 ], hl .range [2 ])
115173 end
174+ if tw_both then
175+ add_low_priority_highlights (bufnr , marks , ns_id , linenr )
176+ end
116177 end
117178 elseif ud_opts .mode == " virtualtext" then
118179 for linenr , hls in pairs (data ) do
119180 for _ , hl in ipairs (hls ) do
120181 if ud_opts .tailwind == " both" and hl_opts .tailwind_lsp then
121- vim .api .nvim_buf_clear_namespace (
122- bufnr ,
123- const .namespace .tailwind_names ,
124- linenr ,
125- linenr + 1
126- )
182+ vim .api .nvim_buf_clear_namespace (bufnr , ns_id , linenr , linenr + 1 )
127183 if ud_opts .tailwind_opts .update_names then
128184 local txt = slice_line (bufnr , linenr , hl .range [1 ], hl .range [2 ])
129185 if txt and not hl_state .updated_colors [txt ] then
@@ -204,11 +260,6 @@ function M.highlight(bufnr, ns_id, line_start, line_end, ud_opts, buf_local_opts
204260 -- Parse lines from matcher
205261 local data = M .parse_lines (bufnr , lines , line_start , ud_opts ) or {}
206262 M .add_highlight (bufnr , ns_id , line_start , line_end , data , ud_opts )
207- -- Tailwind parsing
208- if ud_opts .tailwind == " normal" or ud_opts .tailwind == " both" then
209- local tw_data = M .parse_lines (bufnr , lines , line_start , ud_opts , { tailwind = true }) or {}
210- M .add_highlight (bufnr , const .namespace .tailwind_names , line_start , line_end , tw_data , ud_opts )
211- end
212263 if ud_opts .tailwind == " lsp" or ud_opts .tailwind == " both" then
213264 tailwind .lsp_highlight (
214265 bufnr ,
230281--- @param lines table : Table of lines to parse
231282--- @param line_start number : Buffer line number to start highlighting
232283--- @param ud_opts table : ` user_default_options `
233- --- @param parse_opts table | nil : Parsing options
234- --- - tailwind boolean|nil: use tailwind_names parser
235284--- @return table | nil
236- function M .parse_lines (bufnr , lines , line_start , ud_opts , parse_opts )
237- parse_opts = parse_opts or {}
285+ function M .parse_lines (bufnr , lines , line_start , ud_opts )
238286 local loop_parse_fn = matcher .make (ud_opts )
239287 if not loop_parse_fn then
240288 return
@@ -247,7 +295,7 @@ function M.parse_lines(bufnr, lines, line_start, ud_opts, parse_opts)
247295 while i < # line do
248296 local length , rgb_hex = loop_parse_fn (line , i , bufnr )
249297 if length and not rgb_hex then
250- vim . api . nvim_err_writeln (
298+ utils . log_message (
251299 string.format (
252300 " Colorizer: Error parsing line %d, index %d. Please report this issue." ,
253301 line_nr ,
0 commit comments