馃 a tiny, customizable statusline for neovim
3
fork

Configure Feed

Select the types of activity you want to include in your feed.

at main 211 lines 5.7 kB view raw
1local log = require("lylla.log") 2local utils = require("lylla.utils") 3 4---@class lylla.proto 5---@field wins table<integer, table> 6---@field win integer 7---@field modules any[] 8---@field winbar any[] 9---@field timer uv.uv_timer_t 10---@field refreshau integer 11local statusline = {} 12 13statusline.wins = {} 14 15---@class lylla.proto 16---@field new fun(win: integer): lylla.proto 17function statusline.new(win) 18 if statusline.wins[win] then 19 statusline.wins[win]:close() 20 end 21 local stl = setmetatable({ 22 win = win, 23 modules = vim.deepcopy(require("lylla.config").get().modules, true), 24 winbar = vim.deepcopy(require("lylla.config").get().winbar, true), 25 }, { __index = statusline }) 26 statusline.wins[win] = stl 27 return stl 28end 29 30---@class lylla.proto 31---@field try_new fun(win: integer?): lylla.proto 32function statusline.try_new(win) 33 win = win or vim.api.nvim_get_current_win() 34 35 if statusline.wins[win] then 36 return statusline.wins[win] 37 end 38 return statusline.new(win) 39end 40 41---@class lylla.proto 42---@field init fun(self) 43function statusline:init() 44 if self.initialized then 45 return 46 end 47 48 local err, err_kind 49 ---@diagnostic disable-next-line: assign-type-mismatch 50 self.timer, err, err_kind = vim.uv.new_timer() 51 if not self.timer or err then 52 vim.api.nvim_echo({ { err_kind }, { "\n\t" }, { err } }, true, { err = true }) 53 return 54 end 55 56 local refresh = require("lylla.config").get().refresh_rate 57 self.timer:start(0, refresh, function() 58 self:refresh() 59 end) 60 61 self.refreshau = vim.api.nvim_create_augroup(("@lylla.refresh.%d"):format(self.win), { clear = true }) 62 63 local events = self:getevents() 64 65 for i = 1, #events do 66 local event = events[i] 67 local eventname, eventpattern = unpack(vim.split(event, " "), 1, 2) 68 vim.api.nvim_create_autocmd(eventname, { 69 group = self.refreshau, 70 pattern = eventpattern, 71 callback = function(ev) 72 self:refresh(ev) 73 end, 74 }) 75 end 76 77 self.initialized = true 78end 79 80---@class lylla.proto 81---@field close fun(self) 82function statusline:close() 83 self.timer:stop() 84 self.timer:close() 85 pcall(vim.api.nvim_del_augroup_by_id, self.refreshau) 86 statusline.wins[self.win] = nil 87end 88 89---@class lylla.proto 90---@field getevents fun(self): string[] 91function statusline:getevents() 92 local t = vim.iter(ipairs(self.modules)):fold({}, function(acc, _, module) 93 if type(module) == "table" and module.fn and type(module.fn) == "function" then 94 if module.opts and module.opts.events then 95 return vim.iter(module.opts.events):fold(acc, function(a, event) 96 a[event] = true 97 return a 98 end) 99 end 100 end 101 return acc 102 end) 103 104 return vim.tbl_keys(t) 105end 106 107local function refreshcomponent(self, fn, ev) 108 do 109 local ok, result = pcall(fn, self, ev) 110 if not ok then 111 log.error("[lylla] error occured on refresh:\n\t" .. result) 112 end 113 end 114end 115 116---@class lylla.proto 117---@field refresh fun(self, ev?: vim.api.keyset.create_autocmd.callback_args) 118function statusline:refresh(ev) 119 vim.schedule(function() 120 if not vim.api.nvim_win_is_valid(self.win) then 121 return 122 end 123 124 refreshcomponent(self, statusline.set, ev) 125 refreshcomponent(self, statusline.setwinbar, ev) 126 end) 127end 128 129---@class lylla.proto 130---@field fold fun(self, ev?: vim.api.keyset.create_autocmd.callback_args, modules: any[]): string 131function statusline:fold(ev, modules) 132 if type(modules) ~= "table" or modules == nil then 133 return "" 134 end 135 136 local lst = vim 137 .iter(ipairs(modules)) 138 :map(function(_, module) 139 if type(module) == "table" and module.fn and type(module.fn) == "function" then 140 if module.opts and module.opts.events then 141 -- refresh from timer 142 if not ev and module.prev then 143 return module.prev 144 end 145 -- refresh from non-match event 146 if ev and not vim.tbl_contains(module.opts.events, ev.event) and module.prev then 147 return module.prev 148 end 149 end 150 do 151 local ok, result = pcall(module.fn) 152 if not ok then 153 error(result) 154 end 155 module.prev = result 156 end 157 return module.prev 158 end 159 if type(module) == "function" then 160 local ok, result = pcall(module) 161 if not ok then 162 error(result) 163 end 164 return result 165 end 166 return module 167 end) 168 :totable() 169 return utils.fold(lst) 170end 171 172---@class lylla.proto 173---@field get fun(self, ev?: vim.api.keyset.create_autocmd.callback_args) 174function statusline:get(ev) 175 return self:fold(ev, self.modules) 176end 177 178---@class lylla.proto 179---@field getwinbar fun(self, ev?: vim.api.keyset.create_autocmd.callback_args) 180function statusline:getwinbar(ev) 181 return self:fold(ev, self.winbar) 182end 183 184---@class lylla.proto 185---@field setwinbar fun(self, ev?: vim.api.keyset.create_autocmd.callback_args) 186function statusline:setwinbar(ev) 187 local buf = vim.api.nvim_win_get_buf(self.win) 188 if vim.bo[buf].buftype ~= "" then 189 return 190 end 191 192 local ok, result = pcall(vim.api.nvim_win_call, self.win, function() 193 return self:getwinbar(ev) 194 end) 195 assert(ok, string.format("error occured while trying to evaluate winbar:\n\t%s", result)) 196 197 vim.wo[self.win].winbar = result 198end 199 200---@class lylla.proto 201---@field set fun(self, ev?: vim.api.keyset.create_autocmd.callback_args) 202function statusline:set(ev) 203 local ok, result = pcall(vim.api.nvim_win_call, self.win, function() 204 return self:get(ev) 205 end) 206 assert(ok, string.format("error occured while trying to evaluate statusline:\n\t%s", result)) 207 208 vim.wo[self.win].statusline = result 209end 210 211return statusline