Added optimizationsto statusline

This commit is contained in:
Andrew Haynes
2026-05-10 03:12:18 -04:00
parent 9fefc102c9
commit 86d6e13813
+151 -90
View File
@@ -38,7 +38,6 @@ local function setup_statusline_highlights()
derive_hl('StatusDiagWarn', { base = 'StatusLine', fg = 'DiagnosticWarn' })
derive_hl('StatusDiagHint', { base = 'StatusLine', fg = 'DiagnosticHint' })
derive_hl('StatusCursor', { base = 'StatusLine', fg = 'Normal' })
-- derive_mode_hl('StatusPercent', 'Constant')
end
vim.api.nvim_create_autocmd('ColorScheme', {
callback = setup_statusline_highlights,
@@ -68,6 +67,9 @@ local mode_hl_map = {
--- Git branch (async, cached per buffer, refreshed every 5s)
local function update_git_branch()
if vim.bo.buftype ~= '' then
return
end
vim.system(
{ 'git', 'branch', '--show-current' },
{ text = true },
@@ -90,6 +92,39 @@ vim.api.nvim_create_autocmd({ 'BufEnter', 'FocusGained' }, {
local progress = {}
local spinner_frames =
{ '⣾', '⣽', '⣻', '⢿', '⡿', '⣟', '⣯', '⣷' }
local frame = 0
--- Clean stale progress messages periodically
local progress_timer = vim.uv.new_timer()
local function start_progress_timer()
if not progress_timer:is_active() then
progress_timer:start(0, 200, function()
local now = vim.loop.now()
for k, v in pairs(progress) do
local ttl = v.done and 2000 or 10000
if now - v.t > ttl then
progress[k] = nil
end
end
if next(progress) then
frame = (frame % #spinner_frames) + 1
-- vim.cmd('redrawstatus')
vim.schedule(function()
vim.cmd('redrawstatus')
end)
-- else
else
progress_timer:stop()
vim.schedule(function()
vim.cmd('redrawstatus')
end)
end
-- progress_timer:stop()
-- end
end)
end
end
vim.api.nvim_create_autocmd('LspProgress', {
callback = function(ev)
local d = ev.data
@@ -107,6 +142,7 @@ vim.api.nvim_create_autocmd('LspProgress', {
msg = (value.message or 'working...'):gsub('%%', '%%%%'),
t = vim.loop.now(),
}
start_progress_timer()
elseif value.kind == 'report' then
local msg_parts = {}
if value.message then
@@ -127,123 +163,147 @@ vim.api.nvim_create_autocmd('LspProgress', {
end
end,
})
local frame = 0
--- Clean stale progress messages periodically
vim.uv.new_timer():start(
0,
200,
vim.schedule_wrap(function()
local now = vim.loop.now()
for k, v in pairs(progress) do
local ttl = v.done and 2000 or 10000
if now - v.t > ttl then
progress[k] = nil
end
end
if next(progress) then
frame = (frame % #spinner_frames) + 1
end
vim.cmd('redrawstatus')
end)
)
-- local frame = 0
-- --- Clean stale progress messages periodically
-- vim.uv.new_timer():start(
-- 0,
-- 200,
-- vim.schedule_wrap(function()
-- if next(progress) == nil then
-- return progress_timer:stop()
-- -- end
-- -- else -- no progress
-- -- progress_timer:stop()
-- end
-- local now = vim.loop.now()
-- for k, v in pairs(progress) do
-- local ttl = v.done and 2000 or 10000
-- if now - v.t > ttl then
-- progress[k] = nil
-- end
-- end
-- if next(progress) then
-- frame = (frame % #spinner_frames) + 1
-- end
-- vim.cmd('redrawstatus')
-- end)
-- )
--- Build the LSP section of the statusline
local function lsp_section()
local bufnr = vim.api.nvim_get_current_buf()
local clients = vim.lsp.get_clients({ bufnr = bufnr })
if #clients == 0 then
return ''
vim.api.nvim_create_autocmd({ 'LspAttach', 'LspDetach' }, {
callback = function(args)
-- vim.schedule(function()
local names = {}
for _, c in ipairs(vim.lsp.get_clients({ bufnr = args.buf })) do
table.insert(names, c.name)
end
-- Show progress spinner if any active for visible clients
vim.b[args.buf].lsp_names = #names > 0
and (' ● ' .. table.concat(names, ', ') .. ' ')
or ''
-- end)
end,
})
-- local function lsp_section()
-- -- Only check for progress (dynamic)
-- local latest = nil
-- for _, p in pairs(progress) do
-- -- Simplified check: show latest active or recently done
-- if not latest or p.t > latest.t then
-- latest = p
-- end
-- end
--
-- if latest and (not latest.done or (vim.loop.now() - latest.t < 2000)) then
-- return '%#StatusLspSpinner# ○ ' .. latest.msg .. ' %*'
-- end
--
-- -- Use the cached names from the autocmd
-- return '%#StatusLsp#' .. (vim.b.lsp_names or '') .. '%*'
-- end
local function lsp_section()
-- Only check progress for this buffer's clients
local client_ids = {}
for _, c in ipairs(clients) do
for _, c in
ipairs(vim.lsp.get_clients({ bufnr = vim.api.nvim_get_current_buf() }))
do
client_ids[c.id] = true
end
-- Filter progress by current buffer's clients
local latest = nil
for key, p in pairs(progress) do
local cid = tonumber(key:match('^(%d+):'))
if cid and client_ids[cid] and not p.done then
if cid and client_ids[cid] then
if not latest or p.t > latest.t then
latest = p
end
end
end
if latest then
if latest and (not latest.done or (vim.loop.now() - latest.t < 2000)) then
return '%#StatusLspSpinner# ○ ' .. latest.msg .. ' %*'
end
-- Fallback: most recent done entry (lasts 2s for smooth transition)
for key, p in pairs(progress) do
local cid = tonumber(key:match('^(%d+):'))
if cid and client_ids[cid] and p.done then
if not latest or p.t > latest.t then
latest = p
-- Use the cached names from the autocmd
return '%#StatusLsp#' .. (vim.b.lsp_names or '') .. '%*'
end
--- Diagnostics summary.
local diag_cache = {}
local function update_diag_cache(ev)
local bufnr = ev.buf
local counts = vim.diagnostic.count(bufnr)
local parts = {}
if counts[1] and counts[1] > 0 then
table.insert(parts, '%#StatusDiagError# E' .. counts[1] .. ' %*')
end
if counts[2] and counts[2] > 0 then
table.insert(parts, '%#StatusDiagWarn# W' .. counts[2] .. ' %*')
end
if counts[4] and counts[4] > 0 then
table.insert(parts, '%#StatusDiagHint# H' .. counts[4] .. ' %*')
end
if latest then
return '%#StatusLspSpinner# ○ ' .. latest.msg .. ' %*'
end
-- Idle: show client names
local names = {}
for _, c in ipairs(clients) do
table.insert(names, c.name)
end
return '%#StatusLsp# ● ' .. table.concat(names, ', ') .. ' %*'
diag_cache[bufnr] = table.concat(parts)
end
--- Diagnostics summary.
local function diag_section()
local diag = vim.diagnostic.get(vim.api.nvim_get_current_buf())
local parts = {}
local counts = { errors = 0, warnings = 0, hints = 0 }
for _, d in ipairs(diag) do
if d.severity == vim.diagnostic.severity.ERROR then
counts.errors = counts.errors + 1
elseif d.severity == vim.diagnostic.severity.WARN then
counts.warnings = counts.warnings + 1
elseif d.severity == vim.diagnostic.severity.HINT then
counts.hints = counts.hints + 1
end
end
if counts.errors > 0 then
table.insert(parts, '%#StatusDiagError# E' .. counts.errors .. ' %*')
end
if counts.warnings > 0 then
table.insert(parts, '%#StatusDiagWarn# W' .. counts.warnings .. ' %*')
end
if counts.hints > 0 then
table.insert(parts, '%#StatusDiagHint# H' .. counts.hints .. ' %*')
end
return table.concat(parts)
end
vim.api.nvim_create_autocmd(
'DiagnosticChanged',
{ callback = update_diag_cache }
)
--- Main statusline renderer.
function _G.__statusline()
local m = vim.fn.mode()
local mode_hl = mode_hl_map[m] or 'StatusModeNormal'
local mode_txt = mode_map[m] or ' ?? '
local parts = {}
-- Mode
parts[#parts + 1] = '%#' .. mode_hl .. '#' .. mode_txt .. '%*'
-- Git
local git = vim.b.git_branch or ''
if git ~= '' then
parts[#parts + 1] = '%#StatusGit#  ' .. git .. ' %*'
local cur_buf = vim.api.nvim_get_current_buf()
local parts = {
'%#' .. mode_hl .. '#' .. (mode_map[m] or ' ?? ') .. '%*',
git ~= '' and ('%#StatusGit#  ' .. git .. ' %*') or '',
lsp_section(),
'%=',
-- diag_cache[cur_buf] or '', -- Per-buffer cache lookup
diag_cache[cur_buf] or (function()
local parts = {}
local counts = vim.diagnostic.count(cur_buf)
if counts[1] and counts[1] > 0 then
table.insert(
parts,
'%#StatusDiagError# E' .. counts[1] .. ' %*'
)
end
-- LSP
parts[#parts + 1] = lsp_section()
-- Right align
parts[#parts + 1] = '%='
-- Diagnostics
parts[#parts + 1] = diag_section()
-- Cursor position
parts[#parts + 1] = '%#StatusCursor# %l:%-2c %*'
-- Percentage
parts[#parts + 1] = '%#' .. mode_hl .. '# %p%% %*'
if counts[2] and counts[2] > 0 then
table.insert(parts, '%#StatusDiagWarn# W' .. counts[2] .. ' %*')
end
if counts[4] and counts[4] > 0 then
table.insert(parts, '%#StatusDiagHint# H' .. counts[4] .. ' %*')
end
return table.concat(parts)
end)(),
'%#StatusCursor# %l:%-2c %*',
'%#' .. mode_hl .. '# %p%% %*',
}
return table.concat(parts)
end
vim.o.statusline = '%!v:lua.__statusline()'
local helpers = require('incline.helpers')
local devicons = require('nvim-web-devicons')
require('incline').setup({
window = {
padding = 0,
@@ -255,7 +315,8 @@ require('incline').setup({
if filename == '' then
filename = '[No Name]'
end
local ft_icon, ft_color = devicons.get_icon_color(filename)
local ft_icon, ft_color =
require('nvim-web-devicons').get_icon_color(filename)
local modified = vim.bo[props.buf].modified
return {
ft_icon and {
@@ -263,7 +324,7 @@ require('incline').setup({
ft_icon,
' ',
guibg = ft_color,
guifg = helpers.contrast_color(ft_color),
guifg = require('incline.helpers').contrast_color(ft_color),
} or '',
' ',
{ filename, gui = modified and 'bold,italic' or 'bold' },