🪴 a tiny, customizable statusline for neovim
1---@module 'lylla.config'
2
3---@class lylla.config
4---@field refresh_rate integer
5---@field events string[]
6---@field prefix string
7---@field hls table<'normal'|'visual'|'command'|'insert', vim.api.keyset.highlight>
8---@field modules any[]
9---@field winbar any[]
10
11local utils = require("lylla.utils")
12
13local M = {}
14
15---@param fn fun(): string[]
16---@param opts? { events: string[] }
17---@return table
18local function component(fn, opts)
19 local t = { _type = "component" }
20 t.fn = fn
21 t.opts = opts
22 return t
23end
24
25---@type lylla.config
26---@eval return MiniDoc.afterlines_to_code(MiniDoc.current.eval_section)
27---@text # Default ~
28M.default = {
29 refresh_rate = 300,
30 events = {
31 "WinEnter",
32 "BufEnter",
33 "BufWritePost",
34 "SessionLoadPost",
35 "FileChangedShellPost",
36 "VimResized",
37 "Filetype",
38 "CursorMoved",
39 "CursorMovedI",
40 "ModeChanged",
41 "CmdlineEnter",
42 },
43 prefix = "▌",
44 hls = {},
45 modules = {
46 component(function()
47 local prefix = require("lylla.config").get().prefix
48 local modehl = utils.get_modehl()
49 return {
50 { prefix, modehl },
51 { "[" .. vim.api.nvim_get_mode().mode .. "]", modehl },
52 }
53 end, {
54 events = { "ModeChanged", "CmdlineEnter" },
55 }),
56 { " " },
57 component(function()
58 return {
59 utils.getfilepath(),
60 utils.getfilename(),
61 { " " },
62 }
63 end, {
64 events = {
65 "WinEnter",
66 "BufEnter",
67 "BufWritePost",
68 "FileChangedShellPost",
69 "Filetype",
70 },
71 }),
72 { " " },
73 component(function()
74 return { utils.get_searchcount() }
75 end),
76 { "%=" },
77 {},
78 { "%=" },
79 component(function()
80 return {
81 { { "lsp :: " }, { utils.get_client() or "none" } },
82 { " | ", "NonText" },
83 { { "fmt :: " }, { utils.get_fmt() or "none" } },
84 { " | ", "NonText" },
85 }
86 end, { events = { "FileType" } }),
87 { "%p%%" },
88 { " | ", "NonText" },
89 { "%L lines" },
90 { " | ", "NonText" },
91 { "%l:%c" },
92 { " " },
93 },
94 winbar = {
95 component(function()
96 local prefix = require("lylla.config").get().prefix
97 local modehl = utils.get_modehl()
98 return {
99 { prefix, modehl },
100 }
101 end, {
102 events = { "ModeChanged", "CmdlineEnter" },
103 }),
104 { " " },
105 component(function()
106 return {
107 utils.getfilepath(),
108 utils.getfilename(),
109 { " " },
110 }
111 end, {
112 events = {
113 "WinEnter",
114 "BufEnter",
115 "BufWritePost",
116 "FileChangedShellPost",
117 "Filetype",
118 },
119 }),
120 { " " },
121 component(function()
122 return { utils.get_searchcount() }
123 end),
124 },
125}
126
127---@type lylla.config
128---@diagnostic disable-next-line: missing-fields
129M.config = {}
130
131---@private
132---@generic T: table|any[]
133---@param tdefault T
134---@param toverride T
135---@return T
136local function tmerge(tdefault, toverride)
137 if toverride == nil then
138 return tdefault
139 end
140
141 if vim.islist(tdefault) then
142 return toverride
143 end
144 if vim.tbl_isempty(tdefault) then
145 return toverride
146 end
147
148 return vim.iter(pairs(tdefault)):fold({}, function(tnew, k, v)
149 if toverride[k] == nil or type(v) ~= type(toverride[k]) then
150 tnew[k] = v
151 return tnew
152 end
153 if type(v) == "table" then
154 tnew[k] = tmerge(v, toverride[k])
155 return tnew
156 end
157
158 tnew[k] = toverride[k]
159 return tnew
160 end)
161end
162
163---@param tdefault lylla.config
164---@param toverride lylla.config
165---@return lylla.config
166function M.merge(tdefault, toverride)
167 if vim.fn.has("nvim-0.11.0") == 1 then
168 toverride =
169 vim.tbl_deep_extend("keep", toverride, { editor = { float = { solid_border = vim.o.winborder == "solid" } } })
170 end
171 return tmerge(tdefault, toverride)
172end
173
174---@return lylla.config
175function M.get()
176 return M.merge(M.default, M.config)
177end
178
179---@param cfg lylla.config
180---@return lylla.config
181function M.override(cfg)
182 return M.merge(M.default, cfg)
183end
184
185---@param cfg lylla.config
186function M.set(cfg)
187 M.config = cfg
188end
189
190return M