Skip to content

Instantly share code, notes, and snippets.

@craftzdog
Created October 3, 2023 07:24
Show Gist options
  • Save craftzdog/8012c78715414de7b5608bda08c77105 to your computer and use it in GitHub Desktop.
Save craftzdog/8012c78715414de7b5608bda08c77105 to your computer and use it in GitHub Desktop.
How to highlight HSL colors with `mini.hipatterns`
-- plugins/editor.lua
return {
{
"echasnovski/mini.hipatterns",
event = "BufReadPre",
opts = {
highlighters = {
hsl_color = {
pattern = "hsl%(%d+,? %d+,? %d+%)",
group = function(_, match)
local utils = require("craftzdog.utils")
local h, s, l = match:match("hsl%((%d+),? (%d+),? (%d+)%)")
h, s, l = tonumber(h), tonumber(s), tonumber(l)
local hex_color = utils.hslToHex(h, s, l)
return MiniHipatterns.compute_hex_color_group(hex_color, "bg")
end,
},
},
},
},
}
-- craftzdog/utils.lua
-- https://github.com/EmmanuelOga/columns/blob/master/utils/color.lua
local M = {}
local hexChars = "0123456789abcdef"
function M.hex_to_rgb(hex)
hex = string.lower(hex)
local ret = {}
for i = 0, 2 do
local char1 = string.sub(hex, i * 2 + 2, i * 2 + 2)
local char2 = string.sub(hex, i * 2 + 3, i * 2 + 3)
local digit1 = string.find(hexChars, char1) - 1
local digit2 = string.find(hexChars, char2) - 1
ret[i + 1] = (digit1 * 16 + digit2) / 255.0
end
return ret
end
--[[
* Converts an RGB color value to HSL. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes r, g, and b are contained in the set [0, 255] and
* returns h, s, and l in the set [0, 1].
*
* @param Number r The red color value
* @param Number g The green color value
* @param Number b The blue color value
* @return Array The HSL representation
]]
function M.rgbToHsl(r, g, b)
local max, min = math.max(r, g, b), math.min(r, g, b)
local h = 0
local s = 0
local l = 0
l = (max + min) / 2
if max == min then
h, s = 0, 0 -- achromatic
else
local d = max - min
if l > 0.5 then
s = d / (2 - max - min)
else
s = d / (max + min)
end
if max == r then
h = (g - b) / d
if g < b then
h = h + 6
end
elseif max == g then
h = (b - r) / d + 2
elseif max == b then
h = (r - g) / d + 4
end
h = h / 6
end
return h * 360, s * 100, l * 100
end
--[[
* Converts an HSL color value to RGB. Conversion formula
* adapted from http://en.wikipedia.org/wiki/HSL_color_space.
* Assumes h, s, and l are contained in the set [0, 1] and
* returns r, g, and b in the set [0, 255].
*
* @param Number h The hue
* @param Number s The saturation
* @param Number l The lightness
* @return Array The RGB representation
]]
function M.hslToRgb(h, s, l)
local r, g, b
if s == 0 then
r, g, b = l, l, l -- achromatic
else
function hue2rgb(p, q, t)
if t < 0 then
t = t + 1
end
if t > 1 then
t = t - 1
end
if t < 1 / 6 then
return p + (q - p) * 6 * t
end
if t < 1 / 2 then
return q
end
if t < 2 / 3 then
return p + (q - p) * (2 / 3 - t) * 6
end
return p
end
local q
if l < 0.5 then
q = l * (1 + s)
else
q = l + s - l * s
end
local p = 2 * l - q
r = hue2rgb(p, q, h + 1 / 3)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h - 1 / 3)
end
return r * 255, g * 255, b * 255
end
function M.hexToHSL(hex)
local hsluv = require("solarized-osaka.hsluv")
local rgb = M.hex_to_rgb(hex)
local h, s, l = M.rgbToHsl(rgb[1], rgb[2], rgb[3])
return string.format("hsl(%d, %d, %d)", math.floor(h + 0.5), math.floor(s + 0.5), math.floor(l + 0.5))
end
--[[
* Converts an HSL color value to RGB in Hex representation.
* @param Number h The hue
* @param Number s The saturation
* @param Number l The lightness
* @return String The hex representation
]]
function M.hslToHex(h, s, l)
local r, g, b = M.hslToRgb(h / 360, s / 100, l / 100)
return string.format("#%02x%02x%02x", r, g, b)
end
function M.replaceHexWithHSL()
-- Get the current line number
local line_number = vim.api.nvim_win_get_cursor(0)[1]
-- Get the line content
local line_content = vim.api.nvim_buf_get_lines(0, line_number - 1, line_number, false)[1]
-- Find hex code patterns and replace them
for hex in line_content:gmatch("#[0-9a-fA-F]+") do
local hsl = M.hexToHSL(hex)
line_content = line_content:gsub(hex, hsl)
end
-- Set the line content back
vim.api.nvim_buf_set_lines(0, line_number - 1, line_number, false, { line_content })
end
return M
@emeraldbanks
Copy link

@craftzdog How could the pattern be updated to support hsl(140deg, 52, 55)?

I have tried a number of things and yet haven't figured out the correct pattern.

  • "hsl%(%d+[deg]?,? %d+,? %d+%)"

@0inp
Copy link

0inp commented Aug 2, 2024

Hi !

Sorry to bother you, I'm also interested in this feature and can't make it work.
I've used your code mini.hipatterns config as it is (I've only just put the used functions hslToRgb and hslToHex inside the same lua file, in order to not used a utils module).
With vim.inspect, I manage to print the HEX color computed from each line with the HSL pattern, and that is correct (more or less, but I think there are only rounding approx, which is fine for me).
Nevertheless, I can't get any higlighting working.
On the
return MiniHipatterns.compute_hex_color_group(hex_color, "bg")
I have a warning Undefined global 'MiniHiPatterns'.. I can't understand how is this line working, maybe that could be the source of my problem ?

Is this gist working as it is ?
After that, I want to update the pattern to make it work for string such as "100 15.1% 10%" (without any hsl()) that you can find in nextjs/shadcn themes for example.

Thanks a lot !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment