--- ### 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