馃 a tiny, customizable statusline for neovim
1local utils = require("lylla.utils")
2
3---@type lylla.proto
4---@diagnostic disable-next-line: missing-fields
5local winbar = {}
6
7winbar.wins = {}
8
9---@class lylla.proto
10---@field new fun(self, win): lylla.proto
11function winbar:new(win)
12 if winbar.wins[win] then
13 winbar.wins[win]:close()
14 end
15 local stl = setmetatable({
16 win = win,
17 modules = vim.deepcopy(require("lylla.config").get().winbar, true),
18 }, { __index = winbar })
19 winbar.wins[win] = stl
20 return stl
21end
22
23---@class lylla.proto
24---@field init fun(self)
25function winbar:init()
26 local err, err_kind
27 ---@diagnostic disable-next-line: assign-type-mismatch
28 self.timer, err, err_kind = vim.uv.new_timer()
29 if not self.timer or err then
30 vim.api.nvim_echo({ { err_kind }, { "\n\t" }, { err } }, true, { err = true })
31 return
32 end
33
34 local refresh = require("lylla.config").get().refresh_rate
35 self.timer:start(0, refresh, function()
36 self:refresh()
37 end)
38
39 self.refreshau = vim.api.nvim_create_autocmd(require("lylla.config").get().events, {
40 group = vim.api.nvim_create_augroup("lylla:refresh", { clear = false }),
41 callback = function(ev)
42 self:refresh(ev)
43 end,
44 })
45end
46
47---@class lylla.proto
48---@field close fun(self)
49function winbar:close()
50 self.timer:stop()
51 self.timer:close()
52 vim.api.nvim_del_autocmd(self.refreshau)
53 winbar.wins[self.win] = nil
54end
55
56---@class lylla.proto
57---@field refresh fun(self, ev?: vim.api.keyset.create_autocmd.callback_args)
58function winbar:refresh(ev)
59 vim.schedule(function()
60 if not vim.api.nvim_win_is_valid(self.win) then
61 return
62 end
63
64 local buf = vim.api.nvim_win_get_buf(self.win)
65 if vim.bo[buf].buftype ~= "" then
66 return
67 end
68
69 local ok, result = pcall(vim.api.nvim_win_call, self.win, function()
70 return self:get(ev)
71 end)
72 if not ok then
73 return
74 end
75
76 vim.wo[self.win].winbar = result
77 end)
78end
79
80---@class lylla.proto
81---@field fold fun(self, ev?: vim.api.keyset.create_autocmd.callback_args): string
82function winbar:fold(ev)
83 local modules = vim
84 .iter(ipairs(self.modules))
85 :map(function(i, module)
86 if type(module) == "table" and module._type == "component" then
87 if module.opts and module.opts.events then
88 -- refresh from timer
89 if not ev and module.prev then
90 return module.prev
91 end
92 -- refresh from non-match event
93 if ev and not vim.tbl_contains(module.opts.events, ev.event) and module.prev then
94 return module.prev
95 end
96 end
97 module.prev = module.fn()
98 return module.prev
99 end
100 if type(module) == "function" then
101 module = module()
102 end
103 return module
104 end)
105 :totable()
106 modules = utils.flatten(modules, 1)
107 return vim.iter(modules):fold("", function(str, module)
108 if type(module) ~= "table" or #module == 0 then
109 return str
110 end
111 local text = module[1]
112 if #module > 1 then
113 return str .. "%#" .. module[2] .. "#" .. text .. "%*"
114 end
115 return str .. "%*" .. text
116 end)
117end
118
119---@class lylla.proto
120---@field get fun(self, ev?: vim.api.keyset.create_autocmd.callback_args)
121function winbar:get(ev)
122 return self:fold(ev)
123end
124
125return winbar