Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat!: support for more highlight attrs #150

Merged
merged 2 commits into from
Jul 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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