CHORE: Cleaned up the status bar and finished it

This commit is contained in:
2026-10-03 00:48:06 -04:00
parent 9ea6881d0e
commit bf1b2d5042
+67 -31
View File
@@ -1,4 +1,3 @@
--- Derive a highlight from an existing scheme group.
local function derive_hl(name, opts) local function derive_hl(name, opts)
local base = opts.base and vim.api.nvim_get_hl(0, { name = opts.base }) local base = opts.base and vim.api.nvim_get_hl(0, { name = opts.base })
or {} or {}
@@ -10,7 +9,7 @@ local function derive_hl(name, opts)
bold = opts.bold, bold = opts.bold,
}) })
end end
--- Derive a mode highlight where bg comes from a syntax group and fg contrasts.
local function derive_mode_hl(name, color_group) local function derive_mode_hl(name, color_group)
local color = vim.api.nvim_get_hl(0, { name = color_group }) local color = vim.api.nvim_get_hl(0, { name = color_group })
local status_bg = vim.api.nvim_get_hl(0, { name = 'StatusLine' }) local status_bg = vim.api.nvim_get_hl(0, { name = 'StatusLine' })
@@ -21,7 +20,6 @@ local function derive_mode_hl(name, color_group)
}) })
end end
--- (Re)build all statusline highlight groups from current scheme.
local function setup_statusline_highlights() local function setup_statusline_highlights()
derive_mode_hl('StatusModeNormal', 'Type') derive_mode_hl('StatusModeNormal', 'Type')
derive_mode_hl('StatusModeInsert', 'String') derive_mode_hl('StatusModeInsert', 'String')
@@ -29,9 +27,7 @@ local function setup_statusline_highlights()
derive_mode_hl('StatusModeCommand', 'PreProc') derive_mode_hl('StatusModeCommand', 'PreProc')
derive_mode_hl('StatusModeReplace', 'WarningMsg') derive_mode_hl('StatusModeReplace', 'WarningMsg')
derive_mode_hl('StatusModeTerminal', 'Special') derive_mode_hl('StatusModeTerminal', 'Special')
derive_hl('StatusGit', { base = 'CursorLine', fg = 'LineNr' }) derive_hl('StatusGit', { base = 'CursorLine', fg = 'Normal' })
-- derive_hl('StatusLsp', { base = 'StatusLine', fg = 'DiagnosticInfo' })
-- derive_hl('StatusLspSpinner', { base = 'StatusLine', fg = 'Constant' })
derive_hl( derive_hl(
'StatusLspWorking', 'StatusLspWorking',
{ base = 'StatusLine', fg = 'DiagnosticInfo' } { base = 'StatusLine', fg = 'DiagnosticInfo' }
@@ -45,7 +41,7 @@ local function setup_statusline_highlights()
derive_hl('StatusDiagWarn', { base = 'StatusLine', fg = 'DiagnosticWarn' }) derive_hl('StatusDiagWarn', { base = 'StatusLine', fg = 'DiagnosticWarn' })
derive_hl('StatusDiagHint', { base = 'StatusLine', fg = 'DiagnosticHint' }) derive_hl('StatusDiagHint', { base = 'StatusLine', fg = 'DiagnosticHint' })
derive_hl('StatusCursor', { base = 'CursorLine', fg = 'Normal' }) derive_hl('StatusCursor', { base = 'CursorLine', fg = 'Normal' })
derive_hl('StatusGitAdded', { base = 'StatusLine', fg = 'MoreMsg' }) derive_hl('StatusGitAdded', { base = 'StatusLine', fg = 'DiagnosticOk' })
derive_hl( derive_hl(
'StatusGitRemoved', 'StatusGitRemoved',
{ base = 'StatusLine', fg = 'DiagnosticError' } { base = 'StatusLine', fg = 'DiagnosticError' }
@@ -57,7 +53,6 @@ vim.api.nvim_create_autocmd('ColorScheme', {
}) })
setup_statusline_highlights() setup_statusline_highlights()
--- Mode text mapping.
local mode_map = { local mode_map = {
n = ' NORM ', n = ' NORM ',
i = ' INS ', i = ' INS ',
@@ -73,6 +68,7 @@ local mode_map = {
t = ' TERM ', t = ' TERM ',
['!'] = ' SHELL ', ['!'] = ' SHELL ',
} }
local mode_hl_map = { local mode_hl_map = {
n = 'StatusModeNormal', n = 'StatusModeNormal',
i = 'StatusModeInsert', i = 'StatusModeInsert',
@@ -89,7 +85,6 @@ local mode_hl_map = {
t = 'StatusModeTerminal', t = 'StatusModeTerminal',
} }
--- Git branch (async, cached per buffer, refreshed every 5s)
local function refresh_status(buf) local function refresh_status(buf)
local b = vim.b[buf] local b = vim.b[buf]
local left, right = {}, {} local left, right = {}, {}
@@ -106,28 +101,34 @@ local function refresh_status(buf)
left[#left + 1] = '%#StatusGitRemoved# -' .. diff.removed .. '%*' left[#left + 1] = '%#StatusGitRemoved# -' .. diff.removed .. '%*'
end end
end end
if b.lsp_icon then
left[#left + 1] = '%#' .. b.lsp_hl .. '#' .. b.lsp_icon .. '%*'
end
if b.recording then if b.recording then
right[#right + 1] = '%#StatusRecording# ' .. b.recording .. ' %*' right[#right + 1] = '%#StatusRecording# ' .. b.recording .. ' %*'
end end
if b.diag_str then if b.diag_str then
right[#right + 1] = b.diag_str right[#right + 1] = b.diag_str
end end
if b.lsp_icon and b.lsp_icon ~= '' then
right[#right + 1] = '%#'
.. b.lsp_hl
.. '#'
.. b.lsp_icon
.. (b.lsp_name or '')
.. ' %*'
end
b.status_left = #left > 0 and table.concat(left) or '' b.status_left = #left > 0 and table.concat(left) or ''
b.status_right = #right > 0 and table.concat(right) or '' b.status_right = #right > 0 and table.concat(right) or ''
end end
--- LSP: one icon, three states.
local lsp_icon = { local lsp_icon = {
none = '', none = '',
starting = '',
working = ' 󰄬 ', working = ' 󰄬 ',
ready = ' 󰄭 ', ready = ' 󰄭 ',
} }
local lsp_hl = { local lsp_hl = {
none = 'StatusLspNone', none = 'StatusLspNone',
starting = 'StatusLspNone',
working = 'StatusLspWorking', working = 'StatusLspWorking',
ready = 'StatusLspReady', ready = 'StatusLspReady',
} }
@@ -138,12 +139,18 @@ local function refresh_lsp(buf)
local clients = vim.lsp.get_clients({ bufnr = buf }) local clients = vim.lsp.get_clients({ bufnr = buf })
if #clients == 0 then if #clients == 0 then
state = 'none' state = 'none'
vim.b[buf].lsp_name = ''
else else
local names = {}
for _, client in ipairs(clients) do for _, client in ipairs(clients) do
if active_progress[client.id] then names[#names + 1] = client.name
local prog = active_progress[client.id]
if prog == 'working' then
state = 'working' state = 'working'
break elseif prog == 'starting' and state ~= 'working' then
state = 'starting'
end end
vim.b[buf].lsp_name = table.concat(names, '/')
end end
end end
vim.b[buf].lsp_icon = lsp_icon[state] vim.b[buf].lsp_icon = lsp_icon[state]
@@ -153,15 +160,32 @@ end
vim.api.nvim_create_autocmd({ 'LspAttach', 'LspDetach' }, { vim.api.nvim_create_autocmd({ 'LspAttach', 'LspDetach' }, {
callback = function(ev) callback = function(ev)
if ev.event == 'LspAttach' then
local cid = ev.data.client_id
if not active_progress[cid] then
active_progress[cid] = 'starting'
vim.defer_fn(function()
if active_progress[cid] == 'starting' then
active_progress[cid] = nil
if vim.api.nvim_buf_is_valid(ev.buf) then
refresh_lsp(ev.buf)
end
end
end, 500) -- 500ms grace period to wait for LspProgress
end
elseif ev.event == 'LspDetach' then
active_progress[ev.data.client_id] = nil
end
refresh_lsp(ev.buf) refresh_lsp(ev.buf)
end, end,
}) })
vim.api.nvim_create_autocmd('LspProgress', { vim.api.nvim_create_autocmd('LspProgress', {
callback = function(ev) callback = function(ev)
if ev.match == 'begin' or ev.match == 'report' then local kind = ev.data.params.value and ev.data.params.value.kind
active_progress[ev.data.client_id] = true if kind == 'begin' or kind == 'report' then
elseif ev.match == 'end' then active_progress[ev.data.client_id] = 'working'
elseif kind == 'end' then
active_progress[ev.data.client_id] = nil active_progress[ev.data.client_id] = nil
else else
return return
@@ -175,32 +199,41 @@ vim.api.nvim_create_autocmd('LspProgress', {
end, end,
}) })
--- Git: resolved once per buffer, refreshed on save. local BRANCH_ICON = ''
local function refresh_branch(buf) local function refresh_branch(buf)
local file = vim.api.nvim_buf_get_name(buf)
if file == '' or vim.bo[buf].buftype ~= '' then
vim.b[buf].git_branch = ''
return
end
local cwd = vim.fs.root(buf, '.git') or vim.fs.dirname(file)
vim.system( vim.system(
{ 'git', 'branch', '--show-current' }, { 'git', 'branch', '--show-current' },
{ text = true }, { text = true, cwd = cwd },
vim.schedule_wrap(function(out) vim.schedule_wrap(function(out)
if not vim.api.nvim_buf_is_valid(buf) then if not vim.api.nvim_buf_is_valid(buf) then
return return
end end
vim.b[buf].git_branch = (out.code == 0 and out.stdout or ''):gsub( local branch = (out.code == 0 and out.stdout or ''):gsub('%s+', '')
'%s+', vim.b[buf].git_branch = branch ~= '' and (BRANCH_ICON .. branch)
'' or ''
)
refresh_status(buf) refresh_status(buf)
end) end)
) )
end end
local function refresh_diff(buf) local function refresh_diff(buf)
local file = vim.fn.fnamemodify(vim.fn.bufname(buf), ':p') local file = vim.api.nvim_buf_get_name(buf)
if file == '' then if file == '' or vim.bo[buf].buftype ~= '' then
vim.b[buf].git_diff = nil
return return
end end
local cwd = vim.fs.root(buf, '.git') or vim.fs.dirname(file)
vim.system( vim.system(
{ 'git', 'diff', '--numstat', 'HEAD', '--', file }, { 'git', 'diff', '--numstat', 'HEAD', '--', file },
{ text = true }, { text = true, cwd = cwd },
vim.schedule_wrap(function(out) vim.schedule_wrap(function(out)
if not vim.api.nvim_buf_is_valid(buf) then if not vim.api.nvim_buf_is_valid(buf) then
return return
@@ -209,10 +242,14 @@ local function refresh_diff(buf)
vim.b[buf].git_diff = nil vim.b[buf].git_diff = nil
else else
local added, removed = out.stdout:match('(%d+)%s+(%d+)') local added, removed = out.stdout:match('(%d+)%s+(%d+)')
if added and removed then
vim.b[buf].git_diff = { vim.b[buf].git_diff = {
added = tonumber(added) or 0, added = tonumber(added) or 0,
removed = tonumber(removed) or 0, removed = tonumber(removed) or 0,
} }
else
vim.b[buf].git_diff = nil
end
end end
refresh_status(buf) refresh_status(buf)
end) end)
@@ -266,11 +303,10 @@ vim.api.nvim_create_autocmd('BufWritePost', {
end, end,
}) })
--- Macro: read once when recording starts, not on every redraw.
vim.api.nvim_create_autocmd('RecordingEnter', { vim.api.nvim_create_autocmd('RecordingEnter', {
pattern = '*', pattern = '*',
callback = function(ev) callback = function(ev)
vim.b[ev.buf].recording = '● Rec @' .. vim.fn.reg_recording() vim.b[ev.buf].recording = 'Rec @' .. vim.fn.reg_recording()
refresh_status(ev.buf) refresh_status(ev.buf)
end, end,
}) })
@@ -293,7 +329,7 @@ function _G.__statusline()
b.status_left or '', b.status_left or '',
'%=', '%=',
b.status_right or '', b.status_right or '',
'%#StatusCursor# %l:%-2c %*', '%#StatusCursor# %l:%c ',
'%#' .. hl .. '# %p%% %*', '%#' .. hl .. '# %p%% %*',
}) })
end end