feat: Improve hsl parser

follow https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/hsl

add support for deg, turn and all alpha values

merge hsl and hsla into one parser

less regex computation
This commit is contained in:
akianonymus 2023-02-25 19:50:18 +05:30 committed by Akshay
parent 550332c579
commit afbcc17d12
13 changed files with 115 additions and 123 deletions

View file

@ -387,9 +387,8 @@ Functions: ~
|hsl_to_rgb| - Converts an HSL color value to RGB.
|hsl_function_parser| - Parse for hsl() css function and return rgb hex.
|hsla_function_parser| - Parse for hsl() css function and return rgb hex.
|hsl_function_parser| - Parse for hsl() hsla() css function and return rgb
hex.
|hue_to_rgb| - Convert hsl colour values to rgb.
@ -434,28 +433,20 @@ hsl_to_rgb({h}, {s}, {l}) *colorizer.color.hsl_to_rgb*
hsl_function_parser({line}, {i}) *colorizer.color.hsl_function_parser*
Parse for hsl() css function and return rgb hex.
hsl_function_parser({line}, {i}, {opts}) *colorizer.color.hsl_function_parser*
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
Parameters: ~
{line} - string: Line to parse
{i} - number: Index of line from where to start parsing
{opts} - table: Values passed from matchers like prefix
returns:~
number or nil: Index of line where the hsl function ended
string or nil: rgb hex value
hsla_function_parser({line}, {i}) *colorizer.color.hsla_function_parser*
Parse for hsl() css function and return rgb hex.
Parameters: ~
{line} - string: Line to parse
{i} - number: Index of line from where to start parsing
returns:~
number or nil: Index of line where the hsla function ended
number or nil: Index of line where the hsla/hsl function ended
string or nil: rgb hex value

View file

@ -87,7 +87,7 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated - September </i>
<i style="float:right;">Last updated - February </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -333,7 +333,7 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated - September </i>
<i style="float:right;">Last updated - February </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -70,12 +70,8 @@
<td class="summary">Converts an HSL color value to RGB.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#hsl_function_parser">hsl_function_parser (line, i)</a></td>
<td class="summary">Parse for hsl() css function and return rgb hex.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#hsla_function_parser">hsla_function_parser (line, i)</a></td>
<td class="summary">Parse for hsl() css function and return rgb hex.</td>
<td class="name" nowrap><a href="#hsl_function_parser">hsl_function_parser (line, i, opts)</a></td>
<td class="summary">Parse for hsl() hsla() css function and return rgb hex.</td>
</tr>
<tr>
<td class="name" nowrap><a href="#hue_to_rgb">hue_to_rgb (p, q, t)</a></td>
@ -174,10 +170,11 @@
</dd>
<dt>
<a name = "hsl_function_parser"></a>
<strong>hsl_function_parser (line, i)</strong>
<strong>hsl_function_parser (line, i, opts)</strong>
</dt>
<dd>
Parse for hsl() css function and return rgb hex.
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
<h3>Parameters:</h3>
@ -188,42 +185,15 @@
<li><span class="parameter">i</span>
number: Index of line from where to start parsing
</li>
</ul>
<h3>Returns:</h3>
<ol>
<li>
number|nil: Index of line where the hsl function ended</li>
<li>
string|nil: rgb hex value</li>
</ol>
</dd>
<dt>
<a name = "hsla_function_parser"></a>
<strong>hsla_function_parser (line, i)</strong>
</dt>
<dd>
Parse for hsl() css function and return rgb hex.
<h3>Parameters:</h3>
<ul>
<li><span class="parameter">line</span>
string: Line to parse
</li>
<li><span class="parameter">i</span>
number: Index of line from where to start parsing
<li><span class="parameter">opts</span>
table: Values passed from matchers like prefix
</li>
</ul>
<h3>Returns:</h3>
<ol>
<li>
number|nil: Index of line where the hsla function ended</li>
number|nil: Index of line where the hsla/hsl function ended</li>
<li>
string|nil: rgb hex value</li>
</ol>
@ -424,7 +394,7 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated - September </i>
<i style="float:right;">Last updated - February </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -490,7 +490,7 @@ Setup an autocmd which enables colorizing for the filetypes and options specifie
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated - September </i>
<i style="float:right;">Last updated - February </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -140,7 +140,7 @@ Do not try make the function again if it is present in the cache
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated - September </i>
<i style="float:right;">Last updated - February </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -182,7 +182,7 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated - September </i>
<i style="float:right;">Last updated - February </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -136,7 +136,7 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated - September </i>
<i style="float:right;">Last updated - February </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -64,7 +64,7 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated - September </i>
<i style="float:right;">Last updated - February </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -319,7 +319,7 @@
</div> <!-- id="main" -->
<div id="about">
<i>generated by <a href="http://github.com/stevedonovan/LDoc">LDoc 1.4.6</a></i>
<i style="float:right;">Last updated - September </i>
<i style="float:right;">Last updated - February </i>
</div> <!-- id="about" -->
</div> <!-- id="container" -->
</body>

View file

@ -86,76 +86,99 @@ 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() css function and return rgb hex.
---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
---@return number|nil: Index of line where the hsl function ended
---@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)
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
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*(%d+)%%(,?)(%s?)%s*(%d+)%%%s*(/?,?)%s*([.%d]*)([%%]?)%s*%)()"
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
if opts.prefix == "hsl" then
min_len = CSS_HSL_FN_MINIMUM_LENGTH
end
if #line < i + min_len 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*%)()"
local h, deg, turn, csep1, ssep1, s, csep2, ssep2, l, sep3, a, percent_sign, match_end = line:sub(i):match(pattern)
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
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 (#c_seps:match ",*" == min_commas) then
return
end
end
a = tonumber(a)
if not a or a > 1 then
-- space separator syntax with decimal or percentage alpha
elseif #s_seps:match "%s*" >= min_spaces then
if a then
if not (c_seps == "/") then
return
end
end
else
return
end
h = tonumber(h)
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
return
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
return
s = 100
end
l = tonumber(l)
if l > 100 then
return
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

View file

@ -17,7 +17,7 @@ local parser = {
["_rgb"] = color.rgb_function_parser,
["_rgba"] = color.rgba_function_parser,
["_hsl"] = color.hsl_function_parser,
["_hsla"] = color.hsla_function_parser,
["_hsla"] = color.hsl_function_parser,
}
local matcher = {}
@ -49,7 +49,7 @@ function matcher.compile(matchers, matchers_trie)
if prefix then
local fn = "_" .. prefix
if parser[fn] then
return parser[fn](line, i, matchers[fn])
return parser[fn](line, i, matchers[prefix])
end
end
@ -145,6 +145,10 @@ function matcher.make(options)
table.insert(matchers_prefix, "hsl")
end
for _, value in ipairs(matchers_prefix) do
matchers[value] = { prefix = value }
end
loop_parse_fn = matcher.compile(matchers, matchers_prefix)
MATCHER_CACHE[matcher_key] = loop_parse_fn

View file

@ -20,8 +20,11 @@ White
rgb(0,0,0) rgb(10, 100 , 100)
rgba(200,30,0,1) rgba(200,30,0,0.5)
hsl(300,50%,50%) hsla(300,50%,50%,0.5)
hsla(300,50%,50%,1.0000000000000001)
hsl(300 50% 50%) hsl(300 50% 50% / 1) hsl(100 80% 50% / 0.4)
hsl(990 80% 50% / 0.4) hsl(720 80% 50% / 0.4)
hsl(1turn 80% 50% / 0.4) hsl(0.4turn 80% 50% / 0.4) hsl(1.4turn 80% 50% / 0.4)
hsla(300 50% 50%) hsla(300 50% 50% / 1)
hsla(300 50% 50% / 0.4) hsla(300,50%,50%,05)
hsla(360,50%,50%,1.0000000000000001)
]]
@ -37,9 +40,10 @@ Blueberry Gray1000 BlueGree BlueGray
matcher#add
rgb(10,256,100)
rgb (10,255,100)
rgb(10, 1 00 , 100)
hsla(300,50%,50%,05)
hsla(300,50%,50%,1.000000000000001)
rgb(10, 1 00 , 100)
hsl(300 50% 50% 1)
hsl(300 50% 50 / 1)
hsla(10 10% 10% 1)
hsla(300,50%,50,1.0000000000000001)
hsla(300,50,50,1.0000000000000001)
hsla(361,50,50,1.0000000000000001)