From dde3084106a70b9a79d48f426f6d6fec6fd203f7 Mon Sep 17 00:00:00 2001 From: akianonymus Date: Mon, 27 Feb 2023 14:30:10 +0530 Subject: Separate parsers into individual files --- lua/colorizer/color.lua | 394 +----------------------------------------------- 1 file changed, 1 insertion(+), 393 deletions(-) (limited to 'lua/colorizer/color.lua') diff --git a/lua/colorizer/color.lua b/lua/colorizer/color.lua index 370131b..29af64d 100644 --- a/lua/colorizer/color.lua +++ b/lua/colorizer/color.lua @@ -1,66 +1,7 @@ ----Helper functions to parse different colour formats +---Helper color functions --@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" -local byte_is_alphanumeric = utils.byte_is_alphanumeric -local byte_is_hex = utils.byte_is_hex -local byte_is_valid_colorchar = utils.byte_is_valid_colorchar -local count = utils.count -local parse_hex = utils.parse_hex - local color = {} -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 - - 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 @@ -86,107 +27,6 @@ function color.hsl_to_rgb(h, s, l) 255 * color.hue_to_rgb(p, q, h - 1 / 3) end -local CSS_HSLA_FN_MINIMUM_LENGTH = #"hsla(0,0%,0%)" - 1 -local CSS_HSL_FN_MINIMUM_LENGTH = #"hsl(0,0%,0%)" - 1 ----Parse for hsl() hsla() css function and return rgb hex. --- For more info: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl ----@param line string: Line to parse ----@param i number: Index of line from where to start parsing ----@param opts table: Values passed from matchers like prefix ----@return number|nil: Index of line where the hsla/hsl function ended ----@return string|nil: rgb hex value -function color.hsl_function_parser(line, i, opts) - local min_len = CSS_HSLA_FN_MINIMUM_LENGTH - local min_commas, min_spaces = 2, 2 - local pattern = "^" - .. opts.prefix - .. "%(%s*([.%d]+)([deg]*)([turn]*)(%s?)%s*(,?)%s*(%d+)%%(%s?)%s*(,?)%s*(%d+)%%%s*(/?,?)%s*([.%d]*)([%%]?)%s*%)()" - - if opts.prefix == "hsl" then - min_len = CSS_HSL_FN_MINIMUM_LENGTH - end - - if #line < i + min_len then - return - end - - local h, deg, turn, ssep1, csep1, s, ssep2, csep2, l, sep3, a, percent_sign, match_end = line:sub(i):match(pattern) - if not match_end then - return - end - if a == "" then - a = nil - else - min_commas = min_commas + 1 - end - - -- the text after hue should be either deg or empty - if not ((deg == "") or (deg == "deg") or (turn == "turn")) then - return - end - - local c_seps = ("%s%s%s"):format(csep1, csep2, sep3) - local s_seps = ("%s%s"):format(ssep1, ssep2) - -- comma separator syntax - if c_seps:match "," then - if not (count(c_seps, ",") == min_commas) then - return - end - -- space separator syntax with decimal or percentage alpha - elseif count(s_seps, "%s") >= min_spaces then - if a then - if not (c_seps == "/") then - return - end - end - else - return - end - - if not a then - a = 1 - else - a = tonumber(a) - -- if percentage, then convert to decimal - if percent_sign == "%" then - a = a / 100 - end - -- although alpha doesn't support larger values than 1, css anyways renders it at 1 - if a > 1 then - a = 1 - end - end - - h = tonumber(h) or 1 - -- single turn is 360 - if turn == "turn" then - h = 360 * h - end - - -- if hue angle if greater than 360, then calculate the hue within 360 - if h > 360 then - local turns = h / 360 - h = 360 * (turns - floor(turns)) - end - - -- if saturation or luminance percentage is greater than 100 then reset it to 100 - s = tonumber(s) - if s > 100 then - s = 100 - end - l = tonumber(l) - if l > 100 then - l = 100 - 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. -- Source: https://gist.github.com/mjackson/5311256 ---@param p number @@ -229,236 +69,4 @@ function color.is_bright(r, g, b) end end -local COLOR_MAP -local COLOR_TRIE -local COLOR_NAME_MINLEN, COLOR_NAME_MAXLEN -local COLOR_NAME_SETTINGS = { lowercase = true, strip_digits = false } -local TAILWIND_ENABLED = false ---- Grab all the colour values from `vim.api.nvim_get_color_map` and create a lookup table. --- COLOR_MAP is used to store the colour values ----@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 -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 = {} - COLOR_TRIE = Trie() - for k, v in pairs(api.nvim_get_color_map()) do - if not (COLOR_NAME_SETTINGS.strip_digits and k:match "%d+$") then - COLOR_NAME_MINLEN = COLOR_NAME_MINLEN and min(#k, COLOR_NAME_MINLEN) or #k - COLOR_NAME_MAXLEN = COLOR_NAME_MAXLEN and max(#k, COLOR_NAME_MAXLEN) or #k - local rgb_hex = tohex(v, 6) - COLOR_MAP[k] = rgb_hex - COLOR_TRIE:insert(k) - if COLOR_NAME_SETTINGS.lowercase then - local lowercase = k:lower() - COLOR_MAP[lowercase] = rgb_hex - COLOR_TRIE:insert(lowercase) - end - end - end - if opts and opts.tailwind then - if opts.tailwind == true or opts.tailwind == "normal" or opts.tailwind == "both" then - local tailwind = require "colorizer.tailwind_colors" - -- setup tailwind colors - for k, v in pairs(tailwind.colors) do - for _, pre in ipairs(tailwind.prefixes) do - local name = pre .. "-" .. k - COLOR_NAME_MINLEN = COLOR_NAME_MINLEN and min(#name, COLOR_NAME_MINLEN) or #name - COLOR_NAME_MAXLEN = COLOR_NAME_MAXLEN and max(#name, COLOR_NAME_MAXLEN) or #name - COLOR_MAP[name] = v - COLOR_TRIE:insert(name) - end - end - end - end - TAILWIND_ENABLED = opts.tailwind - end - - if #line < i + COLOR_NAME_MINLEN - 1 then - return - end - - if i > 1 and byte_is_valid_colorchar(line:byte(i - 1)) then - return - end - - local prefix = COLOR_TRIE:longest_prefix(line, i) - if prefix then - -- Check if there is a letter here so as to disallow matching here. - -- Take the Blue out of Blueberry - -- Line end or non-letter. - local next_byte_index = i + #prefix - if #line >= next_byte_index and byte_is_valid_colorchar(line:byte(next_byte_index)) then - return - end - return #prefix, COLOR_MAP[prefix] - end -end - -local CSS_RGBA_FN_MINIMUM_LENGTH = #"rgba(0,0,0)" - 1 -local CSS_RGB_FN_MINIMUM_LENGTH = #"rgb(0,0,0)" - 1 ----Parse for rgb() rgba() css function and return rgb hex. --- For more info: https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/rgb ----@param line string: Line to parse ----@param i number: Index of line from where to start parsing ----@param opts table: Values passed from matchers like prefix ----@return number|nil: Index of line where the rgb/rgba function ended ----@return string|nil: rgb hex value -function color.rgb_function_parser(line, i, opts) - local min_len = CSS_RGBA_FN_MINIMUM_LENGTH - local min_commas, min_spaces, min_percent = 2, 2, 3 - local pattern = "^" - .. opts.prefix - .. "%(%s*([.%d]+)([%%]?)(%s?)%s*(,?)%s*([.%d]+)([%%]?)(%s?)%s*(,?)%s*([.%d]+)([%%]?)%s*(/?,?)%s*([.%d]*)([%%]?)%s*%)()" - - if opts.prefix == "rgb" then - min_len = CSS_RGB_FN_MINIMUM_LENGTH - end - - if #line < i + min_len then - return - end - - local r, unit1, ssep1, csep1, g, unit2, ssep2, csep2, b, unit3, sep3, a, unit_a, match_end = - line:sub(i):match(pattern) - if not match_end then - return - end - - if a == "" then - a = nil - else - min_commas = min_commas + 1 - end - - local units = ("%s%s%s"):format(unit1, unit2, unit3) - if units:match "%%" then - if not ((count(units, "%%")) == min_percent) then - return - end - end - - local c_seps = ("%s%s%s"):format(csep1, csep2, sep3) - local s_seps = ("%s%s"):format(ssep1, ssep2) - -- comma separator syntax - if c_seps:match "," then - if not (count(c_seps, ",") == min_commas) then - return - end - -- space separator syntax with decimal or percentage alpha - elseif count(s_seps, "%s") >= min_spaces then - if a then - if not (c_seps == "/") then - return - end - end - else - return - end - - if not a then - a = 1 - else - a = tonumber(a) - -- if percentage, then convert to decimal - if unit_a == "%" then - a = a / 100 - end - -- although alpha doesn't support larger values than 1, css anyways renders it at 1 - if a > 1 then - a = 1 - end - end - - r = tonumber(r) - if not r then - return - end - g = tonumber(g) - if not g then - return - end - b = tonumber(b) - if not b then - return - end - - if unit1 == "%" then - r = r / 100 * 255 - g = g / 100 * 255 - b = b / 100 * 255 - else - -- although r,g,b doesn't support larger values than 255, css anyways renders it at 255 - if r > 255 then - r = 255 - end - if g > 255 then - g = 255 - end - if b > 255 then - b = 255 - end - end - - local rgb_hex = string.format("%02x%02x%02x", r * a, g * a, b * a) - return match_end - 1, rgb_hex -end - ----parse for #rrggbbaa 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 ----@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 -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 - return - end - - if i > 1 and byte_is_alphanumeric(line:byte(i - 1)) then - return - end - - local n = j + maxlen - 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 >= 7 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 ~= 4 and length ~= 7 and length ~= 9 then - return - end - - if alpha then - 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 9, rgb_hex - end - return (valid_lengths[length - 1] and length), line:sub(i + 1, i + length - 1) -end - return color -- cgit v1.2.3-70-g09d2