aboutsummaryrefslogtreecommitdiff
path: root/lua/colorizer
diff options
context:
space:
mode:
Diffstat (limited to 'lua/colorizer')
-rw-r--r--lua/colorizer/buffer.lua (renamed from lua/colorizer/buffer_utils.lua)146
-rw-r--r--lua/colorizer/color.lua (renamed from lua/colorizer/color_utils.lua)352
-rw-r--r--lua/colorizer/matcher.lua (renamed from lua/colorizer/matcher_utils.lua)44
-rw-r--r--lua/colorizer/sass.lua27
-rw-r--r--lua/colorizer/tailwind.lua53
-rw-r--r--lua/colorizer/utils.lua107
6 files changed, 364 insertions, 365 deletions
diff --git a/lua/colorizer/buffer_utils.lua b/lua/colorizer/buffer.lua
index 1f389c2..3b31daf 100644
--- a/lua/colorizer/buffer_utils.lua
+++ b/lua/colorizer/buffer.lua
@@ -1,5 +1,5 @@
---Helper functions to highlight buffer smartly
---@module colorizer.buffer_utils
+--@module colorizer.buffer
local api = vim.api
local buf_set_virtual_text = api.nvim_buf_set_extmark
local buf_get_lines = api.nvim_buf_get_lines
@@ -7,76 +7,51 @@ local create_namespace = api.nvim_create_namespace
local clear_namespace = api.nvim_buf_clear_namespace
local set_highlight = api.nvim_set_hl
-local color_utils = require "colorizer.color_utils"
-local color_is_bright = color_utils.color_is_bright
+local color = require "colorizer.color"
+local color_is_bright = color.is_bright
+
+local make_matcher = require("colorizer.matcher").make
local sass = require "colorizer.sass"
-local sass_update_variables = sass.sass_update_variables
-local sass_cleanup = sass.sass_cleanup
+local sass_update_variables = sass.update_variables
+local sass_cleanup = sass.cleanup
local tailwind = require "colorizer.tailwind"
-local tailwind_setup_lsp = tailwind.tailwind_setup_lsp_colors
-local tailwind_cleanup = tailwind.tailwind_cleanup
+local tailwind_setup_lsp = tailwind.setup_lsp_colors
+local tailwind_cleanup = tailwind.cleanup
-local make_matcher = require("colorizer.matcher_utils").make_matcher
+local buffer = {}
-local highlight_buffer, rehighlight_buffer
-local BUFFER_LINES = {}
---- Default namespace used in `highlight_buffer` and `colorizer.attach_to_buffer`.
--- @see highlight_buffer
--- @see colorizer.attach_to_buffer
-local DEFAULT_NAMESPACE = create_namespace "colorizer"
local HIGHLIGHT_NAME_PREFIX = "colorizer"
+local HIGHLIGHT_CACHE = {}
+
+--- Default namespace used in `highlight` and `colorizer.attach_to_buffer`.
+-- @see highlight
+-- @see colorizer.attach_to_buffer
+buffer.default_namespace = create_namespace "colorizer"
+
--- Highlight mode which will be use to render the colour
-local HIGHLIGHT_MODE_NAMES = {
+buffer.highlight_mode_names = {
background = "mb",
foreground = "mf",
virtualtext = "mv",
}
-local HIGHLIGHT_CACHE = {}
-
-local function parse_lines(buf, lines, line_start, options)
- local loop_parse_fn = make_matcher(options)
- if not loop_parse_fn then
- return false
- end
-
- local data = {}
- for current_linenum, line in ipairs(lines) do
- current_linenum = current_linenum - 1 + line_start
- data[current_linenum] = data[current_linenum] or {}
-
- -- Upvalues are options and current_linenum
- local i = 1
- while i < #line do
- local length, rgb_hex = loop_parse_fn(line, i, buf)
- if length and rgb_hex then
- table.insert(data[current_linenum], { rgb_hex = rgb_hex, range = { i - 1, i + length - 1 } })
- i = i + length
- else
- i = i + 1
- end
- end
- end
-
- return data
-end
--- Clean the highlight cache
-local function clear_hl_cache()
+function buffer.clear_hl_cache()
HIGHLIGHT_CACHE = {}
end
--- Make a deterministic name for a highlight given these attributes
local function make_highlight_name(rgb, mode)
- return table.concat({ HIGHLIGHT_NAME_PREFIX, HIGHLIGHT_MODE_NAMES[mode], rgb }, "_")
+ return table.concat({ HIGHLIGHT_NAME_PREFIX, buffer.highlight_mode_names[mode], rgb }, "_")
end
local function create_highlight(rgb_hex, mode)
mode = mode or "background"
-- TODO validate rgb format?
rgb_hex = rgb_hex:lower()
- local cache_key = table.concat({ HIGHLIGHT_MODE_NAMES[mode], rgb_hex }, "_")
+ local cache_key = table.concat({ buffer.highlight_mode_names[mode], rgb_hex }, "_")
local highlight_name = HIGHLIGHT_CACHE[cache_key]
-- Look up in our cache.
@@ -112,7 +87,14 @@ local function create_highlight(rgb_hex, mode)
return highlight_name
end
-local function add_highlight(options, buf, ns, data, line_start, line_end)
+--- Create highlight and set highlights
+---@param buf number
+---@param ns number
+---@param line_start number
+---@param line_end number
+---@param data table: table output of `parse_lines`
+---@param options table: Passed in setup, mainly for `user_default_options`
+function buffer.add_highlight(buf, ns, line_start, line_end, data, options)
clear_namespace(buf, ns, line_start, line_end)
local mode = options.mode == "background" and "background" or "foreground"
@@ -140,38 +122,75 @@ end
-- Highlight starting from `line_start` (0-indexed) for each line described by `lines` in the
-- buffer `buf` and attach it to the namespace `ns`.
---@param buf number: buffer id
----@param ns number: The namespace id. Default is DEFAULT_NAMESPACE. Create it with `vim.api.create_namespace`
----@param lines table: the lines to highlight from the buffer.
+---@param ns number: The namespace id. Default is DEFAULT_NAMESPACE. Create it with `vim.api.nvim_create_namespace`
---@param line_start number: line_start should be 0-indexed
---@param line_end number: Last line to highlight
---@param options table: Configuration options as described in `setup`
---@param options_local table: Buffer local variables
---@return nil|boolean|number,table
-function highlight_buffer(buf, ns, lines, line_start, line_end, options, options_local)
+function buffer.highlight(buf, ns, line_start, line_end, options, options_local)
local returns = { detach = { ns = {}, functions = {} } }
if buf == 0 or buf == nil then
buf = api.nvim_get_current_buf()
end
- ns = ns or DEFAULT_NAMESPACE
+ local lines = buf_get_lines(buf, line_start, line_end, false)
- -- only update sass varibled when text is changed
+ ns = ns or buffer.default_namespace
+
+ -- only update sass varibles when text is changed
if options_local.__event ~= "WinScrolled" and options.sass and options.sass.enable then
table.insert(returns.detach.functions, sass_cleanup)
sass_update_variables(buf, 0, -1, nil, make_matcher(options.sass.parsers or { css = true }), options, options_local)
end
- local data = parse_lines(buf, lines, line_start, options)
- add_highlight(options, buf, ns, data, line_start, line_end)
+ local data = buffer.parse_lines(buf, lines, line_start, options) or {}
+ buffer.add_highlight(buf, ns, line_start, line_end, data, options)
if options.tailwind == "lsp" or options.tailwind == "both" then
- tailwind_setup_lsp(buf, options, options_local, add_highlight)
+ tailwind_setup_lsp(buf, options, options_local, buffer.add_highlight)
table.insert(returns.detach.functions, tailwind_cleanup)
end
return true, returns
end
+--- Parse the given lines for colors and return a table containing
+-- rgb_hex and range per line
+---@param buf number
+---@param lines table
+---@param line_start number: This is the buffer line number, from where to start highlighting
+---@param options table: Passed in `colorizer.setup`, Only uses `user_default_options`
+---@return table|nil
+function buffer.parse_lines(buf, lines, line_start, options)
+ local loop_parse_fn = make_matcher(options)
+ if not loop_parse_fn then
+ return
+ end
+
+ local data = {}
+ for current_linenum, line in ipairs(lines) do
+ current_linenum = current_linenum - 1 + line_start
+ data[current_linenum] = data[current_linenum] or {}
+
+ -- Upvalues are options and current_linenum
+ local i = 1
+ while i < #line do
+ local length, rgb_hex = loop_parse_fn(line, i, buf)
+ if length and rgb_hex then
+ table.insert(data[current_linenum], { rgb_hex = rgb_hex, range = { i - 1, i + length - 1 } })
+ i = i + length
+ else
+ i = i + 1
+ end
+ end
+ end
+
+ return data
+end
+
+-- gets used in rehighlight function only
+local BUFFER_LINES = {}
-- get the amount lines to highlight
local function getrow(buf)
if not BUFFER_LINES[buf] then
@@ -222,12 +241,12 @@ end
---@param options_local table|nil: Buffer local variables
---@param use_local_lines boolean|nil Whether to use lines num range from options_local
---@return nil|boolean|number,table
-function rehighlight_buffer(buf, options, options_local, use_local_lines)
+function buffer.rehighlight(buf, options, options_local, use_local_lines)
if buf == 0 or buf == nil then
buf = api.nvim_get_current_buf()
end
- local ns = DEFAULT_NAMESPACE
+ local ns = buffer.default_namespace
local min, max
if use_local_lines and options_local then
@@ -235,20 +254,13 @@ function rehighlight_buffer(buf, options, options_local, use_local_lines)
else
min, max = getrow(buf)
end
- local lines = buf_get_lines(buf, min, max, false)
- local bool, returns = highlight_buffer(buf, ns, lines, min, max, options, options_local or {})
+ local bool, returns = buffer.highlight(buf, ns, min, max, options, options_local or {})
table.insert(returns.detach.functions, function()
BUFFER_LINES[buf] = nil
end)
+
return bool, returns
end
---- @export
-return {
- DEFAULT_NAMESPACE = DEFAULT_NAMESPACE,
- HIGHLIGHT_MODE_NAMES = HIGHLIGHT_MODE_NAMES,
- clear_hl_cache = clear_hl_cache,
- rehighlight_buffer = rehighlight_buffer,
- highlight_buffer = highlight_buffer,
-}
+return buffer
diff --git a/lua/colorizer/color_utils.lua b/lua/colorizer/color.lua
index 2a7e7ab..711c8e4 100644
--- a/lua/colorizer/color_utils.lua
+++ b/lua/colorizer/color.lua
@@ -1,5 +1,11 @@
---Helper functions to parse different colour formats
---@module colorizer.color_utils
+--@module colorizer.color
+local api = vim.api
+
+local bit = require "bit"
+local floor, min, max = math.floor, math.min, math.max
+local band, rshift, lshift, tohex = bit.band, bit.rshift, bit.lshift, bit.tohex
+
local Trie = require "colorizer.trie"
local utils = require "colorizer.utils"
@@ -8,27 +14,153 @@ local byte_is_hex = utils.byte_is_hex
local parse_hex = utils.parse_hex
local percent_or_hex = utils.percent_or_hex
-local bit = require "bit"
-local floor, min, max = math.floor, math.min, math.max
-local band, rshift, lshift, tohex = bit.band, bit.rshift, bit.lshift, bit.tohex
+local color = {}
-local api = vim.api
+local ARGB_MINIMUM_LENGTH = #"0xAARRGGBB" - 1
+---parse for 0xaarrggbb and return rgb hex.
+-- a format used in android apps
+---@param line string: line to parse
+---@param i number: index of line from where to start parsing
+---@return number|nil: index of line where the hex value ended
+---@return string|nil: rgb hex value
+function color.argb_hex_parser(line, i)
+ if #line < i + ARGB_MINIMUM_LENGTH then
+ return
+ end
----Determine whether to use black or white text.
---
--- ref: https://stackoverflow.com/a/1855903/837964
--- https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color
----@param r number: Red
----@param g number: Green
----@param b number: Blue
-local function color_is_bright(r, g, b)
- -- counting the perceptive luminance - human eye favors green color
- local luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
- if luminance > 0.5 then
- return true -- bright colors, black font
+ local j = i + 2
+
+ local n = j + 8
+ local alpha
+ local v = 0
+ while j <= min(n, #line) do
+ local b = line:byte(j)
+ if not byte_is_hex(b) then
+ break
+ end
+ if j - i <= 3 then
+ alpha = parse_hex(b) + lshift(alpha or 0, 4)
+ else
+ v = parse_hex(b) + lshift(v, 4)
+ end
+ j = j + 1
+ end
+ if #line >= j and byte_is_alphanumeric(line:byte(j)) then
+ return
+ end
+ local length = j - i
+ if length ~= 10 then
+ return
+ end
+ alpha = tonumber(alpha) / 255
+ local r = floor(band(rshift(v, 16), 0xFF) * alpha)
+ local g = floor(band(rshift(v, 8), 0xFF) * alpha)
+ local b = floor(band(v, 0xFF) * alpha)
+ local rgb_hex = string.format("%02x%02x%02x", r, g, b)
+ return length, rgb_hex
+end
+
+--- Converts an HSL color value to RGB.
+---@param h number: Hue
+---@param s number: Saturation
+---@param l number: Lightness
+---@return number|nil,number|nil,number|nil
+function color.hsl_to_rgb(h, s, l)
+ if h > 1 or s > 1 or l > 1 then
+ return
+ end
+ if s == 0 then
+ local r = l * 255
+ return r, r, r
+ end
+ local q
+ if l < 0.5 then
+ q = l * (1 + s)
else
- return false -- dark colors, white font
+ q = l + s - l * s
+ end
+ local p = 2 * l - q
+ return 255 * color.hue_to_rgb(p, q, h + 1 / 3),
+ 255 * color.hue_to_rgb(p, q, h),
+ 255 * color.hue_to_rgb(p, q, h - 1 / 3)
+end
+
+local CSS_HSL_FN_MINIMUM_LENGTH = #"hsl(0,0%,0%)" - 1
+---Parse for hsl() css function and return rgb hex.
+---@param line string: Line to parse
+---@param i number: Index of line from where to start parsing
+---@return number|nil: Index of line where the hsl function ended
+---@return string|nil: rgb hex value
+function color.hsl_function_parser(line, i)
+ if #line < i + CSS_HSL_FN_MINIMUM_LENGTH then
+ return
end
+ local h, s, l, match_end = line:sub(i):match "^hsl%(%s*(%d+)%s*,%s*(%d+)%%%s*,%s*(%d+)%%%s*%)()"
+ if not match_end then
+ h, s, l, match_end = line:sub(i):match "^hsl%(%s*(%d+)%s+(%d+)%%%s+(%d+)%%%s*%)()"
+ if not match_end then
+ return
+ end
+ end
+ h = tonumber(h)
+ if h > 360 then
+ return
+ end
+ s = tonumber(s)
+ if s > 100 then
+ return
+ end
+ l = tonumber(l)
+ if l > 100 then
+ return
+ end
+ local r, g, b = color.hsl_to_rgb(h / 360, s / 100, l / 100)
+ if r == nil or g == nil or b == nil then
+ return
+ end
+ local rgb_hex = string.format("%02x%02x%02x", r, g, b)
+ return match_end - 1, rgb_hex
+end
+
+local CSS_HSLA_FN_MINIMUM_LENGTH = #"hsla(0,0%,0%,0)" - 1
+---Parse for hsl() css function and return rgb hex.
+---@param line string: Line to parse
+---@param i number: Index of line from where to start parsing
+---@return number|nil: Index of line where the hsla function ended
+---@return string|nil: rgb hex value
+function color.hsla_function_parser(line, i)
+ if #line < i + CSS_HSLA_FN_MINIMUM_LENGTH then
+ return
+ end
+ local h, s, l, a, match_end = line:sub(i):match "^hsla%(%s*(%d+)%s*,%s*(%d+)%%%s*,%s*(%d+)%%%s*,%s*([.%d]+)%s*%)()"
+ if not match_end then
+ h, s, l, a, match_end = line:sub(i):match "^hsla%(%s*(%d+)%s+(%d+)%%%s+(%d+)%%%s+([.%d]+)%s*%)()"
+ if not match_end then
+ return
+ end
+ end
+ a = tonumber(a)
+ if not a or a > 1 then
+ return
+ end
+ h = tonumber(h)
+ if h > 360 then
+ return
+ end
+ s = tonumber(s)
+ if s > 100 then
+ return
+ end
+ l = tonumber(l)
+ if l > 100 then
+ return
+ end
+ local r, g, b = color.hsl_to_rgb(h / 360, s / 100, l / 100)
+ if r == nil or g == nil or b == nil then
+ return
+ end
+ local rgb_hex = string.format("%02x%02x%02x", r * a, g * a, b * a)
+ return match_end - 1, rgb_hex
end
---Convert hsl colour values to rgb.
@@ -37,7 +169,7 @@ end
---@param q number
---@param t number
---@return number
-local function hue_to_rgb(p, q, t)
+function color.hue_to_rgb(p, q, t)
if t < 0 then
t = t + 1
end
@@ -56,6 +188,23 @@ local function hue_to_rgb(p, q, t)
return p
end
+---Determine whether to use black or white text.
+--
+-- ref: https://stackoverflow.com/a/1855903/837964
+-- https://stackoverflow.com/questions/596216/formula-to-determine-brightness-of-rgb-color
+---@param r number: Red
+---@param g number: Green
+---@param b number: Blue
+function color.is_bright(r, g, b)
+ -- counting the perceptive luminance - human eye favors green color
+ local luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255
+ if luminance > 0.5 then
+ return true -- bright colors, black font
+ else
+ return false -- dark colors, white font
+ end
+end
+
local COLOR_MAP
local COLOR_TRIE
local COLOR_NAME_MINLEN, COLOR_NAME_MAXLEN
@@ -66,7 +215,7 @@ local TAILWIND_ENABLED = false
---@param line string: Line to parse
---@param i number: Index of line from where to start parsing
---@param opts table: Currently contains whether tailwind is enabled or not
-local function color_name_parser(line, i, opts)
+function color.name_parser(line, i, opts)
--- Setup the COLOR_MAP and COLOR_TRIE
if not COLOR_TRIE or opts.tailwind ~= TAILWIND_ENABLED then
COLOR_MAP = {}
@@ -124,36 +273,13 @@ local function color_name_parser(line, i, opts)
end
end
---- Converts an HSL color value to RGB.
----@param h number: Hue
----@param s number: Saturation
----@param l number: Lightness
----@return number|nil,number|nil,number|nil
-local function hsl_to_rgb(h, s, l)
- if h > 1 or s > 1 or l > 1 then
- return
- end
- if s == 0 then
- local r = l * 255
- return r, r, r
- end
- local q
- if l < 0.5 then
- q = l * (1 + s)
- else
- q = l + s - l * s
- end
- local p = 2 * l - q
- return 255 * hue_to_rgb(p, q, h + 1 / 3), 255 * hue_to_rgb(p, q, h), 255 * hue_to_rgb(p, q, h - 1 / 3)
-end
-
local CSS_RGB_FN_MINIMUM_LENGTH = #"rgb(0,0,0)" - 1
---Parse for rgb() css function and return rgb hex.
---@param line string: Line to parse
---@param i number: Index of line from where to start parsing
---@return number|nil: Index of line where the rgb function ended
---@return string|nil: rgb hex value
-local function rgb_function_parser(line, i)
+function color.rgb_function_parser(line, i)
if #line < i + CSS_RGB_FN_MINIMUM_LENGTH then
return
end
@@ -189,7 +315,7 @@ local CSS_RGBA_FN_MINIMUM_LENGTH = #"rgba(0,0,0,0)" - 1
---@param i number: Index of line from where to start parsing
---@return number|nil: Index of line where the rgba function ended
---@return string|nil: rgb hex value
-local function rgba_function_parser(line, i)
+function color.rgba_function_parser(line, i)
if #line < i + CSS_RGBA_FN_MINIMUM_LENGTH then
return
end
@@ -221,128 +347,6 @@ local function rgba_function_parser(line, i)
return match_end - 1, rgb_hex
end
-local CSS_HSL_FN_MINIMUM_LENGTH = #"hsl(0,0%,0%)" - 1
----Parse for hsl() css function and return rgb hex.
----@param line string: Line to parse
----@param i number: Index of line from where to start parsing
----@return number|nil: Index of line where the hsl function ended
----@return string|nil: rgb hex value
-local function hsl_function_parser(line, i)
- if #line < i + CSS_HSL_FN_MINIMUM_LENGTH then
- return
- end
- local h, s, l, match_end = line:sub(i):match "^hsl%(%s*(%d+)%s*,%s*(%d+)%%%s*,%s*(%d+)%%%s*%)()"
- if not match_end then
- h, s, l, match_end = line:sub(i):match "^hsl%(%s*(%d+)%s+(%d+)%%%s+(%d+)%%%s*%)()"
- if not match_end then
- return
- end
- end
- h = tonumber(h)
- if h > 360 then
- return
- end
- s = tonumber(s)
- if s > 100 then
- return
- end
- l = tonumber(l)
- if l > 100 then
- return
- end
- local r, g, b = hsl_to_rgb(h / 360, s / 100, l / 100)
- if r == nil or g == nil or b == nil then
- return
- end
- local rgb_hex = string.format("%02x%02x%02x", r, g, b)
- return match_end - 1, rgb_hex
-end
-
-local CSS_HSLA_FN_MINIMUM_LENGTH = #"hsla(0,0%,0%,0)" - 1
----Parse for hsl() css function and return rgb hex.
----@param line string: Line to parse
----@param i number: Index of line from where to start parsing
----@return number|nil: Index of line where the hsla function ended
----@return string|nil: rgb hex value
-local function hsla_function_parser(line, i)
- if #line < i + CSS_HSLA_FN_MINIMUM_LENGTH then
- return
- end
- local h, s, l, a, match_end = line:sub(i):match "^hsla%(%s*(%d+)%s*,%s*(%d+)%%%s*,%s*(%d+)%%%s*,%s*([.%d]+)%s*%)()"
- if not match_end then
- h, s, l, a, match_end = line:sub(i):match "^hsla%(%s*(%d+)%s+(%d+)%%%s+(%d+)%%%s+([.%d]+)%s*%)()"
- if not match_end then
- return
- end
- end
- a = tonumber(a)
- if not a or a > 1 then
- return
- end
- h = tonumber(h)
- if h > 360 then
- return
- end
- s = tonumber(s)
- if s > 100 then
- return
- end
- l = tonumber(l)
- if l > 100 then
- return
- end
- local r, g, b = hsl_to_rgb(h / 360, s / 100, l / 100)
- if r == nil or g == nil or b == nil then
- return
- end
- local rgb_hex = string.format("%02x%02x%02x", r * a, g * a, b * a)
- return match_end - 1, rgb_hex
-end
-
-local ARGB_MINIMUM_LENGTH = #"0xAARRGGBB" - 1
----parse for 0xaarrggbb and return rgb hex.
--- a format used in android apps
----@param line string: line to parse
----@param i number: index of line from where to start parsing
----@return number|nil: index of line where the hex value ended
----@return string|nil: rgb hex value
-local function argb_hex_parser(line, i)
- if #line < i + ARGB_MINIMUM_LENGTH then
- return
- end
-
- local j = i + 2
-
- local n = j + 8
- local alpha
- local v = 0
- while j <= min(n, #line) do
- local b = line:byte(j)
- if not byte_is_hex(b) then
- break
- end
- if j - i <= 3 then
- alpha = parse_hex(b) + lshift(alpha or 0, 4)
- else
- v = parse_hex(b) + lshift(v, 4)
- end
- j = j + 1
- end
- if #line >= j and byte_is_alphanumeric(line:byte(j)) then
- return
- end
- local length = j - i
- if length ~= 10 then
- return
- end
- alpha = tonumber(alpha) / 255
- local r = floor(band(rshift(v, 16), 0xFF) * alpha)
- local g = floor(band(rshift(v, 8), 0xFF) * alpha)
- local b = floor(band(v, 0xFF) * alpha)
- local rgb_hex = string.format("%02x%02x%02x", r, g, b)
- return length, rgb_hex
-end
-
---parse for #rrggbbaa and return rgb hex.
-- a format used in android apps
---@param line string: line to parse
@@ -350,7 +354,7 @@ end
---@param opts table: Containing minlen, maxlen, valid_lengths
---@return number|nil: index of line where the hex value ended
---@return string|nil: rgb hex value
-local function rgba_hex_parser(line, i, opts)
+function color.rgba_hex_parser(line, i, opts)
local minlen, maxlen, valid_lengths = opts.minlen, opts.maxlen, opts.valid_lengths
local j = i + 1
if #line < j + minlen - 1 then
@@ -398,14 +402,4 @@ local function rgba_hex_parser(line, i, opts)
return (valid_lengths[length - 1] and length), line:sub(i + 1, i + length - 1)
end
---- @export
-return {
- color_is_bright = color_is_bright,
- color_name_parser = color_name_parser,
- rgba_hex_parser = rgba_hex_parser,
- argb_hex_parser = argb_hex_parser,
- rgb_function_parser = rgb_function_parser,
- rgba_function_parser = rgba_function_parser,
- hsl_function_parser = hsl_function_parser,
- hsla_function_parser = hsla_function_parser,
-}
+return color
diff --git a/lua/colorizer/matcher_utils.lua b/lua/colorizer/matcher.lua
index 3f8047a..dba67c0 100644
--- a/lua/colorizer/matcher_utils.lua
+++ b/lua/colorizer/matcher.lua
@@ -1,41 +1,45 @@
---Helper functions for colorizer to enable required parsers
---@module colorizer.matcher_utils
+--@module colorizer.matcher
local Trie = require "colorizer.trie"
local min, max = math.min, math.max
-local color_utils = require "colorizer.color_utils"
-local color_name_parser = color_utils.color_name_parser
-local rgba_hex_parser = color_utils.rgba_hex_parser
+local color = require "colorizer.color"
+local color_name_parser = color.name_parser
+local rgba_hex_parser = color.rgba_hex_parser
local sass = require "colorizer.sass"
-local sass_name_parser = sass.sass_name_parser
+local sass_name_parser = sass.name_parser
-local parser = {}
-parser["_0x"] = color_utils.argb_hex_parser
-parser["_rgb"] = color_utils.rgb_function_parser
-parser["_rgba"] = color_utils.rgba_function_parser
-parser["_hsl"] = color_utils.hsl_function_parser
-parser["_hsla"] = color_utils.hsla_function_parser
-local b_hash, dollar_hash = ("#"):byte(), ("$"):byte()
+local B_HASH, DOLLAR_HASH = ("#"):byte(), ("$"):byte()
+
+local parser = {
+ ["_0x"] = color.argb_hex_parser,
+ ["_rgb"] = color.rgb_function_parser,
+ ["_rgba"] = color.rgba_function_parser,
+ ["_hsl"] = color.hsl_function_parser,
+ ["_hsla"] = color.hsla_function_parser,
+}
+
+local matcher = {}
---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
---@return function: function which will just parse the line for enabled parsers
-local function compile_matcher(matchers, matchers_trie)
+function matcher.compile(matchers, matchers_trie)
local trie = Trie(matchers_trie)
local function parse_fn(line, i, buf)
-- prefix #
if matchers.rgba_hex_parser then
- if line:byte(i) == b_hash then
+ if line:byte(i) == B_HASH then
return rgba_hex_parser(line, i, matchers.rgba_hex_parser)
end
end
-- prefix $, SASS Colour names
if matchers.sass_name_parser then
- if line:byte(i) == dollar_hash then
+ if line:byte(i) == DOLLAR_HASH then
return sass_name_parser(line, i, buf)
end
end
@@ -63,7 +67,7 @@ local MATCHER_CACHE = {}
--Do not try make the function again if it is present in the cache
---@param options table: options created in `colorizer.setup`
---@return function|boolean: function which will just parse the line for enabled parsers
-local function make_matcher(options)
+function matcher.make(options)
local enable_names = options.css or options.names
local enable_sass = options.sass and options.sass.enable
local enable_tailwind = options.tailwind
@@ -128,7 +132,6 @@ local function make_matcher(options)
table.insert(matchers_prefix, "0x")
end
- -- do not mess with the sequence, hsla before hsl, etc
if enable_rgb and enable_hsl then
table.insert(matchers_prefix, "hsla")
table.insert(matchers_prefix, "rgba")
@@ -142,13 +145,10 @@ local function make_matcher(options)
table.insert(matchers_prefix, "hsl")
end
- loop_parse_fn = compile_matcher(matchers, matchers_prefix)
+ loop_parse_fn = matcher.compile(matchers, matchers_prefix)
MATCHER_CACHE[matcher_key] = loop_parse_fn
return loop_parse_fn
end
---- @export
-return {
- make_matcher = make_matcher,
-}
+return matcher
diff --git a/lua/colorizer/sass.lua b/lua/colorizer/sass.lua
index a003624..429ce2c 100644
--- a/lua/colorizer/sass.lua
+++ b/lua/colorizer/sass.lua
@@ -7,6 +7,8 @@ local utils = require "colorizer.utils"
local get_last_modified = utils.get_last_modified
local watch_file = utils.watch_file
+local sass = {}
+
local DOLLAR_HASH = ("$"):byte()
local AT_HASH = ("@"):byte()
local COLON_HASH = (";"):byte()
@@ -29,7 +31,7 @@ end
--- Cleanup sass variables and watch handlers
---@param buf number
-local function sass_cleanup(buf)
+function sass.cleanup(buf)
remove_unused_imports(buf, api.nvim_buf_get_name(buf))
SASS[buf] = nil
end
@@ -40,8 +42,8 @@ end
---@param i number: Index of line from where to start parsing
---@param buf number
---@return number|nil, string|nil
-local function sass_name_parser(line, i, buf)
- local variable_name = line:sub(i):match "^%$([%w_-]+)"
+function sass.name_parser(line, i, buf)
+ local variable_name = line:match("^%$([%w_-]+)", i)
if variable_name then
local rgb_hex = SASS[buf].DEFINITIONS_ALL[variable_name]
if rgb_hex then
@@ -194,6 +196,7 @@ local function sass_parse_lines(buf, line_start, content, name)
local lastm = get_last_modified(v)
if lastm then
+ SASS[buf].IMPORTS[name] = SASS[buf].IMPORTS[name] or {}
SASS[buf].IMPORTS[name][v] = lastm
local cc, inde = {}, 0
for l in io.lines(v) do
@@ -204,12 +207,7 @@ local function sass_parse_lines(buf, line_start, content, name)
cc = nil
end
- require("colorizer.buffer_utils").rehighlight_buffer(
- buf,
- SASS[buf].OPTIONS,
- SASS[buf].LOCAL_OPTIONS,
- true
- )
+ require("colorizer.buffer").rehighlight(buf, SASS[buf].OPTIONS, SASS[buf].LOCAL_OPTIONS, true)
end
SASS[buf].WATCH_IMPORTS[name][v] = watch_file(v, watch_callback)
end
@@ -234,7 +232,7 @@ local function sass_parse_lines(buf, line_start, content, name)
end
end -- sass_parse_lines end
---- Parse the given lines for sass variabled and add to SASS[buf].DEFINITIONS_ALL.
+--- Parse the given lines for sass variabled and add to `SASS[buf].DEFINITIONS_ALL`.
-- which is then used in |sass_name_parser|
-- If lines are not given, then fetch the lines with line_start and line_end
---@param buf number
@@ -244,7 +242,7 @@ end -- sass_parse_lines end
---@param color_parser function|boolean
---@param options table: Buffer options
---@param options_local table|nil: Buffer local variables
-local function sass_update_variables(buf, line_start, line_end, lines, color_parser, options, options_local)
+function sass.update_variables(buf, line_start, line_end, lines, color_parser, options, options_local)
lines = lines or vim.api.nvim_buf_get_lines(buf, line_start, line_end, false)
if not SASS[buf] then
@@ -308,9 +306,4 @@ local function sass_update_variables(buf, line_start, line_end, lines, color_par
SASS[buf].DEFINITIONS_RECURSIVE_CURRENT_ABSOLUTE = nil
end
---- @export
-return {
- sass_cleanup = sass_cleanup,
- sass_name_parser = sass_name_parser,
- sass_update_variables = sass_update_variables,
-}
+return sass
diff --git a/lua/colorizer/tailwind.lua b/lua/colorizer/tailwind.lua
index 88094f5..839cd0c 100644
--- a/lua/colorizer/tailwind.lua
+++ b/lua/colorizer/tailwind.lua
@@ -2,6 +2,8 @@
--@module colorizer.tailwind
local api = vim.api
+local tailwind = {}
+
-- use a different namespace for tailwind as will be cleared if kept in Default namespace
local DEFAULT_NAMESPACE_TAILWIND = api.nvim_create_namespace "colorizer_tailwind"
@@ -9,23 +11,27 @@ local TAILWIND = {}
--- Cleanup tailwind variables and autocmd
---@param buf number
-local function tailwind_cleanup(buf)
+function tailwind.cleanup(buf)
pcall(api.nvim_del_autocmd, TAILWIND[buf] and TAILWIND[buf].AU_ID[1])
pcall(api.nvim_del_autocmd, TAILWIND[buf] and TAILWIND[buf].AU_ID[2])
api.nvim_buf_clear_namespace(buf, DEFAULT_NAMESPACE_TAILWIND, 0, -1)
TAILWIND[buf] = nil
end
-local function highlight_buffer_tailwind(buf, ns, options, add_highlight)
+local function highlight_tailwind(buf, ns, options, add_highlight)
-- it can take some time to actually fetch the results
-- on top of that, tailwindcss is quite slow in neovim
vim.defer_fn(function()
+ if not TAILWIND[buf] or not TAILWIND[buf].CLIENT or not TAILWIND[buf].CLIENT.request then
+ return
+ end
+
local opts = { textDocument = vim.lsp.util.make_text_document_params() }
--@local
---@diagnostic disable-next-line: param-type-mismatch
TAILWIND[buf].CLIENT.request("textDocument/documentColor", opts, function(err, results, _, _)
if err == nil and results ~= nil then
- local datas, line_start, line_end = {}, nil, nil
+ local data, line_start, line_end = {}, nil, nil
for _, color in pairs(results) do
local cur_line = color.range.start.line
if line_start then
@@ -50,22 +56,22 @@ local function highlight_buffer_tailwind(buf, ns, options, add_highlight)
local first_col = color.range.start.character
local end_col = color.range["end"].character
- datas[cur_line] = datas[cur_line] or {}
- table.insert(datas[cur_line], { rgb_hex = rgb_hex, range = { first_col, end_col } })
+ data[cur_line] = data[cur_line] or {}
+ table.insert(data[cur_line], { rgb_hex = rgb_hex, range = { first_col, end_col } })
end
- add_highlight(options, buf, ns, datas, line_start or 0, line_end and (line_end + 2) or -1)
+ add_highlight(buf, ns, line_start or 0, line_end and (line_end + 2) or -1, data, options)
end
end)
end, 10)
end
--- highlight buffer using values returned by tailwindcss
--- To see these table information, see |colorizer.buffer_utils|
+-- To see these table information, see |colorizer.buffer|
---@param buf number
---@param options table
---@param options_local table
---@param add_highlight function
-local function tailwind_setup_lsp_colors(buf, options, options_local, add_highlight)
+function tailwind.setup_lsp_colors(buf, options, options_local, add_highlight)
TAILWIND[buf] = TAILWIND[buf] or {}
TAILWIND[buf].AU_ID = TAILWIND[buf].AU_ID or {}
@@ -73,7 +79,7 @@ local function tailwind_setup_lsp_colors(buf, options, options_local, add_highli
if vim.version().minor >= 8 then
-- create the autocmds so tailwind colours only activate when tailwindcss lsp is active
if not TAILWIND[buf].AU_CREATED then
- tailwind_cleanup(buf)
+ tailwind.cleanup(buf)
TAILWIND[buf].AU_ID[1] = api.nvim_create_autocmd("LspAttach", {
group = options_local.__augroup_id,
buffer = buf,
@@ -84,7 +90,7 @@ local function tailwind_setup_lsp_colors(buf, options, options_local, add_highli
-- wait 100 ms for the first request
TAILWIND[buf].CLIENT = client
vim.defer_fn(function()
- highlight_buffer_tailwind(buf, DEFAULT_NAMESPACE_TAILWIND, options, add_highlight)
+ highlight_tailwind(buf, DEFAULT_NAMESPACE_TAILWIND, options, add_highlight)
end, 100)
end
end
@@ -95,7 +101,7 @@ local function tailwind_setup_lsp_colors(buf, options, options_local, add_highli
group = options_local.__augroup_id,
buffer = buf,
callback = function()
- tailwind_cleanup(buf)
+ tailwind.cleanup(buf)
end,
})
TAILWIND[buf].AU_CREATED = true
@@ -117,16 +123,19 @@ local function tailwind_setup_lsp_colors(buf, options, options_local, add_highli
for _, cl in pairs(tailwind_client) do
if cl["name"] == "tailwindcss" then
tailwind_client = cl
- ok = false
+ ok = true
break
end
end
if
- vim.tbl_isempty(tailwind_client or {})
- or not tailwind_client
- or not tailwind_client.supports_method
- or not tailwind_client.supports_method "textDocument/documentColor"
+ not ok
+ and (
+ vim.tbl_isempty(tailwind_client or {})
+ or not tailwind_client
+ or not tailwind_client.supports_method
+ or not tailwind_client.supports_method "textDocument/documentColor"
+ )
then
return true
end
@@ -135,20 +144,16 @@ local function tailwind_setup_lsp_colors(buf, options, options_local, add_highli
-- wait 500 ms for the first request
vim.defer_fn(function()
- highlight_buffer_tailwind(buf, DEFAULT_NAMESPACE_TAILWIND, options, add_highlight)
- end, 500)
+ highlight_tailwind(buf, DEFAULT_NAMESPACE_TAILWIND, options, add_highlight)
+ end, 1000)
return true
end
-- only try to do tailwindcss highlight if lsp is attached
if TAILWIND[buf].CLIENT then
- highlight_buffer_tailwind(buf, DEFAULT_NAMESPACE_TAILWIND, options, add_highlight)
+ highlight_tailwind(buf, DEFAULT_NAMESPACE_TAILWIND, options, add_highlight)
end
end
----@export
-return {
- tailwind_cleanup = tailwind_cleanup,
- tailwind_setup_lsp_colors = tailwind_setup_lsp_colors,
-}
+return tailwind
diff --git a/lua/colorizer/utils.lua b/lua/colorizer/utils.lua
index 9dad9d8..c83c32d 100644
--- a/lua/colorizer/utils.lua
+++ b/lua/colorizer/utils.lua
@@ -5,6 +5,8 @@ local band, bor, rshift, lshift = bit.band, bit.bor, bit.rshift, bit.lshift
local uv = vim.loop
+local utils = {}
+
-- -- TODO use rgb as the return value from the matcher functions
-- -- instead of the rgb_hex. Can be the highlight key as well
-- -- when you shift it left 8 bits. Use the lower 8 bits for
@@ -22,36 +24,38 @@ local CATEGORY_ALPHA = lshift(1, 1)
local CATEGORY_HEX = lshift(1, 2)
local CATEGORY_ALPHANUM = bor(CATEGORY_ALPHA, CATEGORY_DIGIT)
--- do not run the loop multiple times
-local b = string.byte
-local byte_values = { ["0"] = b "0", ["9"] = b "9", ["a"] = b "a", ["f"] = b "f", ["z"] = b "z" }
-local extra_char = { [b "-"] = true }
-
-for i = 0, 255 do
- local v = 0
- local lowercase = bor(i, 0x20)
- -- Digit is bit 1
- if i >= byte_values["0"] and i <= byte_values["9"] then
- v = bor(v, lshift(1, 0))
- v = bor(v, lshift(1, 2))
- v = bor(v, lshift(i - byte_values["0"], 4))
- elseif lowercase >= byte_values["a"] and lowercase <= byte_values["z"] then
- -- Alpha is bit 2
- v = bor(v, lshift(1, 1))
- if lowercase <= byte_values["f"] then
+do
+ -- do not run the loop multiple times
+ local b = string.byte
+ local byte_values = { ["0"] = b "0", ["9"] = b "9", ["a"] = b "a", ["f"] = b "f", ["z"] = b "z" }
+ local extra_char = { [b "-"] = true }
+
+ for i = 0, 255 do
+ local v = 0
+ local lowercase = bor(i, 0x20)
+ -- Digit is bit 1
+ if i >= byte_values["0"] and i <= byte_values["9"] then
+ v = bor(v, lshift(1, 0))
v = bor(v, lshift(1, 2))
- v = bor(v, lshift(lowercase - byte_values["a"] + 10, 4))
+ v = bor(v, lshift(i - byte_values["0"], 4))
+ elseif lowercase >= byte_values["a"] and lowercase <= byte_values["z"] then
+ -- Alpha is bit 2
+ v = bor(v, lshift(1, 1))
+ if lowercase <= byte_values["f"] then
+ v = bor(v, lshift(1, 2))
+ v = bor(v, lshift(lowercase - byte_values["a"] + 10, 4))
+ end
+ elseif extra_char[i] then
+ v = i
end
- elseif extra_char[i] then
- v = i
+ BYTE_CATEGORY[i] = v
end
- BYTE_CATEGORY[i] = v
end
---Obvious.
---@param byte number
---@return boolean
-local function byte_is_alphanumeric(byte)
+function utils.byte_is_alphanumeric(byte)
local category = BYTE_CATEGORY[byte]
return band(category, CATEGORY_ALPHANUM) ~= 0
end
@@ -59,15 +63,33 @@ end
---Obvious.
---@param byte number
---@return boolean
-local function byte_is_hex(byte)
+function utils.byte_is_hex(byte)
return band(BYTE_CATEGORY[byte], CATEGORY_HEX) ~= 0
end
+--- Get last modified time of a file
+---@param path string: file path
+---@return number|nil: modified time
+function utils.get_last_modified(path)
+ local fd = uv.fs_open(path, "r", 438)
+ if not fd then
+ return
+ end
+
+ local stat = uv.fs_fstat(fd)
+ uv.fs_close(fd)
+ if stat then
+ return stat.mtime.nsec
+ else
+ return
+ end
+end
+
---Merge two tables.
--
-- todo: Remove this and use `vim.tbl_deep_extend`
---@return table
-local function merge(...)
+function utils.merge(...)
local res = {}
for i = 1, select("#", ...) do
local o = select(i, ...)
@@ -84,7 +106,7 @@ end
--- Obvious.
---@param byte number
---@return number
-local function parse_hex(byte)
+function utils.parse_hex(byte)
return rshift(BYTE_CATEGORY[byte], 4)
end
@@ -92,7 +114,7 @@ local b_percent = string.byte "%"
--- Obvious.
---@param v string
---@return number|nil
-local function percent_or_hex(v)
+function utils.percent_or_hex(v)
if v:byte(-1) == b_percent then
return tonumber(v:sub(1, -2)) / 100 * 255
end
@@ -103,30 +125,12 @@ local function percent_or_hex(v)
return x
end
---- Get last modified time of a file
----@param path string: file path
----@return number|nil: modified time
-local function get_last_modified(path)
- local fd = uv.fs_open(path, "r", 438)
- if not fd then
- return
- end
-
- local stat = uv.fs_fstat(fd)
- uv.fs_close(fd)
- if stat then
- return stat.mtime.nsec
- else
- return
- end
-end
-
--- Watch a file for changes and execute callback
---@param path string: File path
---@param callback function: Callback to execute
---@param ... array: params for callback
---@return function|nil
-local function watch_file(path, callback, ...)
+function utils.watch_file(path, callback, ...)
if not path or type(callback) ~= "function" then
return
end
@@ -145,7 +149,7 @@ local function watch_file(path, callback, ...)
callback(filename, unpack(args))
-- Debounce: stop/start.
handle:stop()
- if not err or not get_last_modified(filename) then
+ if not err or not utils.get_last_modified(filename) then
start()
end
end
@@ -165,13 +169,4 @@ local function watch_file(path, callback, ...)
return handle
end
---- @export
-return {
- byte_is_alphanumeric = byte_is_alphanumeric,
- byte_is_hex = byte_is_hex,
- merge = merge,
- parse_hex = parse_hex,
- percent_or_hex = percent_or_hex,
- get_last_modified = get_last_modified,
- watch_file = watch_file,
-}
+return utils