Luigit
repositories / dotfiles

dotfiles

bugabingas dorkfiles

owned by admin

neovim/lua/bugabinga/statusline.lua

Raw
--- Custom statusline using std APIs and no external dependencies
local icon = require 'std.icon'
local table = require 'std.table'
local auto = require 'std.auto'

-- Local state/cache
local cache = {
	git_branch = {}, -- per-directory cache
	lsp_count = 0,
}
local cached_cwd = assert(vim.uv.cwd())

-- Component functions
local function mode_component()
	local mode = vim.api.nvim_get_mode().mode
	local mode_icons = {
		n = icon.normal,       -- Ν
		i = icon.insert,       -- 
		v = icon.visual,       -- 󰒉
		V = icon.visual,
		['\22'] = icon.visual, -- ^V
		c = icon.command,      -- 󰘳
		R = icon.replace,      -- 
	}
	return '%#ModeMsg#' .. (mode_icons[mode] or mode) .. '%*'
end

local function lsp_component()
	local count = cache.lsp_count
	if count == 0 then return '' end
	return '%@v:lua.BugabingaStatusline.on_lsp_click@' .. icon.lsp .. ' ' .. count .. '%X'
end

local registered = {}

local function register(fn)
	table.insert(registered, fn)
end

local function reset()
	for i = #registered, 1, -1 do table.remove(registered, i) end
end

local function plugins_component()
	local results = table.map(function(fn) return fn() end, registered)
	local filtered = table.filter(function(c) return c ~= '' end, results)
	return table.concat(filtered, ' ')
end

local function file_component()
	local name = vim.api.nvim_buf_get_name(0)
	-- An empty name modifies into the working directory, which reads as if the
	-- buffer were a file in it. A nameless buffer says so instead.
	if name == '' then return icon.file .. ' [No Name]' end

	local path = vim.fn.fnamemodify(name, ':~:.')
	if #path > 40 then
		path = '...' .. path:sub(-37)
	end
	local modified = vim.api.nvim_get_option_value('modified', { buf = 0 }) and ' ' .. icon.modified or ''
	return icon.file .. ' ' .. path .. modified
end

local function git_component()
	local cwd = cached_cwd
	if not cache.git_branch[cwd] then
		vim.system({ 'git', 'branch', '--show-current' }, {
			cwd = cwd,
			timeout = 2000,
		}, function(result)
			if result.code == 0 then
				local branch = (result.stdout or ''):gsub('\n', ''):sub(1, 15)
				cache.git_branch[cwd] = icon.branch .. ' ' .. branch
			else
				cache.git_branch[cwd] = ''
			end
			vim.schedule(function() vim.cmd.redrawstatus() end)
		end)
		return ''
	end
	return cache.git_branch[cwd]
end

local function position_component()
	return '%l:%c/%L'
end

local function fileinfo_component()
	local ft = vim.bo.filetype
	local enc = vim.bo.fileencoding or vim.o.encoding
	local fmt = vim.bo.fileformat
	return string.format('%s %s %s', ft, enc, fmt)
end

-- Main statusline function
local function render()
	local components = {
		mode_component(),
		lsp_component(),
		plugins_component(),
		file_component(),
		git_component(),
		'%=' .. position_component(),
		fileinfo_component(),
	}

	local filtered = table.filter(function(c) return c ~= '' end, components)
	return table.concat(filtered, '   ')
end

_G.BugabingaStatusline = {
	render = render,
	register = register,
	reset = reset,
	plugins_component = plugins_component,
	git_component = git_component,
	_get_cache = function() return cache end,
	_get_cached_cwd = function() return cached_cwd end,
	on_lsp_click = function()
		vim.cmd('LspInfo')
	end,
}

vim.opt.statusline = '%!v:lua.BugabingaStatusline.render()'

auto 'statusline' {
	{
		events = 'LspAttach',
		command = function()
			cache.lsp_count = #vim.lsp.get_clients()
			vim.cmd.redrawstatus()
		end,
	},
	{
		events = 'LspDetach',
		command = function()
			cache.lsp_count = #vim.lsp.get_clients()
			vim.cmd.redrawstatus()
		end,
	},
	{
		events = 'DirChanged',
		command = function()
			cached_cwd = assert(vim.uv.cwd())
			cache.git_branch = {}
			vim.cmd.redrawstatus()
		end,
	},
	{
		events = { 'ModeChanged', 'BufEnter', 'WinEnter' },
		command = function() vim.cmd.redrawstatus() end,
	},
}

return BugabingaStatusline