nvim/lua/user/plugins/ui/heirline/components.lua
Hoang Nguyen 0c9e4d20e0
Use aerial.nvim for the breadcrumbs on winbar
Also:
- remove `nvim-navic` from the plugin tree (covered by aerial.nvim now)
- add more filetypes to filetype.lua
- rework heirline's configuration structure to be cleaner
- add some more LSP servers + remove glint
2024-02-13 00:00:00 +07:00

491 lines
14 KiB
Lua

local M = {}
local conditions = require('heirline.conditions')
local utils = require('heirline.utils')
local misc_utils = require('user.util.misc')
local lsp_utils = require('user.util.lsp')
local vars = require('user.config.vars')
M.vi_mode = {
init = function(self)
self.mode = vim.fn.mode(1)
self.mode_color = self.mode_colors[self.mode:sub(1, 1)]
end,
static = {
mode_names = {
n = 'NORMAL',
no = 'OP',
nov = 'OP(v)',
noV = 'OP(V)',
['no\22'] = 'OP(^V)',
niI = 'NORMAL(I)',
niR = 'NORMAL(R)',
niV = 'NORMAL(V)',
nt = 'NORMAL(T)',
ntT = 'NORMAL(T)',
v = 'VISUAL',
vs = 'VISUAL(s)',
V = 'V-LINE',
Vs = 'V-LINE(s)',
['\22'] = 'V-BLOCK',
['\22s'] = 'V-BLOCK(s)',
s = 'SELECT',
S = 'S-LINE',
['\19'] = 'S-BLOCK',
i = 'INSERT',
ic = 'INSERT(c)',
ix = 'INSERT(x)',
R = 'REPLACE',
Rc = 'REPLACE(c)',
Rx = 'REPLACE(x)',
Rv = 'REPLACE(v)',
Rvc = 'REPLACE(vc)',
Rvx = 'REPLACE(vx)',
c = 'COMMAND',
cv = 'EX',
r = 'ENTER',
rm = 'MORE',
['r?'] = 'CONFIRM',
['!'] = 'SHELL',
t = 'TERM',
},
mode_colors = {
n = 'green',
v = 'yellow',
V = 'yellow',
['\22'] = 'yellow',
s = 'orange',
S = 'orange',
['\19'] = 'yellow',
i = 'blue',
R = 'red',
c = 'purple',
r = 'cyan',
['!'] = 'fg',
t = 'fg',
},
},
provider = function(self)
return '' .. self.mode_names[self.mode] .. ' '
end,
hl = function(self)
return { fg = self.mode_color, bold = true }
end,
update = {
'ModeChanged',
pattern = '*:*',
callback = vim.schedule_wrap(function()
vim.cmd('redrawstatus')
end),
},
}
M.file_size = {
condition = misc_utils.buffer_not_empty,
provider = function()
local suffix = { 'b', 'k', 'M', 'G', 'T', 'P', 'E' }
local fsize = vim.fn.getfsize(vim.api.nvim_buf_get_name(0))
fsize = fsize < 0 and 0 or fsize
if fsize == 0 then
return ''
end
if fsize < 1024 then
return fsize .. suffix[1] .. ' '
end
local i = math.floor((math.log(fsize) / math.log(1024)))
return string.format('%.2g%s ', fsize / math.pow(1024, i), suffix[i + 1])
end,
hl = { fg = 'blue', bold = true },
}
M.file_format = {
condition = misc_utils.buffer_not_empty,
provider = function()
local os = vim.bo.fileformat:upper()
local icon = ''
if os == 'UNIX' then
icon = ''
elseif os == 'MAC' then
icon = ''
else
icon = ''
end
return ' ' .. icon .. os
end,
hl = { fg = 'cyan' },
}
M.file_type = {
provider = function()
return ' ' .. string.upper(vim.bo.filetype)
end,
hl = { fg = 'blue', bold = true },
}
M.file_encoding = {
provider = function()
local enc = vim.bo.fenc ~= '' and vim.bo.fenc or vim.o.enc
return enc ~= 'utf-8' and (' ' .. enc:upper())
end,
hl = { fg = 'fg' },
}
M.file_info = {
init = function(self)
self.filename = vim.api.nvim_buf_get_name(0)
end,
{
init = function(self)
local extension = vim.fn.fnamemodify(self.filename, ':e')
self.icon, self.icon_color =
require('nvim-web-devicons').get_icon_color(self.filename, extension, { default = true })
end,
provider = function(self)
return self.icon and (self.icon .. ' ')
end,
hl = function(self)
return { fg = self.icon_color and self.icon_color or 'fg' }
end,
},
{
provider = function(self)
if self.filename == '' then
return '[No Name]'
end
return vim.fn.fnamemodify(self.filename, ':p:t')
end,
on_click = {
callback = function()
require('telescope.builtin').find_files()
end,
name = 'heirline_file_name',
},
hl = function()
if vim.bo.modified then
return { fg = 'cyan', bold = true }
end
return { fg = 'blue', bold = true }
end,
},
{
condition = function()
return vim.bo.modified
end,
provider = '',
hl = { fg = 'green' },
},
{
condition = function()
return not vim.bo.modifiable or vim.bo.readonly
end,
provider = '',
hl = { fg = 'red' },
},
{ provider = '%<' },
}
M.ruler = {
provider = ' %7(%l/%3L%):%2c %P',
hl = { fg = 'fg' },
}
M.lsp_servers = {
condition = conditions.lsp_attached,
update = { 'LspAttach', 'LspDetach' },
provider = function()
local clients = {}
for _, client in ipairs(lsp_utils.active_lsp_clients { bufnr = 0 }) do
table.insert(clients, client.name)
end
return '  [' .. table.concat(clients, ', ') .. ']'
end,
on_click = {
callback = function()
vim.defer_fn(function()
vim.cmd('LspInfo')
end, 100)
end,
name = 'heirline_lsp_servers',
},
hl = { fg = 'purple' },
}
M.linters_formatters = {
condition = function()
return misc_utils.loaded('conform.nvim') or misc_utils.loaded('nvim-lint')
end,
update = { 'BufReadPost', 'BufWritePost', 'InsertLeave' },
provider = function()
local ret = {}
if misc_utils.loaded('nvim-lint') then
vim.list_extend(ret, lsp_utils.active_linters(0))
end
if misc_utils.loaded('conform.nvim') then
for _, formatter in ipairs(require('conform').list_formatters()) do
if formatter and formatter.name and (not vim.tbl_contains(ret, formatter.name)) then
table.insert(ret, formatter.name)
end
end
end
return '  [' .. table.concat(ret, ', ') .. ']'
end,
hl = { fg = 'cyan' },
}
M.aerial = {
flexible = 3,
hl = 'Normal',
update = 'CursorMoved',
condition = function()
return conditions.lsp_attached or misc_utils.loaded('nvim-treesitter')
end,
static = {
encode_pos = function(line, col, winnr)
return bit.bor(bit.lshift(line, 16), bit.lshift(col, 6), winnr)
end,
decode_pos = function(c)
return bit.rshift(c, 16), bit.band(bit.rshift(c, 6), 1023), bit.band(c, 63)
end,
},
{
provider = function(self)
local data = require('aerial').get_location(true) or {}
local children = {}
-- create a child for each level
for i, d in ipairs(data) do
local hlgroup = string.format('Aerial%sIcon', d.kind)
local child = {
{
provider = string.format('%s', d.icon),
hl = vim.fn.hlexists(hlgroup) == 1 and hlgroup or nil,
},
{ provider = string.gsub(d.name, '%%', '%%%%'):gsub('%s*->%s*', '') },
on_click = {
minwid = self.encode_pos(d.lnum, d.col, self.winnr),
callback = function(_, minwid)
local lnum, col, winnr = self.decode_pos(minwid)
vim.api.nvim_win_set_cursor(vim.fn.win_getid(winnr), { lnum, col })
end,
name = 'heirline_aerial',
},
}
if #data >= 1 and i < #data then
table.insert(child, { provider = ' > ', hl = { fg = 'cyan' } })
end
table.insert(children, child)
end
-- instantiate the new child, overwriting the previous one
self[1] = self:new(children, 1)
end,
},
{ provider = '' },
}
local function overseer_tasks_for_status(status)
return {
condition = function(self)
return self.tasks[status]
end,
provider = function(self)
return string.format('%s%d', self.symbols[status], #self.tasks[status])
end,
hl = function(self)
return { fg = self.colors[status] }
end,
}
end
M.overseer = {
condition = function()
return misc_utils.loaded('overseer.nvim')
end,
init = function(self)
local tasks = require('overseer.task_list').list_tasks { unique = true }
self.tasks = require('overseer.util').tbl_group_by(tasks, 'status')
end,
static = {
symbols = {
CANCELED = '󰜺 ',
FAILURE = '',
RUNNING = '',
SUCCESS = '',
},
colors = {
CANCELED = 'highlight',
FAILURE = 'red',
RUNNING = 'yellow',
SUCCESS = 'green',
},
},
overseer_tasks_for_status('CANCELED'),
overseer_tasks_for_status('RUNNING'),
overseer_tasks_for_status('SUCCESS'),
overseer_tasks_for_status('FAILURE'),
on_click = {
callback = function()
require('neotest').run.run_last()
end,
name = 'heirline_overseer',
},
}
M.diagnostics = {
condition = conditions.has_diagnostics,
update = { 'DiagnosticChanged', 'BufEnter' },
init = function(self)
self.errors = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.ERROR })
self.warnings = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.WARN })
self.hints = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.HINT })
self.infos = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.INFO })
end,
on_click = {
callback = function()
require('telescope.builtin').diagnostics {
layout_strategy = 'center',
bufnr = 0,
}
end,
name = 'heirline_diagnostics',
},
{
condition = function(self)
return self.errors > 0
end,
provider = function(self)
return ' ' .. vars.icons.notify.error .. ' ' .. self.errors
end,
hl = { fg = utils.get_highlight('DiagnosticError').fg },
},
{
condition = function(self)
return self.warnings > 0
end,
provider = function(self)
return ' ' .. vars.icons.notify.warn .. ' ' .. self.warnings
end,
hl = { fg = utils.get_highlight('DiagnosticWarn').fg },
},
{
condition = function(self)
return self.hints > 0
end,
provider = function(self)
return ' ' .. vars.icons.notify.hint .. ' ' .. self.hints
end,
hl = { fg = utils.get_highlight('DiagnosticHint').fg },
},
{
condition = function(self)
return self.infos > 0
end,
provider = function(self)
return ' ' .. vars.icons.notify.info .. ' ' .. self.infos
end,
hl = { fg = utils.get_highlight('DiagnosticInfo').fg },
},
}
M.code_action = {
condition = conditions.lsp_attached,
update = { 'CursorMoved' },
provider = function()
if vim.b.has_code_action_text ~= nil then
return ' ' .. vim.b.has_code_action_text
end
end,
on_click = {
callback = function()
vim.defer_fn(function()
return vim.lsp.buf.code_action()
end, 100)
end,
name = 'heirline_code_action',
},
hl = { fg = 'cyan', bold = true },
}
M.git = {
condition = conditions.is_git_repo,
init = function(self)
self.status_dict = vim.b.gitsigns_status_dict
end,
{
provider = function(self)
return '' .. (self.status_dict.head == '' and 'main' or self.status_dict.head)
end,
on_click = {
name = 'heirline_git_branch',
callback = function()
require('telescope.builtin').git_branches()
end,
},
hl = { fg = 'green', bold = true },
},
{
condition = function(self)
return self.status_dict.added ~= 0 or self.status_dict.removed ~= 0 or self.status_dict.changed ~= 0
end,
{ provider = '(', hl = { fg = 'green', bold = true } },
{
provider = function(self)
local count = self.status_dict.added or 0
return count > 0 and ('+' .. count)
end,
hl = { fg = utils.get_highlight('DiffAdd').fg },
},
{
provider = function(self)
local count = self.status_dict.removed or 0
return count > 0 and ('-' .. count)
end,
hl = { fg = utils.get_highlight('DiffDelete').fg },
},
{
provider = function(self)
local count = self.status_dict.changed or 0
return count > 0 and ('~' .. count)
end,
hl = { fg = utils.get_highlight('DiffChange').fg },
},
{ provider = ')', hl = { fg = 'green', bold = true } },
},
}
M.dap = {
condition = function()
if not misc_utils.loaded('nvim-dap') then
return false
end
return require('dap').session() ~= nil
end,
provider = function()
return '' .. require('dap').status()
end,
on_click = {
callback = function()
require('dap').continue()
end,
name = 'heirline_dap',
},
hl = { fg = 'red', bold = true },
}
M.macro = {
condition = function()
return vim.fn.reg_recording() ~= '' and vim.o.cmdheight == 0
end,
provider = function()
return ' @ Recording ' .. vim.fn.reg_recording()
end,
update = { 'RecordingEnter', 'RecordingLeave' },
hl = { fg = 'green', bold = true },
}
return M