2023-12-31 00:10:56 +02:00

436 lines
17 KiB
Lua

--- ### AstroNvim Status Components
--
-- Statusline related component functions to use with Heirline
--
-- This module can be loaded with `local component = require "astronvim.utils.status.component"`
--
-- @module astronvim.utils.status.component
-- @copyright 2023
-- @license GNU General Public License v3.0
local M = {}
local condition = require "astronvim.utils.status.condition"
local env = require "astronvim.utils.status.env"
local hl = require "astronvim.utils.status.hl"
local init = require "astronvim.utils.status.init"
local provider = require "astronvim.utils.status.provider"
local status_utils = require "astronvim.utils.status.utils"
local utils = require "astronvim.utils"
local buffer_utils = require "astronvim.utils.buffer"
local extend_tbl = utils.extend_tbl
local get_icon = utils.get_icon
local is_available = utils.is_available
--- A Heirline component for filling in the empty space of the bar
---@param opts? table options for configuring the other fields of the heirline component
---@return table # The heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.fill()
function M.fill(opts) return extend_tbl({ provider = provider.fill() }, opts) end
--- A function to build a set of children components for an entire file information section
---@param opts? table options for configuring file_icon, filename, filetype, file_modified, file_read_only, and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.file_info()
function M.file_info(opts)
opts = extend_tbl({
file_icon = { hl = hl.file_icon "statusline", padding = { left = 1, right = 1 } },
filename = {},
file_modified = { padding = { left = 1 } },
file_read_only = { padding = { left = 1 } },
surround = { separator = "left", color = "file_info_bg", condition = condition.has_filetype },
hl = hl.get_attributes "file_info",
}, opts)
return M.builder(status_utils.setup_providers(opts, {
"file_icon",
"unique_path",
"filename",
"filetype",
"file_modified",
"file_read_only",
"close_button",
}))
end
--- A function with different file_info defaults specifically for use in the tabline
---@param opts? table options for configuring file_icon, filename, filetype, file_modified, file_read_only, and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.tabline_file_info()
function M.tabline_file_info(opts)
return M.file_info(extend_tbl({
file_icon = {
condition = function(self) return not self._show_picker end,
hl = hl.file_icon "tabline",
},
unique_path = {
hl = function(self) return hl.get_attributes(self.tab_type .. "_path") end,
},
close_button = {
hl = function(self) return hl.get_attributes(self.tab_type .. "_close") end,
padding = { left = 1, right = 1 },
on_click = {
callback = function(_, minwid) buffer_utils.close(minwid) end,
minwid = function(self) return self.bufnr end,
name = "heirline_tabline_close_buffer_callback",
},
},
padding = { left = 1, right = 1 },
hl = function(self)
local tab_type = self.tab_type
if self._show_picker and self.tab_type ~= "buffer_active" then tab_type = "buffer_visible" end
return hl.get_attributes(tab_type)
end,
surround = false,
}, opts))
end
--- A function to build a set of children components for an entire navigation section
---@param opts? table options for configuring ruler, percentage, scrollbar, and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.nav()
function M.nav(opts)
opts = extend_tbl({
ruler = {},
percentage = { padding = { left = 1 } },
scrollbar = { padding = { left = 1 }, hl = { fg = "scrollbar" } },
surround = { separator = "right", color = "nav_bg" },
hl = hl.get_attributes "nav",
update = { "CursorMoved", "CursorMovedI", "BufEnter" },
}, opts)
return M.builder(status_utils.setup_providers(opts, { "ruler", "percentage", "scrollbar" }))
end
--- A function to build a set of children components for information shown in the cmdline
---@param opts? table options for configuring macro recording, search count, and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.cmd_info()
function M.cmd_info(opts)
opts = extend_tbl({
macro_recording = {
icon = { kind = "MacroRecording", padding = { right = 1 } },
condition = condition.is_macro_recording,
update = {
"RecordingEnter",
"RecordingLeave",
callback = vim.schedule_wrap(function() vim.cmd.redrawstatus() end),
},
},
search_count = {
icon = { kind = "Search", padding = { right = 1 } },
padding = { left = 1 },
condition = condition.is_hlsearch,
},
showcmd = {
padding = { left = 1 },
condition = condition.is_statusline_showcmd,
},
surround = {
separator = "center",
color = "cmd_info_bg",
condition = function()
return condition.is_hlsearch() or condition.is_macro_recording() or condition.is_statusline_showcmd()
end,
},
condition = function() return vim.opt.cmdheight:get() == 0 end,
hl = hl.get_attributes "cmd_info",
}, opts)
return M.builder(status_utils.setup_providers(opts, { "macro_recording", "search_count", "showcmd" }))
end
--- A function to build a set of children components for a mode section
---@param opts? table options for configuring mode_text, paste, spell, and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.mode { mode_text = true }
function M.mode(opts)
opts = extend_tbl({
mode_text = false,
paste = false,
spell = false,
surround = { separator = "left", color = hl.mode_bg },
hl = hl.get_attributes "mode",
update = {
"ModeChanged",
pattern = "*:*",
callback = vim.schedule_wrap(function() vim.cmd.redrawstatus() end),
},
}, opts)
if not opts["mode_text"] then opts.str = { str = " " } end
return M.builder(status_utils.setup_providers(opts, { "mode_text", "str", "paste", "spell" }))
end
--- A function to build a set of children components for an LSP breadcrumbs section
---@param opts? table options for configuring breadcrumbs and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.breadcumbs()
function M.breadcrumbs(opts)
opts = extend_tbl({ padding = { left = 1 }, condition = condition.aerial_available, update = "CursorMoved" }, opts)
opts.init = init.breadcrumbs(opts)
return opts
end
--- A function to build a set of children components for the current file path
---@param opts? table options for configuring path and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.separated_path()
function M.separated_path(opts)
opts = extend_tbl({ padding = { left = 1 }, update = { "BufEnter", "DirChanged" } }, opts)
opts.init = init.separated_path(opts)
return opts
end
--- A function to build a set of children components for a git branch section
---@param opts? table options for configuring git branch and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.git_branch()
function M.git_branch(opts)
opts = extend_tbl({
git_branch = { icon = { kind = "GitBranch", padding = { right = 1 } } },
surround = { separator = "left", color = "git_branch_bg", condition = condition.is_git_repo },
hl = hl.get_attributes "git_branch",
on_click = {
name = "heirline_branch",
callback = function()
if is_available "telescope.nvim" then
vim.defer_fn(function() require("telescope.builtin").git_branches() end, 100)
end
end,
},
update = { "User", pattern = "GitSignsUpdate" },
init = init.update_events { "BufEnter" },
}, opts)
return M.builder(status_utils.setup_providers(opts, { "git_branch" }))
end
--- A function to build a set of children components for a git difference section
---@param opts? table options for configuring git changes and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.git_diff()
function M.git_diff(opts)
opts = extend_tbl({
added = { icon = { kind = "GitAdd", padding = { left = 1, right = 1 } } },
changed = { icon = { kind = "GitChange", padding = { left = 1, right = 1 } } },
removed = { icon = { kind = "GitDelete", padding = { left = 1, right = 1 } } },
hl = hl.get_attributes "git_diff",
on_click = {
name = "heirline_git",
callback = function()
if is_available "telescope.nvim" then
vim.defer_fn(function() require("telescope.builtin").git_status() end, 100)
end
end,
},
surround = { separator = "left", color = "git_diff_bg", condition = condition.git_changed },
update = { "User", pattern = "GitSignsUpdate" },
init = init.update_events { "BufEnter" },
}, opts)
return M.builder(status_utils.setup_providers(opts, { "added", "changed", "removed" }, function(p_opts, p)
local out = status_utils.build_provider(p_opts, p)
if out then
out.provider = "git_diff"
out.opts.type = p
if out.hl == nil then out.hl = { fg = "git_" .. p } end
end
return out
end))
end
--- A function to build a set of children components for a diagnostics section
---@param opts? table options for configuring diagnostic providers and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.diagnostics()
function M.diagnostics(opts)
opts = extend_tbl({
ERROR = { icon = { kind = "DiagnosticError", padding = { left = 1, right = 1 } } },
WARN = { icon = { kind = "DiagnosticWarn", padding = { left = 1, right = 1 } } },
INFO = { icon = { kind = "DiagnosticInfo", padding = { left = 1, right = 1 } } },
HINT = { icon = { kind = "DiagnosticHint", padding = { left = 1, right = 1 } } },
surround = { separator = "left", color = "diagnostics_bg", condition = condition.has_diagnostics },
hl = hl.get_attributes "diagnostics",
on_click = {
name = "heirline_diagnostic",
callback = function()
if is_available "telescope.nvim" then
vim.defer_fn(function() require("telescope.builtin").diagnostics() end, 100)
end
end,
},
update = { "DiagnosticChanged", "BufEnter" },
}, opts)
return M.builder(status_utils.setup_providers(opts, { "ERROR", "WARN", "INFO", "HINT" }, function(p_opts, p)
local out = status_utils.build_provider(p_opts, p)
if out then
out.provider = "diagnostics"
out.opts.severity = p
if out.hl == nil then out.hl = { fg = "diag_" .. p } end
end
return out
end))
end
--- A function to build a set of children components for a Treesitter section
---@param opts? table options for configuring diagnostic providers and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.treesitter()
function M.treesitter(opts)
opts = extend_tbl({
str = { str = "TS", icon = { kind = "ActiveTS", padding = { right = 1 } } },
surround = {
separator = "right",
color = "treesitter_bg",
condition = condition.treesitter_available,
},
hl = hl.get_attributes "treesitter",
update = { "OptionSet", pattern = "syntax" },
init = init.update_events { "BufEnter" },
}, opts)
return M.builder(status_utils.setup_providers(opts, { "str" }))
end
--- A function to build a set of children components for an LSP section
---@param opts? table options for configuring lsp progress and client_name providers and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.lsp()
function M.lsp(opts)
opts = extend_tbl({
lsp_progress = {
str = "",
padding = { right = 1 },
update = {
"User",
pattern = "AstroLspProgress",
callback = vim.schedule_wrap(function() vim.cmd.redrawstatus() end),
},
},
lsp_client_names = {
str = "LSP",
update = {
"LspAttach",
"LspDetach",
"BufEnter",
callback = vim.schedule_wrap(function() vim.cmd.redrawstatus() end),
},
icon = { kind = "ActiveLSP", padding = { right = 2 } },
},
hl = hl.get_attributes "lsp",
surround = { separator = "right", color = "lsp_bg", condition = condition.lsp_attached },
on_click = {
name = "heirline_lsp",
callback = function()
vim.defer_fn(function() vim.cmd.LspInfo() end, 100)
end,
},
}, opts)
return M.builder(status_utils.setup_providers(
opts,
{ "lsp_progress", "lsp_client_names" },
function(p_opts, p, i)
return p_opts
and {
flexible = i,
status_utils.build_provider(p_opts, provider[p](p_opts)),
status_utils.build_provider(p_opts, provider.str(p_opts)),
}
or false
end
))
end
--- A function to build a set of components for a foldcolumn section in a statuscolumn
---@param opts? table options for configuring foldcolumn and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.foldcolumn()
function M.foldcolumn(opts)
opts = extend_tbl({
foldcolumn = { padding = { right = 1 } },
condition = condition.foldcolumn_enabled,
on_click = {
name = "fold_click",
callback = function(...)
local char = status_utils.statuscolumn_clickargs(...).char
local fillchars = vim.opt_local.fillchars:get()
if char == (fillchars.foldopen or get_icon "FoldOpened") then
vim.cmd "norm! zc"
elseif char == (fillchars.foldcolse or get_icon "FoldClosed") then
vim.cmd "norm! zo"
end
end,
},
}, opts)
return M.builder(status_utils.setup_providers(opts, { "foldcolumn" }))
end
--- A function to build a set of components for a numbercolumn section in statuscolumn
---@param opts? table options for configuring numbercolumn and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.numbercolumn()
function M.numbercolumn(opts)
opts = extend_tbl({
numbercolumn = { padding = { right = 1 } },
condition = condition.numbercolumn_enabled,
on_click = {
name = "line_click",
callback = function(...)
local args = status_utils.statuscolumn_clickargs(...)
if args.mods:find "c" then
local dap_avail, dap = pcall(require, "dap")
if dap_avail then vim.schedule(dap.toggle_breakpoint) end
end
end,
},
}, opts)
return M.builder(status_utils.setup_providers(opts, { "numbercolumn" }))
end
--- A function to build a set of components for a signcolumn section in statuscolumn
---@param opts? table options for configuring signcolumn and the overall padding
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").component.signcolumn()
function M.signcolumn(opts)
opts = extend_tbl({
signcolumn = {},
condition = condition.signcolumn_enabled,
on_click = {
name = "sign_click",
callback = function(...)
local args = status_utils.statuscolumn_clickargs(...)
if args.sign and args.sign.name and env.sign_handlers[args.sign.name] then
env.sign_handlers[args.sign.name](args)
end
end,
},
}, opts)
return M.builder(status_utils.setup_providers(opts, { "signcolumn" }))
end
--- A general function to build a section of astronvim status providers with highlights, conditions, and section surrounding
---@param opts? table a list of components to build into a section
---@return table # The Heirline component table
-- @usage local heirline_component = require("astronvim.utils.status").components.builder({ { provider = "file_icon", opts = { padding = { right = 1 } } }, { provider = "filename" } })
function M.builder(opts)
opts = extend_tbl({ padding = { left = 0, right = 0 } }, opts)
local children = {}
if opts.padding.left > 0 then -- add left padding
table.insert(children, { provider = status_utils.pad_string(" ", { left = opts.padding.left - 1 }) })
end
for key, entry in pairs(opts) do
if
type(key) == "number"
and type(entry) == "table"
and provider[entry.provider]
and (entry.opts == nil or type(entry.opts) == "table")
then
entry.provider = provider[entry.provider](entry.opts)
end
children[key] = entry
end
if opts.padding.right > 0 then -- add right padding
table.insert(children, { provider = status_utils.pad_string(" ", { right = opts.padding.right - 1 }) })
end
return opts.surround
and status_utils.surround(opts.surround.separator, opts.surround.color, children, opts.surround.condition)
or children
end
return M