225 lines
5.5 KiB
Lua
225 lines
5.5 KiB
Lua
|
-- Very minimal framework to write a status/tabline
|
||
|
-- Based on a (literally) stripped-down express_line.
|
||
|
-- License: MIT
|
||
|
-- Copyright © 2022 TJ DeVries, Michele Guerini Rocco
|
||
|
|
||
|
|
||
|
local fl = {}
|
||
|
|
||
|
-- Misc utilities
|
||
|
fl.utils = {
|
||
|
-- Built-in segments
|
||
|
line_number = '%l',
|
||
|
column_number = '%c',
|
||
|
percent = "%p%%",
|
||
|
filename = '%t',
|
||
|
split = '%=',
|
||
|
|
||
|
-- Highlights a string
|
||
|
highlight = function(group, content)
|
||
|
return ('%%#%s#'):format(group) .. content .. '%*'
|
||
|
end,
|
||
|
|
||
|
-- Creates a subsection {highlight, items, separator, stop}
|
||
|
subsection = function(cfg)
|
||
|
local segments = {}
|
||
|
|
||
|
if cfg.user ~= nil then
|
||
|
table.insert(segments, ('%%%s*'):format(cfg.user))
|
||
|
end
|
||
|
if cfg.highlight ~= nil then
|
||
|
table.insert(segments, ('%%#%s#'):format(cfg.highlight))
|
||
|
end
|
||
|
|
||
|
table.insert(segments, function(win, buf)
|
||
|
local res, last_active = "", false
|
||
|
|
||
|
for i, item in pairs(cfg.items) do
|
||
|
local part = type(item) == 'string' and item or item(win, buf)
|
||
|
if part and last_active then
|
||
|
res = res .. (cfg.separator or ' ')
|
||
|
end
|
||
|
if part then
|
||
|
res = res .. part
|
||
|
last_active = true
|
||
|
end
|
||
|
end
|
||
|
return res
|
||
|
end)
|
||
|
table.insert(segments, cfg.stop or ' ')
|
||
|
|
||
|
if highlight ~= nil or user ~= nil then
|
||
|
table.insert(segments, '%*')
|
||
|
end
|
||
|
|
||
|
return segments
|
||
|
end
|
||
|
}
|
||
|
|
||
|
-- Neovim Buffer class
|
||
|
local Buffer = {}
|
||
|
local buf_props = {
|
||
|
name = function(buf) return vim.api.nvim_buf_get_name(buf.bufnr) end,
|
||
|
is_active = function(buf) return buf.bufnr == vim.api.nvim_get_current_buf() end,
|
||
|
}
|
||
|
function Buffer:new(bufnr)
|
||
|
if bufnr == 0 then
|
||
|
bufnr = vim.api.nvim_buf_get_number(0)
|
||
|
end
|
||
|
|
||
|
return setmetatable({ bufnr = bufnr, }, { __index=function(t, k)
|
||
|
if Buffer[k] ~= nil then
|
||
|
t[k] = Buffer[k]
|
||
|
elseif buf_props[k] ~= nil then
|
||
|
t[k] = buf_props[k](t)
|
||
|
else
|
||
|
t[k] = vim.api.nvim_buf_get_option(t.bufnr, k)
|
||
|
end
|
||
|
return t[k]
|
||
|
end
|
||
|
})
|
||
|
end
|
||
|
|
||
|
-- Neovim Window class
|
||
|
local Window = {}
|
||
|
local win_props = {
|
||
|
width = function(win) return vim.api.nvim_win_get_width(win.winid) end,
|
||
|
height = function(win) return vim.api.nvim_win_get_height(win.winid) end,
|
||
|
is_active = function(win) return win.winid == vim.api.nvim_get_current_win() end
|
||
|
}
|
||
|
function Window:new(winid)
|
||
|
return setmetatable({winid=winid}, { __index=function(t, k)
|
||
|
local result
|
||
|
if Window[k] ~= nil then
|
||
|
result = Window[k]
|
||
|
elseif win_props[k] ~= nil then
|
||
|
result = win_props[k](t)
|
||
|
end
|
||
|
return result
|
||
|
end
|
||
|
})
|
||
|
end
|
||
|
|
||
|
-- Evaluates the statusline segments
|
||
|
local processor = function(items, window, buffer)
|
||
|
local winid = window.winid
|
||
|
|
||
|
return function()
|
||
|
if not vim.api.nvim_win_is_valid(winid) then return end
|
||
|
|
||
|
buffer = Buffer:new(buffer.bufnr)
|
||
|
local waiting = {}
|
||
|
local statusline = {}
|
||
|
local effects = {}
|
||
|
|
||
|
for k, v in ipairs(items) do
|
||
|
local ok, result, effect
|
||
|
if type(v) == 'string' then
|
||
|
ok, result = true, v
|
||
|
elseif type(v) == 'function' then
|
||
|
ok, result, effect = pcall(v, window, buffer)
|
||
|
else
|
||
|
ok = false
|
||
|
end
|
||
|
|
||
|
if not ok then
|
||
|
statusline[k] = ''
|
||
|
else
|
||
|
if type(result) == 'thread' then
|
||
|
table.insert(waiting, {index=k, thread=result, effect=effect})
|
||
|
else
|
||
|
statusline[k], effects[k] = result, effect
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
local remaining = table.getn(waiting)
|
||
|
local completed = 0
|
||
|
|
||
|
local start = os.time()
|
||
|
while start + 2 > os.time() do
|
||
|
if remaining == completed then
|
||
|
break
|
||
|
end
|
||
|
|
||
|
for i = 1, remaining do
|
||
|
local wait_val = waiting[i]
|
||
|
|
||
|
if wait_val ~= nil then
|
||
|
local index, thread = wait_val.index, wait_val.thread
|
||
|
local _, res = coroutine.resume(thread, window, buffer)
|
||
|
|
||
|
if coroutine.status(thread) == 'dead' then
|
||
|
statusline[index] = res
|
||
|
-- Remove
|
||
|
completed = completed + 1
|
||
|
waiting[i] = nil
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Filter nils and concat
|
||
|
local final = {}
|
||
|
for k, v in ipairs(statusline) do
|
||
|
if effects[k] then v = effects[k](v) end
|
||
|
if v then table.insert(final, v) end
|
||
|
end
|
||
|
|
||
|
return table.concat(final, "")
|
||
|
end
|
||
|
end
|
||
|
|
||
|
-- Creates a table of all window/buffers
|
||
|
local get_new_windows_table = function()
|
||
|
return setmetatable({}, {__index = function(self, winid)
|
||
|
local val = setmetatable({}, {
|
||
|
__index = function(win_table, bufnr)
|
||
|
|
||
|
if not fl.generator then return function() return '' end end
|
||
|
|
||
|
local window = Window:new(winid)
|
||
|
local buffer = Buffer:new(bufnr)
|
||
|
local items = vim.tbl_flatten(fl.generator(window, buffer))
|
||
|
local p = processor(items, window, buffer)
|
||
|
|
||
|
rawset(win_table, bufnr, p)
|
||
|
return p
|
||
|
end,
|
||
|
})
|
||
|
|
||
|
rawset(self, winid, val)
|
||
|
return val
|
||
|
end,
|
||
|
})
|
||
|
end
|
||
|
|
||
|
-- Draws the status/tabline
|
||
|
function draw_statusline(winid)
|
||
|
local bufnr = vim.api.nvim_win_get_buf(winid)
|
||
|
return fl._window_statuslines[winid][bufnr]()
|
||
|
end
|
||
|
function draw_tabline()
|
||
|
return fl._tabline()
|
||
|
end
|
||
|
|
||
|
-- Set up a statusline per window
|
||
|
fl.setup_statusline = function(generator)
|
||
|
fl._window_statuslines = get_new_windows_table()
|
||
|
fl.generator = generator
|
||
|
vim.api.nvim_create_autocmd(
|
||
|
{'BufWinEnter','WinEnter'},
|
||
|
{pattern='*', callback=function()
|
||
|
vim.wo.statusline = "%!luaeval('draw_statusline("..vim.api.nvim_get_current_win()..")')"
|
||
|
end
|
||
|
})
|
||
|
end
|
||
|
|
||
|
-- Set up a global tabline
|
||
|
fl.setup_tabline = function(generator)
|
||
|
fl._tabline = generator
|
||
|
vim.cmd('set tabline=%!v:lua.draw_tabline()')
|
||
|
end
|
||
|
|
||
|
return fl
|