@@ -59,7 +59,6 @@ local function create_highlight(rgb_hex, mode)
59
59
if mode == " foreground" then
60
60
vim .api .nvim_set_hl (0 , highlight_name , { fg = " #" .. rgb_hex })
61
61
else
62
- -- TODO: 2025-01-11 - Should this check for background or virtualtext
63
62
local rr , gg , bb = rgb_hex :sub (1 , 2 ), rgb_hex :sub (3 , 4 ), rgb_hex :sub (5 , 6 )
64
63
local r , g , b = tonumber (rr , 16 ), tonumber (gg , 16 ), tonumber (bb , 16 )
65
64
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)
77
76
return string.sub (lines [1 ], start_col + 1 , end_col )
78
77
end
79
78
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
+
80
132
--- Create highlight and set highlights
81
133
--- @param bufnr number : Buffer number (0 for current )
82
134
--- @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
93
145
hl_opts = hl_opts or {}
94
146
vim .api .nvim_buf_clear_namespace (bufnr , ns_id , line_start , line_end )
95
147
if ud_opts .mode == " background" or ud_opts .mode == " foreground" then
148
+ local tw_both = ud_opts .tailwind == " both" and hl_opts .tailwind_lsp
96
149
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
97
163
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 )
111
169
end
112
170
end
113
171
local hlname = create_highlight (hl .rgb_hex , ud_opts .mode )
114
172
vim .api .nvim_buf_add_highlight (bufnr , ns_id , hlname , linenr , hl .range [1 ], hl .range [2 ])
115
173
end
174
+ if tw_both then
175
+ add_low_priority_highlights (bufnr , marks , ns_id , linenr )
176
+ end
116
177
end
117
178
elseif ud_opts .mode == " virtualtext" then
118
179
for linenr , hls in pairs (data ) do
119
180
for _ , hl in ipairs (hls ) do
120
181
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 )
127
183
if ud_opts .tailwind_opts .update_names then
128
184
local txt = slice_line (bufnr , linenr , hl .range [1 ], hl .range [2 ])
129
185
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
204
260
-- Parse lines from matcher
205
261
local data = M .parse_lines (bufnr , lines , line_start , ud_opts ) or {}
206
262
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
212
263
if ud_opts .tailwind == " lsp" or ud_opts .tailwind == " both" then
213
264
tailwind .lsp_highlight (
214
265
bufnr ,
230
281
--- @param lines table : Table of lines to parse
231
282
--- @param line_start number : Buffer line number to start highlighting
232
283
--- @param ud_opts table : ` user_default_options `
233
- --- @param parse_opts table | nil : Parsing options
234
- --- - tailwind boolean|nil: use tailwind_names parser
235
284
--- @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 )
238
286
local loop_parse_fn = matcher .make (ud_opts )
239
287
if not loop_parse_fn then
240
288
return
@@ -247,7 +295,7 @@ function M.parse_lines(bufnr, lines, line_start, ud_opts, parse_opts)
247
295
while i < # line do
248
296
local length , rgb_hex = loop_parse_fn (line , i , bufnr )
249
297
if length and not rgb_hex then
250
- vim . api . nvim_err_writeln (
298
+ utils . log_message (
251
299
string.format (
252
300
" Colorizer: Error parsing line %d, index %d. Please report this issue." ,
253
301
line_nr ,
0 commit comments