Skip to content

Commit

Permalink
feat!: support for more highlight attrs (#150)
Browse files Browse the repository at this point in the history
Breaking: configuring gui attributes is now done via separate table fields instead of a string.
  • Loading branch information
willothy committed Jul 31, 2023
1 parent c2842a5 commit e0db489
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 43 deletions.
72 changes: 53 additions & 19 deletions lua/cokeline/components.lua
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,28 @@ Component.__index = Component
---@param i number
---@return Component<Cx>
Component.new = function(c, i, default_hl)
local function attr(name, default)
if c[name] ~= nil then
return c[name]
end
if default_hl[name] ~= nil then
return default_hl[name]
end
return default
end
-- `default_hl` is `nil` when called by `components.lua#63`
default_hl = default_hl or _G.cokeline.config.default_hl
local component = {
index = i,
text = c.text,
fg = c.fg or default_hl.fg or "NONE",
bg = c.bg or default_hl.bg or "NONE",
style = c.style or default_hl.style or "NONE",
fg = attr("fg", "NONE"),
bg = attr("bg", "NONE"),
sp = attr("sp", "NONE"),
bold = attr("bold"),
italic = attr("italic"),
underline = attr("underline"),
undercurl = attr("undercurl"),
strikethrough = attr("strikethrough"),
highlight = c.highlight,
delete_buffer_on_left_click = c.delete_buffer_on_left_click or false,
on_click = c.on_click,
Expand Down Expand Up @@ -68,8 +82,13 @@ end
Component.render = function(self, context)
---@return string
local evaluate = function(field)
return (type(field) == "string" and field)
or (type(field) == "function" and field(context.provider))
if field == nil then
return
end
if type(field) == "function" then
return field(context.provider)
end
return field
end

local component = vim.deepcopy(self)
Expand All @@ -84,26 +103,41 @@ Component.render = function(self, context)
component.bufnr = context.provider.number
end

-- `evaluate(self.hl.*)` might return `nil`, in that case we fallback to the
-- default highlight first and to NONE if that's `nil` too.
local style = evaluate(self.style)
or evaluate(_G.cokeline.config.default_hl.style)
or "NONE"
local fg = evaluate(self.fg)
or evaluate(_G.cokeline.config.default_hl.fg)
or "NONE"
local bg = evaluate(self.bg)
or evaluate(_G.cokeline.config.default_hl.bg)
or "NONE"

if component.highlight then
component.hlgroup = Hlgroup.new_existing(evaluate(component.highlight))
else
-- `evaluate(self.hl.*)` might return `nil`, in that case we fallback to the
-- default highlight first and to NONE if that's `nil` too.
-- local style = evaluate(self.style)
-- or evaluate(_G.cokeline.config.default_hl.style)
-- or "NONE"
local fg = evaluate(self.fg)
or evaluate(_G.cokeline.config.default_hl.fg)
or "NONE"
local bg = evaluate(self.bg)
or evaluate(_G.cokeline.config.default_hl.bg)
or "NONE"
local sp = evaluate(self.sp)
or evaluate(_G.cokeline.config.default_hl.sp)
or "NONE"
local attrs = {}
attrs.bold = evaluate(self.bold)
or evaluate(_G.cokeline.config.default_hl.bold)
attrs.italic = evaluate(self.italic)
or evaluate(_G.cokeline.config.default_hl.italic)
attrs.underline = evaluate(self.underline)
or evaluate(_G.cokeline.config.default_hl.underline)
attrs.undercurl = evaluate(self.undercurl)
or evaluate(_G.cokeline.config.default_hl.undercurl)
attrs.strikethrough = evaluate(self.strikethrough)
or evaluate(_G.cokeline.config.default_hl.strikethrough)

component.hlgroup = Hlgroup.new(
("Cokeline_%s_%s"):format(component.bufnr or context.kind, self.index),
style,
fg,
bg
bg,
sp,
attrs
)
end

Expand Down
122 changes: 98 additions & 24 deletions lua/cokeline/hlgroups.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,69 @@
local get_hex = require("cokeline.utils").get_hex
local cmd = vim.cmd
local M = {}

-- These functions are called a LOT.
-- Cache the results to avoid repeat API calls
local cache = {
hex = {},
groups = {},
}

-- Invalidate the cache on colorscheme change
vim.api.nvim_create_autocmd("Colorscheme", {
group = vim.api.nvim_create_augroup(
"cokeline_color_cache",
{ clear = true }
),
callback = function()
cache.groups = {}
end,
})

---@param rgb integer
---@return string hex
function M.hex(rgb)
if cache.hex[rgb] then
return cache.hex[rgb]
end
local band, lsr = bit.band, bit.rshift

local r = lsr(band(rgb, 0xff00000), 16)
local g = lsr(band(rgb, 0x00ff00), 8)
local b = band(rgb, 0x0000ff)

return ("#%02x%02x%02x"):format(r, g, b)
end

function M.get_hl(name)
if cache.groups[name] then
return cache.groups[name]
end
local hl = vim.api.nvim_get_hl(0, { name = name })
if not hl then
return
end
if hl.fg then
hl.fg = M.hex(hl.fg)
end
if hl.bg then
hl.bg = M.hex(hl.bg)
end
if hl.sp then
hl.sp = M.hex(hl.sp)
end
cache.groups[name] = hl
return hl
end

function M.get_hl_attr(name, attr)
if cache.hex[name] and cache.hex[name][attr] then
return cache.hex[name][attr]
end
local hl = M.get_hl(name)
if not hl then
return
end
return hl[attr]
end

---@class Hlgroup
---@field name string
Expand All @@ -11,30 +75,40 @@ Hlgroup.__index = Hlgroup

---Sets the highlight group, then returns a new `Hlgroup` table.
---@param name string
---@param gui string
---@param guifg string
---@param guibg string
---@param fg string
---@param bg string | nil
---@param sp string | nil
---@param gui_attrs table<string, bool>
---@return Hlgroup
Hlgroup.new = function(name, gui, guifg, guibg)
if vim.fn.hlexists(guifg) == 1 then
guifg = get_hex(guifg, "fg")
Hlgroup.new = function(name, fg, bg, sp, gui_attrs)
local hlgroup = {}
if vim.fn.hlexists(fg) == 1 then
hlgroup.fg = M.get_hl_attr(fg, "fg")
else
hlgroup.fg = fg
end
if vim.fn.hlexists(bg) == 1 then
hlgroup.bg = M.get_hl_attr(bg, "bg")
else
hlgroup.bg = bg
end
if vim.fn.hlexists(guibg) == 1 then
guibg = get_hex(guibg, "bg")
if sp and vim.fn.hlexists(sp) == 1 then
hlgroup.sp = M.get_hl_attr(sp, "sp")
else
hlgroup.sp = sp
end

-- Clear the highlight group before (re)defining it.
cmd(("highlight clear %s"):format(name))
cmd(
("highlight %s gui=%s guifg=%s guibg=%s"):format(name, gui, guifg, guibg)
)
for attr, val in pairs(gui_attrs) do
if type(val) == "boolean" then
hlgroup[attr] = val
elseif type(val) == "string" and vim.fn.hlexists(val) == 1 then
hlgroup[attr] = M.get_hl_attr(val, attr)
end
end

local hlgroup = {
name = name,
gui = gui,
guifg = guifg,
guibg = guibg,
}
vim.api.nvim_set_hl(0, name, hlgroup)

hlgroup.name = name
setmetatable(hlgroup, Hlgroup)
return hlgroup
end
Expand All @@ -58,6 +132,6 @@ Hlgroup.embed = function(self, text)
return ("%%#%s#%s%%*"):format(self.name, text)
end

return {
Hlgroup = Hlgroup,
}
M.Hlgroup = Hlgroup

return M
1 change: 1 addition & 0 deletions lua/cokeline/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local vim_fn = vim.fn

---Returns the color set by the current colorscheme for the `attr` attribute of
---the `hlgroup_name` highlight group in hexadecimal format.
---@deprecated
---@param hlgroup_name string
---@param attr '"fg"' | '"bg"'
---@return string
Expand Down

0 comments on commit e0db489

Please sign in to comment.