Skip to content

Commit

Permalink
Fix/132 multiple virtualtext (#133)
Browse files Browse the repository at this point in the history
* fix: clear all namespaces when detaching from buffer

* chore: removes unused tailwind_names namespace

* fix: when tailwind = "both", reapply default highlights while avoiding collisions

* chore: creates message logger to use vim.api.nvim_echo for nvim >= .11
  • Loading branch information
catgoose authored Jan 14, 2025
1 parent fb8296c commit 86c9a6a
Show file tree
Hide file tree
Showing 10 changed files with 103 additions and 47 deletions.
10 changes: 6 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ SCRIPTS_DIR=scripts
TRIE_TEST_SCRIPT=$(SCRIPTS_DIR)/trie-test.sh
TRIE_BENCHMARK_SCRIPT=$(SCRIPTS_DIR)/trie-benchmark.sh
MINIMAL_SCRIPT=$(SCRIPTS_DIR)/minimal-colorizer.sh
MINIMAL_COLORIZER=colorizer_minimal
MINIMAL_TRIE=colorizer_trie

help:
@echo "Available targets:"
Expand All @@ -27,9 +29,9 @@ minimal:
@bash $(MINIMAL_SCRIPT)

clean:
@echo "Removing test/colorizer_repro"
@rm -rf test/colorizer_repro
@echo "Removing test/trie/colorizer_trie"
@rm -rf test/trie/colorizer_trie
@echo "Removing test/"$(MINIMAL_COLORIZER)
@rm -rf test/$(MINIMAL_COLORIZER)
@echo "Removing test/trie/"$(MINIMAL_TRIE)
@rm -rf test/trie/$(MINIMAL_TRIE)

.PHONY: help trie trie-test trie-benchmark minimal clean
3 changes: 3 additions & 0 deletions lua/colorizer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,9 @@ function M.detach_from_buffer(bufnr)
if bufnr < 0 then
return -1
end
for _, ns_id in pairs(const.namespace) do
vim.api.nvim_buf_clear_namespace(bufnr, ns_id, 0, -1)
end
vim.api.nvim_buf_clear_namespace(bufnr, const.namespace.default, 0, -1)
if colorizer_state.buffer_local[bufnr] then
for _, namespace in pairs(colorizer_state.buffer_local[bufnr].__detach.ns_id) do
Expand Down
108 changes: 78 additions & 30 deletions lua/colorizer/buffer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ local function create_highlight(rgb_hex, mode)
if mode == "foreground" then
vim.api.nvim_set_hl(0, highlight_name, { fg = "#" .. rgb_hex })
else
-- TODO: 2025-01-11 - Should this check for background or virtualtext
local rr, gg, bb = rgb_hex:sub(1, 2), rgb_hex:sub(3, 4), rgb_hex:sub(5, 6)
local r, g, b = tonumber(rr, 16), tonumber(gg, 16), tonumber(bb, 16)
local fg_color = color.is_bright(r, g, b) and "Black" or "White"
Expand All @@ -77,6 +76,59 @@ local function slice_line(bufnr, line, start_col, end_col)
return string.sub(lines[1], start_col + 1, end_col)
end

--- Add low priority highlights. Trims highlight ranges to avoid collisions.
---@param bufnr number: Buffer number
---@param extmarks table: List of low priority extmarks to reapply
---@param priority_ns_id number: Namespace id for priority highlights
---@param linenr number: Line number
local function add_low_priority_highlights(bufnr, extmarks, priority_ns_id, linenr)
local priority_marks = vim.api.nvim_buf_get_extmarks(
bufnr,
priority_ns_id,
{ linenr, 0 },
{ linenr + 1, 0 },
{ details = true }
)
for _, default_mark in ipairs(extmarks) do
local default_start = default_mark[3] -- Start column
local default_end = default_mark[4].end_col
local hl_group = default_mark[4].hl_group
local non_overlapping_ranges = { { default_start, default_end } }
for _, lsp_mark in ipairs(priority_marks) do
local lsp_start = lsp_mark[3]
local lsp_end = lsp_mark[4].end_col
-- Adjust ranges to avoid collisions
local new_ranges = {}
for _, range in ipairs(non_overlapping_ranges) do
local start, end_ = range[1], range[2]
if lsp_start <= end_ and lsp_end >= start then
-- Collision detected, split range
if start < lsp_start then
table.insert(new_ranges, { start, lsp_start })
end
if lsp_end < end_ then
table.insert(new_ranges, { lsp_end, end_ })
end
else
-- No collision, keep range
table.insert(new_ranges, { start, end_ })
end
end
non_overlapping_ranges = new_ranges
end
for _, range in ipairs(non_overlapping_ranges) do
vim.api.nvim_buf_add_highlight(
bufnr,
default_mark[4].ns_id, -- Original namespace
hl_group,
linenr,
range[1],
range[2]
)
end
end
end

--- Create highlight and set highlights
---@param bufnr number: Buffer number (0 for current)
---@param ns_id number: Namespace id for which to create highlights
Expand All @@ -93,37 +145,41 @@ function M.add_highlight(bufnr, ns_id, line_start, line_end, data, ud_opts, hl_o
hl_opts = hl_opts or {}
vim.api.nvim_buf_clear_namespace(bufnr, ns_id, line_start, line_end)
if ud_opts.mode == "background" or ud_opts.mode == "foreground" then
local tw_both = ud_opts.tailwind == "both" and hl_opts.tailwind_lsp
for linenr, hls in pairs(data) do
local marks
if tw_both then
marks = vim.api.nvim_buf_get_extmarks(
bufnr,
const.namespace.default,
{ linenr, 0 },
{ linenr + 1, 0 },
{ details = true }
)
-- clear default namespace to apply LSP highlights, then rehighlight non-overlapping default highlights
-- Fixes: https://github.com/catgoose/nvim-colorizer.lua/issues/61
vim.api.nvim_buf_clear_namespace(bufnr, const.namespace.default, linenr, linenr + 1)
end
for _, hl in ipairs(hls) do
if ud_opts.tailwind == "both" and hl_opts.tailwind_lsp then
vim.api.nvim_buf_clear_namespace(
bufnr,
const.namespace.tailwind_names,
linenr,
linenr + 1
)
if ud_opts.tailwind_opts.update_names then
local txt = slice_line(bufnr, linenr, hl.range[1], hl.range[2])
if txt and not hl_state.updated_colors[txt] then
hl_state.updated_colors[txt] = true
names.update_color(txt, hl.rgb_hex)
end
if tw_both and ud_opts.tailwind_opts.update_names then
local txt = slice_line(bufnr, linenr, hl.range[1], hl.range[2])
if txt and not hl_state.updated_colors[txt] then
hl_state.updated_colors[txt] = true
names.update_color(txt, hl.rgb_hex)
end
end
local hlname = create_highlight(hl.rgb_hex, ud_opts.mode)
vim.api.nvim_buf_add_highlight(bufnr, ns_id, hlname, linenr, hl.range[1], hl.range[2])
end
if tw_both then
add_low_priority_highlights(bufnr, marks, ns_id, linenr)
end
end
elseif ud_opts.mode == "virtualtext" then
for linenr, hls in pairs(data) do
for _, hl in ipairs(hls) do
if ud_opts.tailwind == "both" and hl_opts.tailwind_lsp then
vim.api.nvim_buf_clear_namespace(
bufnr,
const.namespace.tailwind_names,
linenr,
linenr + 1
)
vim.api.nvim_buf_clear_namespace(bufnr, ns_id, linenr, linenr + 1)
if ud_opts.tailwind_opts.update_names then
local txt = slice_line(bufnr, linenr, hl.range[1], hl.range[2])
if txt and not hl_state.updated_colors[txt] then
Expand Down Expand Up @@ -204,11 +260,6 @@ function M.highlight(bufnr, ns_id, line_start, line_end, ud_opts, buf_local_opts
-- Parse lines from matcher
local data = M.parse_lines(bufnr, lines, line_start, ud_opts) or {}
M.add_highlight(bufnr, ns_id, line_start, line_end, data, ud_opts)
-- Tailwind parsing
if ud_opts.tailwind == "normal" or ud_opts.tailwind == "both" then
local tw_data = M.parse_lines(bufnr, lines, line_start, ud_opts, { tailwind = true }) or {}
M.add_highlight(bufnr, const.namespace.tailwind_names, line_start, line_end, tw_data, ud_opts)
end
if ud_opts.tailwind == "lsp" or ud_opts.tailwind == "both" then
tailwind.lsp_highlight(
bufnr,
Expand All @@ -230,11 +281,8 @@ end
---@param lines table: Table of lines to parse
---@param line_start number: Buffer line number to start highlighting
---@param ud_opts table: `user_default_options`
---@param parse_opts table|nil: Parsing options
--- - tailwind boolean|nil: use tailwind_names parser
---@return table|nil
function M.parse_lines(bufnr, lines, line_start, ud_opts, parse_opts)
parse_opts = parse_opts or {}
function M.parse_lines(bufnr, lines, line_start, ud_opts)
local loop_parse_fn = matcher.make(ud_opts)
if not loop_parse_fn then
return
Expand All @@ -247,7 +295,7 @@ function M.parse_lines(bufnr, lines, line_start, ud_opts, parse_opts)
while i < #line do
local length, rgb_hex = loop_parse_fn(line, i, bufnr)
if length and not rgb_hex then
vim.api.nvim_err_writeln(
utils.log_message(
string.format(
"Colorizer: Error parsing line %d, index %d. Please report this issue.",
line_nr,
Expand Down
1 change: 0 additions & 1 deletion lua/colorizer/constants.lua
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ M.plugin = {
-- - tailwind - Namespace used for creating extmarks to prevent tailwind name parsing from overwriting tailwind lsp highlights
M.namespace = {
default = vim.api.nvim_create_namespace(M.plugin.name),
tailwind_names = vim.api.nvim_create_namespace(M.plugin.name .. "_tailwind_names"),
tailwind_lsp = vim.api.nvim_create_namespace(M.plugin.name .. "_tailwind_lsp"),
}

Expand Down
2 changes: 0 additions & 2 deletions lua/colorizer/matcher.lua
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@ parsers.prefix = {
["_hsla"] = parsers.hsl_function,
}

-- TODO: 2024-12-31 - Return multiple parse_fn for tailwind parser?

---Form a trie stuct with the given prefixes
---@param matchers table: List of prefixes, {"rgb", "hsl"}
---@param matchers_trie table: Table containing information regarding non-trie based parsers
Expand Down
10 changes: 4 additions & 6 deletions lua/colorizer/parser/names.lua
Original file line number Diff line number Diff line change
Expand Up @@ -77,9 +77,7 @@ local function handle_names_custom(names_custom)
if status and type(result) == "table" then
names = result
else
vim.api.nvim_err_writeln(
"Error in names_custom function: " .. (result or "Invalid return value")
)
utils.log_message("Error in names_custom function: " .. (result or "Invalid return value"))
return
end
end
Expand All @@ -92,11 +90,11 @@ local function handle_names_custom(names_custom)
if normalized_hex:match("^%x%x%x%x%x%x$") then
add_color(name, normalized_hex)
else
vim.api.nvim_err_writeln("Invalid hex code for '" .. name .. "': " .. normalized_hex)
utils.log_message(string.format("Invalid hex code for '%s': %s", name, normalized_hex))
end
else
vim.api.nvim_err_writeln(
"Invalid value for '" .. name .. "': Expected string, got " .. type(hex)
utils.log_message(
string.format("Invalid value for '%s': Expected string, got %s", name, type(hex))
)
end
end
Expand Down
2 changes: 1 addition & 1 deletion lua/colorizer/tailwind.lua
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ local function highlight(bufnr, ud_opts, add_highlight)
lsp_cache[bufnr].document_params,
function(err, results, _, _)
if err ~= nil then
vim.api.nvim_err_writeln("tailwind.highlight: Error: " .. err)
utils.log_message("tailwind.highlight: Error: " .. err)
end
if err == nil and results ~= nil then
local data, line_start, line_end = {}, nil, nil
Expand Down
8 changes: 8 additions & 0 deletions lua/colorizer/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -224,4 +224,12 @@ function M.visible_line_range(bufnr)
return range[1] - 1, range[2]
end

function M.log_message(message)
if vim.version().minor >= 11 then
vim.api.nvim_echo({ { message, "ErrorMsg" } }, true, {})
else
vim.api.nvim_err_writeln(message)
end
end

return M
2 changes: 1 addition & 1 deletion test/.gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
colorizer_repro/
colorizer_minimal/
colorizer_trie/
4 changes: 2 additions & 2 deletions test/minimal-colorizer.lua
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
-- Run this file as `nvim --clean -u minimal-colorizer.lua`

local settings = {
use_remote = false, -- Use colorizer master or local git directory
base_dir = "colorizer_repro", -- Directory to clone lazy.nvim
use_remote = true, -- Use colorizer master or local git directory
base_dir = "colorizer_minimal", -- Directory to clone lazy.nvim
local_plugin_dir = os.getenv("HOME") .. "/git/nvim-colorizer.lua", -- Local git directory for colorizer. Used if use_remote is false
expect = "expect.lua",
plugins = { -- add any additional plugins
Expand Down

0 comments on commit 86c9a6a

Please sign in to comment.