Skip to content

Commit

Permalink
feat: highlights 0xRRGGBB and 0xRGB (norcalli#91)
Browse files Browse the repository at this point in the history
* ref: cleans up lua

* feat: highlights 0xRRGGBB and 0xRGB

* chore: updates function parameter type hints

* ref: renames parsers and updates readme
  • Loading branch information
catgoose authored Nov 6, 2024
1 parent e614ff5 commit f134063
Show file tree
Hide file tree
Showing 12 changed files with 93 additions and 94 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# colorizer.lua ( NOT MAINTAINED, Looking for maintainers )
# colorizer.lua



Expand Down
49 changes: 19 additions & 30 deletions lua/colorizer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ local merge = utils.merge
local api = vim.api
local augroup = api.nvim_create_augroup
local autocmd = api.nvim_create_autocmd
local buf_get_option = api.nvim_get_option_value
local get_option_value = api.nvim_get_option_value
local clear_namespace = api.nvim_buf_clear_namespace
local current_buf = api.nvim_get_current_buf

Expand All @@ -84,8 +84,7 @@ colorizer.DEFAULT_NAMESPACE = buffer_utils.default_namespace
colorizer.highlight_buffer = buffer_utils.highlight

-- USER FACING FUNCTIONALITY --
local AUGROUP_ID
local AUGROUP_NAME = "ColorizerSetup"
local AU_GROUP = augroup("ColorizerSetup", {})
-- buffer specific options given in setup
local BUFFER_OPTIONS = {}
-- buffer local options created after setup
Expand Down Expand Up @@ -186,16 +185,16 @@ local SETUP_SETTINGS = {
}

--- Make new buffer Configuration
---@param bufnr number: buffer number (0 for current)
---@param bufnr number: Buffer number
---@param bo_type 'buftype'|'filetype': The type of buffer option
---@return table
local function new_buffer_options(bufnr, bo_type)
local value = buf_get_option(bo_type, { buf = bufnr })
local value = get_option_value(bo_type, { buf = bufnr })
return OPTIONS.filetype[value] or SETUP_SETTINGS.default_options
end

--- Parse buffer Configuration and convert aliases to normal values
---@param options table: options table
---@param options table: Buffer options table
---@return table
local function parse_buffer_options(options)
local includes = {
Expand Down Expand Up @@ -240,8 +239,8 @@ local function parse_buffer_options(options)
end

--- Check if attached to a buffer.
---@param bufnr number|nil: A value of 0 implies the current buffer.
---@return number|nil: if attached to the buffer, false otherwise.
---@param bufnr number|nil: Buffer number (0 for current)
---@return number|nil: Attached buffer number, nil otherwise.
---@see colorizer.buffer.highlight
function colorizer.is_buffer_attached(bufnr)
if bufnr == 0 or not bufnr then
Expand All @@ -254,7 +253,7 @@ function colorizer.is_buffer_attached(bufnr)
end

local au = api.nvim_get_autocmds {
group = AUGROUP_ID,
group = AU_GROUP,
event = { "WinScrolled", "TextChanged", "TextChangedI", "TextChangedP" },
buffer = bufnr,
}
Expand Down Expand Up @@ -312,14 +311,10 @@ function colorizer.attach_to_buffer(bufnr, options, bo_type)
-- set options by grabbing existing or creating new options, then parsing
options = parse_buffer_options(options or colorizer.get_buffer_options(bufnr) or new_buffer_options(bufnr, bo_type))

if not buffer_utils.highlight_mode_names[options.mode] then
if options.mode ~= nil then
local mode = options.mode
vim.defer_fn(function()
-- just notify the user once
vim.notify_once(string.format("Warning: Invalid mode given to colorizer setup [ %s ]", mode))
end, 0)
end
if options.mode and not buffer_utils.highlight_mode_names[options.mode] then
vim.defer_fn(function()
vim.notify_once(string.format("Warning: Invalid mode given to colorizer setup [ %s ]", options.mode))
end, 0)
options.mode = "background"
end

Expand All @@ -341,25 +336,23 @@ function colorizer.attach_to_buffer(bufnr, options, bo_type)
end

local autocmds = {}
local au_group_id = AUGROUP_ID
local au_group_id = AU_GROUP

local text_changed_au = { "TextChanged", "TextChangedI", "TextChangedP" }
-- only enable InsertLeave in sass, rest don't require it
if options.sass and options.sass.enable then
table.insert(text_changed_au, "InsertLeave")
end

if CURRENT_BUF == 0 then
CURRENT_BUF = bufnr
end
CURRENT_BUF = CURRENT_BUF == 0 and bufnr or CURRENT_BUF

if options.always_update then
-- attach using lua api so buffer gets updated even when not the current buffer
-- completely moving to buf_attach is not possible because it doesn't handle all the text change events
vim.api.nvim_buf_attach(bufnr, false, {
on_lines = function(_, buffer)
-- only reload if the buffer is not the current one
if not (CURRENT_BUF == buffer) then
if CURRENT_BUF ~= buffer then
-- only reload if it was not disabled using detach_from_buffer
if BUFFER_OPTIONS[bufnr] then
rehighlight_buffer(bufnr, options, BUFFER_LOCAL[bufnr])
Expand All @@ -368,7 +361,7 @@ function colorizer.attach_to_buffer(bufnr, options, bo_type)
end,
on_reload = function(_, buffer)
-- only reload if the buffer is not the current one
if not (CURRENT_BUF == buffer) then
if CURRENT_BUF ~= buffer then
-- only reload if it was not disabled using detach_from_buffer
if BUFFER_OPTIONS[bufnr] then
rehighlight_buffer(bufnr, options, BUFFER_LOCAL[bufnr])
Expand Down Expand Up @@ -509,8 +502,6 @@ function colorizer.setup(config)
end
end

AUGROUP_ID = augroup(AUGROUP_NAME, {})

local aucmd = { buftype = "BufWinEnter", filetype = "FileType" }
local function parse_opts(bo_type, tbl)
if type(tbl) == "table" then
Expand Down Expand Up @@ -542,7 +533,7 @@ function colorizer.setup(config)
end
end
autocmd({ aucmd[bo_type] }, {
group = AUGROUP_ID,
group = AU_GROUP,
pattern = bo_type == "filetype" and (SETUP_SETTINGS.all[bo_type] and "*" or list) or nil,
callback = function()
COLORIZER_SETUP_HOOK(bo_type)
Expand All @@ -557,7 +548,7 @@ function colorizer.setup(config)
parse_opts("buftype", buftypes)

autocmd("ColorScheme", {
group = AUGROUP_ID,
group = AU_GROUP,
callback = function()
require("colorizer").clear_highlight_cache()
end,
Expand All @@ -569,9 +560,7 @@ end
---@return table|nil
function colorizer.get_buffer_options(bufnr)
local buffer = colorizer.is_buffer_attached(bufnr)
if buffer then
return BUFFER_OPTIONS[buffer]
end
return BUFFER_OPTIONS[buffer]
end

--- Reload all of the currently active highlighted buffers.
Expand Down
33 changes: 15 additions & 18 deletions lua/colorizer/buffer.lua
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@ local function create_highlight(rgb_hex, mode)
end

--- Create highlight and set highlights
---@param bufnr number: buffer number (0 for current)
---@param ns_id number
---@param bufnr number: Buffer number (0 for current)
---@param ns_id number: Namespace id, default is "colorizer" created with vim.api.nvim_create_namespace
---@param line_start number
---@param line_end number
---@param data table: table output of `parse_lines`
Expand Down Expand Up @@ -135,23 +135,20 @@ end
--- Highlight the buffer region.
-- Highlight starting from `line_start` (0-indexed) for each line described by `lines` in the
-- buffer id `bufnr` and attach it to the namespace id `ns_id`.
---@param bufnr number: buffer number (0 for current)
---@param ns_id number: namespace id. default is "colorizer", created with vim.api.nvim_create_namespace
---@param bufnr number: Buffer number, 0 for current
---@param ns_id number: Namespace id, default is "colorizer" created 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
---@return boolean,table
function buffer.highlight(bufnr, ns_id, line_start, line_end, options, options_local)
local returns = { detach = { ns_id = {}, functions = {} } }
if bufnr == 0 or bufnr == nil then
bufnr = api.nvim_get_current_buf()
end
bufnr = (bufnr == 0 or not bufnr) and current_buf() or bufnr
ns_id = ns_id or buffer.default_namespace

local returns = { detach = { ns_id = {}, functions = {} } }
local lines = buf_get_lines(bufnr, line_start, line_end, false)

ns_id = ns_id 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)
Expand All @@ -171,8 +168,8 @@ end

--- Parse the given lines for colors and return a table containing
-- rgb_hex and range per line
---@param bufnr number: buffer number (0 for current)
---@param lines table: table of lines to parse
---@param bufnr number: Buffer number (0 for current)
---@param lines table: Table of lines to parse
---@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
Expand Down Expand Up @@ -248,19 +245,19 @@ local function getrow(bufnr)
end

--- Rehighlight the buffer if colorizer is active
---@param bufnr number: buffer number (0 for current)
---@param bufnr number: Buffer number, (0 for current)
---@param options table: Buffer options
---@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 buffer.rehighlight(bufnr, options, options_local, use_local_lines)
bufnr = (bufnr == 0 or not bufnr) and current_buf() or bufnr

local ns_id = buffer.default_namespace

local min, max
local min = 0
local max = -1
if use_local_lines and options_local then
min, max = options_local.__startline or 0, options_local.__endline or -1
min = options_local.__startline or min
max = options_local.__endline or max
else
min, max = getrow(bufnr)
end
Expand Down
19 changes: 9 additions & 10 deletions lua/colorizer/matcher.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ local sass_name_parser = require("colorizer.sass").name_parser

local B_HASH, DOLLAR_HASH = ("#"):byte(), ("$"):byte()

-- TODO: 2024-11-05 - Instead of AARRGGBB vs RRGGBBAA parsers, should we have
-- 0x, # prefix parsers for 0xAARRGGBB, 0xRRGGBB, 0xRGB and #RRGGBBAA, #RRGGBB,
-- #RGB?
local parser = {
["_0x"] = argb_hex_parser,
["_rgb"] = rgb_function_parser,
Expand All @@ -32,19 +35,15 @@ local matcher = {}
function matcher.compile(matchers, matchers_trie)
local trie = Trie(matchers_trie)

local function parse_fn(line, i, buf)
local function parse_fn(line, i, bufnr)
-- prefix #
if matchers.rgba_hex_parser then
if line:byte(i) == B_HASH then
return rgba_hex_parser(line, i, matchers.rgba_hex_parser)
end
if matchers.rgba_hex_parser and line:byte(i) == B_HASH then
return rgba_hex_parser(line, i, matchers.rgba_hex_parser)
end

-- prefix $, SASS Colour names
if matchers.sass_name_parser then
if line:byte(i) == DOLLAR_HASH then
return sass_name_parser(line, i, buf)
end
if matchers.sass_name_parser and line:byte(i) == DOLLAR_HASH then
return sass_name_parser(line, i, bufnr)
end

-- Prefix 0x, rgba, rgb, hsla, hsl
Expand Down Expand Up @@ -112,7 +111,6 @@ function matcher.make(options)

local matchers = {}
local matchers_prefix = {}
matchers.max_prefix_length = 0

if enable_names then
matchers.color_name_parser = { tailwind = options.tailwind }
Expand All @@ -138,6 +136,7 @@ function matcher.make(options)
matchers.rgba_hex_parser.minlen = minlen
end

-- TODO: 2024-11-05 - Add custom prefixes
if enable_AARRGGBB then
table.insert(matchers_prefix, "0x")
end
Expand Down
49 changes: 28 additions & 21 deletions lua/colorizer/parser/argb_hex.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
---Helper function to parse argb

local bit = require "bit"
local floor, min = math.floor, math.min
local band, rshift, lshift = bit.band, bit.rshift, bit.lshift
Expand All @@ -11,46 +9,55 @@ local parse_hex = utils.parse_hex

local parser = {}

local ARGB_MINIMUM_LENGTH = #"0xAARRGGBB" - 1
---parse for 0xaarrggbb and return rgb hex.
-- a format used in android apps
---parse for 0xAARRGGBB, 0xRRGGBB, or 0xRGB 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 hex value ended
---@return string|nil: rgb hex value
function parser.argb_hex_parser(line, i)
if #line < i + ARGB_MINIMUM_LENGTH then
local minlen = #"0xRGB" - 1
local maxlen = #"0xAARRGGBB" - 1
if #line < i + minlen then
return
end

local j = i + 2

local n = j + 8
local alpha
local n = j + maxlen
local alpha, r, g, b
local v = 0

while j <= min(n, #line) do
local b = line:byte(j)
if not byte_is_hex(b) then
local byte = line:byte(j) -- Renamed to `byte` to avoid conflict with `b`
if not byte_is_hex(byte) 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
v = parse_hex(byte) + lshift(v, 4)
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

if length == 10 then -- 0xAARRGGBB
alpha = band(rshift(v, 24), 0xFF) / 255
r = floor(band(rshift(v, 16), 0xFF) * alpha)
g = floor(band(rshift(v, 8), 0xFF) * alpha)
b = floor(band(v, 0xFF) * alpha)
elseif length == 8 then -- 0xRRGGBB
r = band(rshift(v, 16), 0xFF)
g = band(rshift(v, 8), 0xFF)
b = band(v, 0xFF)
elseif length == 5 then -- 0xRGB
r = band(rshift(v, 8), 0xF) * 17
g = band(rshift(v, 4), 0xF) * 17
b = band(v, 0xF) * 17
else
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
Expand Down
3 changes: 1 addition & 2 deletions lua/colorizer/parser/hsl.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
---Helper function to parse argb
local count = require("colorizer.utils").count
local floor = math.floor

Expand Down Expand Up @@ -100,7 +99,7 @@ function parser.hsl_function_parser(line, i, opts)
end

local r, g, b = hsl_to_rgb(h / 360, s / 100, l / 100)
if r == nil or g == nil or b == nil then
if not r or not g or not b then
return
end
local rgb_hex = string.format("%02x%02x%02x", r * a, g * a, b * a)
Expand Down
1 change: 0 additions & 1 deletion lua/colorizer/parser/names.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
---Helper function to parse argb
local api = vim.api

local bit = require "bit"
Expand Down
1 change: 0 additions & 1 deletion lua/colorizer/parser/rgb.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
---Helper function to parse argb
local count = require("colorizer.utils").count

local parser = {}
Expand Down
Loading

0 comments on commit f134063

Please sign in to comment.