neovim configuration using rocks.nvim plugin manager
at main 6.9 kB view raw
1---@class bt.util.highlights 2local M = {} 3 4-- TODO: fuck. rewrite this sometime 5-- dealing with types is way hard then I thought 6 7---@alias HLAttr {from: string, attr: "fg" | "bg", alter: integer} 8---@alias float number 9 10---@class HLData 11---@field fg? string foreground 12---@field bg? string background 13---@field sp? string special 14---@field blend? integer between 0 and 100 15---@field bold? boolean 16---@field standout? boolean 17---@field underline? boolean 18---@field undercurl? boolean 19---@field underdouble? boolean 20---@field underdotted? boolean 21---@field underdashed? boolean 22---@field strikethrough? boolean 23---@field italic? boolean 24---@field reverse? boolean 25---@field nocombine? boolean 26---@field link? string 27---@field default? boolean 28 29---@alias HLAttrName 30---| '"fg"' 31---| '"bg"' 32---| '"sp"' 33---| '"blend"' 34---| '"bold"' 35---| '"standout"' 36---| '"underline"' 37---| '"undercurl"' 38---| '"underdouble"' 39---| '"underdotted"' 40---| '"underdashed"' 41---| '"strikethrough"' 42---| '"italic"' 43---| '"reverse"' 44---| '"nocombine"' 45---| '"link"' 46---| '"default"' 47 48---@class HLArgs: HLData 49---@field fg? string | HLAttr 50---@field bg? string | HLAttr 51---@field sp? string | HLAttr 52---@field clear? boolean clear existing highlight 53---@field inherit? string inherit other highlight 54 55local function num_to_hex(color) 56 return string.format("#%06X", color) 57end 58 59---@param opts? {name?: string, link?: boolean} 60---@param ns? integer 61---@return vim.api.keyset.hl_info|nil 62local function get_hl_as_hex(opts, ns) 63 opts = opts or {} 64 ns = ns or 0 65 opts.link = opts.link ~= nil and opts.link or false 66 local hl = vim.api.nvim_get_hl(ns, opts) 67 if vim.tbl_isempty(hl) then 68 return nil 69 end 70 hl.fg = hl.fg and num_to_hex(hl.fg) 71 hl.bg = hl.bg and num_to_hex(hl.bg) 72 return hl 73end 74 75---Change the brightness of a color, negative numbers darken and positive ones brighten 76---see: 77---1. https://stackoverflow.com/q/5560248 78---2. https://stackoverflow.com/a/37797380 79---@param color string A hex color 80---@param percent float a negative number darkens and a positive one brightens 81---@return string 82function M.tint(color, percent) 83 assert(color and percent, "cannot alter a color without specifying a color and percentage") 84 local r = tonumber(color:sub(2, 3), 16) 85 local g = tonumber(color:sub(4, 5), 16) 86 local b = tonumber(color:sub(6), 16) 87 if not r or not g or not b then 88 return "NONE" 89 end 90 local blend = function(component) 91 component = math.floor(component * (1 + percent)) 92 return math.min(math.max(component, 0), 255) 93 end 94 return string.format("#%02x%02x%02x", blend(r), blend(g), blend(b)) 95end 96 97---Get the value a highlight group whilst handling errors and fallbacks as well as returning a gui value 98---If no attribute is specified return the entire highlight table 99---in the right format 100---@param group string 101---@param attribute HLAttrName 102---@param fallback string? 103---@return string 104function M.get(group, attribute, fallback) 105 local data = get_hl_as_hex({ name = group }) 106 local color = (data and data[attribute]) or fallback or "NONE" 107 if not color then 108 local error_msg = 109 string.format("failed to get highlight %s for attribute %s\n%s", group, attribute, debug.traceback()) 110 local error_title = string.format("Highlight - get(%s)", group) 111 vim.schedule(function() 112 vim.notify(error_msg, vim.log.levels.ERROR, { title = error_title }) 113 end) 114 return "NONE" 115 end 116 return color 117end 118 119---resolve fg/bg/sp attribute type 120---@param hl string | HLAttr 121---@param attr string 122---@return string 123local function resolve_from_attr(hl, attr) 124 if type(hl) ~= "table" then 125 return hl 126 end 127 local color = M.get(hl.from, hl.attr or attr) 128 color = color == "NONE" and M.get("Normal", hl.attr or attr) or color 129 -- TODO: tint color 130 return color 131end 132 133--- Sets a neovim highlight with some syntactic sugar. It takes a highlight table and converts 134--- any highlights specified as `GroupName = {fg = { from = 'group'}}` into the underlying colour 135--- by querying the highlight property of the from group so it can be used when specifying highlights 136--- as a shorthand to derive the right colour. 137--- For example: 138--- ```lua 139--- M.set({ MatchParen = {fg = {from = 'ErrorMsg'}}}) 140--- ``` 141--- This will take the foreground colour from ErrorMsg and set it to the foreground of MatchParen. 142--- NOTE: this function must NOT mutate the options table as these are re-used when the colorscheme is updated 143--- 144---@param ns integer 145---@param name string 146---@param opts HLArgs 147---@overload fun(name: string, opts: HLArgs) 148function M.set(ns, name, opts) 149 if type(ns) == "string" and type(name) == "table" then 150 opts, name, ns = name, ns, 0 151 end 152 153 local hl = opts.clear and {} or get_hl_as_hex({ name = opts.inherit or name }) or {} 154 -- clear cterm 155 if not opts.cterm then 156 hl.cterm = nil 157 end 158 for attribute, data in pairs(opts) do 159 if attribute ~= "clear" and attribute ~= "inherit" then 160 local new_data = resolve_from_attr(data, attribute) 161 hl[attribute] = new_data 162 end 163 end 164 165 -- FIXME: this part 166 vim.api.nvim_set_hl(ns, name, hl --[[@as vim.api.keyset.highlight]]) 167end 168 169---Apply a list of highlights 170---@param hls table<string, HLArgs> 171---@param namespace integer? 172function M.all(hls, namespace) 173 for name, args in pairs(hls) do 174 M.set(namespace or 0, name, args) 175 end 176end 177 178---Set window local highlights 179---@param name string 180---@param win_id number 181---@param hls table<string, HLArgs> 182function M.set_winhl(name, win_id, hls) 183 local namespace = vim.api.nvim_create_namespace(name) 184 M.all(hls, namespace) 185 vim.api.nvim_win_set_hl_ns(win_id, namespace) 186end 187 188---Run `cb()` on `ColorScheme` event. 189---This is useful when *color override* code is quite complicate 190---@param name string 191---@param cb function 192function M.plugin_wrap(name, cb) 193 cb() 194 local augroup_name = name:gsub("^%l", string.upper) .. "HighlightOverrides" 195 vim.api.nvim_create_autocmd({ "ColorScheme", "UIEnter" }, { 196 group = vim.api.nvim_create_augroup(augroup_name, { clear = true }), 197 callback = function() 198 -- Defer resetting these highlights to ensure they apply *after* other overrides 199 vim.defer_fn(function() 200 cb() 201 end, 1) 202 end, 203 }) 204end 205 206---Apply highlights for a plugin and refresh on colorscheme change 207---@param name string plugin name 208---@param hls table<string, HLArgs> 209function M.plugin(name, hls) 210 M.plugin_wrap(name, function() 211 M.all(hls) 212 end) 213end 214 215---Apply highlight to given text 216---@param content any 217---@param hlgroup string 218---@return string 219function M.hl_text(content, hlgroup) 220 return string.format("%%#%s#%s%%*", hlgroup, content) 221end 222 223return M