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