🪴 a tiny, customizable statusline for neovim
1---@module 'lylla.config'
2
3---@class lylla.item
4---@field fn fun(): any
5---@field opts? { events?: string[] }
6
7---@alias lylla.item.tuple {[1]: string, [2]?: string}
8
9---@class lylla.config
10---@field refresh_rate integer
11---@field events string[]
12---@field hls table<'normal'|'visual'|'command'|'insert', vim.api.keyset.highlight>
13---@field modules (lylla.item|lylla.item.tuple|string)[]
14---@field winbar any[]
15
16local M = {}
17
18---@type lylla.config
19---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
20---@text # Default ~
21M.default = {
22 refresh_rate = 300,
23 events = {
24 "WinEnter",
25 "BufEnter",
26 "BufWritePost",
27 "SessionLoadPost",
28 "FileChangedShellPost",
29 "VimResized",
30 "Filetype",
31 "CursorMoved",
32 "CursorMovedI",
33 "ModeChanged",
34 "CmdlineEnter",
35 },
36 hls = {},
37 modules = {
38 "%<%f %h%w%m%r",
39 "%=",
40 {
41 fn = function()
42 if vim.o.showcmdloc == "statusline" then
43 return "%-10.S"
44 end
45 return ""
46 end,
47 },
48 { " " },
49 {
50 fn = function()
51 if not vim.b.keymap_name then
52 return ""
53 end
54 return "<" .. vim.b.keymap_name .. ">"
55 end,
56 },
57 { " " },
58 {
59 fn = function()
60 if vim.bo.busy > 0 then
61 return "◐ "
62 end
63 return ""
64 end,
65 },
66 { " " },
67 {
68 fn = function()
69 if not package.loaded["vim.diagnostic"] then
70 return ""
71 end
72 return vim.diagnostic.status()
73 end,
74 opts = {
75 events = { "DiagnosticChanged" },
76 },
77 },
78 { " " },
79 {
80 fn = function()
81 if not vim.o.ruler then
82 return ""
83 end
84 if vim.o.rulerformat == "" then
85 return "%-14.(%l,%c%V%) %P"
86 end
87 return vim.o.rulerformat
88 end,
89 },
90 },
91 winbar = {},
92}
93
94---@type lylla.config
95---@diagnostic disable-next-line: missing-fields
96M.config = {}
97
98---@private
99---@generic T: table|any[]
100---@param tdefault T
101---@param toverride T
102---@return T
103local function tmerge(tdefault, toverride)
104 if toverride == nil then
105 return tdefault
106 end
107
108 if vim.islist(tdefault) then
109 return toverride
110 end
111 if vim.tbl_isempty(tdefault) then
112 return toverride
113 end
114
115 return vim.iter(pairs(tdefault)):fold({}, function(tnew, k, v)
116 if toverride[k] == nil or type(v) ~= type(toverride[k]) then
117 tnew[k] = v
118 return tnew
119 end
120 if type(v) == "table" then
121 tnew[k] = tmerge(v, toverride[k])
122 return tnew
123 end
124
125 tnew[k] = toverride[k]
126 return tnew
127 end)
128end
129
130---@param tdefault lylla.config
131---@param toverride lylla.config
132---@return lylla.config
133function M.merge(tdefault, toverride)
134 if vim.fn.has("nvim-0.11.0") == 1 then
135 toverride =
136 vim.tbl_deep_extend("keep", toverride, { editor = { float = { solid_border = vim.o.winborder == "solid" } } })
137 end
138 return tmerge(tdefault, toverride)
139end
140
141---@return lylla.config
142function M.get()
143 return M.merge(M.default, M.config)
144end
145
146---@param cfg lylla.config
147---@return lylla.config
148function M.override(cfg)
149 return M.merge(M.default, cfg)
150end
151
152---@param cfg lylla.config
153function M.set(cfg)
154 M.config = cfg
155end
156
157return M