aboutsummaryrefslogtreecommitdiff
path: root/lua/colorizer/parser/hsl.lua
diff options
context:
space:
mode:
Diffstat (limited to 'lua/colorizer/parser/hsl.lua')
-rw-r--r--lua/colorizer/parser/hsl.lua110
1 files changed, 110 insertions, 0 deletions
diff --git a/lua/colorizer/parser/hsl.lua b/lua/colorizer/parser/hsl.lua
new file mode 100644
index 0000000..cb6a19d
--- /dev/null
+++ b/lua/colorizer/parser/hsl.lua
@@ -0,0 +1,110 @@
+---Helper function to parse argb
+local count = require("colorizer.utils").count
+local floor = math.floor
+
+local hsl_to_rgb = require("colorizer.color").hsl_to_rgb
+
+local parser = {}
+
+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 parser.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 = 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
+
+return parser.hsl_function_parser