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