aboutsummaryrefslogtreecommitdiff
path: root/lua/plugins/heirline.lua
diff options
context:
space:
mode:
authorsrdusr <trevorgray@srdusr.com>2025-09-24 04:19:28 +0200
committersrdusr <trevorgray@srdusr.com>2025-09-24 04:19:28 +0200
commit7ed2303648bf83bb081d9bd863660ebf2344ce47 (patch)
tree702f5f832796b572d0faee31c0eb15507e91f49a /lua/plugins/heirline.lua
parent2a8020a2e9b7ef2ee77ddee14892127a4eb95187 (diff)
downloaddotfiles-7ed2303648bf83bb081d9bd863660ebf2344ce47.tar.gz
dotfiles-7ed2303648bf83bb081d9bd863660ebf2344ce47.zip
Squashed 'common/config/nvim/' changes from 2a8020a..966d12a
966d12a Update/Overhaul git-subtree-dir: common/config/nvim git-subtree-split: 966d12ac730c83da90d60ab24eae539b2ea69441
Diffstat (limited to 'lua/plugins/heirline.lua')
-rwxr-xr-x[-rw-r--r--]lua/plugins/heirline.lua2631
1 files changed, 1419 insertions, 1212 deletions
diff --git a/lua/plugins/heirline.lua b/lua/plugins/heirline.lua
index c41aff3..a4c2fc3 100644..100755
--- a/lua/plugins/heirline.lua
+++ b/lua/plugins/heirline.lua
@@ -1,1290 +1,1497 @@
-local conditions = require('heirline.conditions')
-local utils = require('heirline.utils')
-
-local colors = {
- --bg = "#23232e",
- bg = nil,
- nobg = 'NONE',
- white = '#f8f8f2',
- black = '#000000',
- darkgray = '#23232e',
- gray = '#2d2b3a',
- lightgray = '#d6d3ea',
- pink = '#f92672',
- green = '#50fa7b',
- blue = '#39BAE6',
- yellow = '#f1fa8c',
- orange = '#ffb86c',
- purple = '#BF40BF',
- violet = '#7F00FF',
- red = '#ff5555',
- cyan = '#66d9eC',
- diag = {
- warn = utils.get_highlight('DiagnosticSignWarn').fg,
- error = utils.get_highlight('DiagnosticSignError').fg,
- hint = utils.get_highlight('DiagnosticSignHint').fg,
- info = utils.get_highlight('DiagnosticSignInfo').fg,
- },
- git = {
- active = '#f34f29',
- del = '#ff5555',
- add = '#50fa7b',
- change = '#ae81ff',
- },
-}
-
-require('heirline').load_colors(colors)
-
-local Align = { provider = '%=', hl = { bg = colors.bg } }
-local Space = { provider = ' ', hl = { bg = colors.bg } }
-local Tab = { provider = ' ' }
-local LeftSpace = { provider = '' }
-local RightSpace = { provider = '' }
-
-local ViMode = {
- init = function(self)
- self.mode = vim.fn.mode(1)
- --if not self.once then
- -- vim.cmd("au ModeChanged *:*o redrawstatus")
- --end
- --self.once = true
- end,
- static = {
- mode_names = {
- n = ' NORMAL ',
- no = 'PENDING ',
- nov = ' N? ',
- noV = ' N? ',
- ['no\22'] = ' N? ',
- niI = ' Ni ',
- niR = ' Nr ',
- niV = ' Nv ',
- nt = 'TERMINAL',
- v = ' VISUAL ',
- vs = ' Vs ',
- V = ' V·LINE ',
- ['\22'] = 'V·BLOCK ',
- ['\22s'] = 'V·BLOCK ',
- s = ' SELECT ',
- S = ' S·LINE ',
- ['\19'] = 'S·BLOCK ',
- i = ' INSERT ',
- ix = 'insert x',
- ic = 'insert c',
- R = 'REPLACE ',
- Rc = ' Rc ',
- Rx = ' Rx ',
- Rv = 'V·REPLACE ',
- Rvc = ' Rv ',
- Rvx = ' Rv ',
- c = 'COMMAND ',
- cv = ' VIM EX ',
- ce = ' EX ',
- r = ' PROMPT ',
- rm = ' MORE ',
- ['r?'] = 'CONFIRM ',
- ['!'] = ' SHELL ',
- t = 'TERMINAL',
+local M = {}
+
+-- Safe require function to handle missing dependencies
+local function safe_require(module)
+ local ok, result = pcall(require, module)
+ return ok and result or nil
+end
+
+-- These will be initialized in M.setup()
+local heirline = nil
+local conditions = {}
+local utils = {}
+local colors = {}
+
+function M.setup()
+ heirline = safe_require("heirline")
+ if not heirline then
+ return
+ end
+
+ -- Initialize conditions and utils after heirline is loaded
+ conditions = require("heirline.conditions") or {}
+ utils = require("heirline.utils") or {}
+
+
+ -- Initialize colors after safe_fg is defined
+ colors = {
+ bg = "NONE",
+ nobg = "NONE",
+ white = "#f8f8f2",
+ black = "#000000",
+ darkgray = "#23232e",
+ gray = "#2d2b3a",
+ lightgray = "#d6d3ea",
+ pink = "#f92672",
+ green = "#50fa7b",
+ blue = "#39BAE6",
+ yellow = "#f1fa8c",
+ orange = "#ffb86c",
+ purple = "#BF40BF",
+ violet = "#7F00FF",
+ red = "#ff5555",
+ cyan = "#66d9eC",
+ --diag = {
+ -- warn = safe_fg("DiagnosticSignWarn", "#ffb86c"),
+ -- error = safe_fg("DiagnosticSignError", "#ff5555"),
+ -- hint = safe_fg("DiagnosticSignHint", "#50fa7b"),
+ -- info = safe_fg("DiagnosticSignInfo", "#66d9eC"),
+ --},
+ diag = {
+ warn = utils.get_highlight("DiagnosticSignWarn").fg,
+ error = utils.get_highlight("DiagnosticSignError").fg,
+ hint = utils.get_highlight("DiagnosticSignHint").fg,
+ info = utils.get_highlight("DiagnosticSignInfo").fg,
},
- },
- provider = function(self)
- return ' %2(' .. self.mode_names[self.mode] .. '%) '
- end,
- hl = function(self)
- return { fg = 'colors.black', bg = self.mode_color, bold = true }
- end,
- update = {
- 'ModeChanged',
- 'VimEnter',
- pattern = '*:*',
- callback = vim.schedule_wrap(function()
- vim.cmd('redrawstatus')
- end),
- },
-}
-
--- LSP
-local LSPActive = {
- condition = conditions.lsp_attached,
- update = { 'LspAttach', 'LspDetach' },
- provider = function()
- local buf_clients = vim.lsp.buf_get_clients()
- local buf_client_names = {}
-
- -- add client
- for _, client in pairs(buf_clients) do
- if client.name ~= 'null-ls' then
- table.insert(buf_client_names, client.name)
+ git = {
+ active = "#f34f29",
+ del = "#ff5555",
+ add = "#50fa7b",
+ change = "#ae81ff",
+ },
+ }
+
+ -- Only load colors if heirline is available
+ if heirline.load_colors then
+ local ok, err = pcall(heirline.load_colors, colors)
+ if not ok then
+ vim.notify("Failed to load Heirline colors: " .. tostring(err), vim.log.levels.ERROR)
+ end
+ end
+
+ local function get_icon(icon, fallback)
+ -- Check if we have Nerd Fonts available
+ local has_nerd_fonts = vim.g.statusline_has_nerd_fonts
+ if has_nerd_fonts == nil then
+ -- Cache the result to avoid repeated checks
+ if vim.fn.has('unix') == 1 and vim.fn.executable('fc-list') == 1 then
+ local handle = io.popen('fc-list | grep -i nerd')
+ local result = handle:read('*a')
+ handle:close()
+ has_nerd_fonts = result ~= ""
+ else
+ -- On non-Unix systems or if fc-list isn't available, assume no Nerd Fonts
+ has_nerd_fonts = false
end
+ vim.g.statusline_has_nerd_fonts = has_nerd_fonts
end
- return '⚙️ ' .. table.concat(buf_client_names, '')
- end,
- hl = { fg = colors.lightgray, bold = false },
-}
-local Navic = {
- condition = function()
- return require('nvim-navic').is_available()
- end,
- static = {
- -- create a type highlight map
- type_hl = {
- File = 'Directory',
- Module = '@include',
- Namespace = '@namespace',
- Package = '@include',
- Class = '@structure',
- Method = '@method',
- Property = '@property',
- Field = '@field',
- Constructor = '@constructor',
- Enum = '@field',
- Interface = '@type',
- Function = '@function',
- Variable = '@variable',
- Constant = '@constant',
- String = '@string',
- Number = '@number',
- Boolean = '@boolean',
- Array = '@field',
- Object = '@type',
- Key = '@keyword',
- Null = '@comment',
- EnumMember = '@field',
- Struct = '@structure',
- Event = '@keyword',
- Operator = '@operator',
- TypeParameter = '@type',
+
+ -- Return the appropriate string based on font availability
+ local result = has_nerd_fonts and icon or (fallback or '')
+ -- Trim any whitespace to prevent layout issues
+ return vim.trim(result)
+ end
+
+ -- Define all components after colors and utils are initialized
+
+ --local Signs = {
+ -- Error = "✘",
+ -- Warn = "",
+ -- Hint = "◉",
+ -- Info = "",
+ --}
+ local Icons = {
+ Signs = {
+ Error = "✘",
+ Warn = "",
+ Hint = "◉",
+ Info = "",
+ LSP = get_icon("⚙️", "LSP"),
},
- -- bit operation dark magic, see below...
- enc = function(line, col, winnr)
- return bit.bor(bit.lshift(line, 16), bit.lshift(col, 6), winnr)
+ ---- LSP/Debug
+ Error = get_icon("✘", "E"),
+ Warn = get_icon("", "W"),
+ Hint = get_icon("◉", "H"),--
+ Info = get_icon("ℹ", "I"),
+ --LSP = get_icon("⚙️", "LSP"),
+
+ -- Diagnostic
+ Diagnostic = {
+ error = get_icon("✘", "E"),
+ warn = get_icon("", "W"),
+ hint = get_icon("", "H"),
+ info = get_icon("ℹ", "I"),
+ },
+
+
+--local GitIcons = {
+-- added = "✚", -- plus in diff style
+-- modified = "", -- nf-oct-diff_modified
+-- removed = "", -- nf-oct-diff_removed
+--}
+--added = "", -- nf-oct-diff_added
+--modified = "", -- nf-oct-diff_modified
+--removed = "", -- nf-oct-diff_removed
+--local GitIcons = {
+-- added = "", -- nf-fa-plus_square
+-- modified = "", -- nf-fa-file_text_o
+-- removed = "", -- nf-fa-minus_square
+--}
+ -- Git
+ Git = {
+ branch = get_icon(" ", "⎇ "),
+ added = get_icon("+ ", "+"),
+ removed = get_icon("- ", "-"),
+ modified = get_icon("~ ", "~"),
+ renamed = get_icon("", "r"),
+ untracked = get_icon("", "?"),
+ ignored = get_icon("", "."),
+ },
+
+ -- UI Elements
+ UI = {
+ left_separator = get_icon("", ""),
+ right_separator = get_icon("", ""),
+ thin_separator = get_icon("▏", "|"),
+ ellipsis = get_icon("…", "..."),
+ arrow_left = get_icon("◀", "<"),
+ arrow_right = get_icon("▶", ">"),
+ close = get_icon("✕", "x"),
+ big_close = get_icon(" ", "x "),
+ modified = get_icon(" + ", "*"),
+ readonly = get_icon("", "RO"),
+ lock = get_icon("", "[L]"),
+ clock = get_icon("🕒", "[TIME]"),
+ buffer = get_icon("", "[BUF]"),
+ tab = get_icon("", "[TAB]"),
+ search = get_icon("🔍", "[SEARCH]"),
+ spell = get_icon("暈", "[SPELL]"),
+ whitespace = get_icon("␣", "[WS]"),
+ newline = get_icon("↵", "[NL]"),
+ indent = get_icon("▏", "|"),
+ fold = get_icon("", ">"),
+ fold_open = get_icon("", "v"),
+ fold_closed = get_icon("", ">"),
+ },
+
+ -- File types
+ File = {
+ default = get_icon("", "[F]"),
+ directory = get_icon("", "[D]"),
+ symlink = get_icon("", "[L]"),
+ executable = get_icon("", "[X]"),
+ image = get_icon("", "[IMG]"),
+ archive = get_icon("", "[ARC]"),
+ audio = get_icon("", "[AUD]"),
+ video = get_icon("", "[VID]"),
+ document = get_icon("", "[DOC]"),
+ config = get_icon("", "[CFG]"),
+ code = get_icon("", "[CODE]"),
+ terminal = get_icon("", "[TERM]"),
+ },
+
+ -- File format indicators
+ Format = {
+ unix = get_icon("", "[UNIX]"),
+ dos = get_icon("", "[DOS]"),
+ mac = get_icon("", "[MAC]"),
+ },
+
+ -- Version control
+ VCS = {
+ branch = get_icon("", "[BR]"),
+ git = get_icon("", "[GIT]"),
+ github = get_icon("", "[GH]"),
+ gitlab = get_icon("", "[GL]"),
+ bitbucket = get_icon("", "[BB]"),
+ },
+
+ -- Programming languages
+ Lang = {
+ lua = get_icon("", "[LUA]"),
+ python = get_icon("", "[PY]"),
+ javascript = get_icon("", "[JS]"),
+ typescript = get_icon("", "[TS]"),
+ html = get_icon("", "[HTML]"),
+ css = get_icon("", "[CSS]"),
+ json = get_icon("", "[JSON]"),
+ markdown = get_icon("", "[MD]"),
+ docker = get_icon("", "[DKR]"),
+ rust = get_icon("", "[RS]"),
+ go = get_icon("", "[GO]"),
+ java = get_icon("", "[JAVA]"),
+ c = get_icon("", "[C]"),
+ cpp = get_icon("", "[C++]"),
+ ruby = get_icon("", "[RB]"),
+ php = get_icon("", "[PHP]"),
+ haskell = get_icon("", "[HS]"),
+ scala = get_icon("", "[SCALA]"),
+ elixir = get_icon("", "[EXS]"),
+ clojure = get_icon("", "[CLJ]"),
+ },
+
+ -- UI Indicators
+ Indicator = {
+ error = get_icon("✘", "[E]"),
+ warning = get_icon("⚠", "[W]"),
+ info = get_icon("ℹ", "[I]"),
+ hint = get_icon("", "[H]"),
+ success = get_icon("✓", "[OK]"),
+ question = get_icon("?", "[?]"),
+ star = get_icon("★", "[*]"),
+ heart = get_icon("❤", "<3"),
+ lightning = get_icon("⚡", "[!]"),
+ check = get_icon("✓", "[√]"),
+ cross = get_icon("✗", "[x]"),
+ plus = get_icon("+", "[+]"),
+ recording = get_icon(" ", "q")
+ },
+
+ -- File operations
+ FileOp = {
+ new = get_icon("", "[NEW]"),
+ save = get_icon("💾", "[SAVE]"),
+ open = get_icon("📂", "[OPEN]"),
+ close = get_icon("✕", "[X]"),
+ undo = get_icon("↩", "[UNDO]"),
+ redo = get_icon("↪", "[REDO]"),
+ cut = get_icon("✂", "[CUT]"),
+ copy = get_icon("⎘", "[COPY]"),
+ paste = get_icon("📋", "[PASTE]"),
+ search = get_icon("🔍", "[FIND]"),
+ replace = get_icon("🔄", "[REPLACE]"),
+ },
+
+ -- Navigation
+ Nav = {
+ left = get_icon("←", "[<]"),
+ right = get_icon("→", "[>]"),
+ up = get_icon("↑", "[^]"),
+ down = get_icon("↓", "[v]"),
+ first = get_icon("⏮", "[<<]"),
+ last = get_icon("⏭", "[>>]"),
+ prev = get_icon("◀", "[<]"),
+ next = get_icon("▶", "[>]"),
+ back = get_icon("↩", "[B]"),
+ forward = get_icon("↪", "[F]"),
+ },
+
+ -- Editor states
+ State = {
+ insert = get_icon("", "[INS]"),
+ normal = get_icon("🚀", "[NOR]"),
+ visual = get_icon("👁", "[VIS]"),
+ replace = get_icon("🔄", "[REP]"),
+ command = get_icon("", "[CMD]"),
+ terminal = get_icon("", "[TERM]"),
+ select = get_icon("🔍", "[SEL]"),
+ },
+
+ -- Common symbols
+ Symbol = {
+ dot = get_icon("•", "•"),
+ bullet = get_icon("•", "•"),
+ middle_dot = get_icon("·", "·"),
+ ellipsis = get_icon("…", "..."),
+ check = get_icon("✓", "[OK]"),
+ cross = get_icon("✗", "[X]"),
+ arrow_right = get_icon(" ", "->"),
+ arrow_left = get_icon(" ", "<-"),
+ double_arrow_right = get_icon("»", ">>"),
+ double_arrow_left = get_icon("«", "<<"),
+ chevron_right = get_icon("›", ">"),
+ chevron_left = get_icon("‹", "<"),
+ },
+
+ -- Document symbols
+ DocSymbol = {
+ class = get_icon("", "[C]"),
+ function_icon = get_icon("", "[F]"),
+ method = get_icon("", "[M]"),
+ property = get_icon("", "[P]"),
+ field = get_icon("ﰠ", "[F]"),
+ constructor = get_icon("", "[C]"),
+ enum = get_icon("", "[E]"),
+ interface = get_icon("", "[I]"),
+ variable = get_icon("", "[V]"),
+ constant = get_icon("", "[C]"),
+ string = get_icon("", "[S]"),
+ number = get_icon("", "[N]"),
+ boolean = get_icon("◩", "[B]"),
+ array = get_icon("", "[A]"),
+ object = get_icon("⦿", "[O]"),
+ key = get_icon("🔑", "[K]"),
+ null = get_icon("NULL", "Ø"),
+ enum_member = get_icon("", "[E]"),
+ struct = get_icon("פּ", "[S]"),
+ event = get_icon("", "[E]"),
+ operator = get_icon("", "[O]"),
+ type_parameter = get_icon("", "[T]"),
+ },
+ }
+ local Align = { provider = "%=", hl = { bg = colors.bg } }
+ local Space = { provider = " ", hl = { bg = colors.bg } }
+ local Tab = { provider = " " }
+ local LeftSpace = { provider = "" }
+ local RightSpace = { provider = "" }
+
+ local ViMode = {
+ init = function(self)
+ self.mode = vim.fn.mode(1)
+ -- Store the initial mode
+ self.prev_mode = self.mode
+
+ -- Set up autocommand to force redraw on mode change
+ vim.api.nvim_create_autocmd("ModeChanged", {
+ pattern = "*:*",
+ callback = function()
+ -- Only redraw if the mode actually changed
+ local current_mode = vim.fn.mode(1)
+ if current_mode ~= self.prev_mode then
+ self.prev_mode = current_mode
+ vim.schedule(function()
+ vim.cmd("redrawstatus")
+ end)
+ end
+ end,
+ })
end,
- -- line: 16 bit (65535); col: 10 bit (1023); winnr: 6 bit (63)
- dec = function(c)
- local line = bit.rshift(c, 16)
- local col = bit.band(bit.rshift(c, 6), 1023)
- local winnr = bit.band(c, 63)
- return line, col, winnr
+ static = {
+ mode_names = {
+ n = " NORMAL ",
+ no = "PENDING ",
+ nov = " N? ",
+ noV = " N? ",
+ ["no\22"] = " N? ",
+ niI = " Ni ",
+ niR = " Nr ",
+ niV = " Nv ",
+ nt = "TERMINAL",
+ v = " VISUAL ",
+ vs = " Vs ",
+ V = " V·LINE ",
+ ["\22"] = "V·BLOCK ",
+ ["\22s"] = "V·BLOCK ",
+ s = " SELECT ",
+ S = " S·LINE ",
+ ["\19"] = "S·BLOCK ",
+ i = " INSERT ",
+ ix = "insert x",
+ ic = "insert c",
+ R = "REPLACE ",
+ Rc = " Rc ",
+ Rx = " Rx ",
+ Rv = "V·REPLACE ",
+ Rvc = " Rv ",
+ Rvx = " Rv ",
+ c = "COMMAND ",
+ cv = " VIM EX ",
+ ce = " EX ",
+ r = " PROMPT ",
+ rm = " MORE ",
+ ["r?"] = "CONFIRM ",
+ ["!"] = " SHELL ",
+ t = "TERMINAL",
+ },
+ },
+ provider = function(self)
+ return " %2(" .. self.mode_names[self.mode] .. "%) "
end,
- },
- init = function(self)
- local data = require('nvim-navic').get_data() or {}
- local children = {}
- -- create a child for each level
- for i, d in ipairs(data) do
- -- encode line and column numbers into a single integer
- local pos = self.enc(d.scope.start.line, d.scope.start.character, self.winnr)
- local child = {
- {
- provider = d.icon,
- hl = self.type_hl[d.type],
- },
- {
- -- escape `%`s (elixir) and buggy default separators
- provider = d.name:gsub('%%', '%%%%'):gsub('%s*->%s*', ''),
- -- highlight icon only or location name as well
- -- hl = self.type_hl[d.type],
-
- on_click = {
- -- pass the encoded position through minwid
- minwid = pos,
- callback = function(_, minwid)
- -- decode
- local line, col, winnr = self.dec(minwid)
- vim.api.nvim_win_set_cursor(vim.fn.win_getid(winnr), { line, col })
- end,
- name = 'heirline_navic',
- },
- },
- }
- -- add a separator only if needed
- if #data > 1 and i < #data then
- table.insert(child, {
- provider = ' > ',
- hl = { fg = 'bright_fg' },
- })
+ hl = function(self)
+ return { fg = "colors.black", bg = self.mode_color, bold = true }
+ end,
+ update = {
+ "ModeChanged",
+ "VimEnter",
+ "BufEnter",
+ "WinEnter",
+ "TabEnter",
+ pattern = "*:*",
+ callback = vim.schedule_wrap(function()
+ vim.cmd("redrawstatus")
+ end),
+ },
+ }
+
+ -- LSP
+ local LSPActive = {
+ condition = function()
+ local ok, _ = pcall(function()
+ local buf = vim.api.nvim_get_current_buf()
+ return #vim.lsp.get_clients({ bufnr = buf }) > 0
+ end)
+ return ok or false
+ end,
+ update = { "LspAttach", "LspDetach", "BufEnter" },
+ provider = function()
+ local ok, result = pcall(function()
+ local buf = vim.api.nvim_get_current_buf()
+ if not vim.api.nvim_buf_is_valid(buf) then return "" end
+
+ local clients = vim.lsp.get_clients({ bufnr = buf })
+ if not clients or #clients == 0 then return "" end
+
+ local client_names = {}
+ for _, client in ipairs(clients) do
+ if client and client.name and client.name ~= "null-ls" then
+ table.insert(client_names, client.name)
+ end
+ end
+
+ if #client_names > 0 then
+ return Icons.Signs.LSP .. " " .. table.concat(client_names, "/") .. " "
+ end
+
+ return ""
+ end)
+
+ if not ok then
+ vim.schedule(function()
+ vim.notify_once("Error in LSPActive provider: " .. tostring(result), vim.log.levels.DEBUG)
+ end)
+ return ""
end
- table.insert(children, child)
- end
- -- instantiate the new child, overwriting the previous one
- self.child = self:new(children, 1)
- end,
- -- evaluate the children containing navic components
- provider = function(self)
- return self.child:eval()
- end,
- hl = { fg = 'gray' },
- update = 'CursorMoved',
-}
-
--- Diagnostics
-local Diagnostics = {
- condition = conditions.has_diagnostics,
- static = {
- error_icon = vim.fn.sign_getdefined('DiagnosticSignError')[1].text,
- warn_icon = vim.fn.sign_getdefined('DiagnosticSignWarn')[1].text,
- info_icon = vim.fn.sign_getdefined('DiagnosticSignInfo')[1].text,
- hint_icon = vim.fn.sign_getdefined('DiagnosticSignHint')[1].text,
- },
- 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.info = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.INFO })
- end,
- update = { 'DiagnosticChanged', 'BufEnter' },
- {
- provider = function(self)
- -- 0 is just another output, we can decide to print it or not!
- return self.errors > 0 and (self.error_icon .. self.errors .. ' ')
+
+ return result or ""
end,
- hl = { fg = colors.diag.error, bg = colors.bg },
- },
- {
- provider = function(self)
- return self.warnings > 0 and (self.warn_icon .. self.warnings .. ' ')
+ hl = { fg = "lightgray", bold = false },
+ }
+
+ local Navic = {
+ condition = function()
+ local ok, navic = pcall(require, "nvim-navic")
+ return ok and navic.is_available()
end,
- hl = { fg = colors.diag.warn, bg = colors.bg },
- },
- {
- provider = function(self)
- return self.info > 0 and (self.info_icon .. self.info .. ' ')
+ static = {
+ type_hl = {
+ File = "Directory",
+ Module = "@include",
+ Namespace = "@namespace",
+ Package = "@include",
+ Class = "@structure",
+ Method = "@method",
+ Property = "@property",
+ Field = "@field",
+ Constructor = "@constructor",
+ Enum = "@field",
+ Interface = "@type",
+ Function = "@function",
+ Variable = "@variable",
+ Constant = "@constant",
+ String = "@string",
+ Number = "@number",
+ Boolean = "@boolean",
+ Array = "@field",
+ Object = "@type",
+ Key = "@keyword",
+ Null = "@comment",
+ EnumMember = "@field",
+ Struct = "@structure",
+ Event = "@keyword",
+ Operator = "@operator",
+ TypeParameter = "@type",
+ },
+ enc = function(line, col, winnr)
+ return bit.bor(bit.lshift(line, 16), bit.lshift(col, 6), winnr)
+ end,
+ dec = function(c)
+ local line = bit.rshift(c, 16)
+ local col = bit.band(bit.rshift(c, 6), 1023)
+ local winnr = bit.band(c, 63)
+ return line, col, winnr
+ end,
+ },
+ init = function(self)
+ local data = require("nvim-navic").get_data() or {}
+ local children = {}
+ for i, d in ipairs(data) do
+ local pos = self.enc(d.scope.start.line, d.scope.start.character, self.winnr)
+ local child = {
+ {
+ provider = d.icon,
+ hl = self.type_hl[d.type],
+ },
+ {
+ provider = d.name:gsub("%%", "%%%%"):gsub("%s*->%s*", ""),
+ on_click = {
+ minwid = pos,
+ callback = function(_, minwid)
+ local line, col, winnr = self.dec(minwid)
+ vim.api.nvim_win_set_cursor(vim.fn.win_getid(winnr), { line, col })
+ end,
+ name = "heirline_navic",
+ },
+ },
+ }
+ if #data > 1 and i < #data then
+ table.insert(child, {
+ provider = " > ",
+ hl = { fg = "bright_fg" },
+ })
+ end
+ table.insert(children, child)
+ end
+ self.child = self:new(children, 1)
end,
- hl = { fg = colors.diag.info, bg = colors.bg },
- },
- {
provider = function(self)
- return self.hints > 0 and (self.hint_icon .. self.hints)
+ return self.child:eval()
end,
- hl = { fg = colors.diag.hint, bg = colors.bg },
- },
- on_click = {
- callback = function()
- require('trouble').toggle({ mode = 'document_diagnostics' })
- -- or
- -- vim.diagnostic.setqflist()
+ hl = { fg = "gray" },
+ update = "CursorMoved",
+ }
+
+ -- Diagnostics
+ local Diagnostics = {
+ condition = conditions.has_diagnostics,
+ static = {
+ error_icon = Icons.Error,
+ warn_icon = Icons.Warn,
+ info_icon = Icons.Info,
+ hint_icon = Icons.Hint,
+ },
+ 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.info = #vim.diagnostic.get(0, { severity = vim.diagnostic.severity.INFO })
end,
- name = 'heirline_diagnostics',
- },
-}
-
--- Git
--- For the ones who're not (too) afraid of changes! Uses gitsigns.
-local Git = {
- condition = conditions.is_git_repo,
- init = function(self)
- self.status_dict = vim.b.gitsigns_status_dict
- self.has_changes = self.status_dict.added ~= 0 or self.status_dict.removed ~= 0 or self.status_dict.changed ~= 0
- end,
- --{
- -- -- git branch name
- -- provider = function(self)
- -- --return ' ' .. self.status_dict.head
- -- return '  ' .. self.status_dict.head
- -- end,
- -- --hl = { bold = true },
- -- hl = { fg = colors.git.active, bold = true, bg = colors.bg },
- --},
- -- You could handle delimiters, icons and counts similar to Diagnostics
- {
- -- git branch icon
- provider = function()
- return '  '
+ update = { "DiagnosticChanged", "BufEnter" },
+ {
+ provider = function(self)
+ return self.errors > 0 and (self.error_icon .. " " .. self.errors .. " ")
+ end,
+ hl = { fg = colors.diag.error, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ return self.warnings > 0 and (self.warn_icon .. " " .. self.warnings .. " ")
+ end,
+ hl = { fg = colors.diag.warn, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ return self.info > 0 and (self.info_icon .. " " .. self.info .. " ")
+ end,
+ hl = { fg = colors.diag.info, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ return self.hints > 0 and (self.hint_icon .. " " .. self.hints .. " ")
+ end,
+ hl = { fg = colors.diag.hint, bg = colors.bg },
+ },
+ on_click = {
+ callback = function()
+ local ok, _ = pcall(require, "trouble")
+ if ok then
+ require("trouble").toggle({ mode = "document_diagnostics" })
+ else
+ vim.diagnostic.setqflist()
+ end
+ end,
+ name = "heirline_diagnostics",
+ },
+ }
+
+ -- Git
+ local Git = {
+ condition = conditions.is_git_repo,
+ init = function(self)
+ self.status_dict = vim.b.gitsigns_status_dict or {}
+ self.has_changes = (self.status_dict.added or 0) ~= 0 or
+ (self.status_dict.removed or 0) ~= 0 or
+ (self.status_dict.changed or 0) ~= 0
end,
- hl = { fg = colors.git.active, bg = colors.bg },
- },
+ {
+ provider = function()
+ return " " .. Icons.Git.branch .. " "
+ end,
+ hl = { fg = colors.git.active, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ return self.status_dict.head or ""
+ end,
+ hl = { fg = colors.white, bg = colors.bg },
+ },
+ {
+ condition = function(self)
+ return self.has_changes
+ end,
+ provider = "",
+ },
+ {
+ provider = function(self)
+ local count = self.status_dict.added or 0
+ return count > 0 and (" " .. Icons.Git.added .. count) or ""
+ end,
+ hl = { fg = colors.git.add, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ local count = self.status_dict.removed or 0
+ return count > 0 and (" " .. Icons.Git.removed .. count) or ""
+ end,
+ hl = { fg = colors.git.del, bg = colors.bg },
+ },
+ {
+ provider = function(self)
+ local count = self.status_dict.changed or 0
+ return count > 0 and (" " .. Icons.Git.modified .. count) or ""
+ end,
+ hl = { fg = colors.git.change, bg = colors.bg },
+ },
+ on_click = {
+ callback = function()
+ vim.defer_fn(function()
+ vim.cmd("Lazygit")
+ end, 100)
+ end,
+ name = "heirline_git",
+ },
+ }
- {
- -- git branch name
- provider = function(self)
- return self.status_dict.head
+ -- FileNameBlock: FileIcon, FileName and friends
+ local FileNameBlock = {
+ init = function(self)
+ self.filename = vim.api.nvim_buf_get_name(0)
end,
- hl = { fg = colors.white, bg = colors.bg },
- },
+ hl = { bg = colors.bg },
+ }
+
+ local FileIcon = {
+ init = function(self)
+ local filename = self.filename or vim.api.nvim_buf_get_name(0)
+ local extension = vim.fn.fnamemodify(filename, ":e")
+
+ local has_nerd_fonts = vim.g.statusline_has_nerd_fonts
+ if has_nerd_fonts == nil then
+ if vim.fn.has('unix') == 1 and vim.fn.executable('fc-list') == 1 then
+ local handle = io.popen('fc-list | grep -i nerd')
+ local result = handle:read('*a')
+ handle:close()
+ has_nerd_fonts = result ~= ""
+ else
+ has_nerd_fonts = false
+ end
+ vim.g.statusline_has_nerd_fonts = has_nerd_fonts
+ end
- {
- condition = function(self)
- return self.has_changes
+ local icon, icon_color
+ if has_nerd_fonts then
+ icon, icon_color = require("nvim-web-devicons").get_icon_color(filename, extension, { default = true })
+ end
+
+ if vim.fn.isdirectory(filename) == 1 then
+ self.icon = has_nerd_fonts and Icons.File.directory or "[DIR]"
+ self.icon_color = colors.blue
+ else
+ if has_nerd_fonts and icon then
+ self.icon = icon .. " "
+ self.icon_color = icon_color or colors.blue
+ else
+ if extension == "" then
+ self.icon = Icons.File.default
+ else
+ local file_icon = Icons.File[extension:lower()] or Icons.File.default
+ if type(file_icon) == "table" then
+ self.icon = file_icon[1] or Icons.File.default
+ else
+ self.icon = file_icon
+ end
+ end
+ self.icon_color = colors.blue
+ end
+ end
end,
- --provider = "("
- provider = '',
- },
- {
provider = function(self)
- local count = self.status_dict.added or 0
- --return count > 0 and ("+" .. count)
- return count > 0 and ('  ' .. count)
+ return self.icon
end,
- --hl = { fg = "git_add" },
- hl = { fg = colors.git.add, bg = colors.bg },
- },
- {
- provider = function(self)
- local count = self.status_dict.removed or 0
- --return count > 0 and ("-" .. count)
- return count > 0 and ('  ' .. count)
+ hl = function(self)
+ return { fg = self.icon_color, bold = true }
end,
- --hl = { fg = "git_del" },
- hl = { fg = colors.git.del, bg = colors.bg },
- },
- {
+ }
+
+ local FileName = {
provider = function(self)
- local count = self.status_dict.changed or 0
- --return count > 0 and ("~" .. count)
- return count > 0 and ('  ' .. count)
+ local filename = vim.fn.fnamemodify(self.filename, ":.")
+ if filename == "" then
+ return "No Name"
+ end
+ if not conditions.width_percent_below(#filename, 0.25) then
+ filename = vim.fn.pathshorten(filename)
+ end
+ return filename
end,
- --hl = { fg = "git_change" },
- hl = { fg = colors.git.change, bg = colors.bg },
- },
- --{
- -- condition = function(self)
- -- return self.has_changes
- -- end,
- -- provider = ")",
- --},
- on_click = {
- callback = function()
- -- If you want to use Fugitive:
- -- vim.cmd("G")
-
- -- If you prefer Lazygit
- -- use vim.defer_fn() if the callback requires
- -- opening of a floating window
- -- (this also applies to telescope)
- vim.defer_fn(function()
- vim.cmd('Lazygit')
- end, 100)
+ hl = { fg = colors.white, bold = false, bg = colors.bg },
+ }
+
+ local FileFlags = {
+ {
+ provider = function()
+ if vim.bo.modified then
+ return " +"
+ end
+ end,
+ hl = { fg = colors.green, bg = colors.bg },
+ },
+ {
+ provider = function()
+ if not vim.bo.modifiable or vim.bo.readonly then
+ return " " .. Icons.UI.lock
+ end
+ end,
+ hl = { fg = colors.orange, bold = true, bg = colors.bg },
+ },
+ }
+
+ local FileNameModifier = {
+ hl = function()
+ if vim.bo.modified then
+ return { fg = colors.green, bold = false, force = true }
+ end
end,
- name = 'heirline_git',
- },
-}
-
--- Debugger
--- Display informations from nvim-dap!
--- Note that we add spaces separately, so that only the icon characters will be clickable
---local DAPMessages = {
--- condition = function()
--- local session = require("dap").session()
--- return session ~= nil
--- end,
--- provider = function()
--- return " " .. require("dap").status() .. " "
--- end,
--- hl = "Debug",
--- {
--- provider = "",
--- on_click = {
--- callback = function()
--- require("dap").step_into()
--- end,
--- name = "heirline_dap_step_into",
--- },
--- },
--- { provider = " " },
--- {
--- provider = "",
--- on_click = {
--- callback = function()
--- require("dap").step_out()
--- end,
--- name = "heirline_dap_step_out",
--- },
--- },
--- { provider = " " },
--- {
--- provider = " ",
--- on_click = {
--- callback = function()
--- require("dap").step_over()
--- end,
--- name = "heirline_dap_step_over",
--- },
--- },
--- { provider = " " },
--- {
--- provider = "ﰇ",
--- on_click = {
--- callback = function()
--- require("dap").run_last()
--- end,
--- name = "heirline_dap_run_last",
--- },
--- },
--- { provider = " " },
--- {
--- provider = "",
--- on_click = {
--- callback = function()
--- require("dap").terminate()
--- require("dapui").close({})
--- end,
--- name = "heirline_dap_close",
--- },
--- },
--- { provider = " " },
--- -- icons:       ﰇ  
---}
+ }
--- Tests
--- This requires the great ultest.
---local UltTest = {
--- condition = function()
--- return vim .api.nvim_call_function("ultest#is_test_file", {}) ~= 0
--- end,
--- static = {
--- passed_icon = vim.fn.sign_getdefined("test_pass")[1].text,
--- failed_icon = vim.fn.sign_getdefined("test_fail")[1].text,
--- passed_hl = { fg = utils.get_highlight("UltestPass").fg },
--- failed_hl = { fg = utils.get_highlight("UltestFail").fg },
--- },
--- init = function(self)
--- self.status = vim.api.nvim_call_function("ultest#status", {})
--- end,
---
--- -- again, if you'd like icons and numbers to be colored differently,
--- -- just split the component in two
--- {
--- provider = function(self)
--- return self.passed_icon .. self.status.passed .. " "
--- end,
--- hl = function(self)
--- return self.passed_hl
--- end,
--- },
--- {
--- provider = function(self)
--- return self.failed_icon .. self.status.failed .. " "
--- end,
--- hl = function(self)
--- return self.failed_hl
--- end,
--- },
--- {
--- provider = function(self)
--- return "of " .. self.status.tests - 1
--- end,
--- },
---}
+ -- FileType, FileEncoding and FileFormat
+ local FileType = {
+ provider = function()
+ return vim.bo.filetype
+ end,
+ hl = { fg = colors.white, bold = false, bg = colors.bg },
+ }
--- FileNameBlock: FileIcon, FileName and friends
-local FileNameBlock = {
- -- let's first set up some attributes needed by this component and it's children
- init = function(self)
- self.filename = vim.api.nvim_buf_get_name(0)
- end,
- --hl = { fg = utils.get_highlight("Statusline").fg, bold = true, bg = colors.bg },
- hl = { bg = colors.bg },
-}
-
--- FileIcon, FileName, FileFlags and FileNameModifier
-local FileIcon = {
- init = function(self)
- local filename = self.filename
- local extension = vim.fn.fnamemodify(filename, ':e')
- self.icon, self.icon_color = require('nvim-web-devicons').get_icon_color(filename, extension, { default = true })
- end,
- provider = function(self)
- return self.icon and (self.icon .. ' ')
- end,
- hl = function(self)
- return { fg = self.icon_color, bg = colors.bg }
- end,
-}
-
-local FileName = {
- provider = function(self)
- -- first, trim the pattern relative to the current directory. For other
- -- options, see :h filename-modifers
- local filename = vim.fn.fnamemodify(self.filename, ':.')
- if filename == '' then
- return 'No Name'
- end
- -- now, if the filename would occupy more than 1/4th of the available
- -- space, we trim the file path to its initials
- -- See Flexible Components section below for dynamic truncation
- if not conditions.width_percent_below(#filename, 0.25) then
- filename = vim.fn.pathshorten(filename)
- end
- return filename
- end,
- --hl = { fg = utils.get_highlight("Statusline").fg, bold = false, bg = colors.bg },
- hl = { fg = colors.white, bold = false, bg = colors.bg },
-}
-
-local FileFlags = {
- {
+ local FileEncoding = {
+ Space,
provider = function()
- if vim.bo.modified then
- return ' [+]' -- ±[+]
- end
+ local enc = (vim.bo.fenc ~= "" and vim.bo.fenc) or vim.o.enc
+ return enc:lower()
+ end,
+ hl = { bg = colors.bg, bold = false },
+ }
+
+ local FileFormat = {
+ provider = function()
+ local fmt = vim.bo.fileformat
+ return fmt ~= "unix" and fmt:lower() or ""
end,
- hl = { fg = colors.green, bg = colors.bg },
- },
- {
+ hl = { fg = utils.get_highlight("Statusline").fg, bold = true, bg = colors.bg },
+ }
+
+ local FileSize = {
provider = function()
- if not vim.bo.modifiable or vim.bo.readonly then
- return ' '
+ local suffix = { "b", "k", "M", "G", "T", "P", "E" }
+ local filename = vim.api.nvim_buf_get_name(0)
+ local fsize = vim.fn.getfsize(filename)
+ fsize = (fsize < 0 and 0) or fsize
+ 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 = colors.orange },
- hl = { fg = colors.orange, bold = true, bg = colors.bg },
- },
-}
-
-local FileNameModifier = {
- hl = function()
- if vim.bo.modified then
- return { fg = colors.green, bold = false, force = true }
- end
- end,
-}
-
--- FileType, FileEncoding and FileFormat
-local FileType = {
- provider = function()
- return vim.bo.filetype
- end,
- hl = { fg = colors.white, bold = false, bg = colors.bg },
-}
-
-local FileEncoding = {
- Space,
- provider = function()
- local enc = (vim.bo.fenc ~= '' and vim.bo.fenc) or vim.o.enc -- :h 'enc'
- return enc:lower()
- end,
- --hl = { fg = utils.get_highlight("Statusline").fg, bold = true, bg = colors.bg },
- hl = { bg = colors.bg, bold = false },
-}
-
-local FileFormat = {
- provider = function()
- local fmt = vim.bo.fileformat
- --return fmt ~= "unix" and fmt:upper()
- return fmt ~= 'unix' and fmt:lower()
- end,
- hl = { fg = utils.get_highlight('Statusline').fg, bold = true, bg = colors.bg },
-}
-
--- FileSize and FileLastModified
-local FileSize = {
- provider = function()
- -- stackoverflow, compute human readable file size
- 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 < 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 = utils.get_highlight('Statusline').fg, bold = true, bg = colors.bg },
-}
-
-local FileLastModified = {
- -- did you know? Vim is full of functions!
- provider = function()
- local ftime = vim.fn.getftime(vim.api.nvim_buf_get_name(0))
- return (ftime > 0) and os.date('%c', ftime)
- end,
- hl = { fg = utils.get_highlight('Statusline').fg, bold = true, bg = colors.bg },
-}
-
--- Spell
--- Add indicator when spell is set!
-local Spell = {
- condition = function()
- return vim.wo.spell
- end,
- provider = ' 暈',
- hl = { bold = true, fg = colors.yellow },
-}
-
-local HelpFileName = {
- condition = function()
- return vim.bo.filetype == 'help'
- end,
- provider = function()
- local filename = vim.api.nvim_buf_get_name(0)
- return vim.fn.fnamemodify(filename, ':t')
- end,
- hl = { fg = colors.blue },
-}
-
-local SearchCount = {
- condition = function()
- return vim.v.hlsearch ~= 0 and vim.o.cmdheight == 0
- end,
- init = function(self)
- local ok, search = pcall(vim.fn.searchcount)
- if ok and search.total then
- self.search = search
- end
- end,
- provider = function(self)
- local search = self.search
- return string.format('[%d/%d]', search.current, math.min(search.total, search.maxcount))
- end,
-}
-
-local MacroRec = {
- condition = function()
- return vim.fn.reg_recording() ~= '' and vim.o.cmdheight == 0
- end,
- provider = ' ',
- hl = { fg = 'orange', bold = true },
- utils.surround({ '[', ']' }, nil, {
+ hl = { fg = utils.get_highlight("Statusline").fg, bold = true, bg = colors.bg },
+ }
+
+ local FileLastModified = {
provider = function()
- return vim.fn.reg_recording()
+ local filename = vim.api.nvim_buf_get_name(0)
+ local ftime = vim.fn.getftime(filename)
+ return (ftime > 0) and os.date("%c", ftime) or ""
end,
- hl = { fg = 'green', bold = true },
- }),
- update = {
- 'RecordingEnter',
- 'RecordingLeave',
- },
-}
-
-local ShowCmd = {
- condition = function()
- return vim.o.cmdheight == 0
- end,
- provider = ':%3.5(%S%)',
-}
-
-local cursor_location = {
- { provider = '%1(%4l:%-3(%c%)%) %*', hl = { fg = colors.black, bold = true } },
-}
-
-local Ruler = { cursor_location }
-
---utils.make_flexible_component(
--- 3,
--- { Ruler, hl = { fg = utils.get_highlight("statusline").bg, force = true } },
--- { provider = "%<" }
---),
---local cursor_location = {
--- { provider = "%7(%l:%c%) ", hl = { bold = true } },
--- {
--- provider = " ",
--- hl = function(self)
--- local color = self:mode_color()
--- return { fg = color, bold = true }
--- end,
--- },
---}
+ hl = { fg = utils.get_highlight("Statusline").fg, bold = true, bg = colors.bg },
+ }
-local WordCount = {
- condition = function()
- return conditions.buffer_matches({
- filetype = {
- 'markdown',
- 'txt',
- 'vimwiki',
- },
- })
- end,
- Space,
- {
+ local Spell = {
+ condition = function()
+ return vim.wo.spell
+ end,
provider = function()
- return 'W:' .. vim.fn.wordcount().words
+ return " " .. Icons.Indicator.spell .. " "
end,
- },
-}
-
--- Working Directory
-local WorkDir = {
- init = function(self)
- self.icon = (vim.fn.haslocaldir(0) == 1 and 'l' or 'g') .. ' ' .. ' '
- local cwd = vim.fn.getcwd(0)
- self.cwd = vim.fn.fnamemodify(cwd, ':~')
- end,
- hl = { fg = 'colors.blue', bold = true },
- flexible = 1,
- {
- -- evaluates to the full-lenth path
- provider = function(self)
- local trail = self.cwd:sub(-1) == '/' and '' or '/'
- return self.icon .. self.cwd .. trail .. ' '
+ hl = { bold = true, fg = colors.yellow },
+ }
+
+ local HelpFileName = {
+ condition = function()
+ return vim.bo.filetype == "help"
+ end,
+ provider = function()
+ local filename = vim.api.nvim_buf_get_name(0)
+ return vim.fn.fnamemodify(filename, ":t")
+ end,
+ hl = { fg = colors.blue },
+ }
+
+ local SearchCount = {
+ condition = function()
+ return vim.v.hlsearch ~= 0 and vim.o.cmdheight == 0
+ end,
+ init = function(self)
+ local ok, search = pcall(vim.fn.searchcount, { recompute = 1, maxcount = -1 })
+ if ok and search.total then
+ self.search = search
+ end
end,
- },
- {
- -- evaluates to the shortened path
provider = function(self)
- local cwd = vim.fn.pathshorten(self.cwd)
- local trail = self.cwd:sub(-1) == '/' and '' or '/'
- return self.icon .. cwd .. trail .. ' '
+ local search = self.search or { current = 0, total = 0, maxcount = 0 }
+ return string.format("[%d/%d]", search.current, math.min(search.total, search.maxcount))
end,
- },
- {
- -- evaluates to "", hiding the component
- provider = '',
- },
-}
-
--- Snippets Indicator
--- This requires ultisnips
---local Snippets = {
--- -- check that we are in insert or select mode
--- condition = function()
--- return vim.tbl_contains({'s', 'i'}, vim.fn.mode())
--- end,
--- provider = function()
--- local forward = (vim.fn["UltiSnips#CanJumpForwards"]() == 1) and "" or ""
--- local backward = (vim.fn["UltiSnips#CanJumpBackwards"]() == 1) and " " or ""
--- return backward .. forward
--- end,
--- hl = { fg = "red", bold = true },
---}
+ update = { "CursorMoved", "CursorMovedI", "SearchWrapped" },
+ }
--- let's add the children to our FileNameBlock component
-FileNameBlock = utils.insert(
- FileNameBlock,
- FileIcon,
- utils.insert(FileNameModifier, FileName), -- a new table where FileName is a child of FileNameModifier
- unpack(FileFlags), -- A small optimisation, since their parent does nothing
- { provider = '%<' } -- this means that the statusline is cut here when there's not enough space
-)
-
-local FileInfoBlock = {
- -- let's first set up some attributes needed by this component and it's children
- init = function(self)
- self.filename = vim.api.nvim_buf_get_name(0)
- end,
-}
-
-FileInfoBlock = utils.insert(
- FileInfoBlock,
- Space,
- FileIcon,
- FileType,
- { provider = '%<' } -- this means that the statusline is cut here when there's not enough space
-)
-
-LeftSpace = utils.surround({ '', '' }, function(self)
- return self:mode_color()
-end, { LeftSpace, hl = { fg = utils.get_highlight('statusline').bg, force = true } })
-
-RightSpace = utils.surround({ '', '' }, function(self)
- return self:mode_color()
-end, { RightSpace, hl = { fg = utils.get_highlight('statusline').bg, force = true } })
-
-LSPActive = utils.surround({ '', '' }, function(self)
- return self:mode_color()
-end, { Space, LSPActive, hl = { bg = colors.darkgray, force = true } })
-
-FileInfoBlock = utils.surround({ '', '' }, function(self)
- return self:mode_color()
-end, { FileInfoBlock, Space, hl = { bg = colors.black, force = true } })
-
-ViMode = utils.surround({ '', '' }, function(self)
- return self:mode_color()
-end, { ViMode, hl = { fg = colors.black, force = true } })
-
-Ruler = utils.surround({ '', '' }, function(self)
- return self:mode_color()
-end, { Ruler, hl = { fg = colors.black, force = true } })
-
-local left = {
- { RightSpace, hl = { bg = colors.nobg, force = true } },
- { ViMode, hl = { bg = utils.get_highlight('statusline').bg, bold = false } },
- { LeftSpace, hl = { bg = colors.nobg, force = true } },
- { Space, hl = { bg = colors.nobg, force = true } },
- { FileNameBlock, hl = { bg = colors.nobg, force = true } },
- { Space, hl = { bg = colors.nobg, force = true } },
- { Git, hl = { bg = colors.nobg, force = true } },
-}
-
-local middle = {
- { Align, hl = { bg = colors.nobg, force = true } },
- --{ Navic, hl = { bg = colors.nobg, force = true } },
- --{ DAPMessages, hl = { bg = colors.nobg, force = true } },
- { Align, hl = { bg = colors.nobg, force = true } },
-}
-
-local right = {
- --{ Space, hl = { bg = colors.nobg, force = true } },
- { Diagnostics, hl = { bg = colors.nobg, force = true } },
- { Space, hl = { bg = colors.nobg, force = true } },
- { LSPActive, hl = { bg = colors.nobg, force = true } },
- { Space, hl = { bg = colors.nobg, force = true } },
- { FileInfoBlock, hl = { bg = colors.nobg, force = true } },
- { RightSpace, hl = { bg = colors.nobg, force = true } },
- { Ruler, hl = { fg = utils.get_highlight('statusline').bg, bold = false } },
- { LeftSpace, hl = { bg = colors.nobg, force = true } },
-}
-
-local sections = { left, middle, right }
-local DefaultStatusline = { sections }
-
-local specialleft = {
- { RightSpace, hl = { bg = colors.nobg, force = true } },
- { ViMode, hl = { bg = utils.get_highlight('statusline').bg, bold = false } },
- { LeftSpace, hl = { bg = colors.nobg, force = true } },
-}
-
-local specialmiddle = {
- { Align, hl = { bg = colors.nobg, force = true } },
- --{ DAPMessages, hl = { bg = colors.nobg, force = true } },
- { Align, hl = { bg = colors.nobg, force = true } },
-}
-
-local specialright = {
- { RightSpace, hl = { bg = colors.nobg, force = true } },
- { Ruler, hl = { fg = utils.get_highlight('statusline').bg, bold = false } },
- { LeftSpace, hl = { bg = colors.nobg, force = true } },
-}
-
-local specialsections = { specialleft, specialmiddle, specialright }
-
-local InactiveStatusline = {
- condition = conditions.is_not_active,
- --{ FileNameBlock, hl = { bg = colors.nobg, force = true } },
- --{ Align, hl = { bg = colors.nobg, force = true } },
- specialsections,
-}
-
-local SpecialStatusline = {
- condition = function()
- return conditions.buffer_matches({
- buftype = { 'nofile', 'prompt', 'help', 'quickfix' },
- filetype = { '^git.*', 'fugitive', 'dashboard' },
- })
- end,
- specialsections,
-}
-
---local InactiveStatusline = SpecialStatusline
-
-local TerminalStatusline = {
- condition = function()
- return conditions.buffer_matches({ buftype = { 'terminal' } })
- end,
- specialsections,
-}
-
-local StatusLine = {
- static = {
- --mode_colors = {
- -- n = colors.blue,
- -- i = colors.green,
- -- v = colors.purple,
- -- V = colors.purple,
- -- ["\22"] = colors.purple,
- -- c = colors.orange,
- -- s = colors.purple,
- -- S = colors.purple,
- -- ["\19"] = colors.purple,
- -- R = colors.red,
- -- r = colors.red,
- -- ["!"] = colors.orange,
- -- t = colors.orange,
- --},
- mode_colors = {
- n = colors.blue,
- no = colors.blue,
- nov = colors.blue,
- noV = colors.blue,
- ['no\22'] = colors.blue,
- niI = colors.blue,
- niR = colors.blue,
- niV = colors.blue,
- nt = colors.blue,
- v = colors.purple,
- vs = colors.purple,
- V = colors.purple,
- ['\22'] = colors.purple,
- ['\22s'] = colors.purple,
- s = colors.purple,
- S = colors.purple,
- ['\19'] = colors.purple,
- i = colors.green,
- ix = colors.green,
- ic = colors.green,
- R = colors.red,
- Rc = colors.red,
- Rx = colors.red,
- Rv = colors.red,
- Rvc = colors.red,
- Rvx = colors.red,
- c = colors.orange,
- cv = colors.orange,
- ce = colors.orange,
- r = colors.red,
- rm = colors.red,
- ['r?'] = colors.red,
- ['!'] = colors.orange,
- t = colors.orange,
- },
- mode_color = function(self)
- local mode = conditions.is_active() and vim.fn.mode() or 'n'
- return self.mode_colors[mode]
+ local MacroRec = {
+ condition = function()
+ return vim.fn.reg_recording() ~= "" and vim.o.cmdheight == 0
end,
- hl = function(self)
- local color = self:mode_color() -- here!
- return { bg = color }
+ provider = function()
+ return Icons.Indicator.recording .. " "
end,
- },
- fallthrough = false,
- SpecialStatusline,
- TerminalStatusline,
- InactiveStatusline,
- DefaultStatusline,
-}
-
---
---- WinBar
---
-local WinbarFileNameBlock = {
- -- let's first set up some attributes needed by this component and it's children
- init = function(self)
- self.filename = vim.api.nvim_buf_get_name(0)
- end,
- hl = { bg = colors.bg },
-}
-
-local WinbarFileName = {
- provider = function(self)
- -- first, trim the pattern relative to the current directory. For other
- -- options, see :h filename-modifers
- local filename = vim.fn.fnamemodify(self.filename, ':.')
- if filename == '' then
- return 'No Name'
- end
- -- now, if the filename would occupy more than 1/4th of the available
- -- space, we trim the file path to its initials
- -- See Flexible Components section below for dynamic truncation
- if not conditions.width_percent_below(#filename, 0.25) then
- filename = vim.fn.pathshorten(filename)
- end
- return filename
- end,
- --hl = { fg = utils.get_highlight("Statusline").fg, bold = false, bg = colors.bg },
- hl = { fg = colors.gray, bold = false, bg = colors.bg },
-}
-
-WinbarFileNameBlock = utils.insert(
- WinbarFileNameBlock,
- FileIcon,
- utils.insert(WinbarFileName), -- a new table where FileName is a child of FileNameModifier
- unpack(FileFlags), -- A small optimisation, since their parent does nothing
- { provider = '%<' } -- this means that the statusline is cut here when there's not enough space
-)
-
-vim.api.nvim_create_autocmd('User', {
- pattern = 'HeirlineInitWinbar',
- callback = function(args)
- local buf = args.buf
- local buftype = vim.tbl_contains({ 'prompt', 'nofile', 'help', 'quickfix' }, vim.bo[buf].buftype)
- local filetype = vim.tbl_contains({ 'gitcommit', 'fugitive' }, vim.bo[buf].filetype)
- if buftype or filetype then
- vim.opt_local.winbar = nil
- end
- end,
-})
-
-On_click = {
- -- get the window id of the window in which the component was evaluated
- minwid = function()
- return vim.api.nvim_get_current_win()
- end,
- callback = function(_, minwid)
- -- winid is the window id of the window the component was clicked from
- local winid = minwid
- -- do something with the window id, e.g.:
- local buf = vim.api.nvim_win_get_buf(winid)
- -- ...
- end,
-}
-
-local CloseButton = {
- condition = function(self)
- return not vim.bo.modified
- end,
- -- a small performance improvement:
- -- re register the component callback only on layout/buffer changes.
- update = { 'WinNew', 'WinClosed', 'BufEnter' },
- { provider = ' ' },
- {
- provider = '',
- hl = { fg = 'gray' },
- On_click = {
- minwid = function()
- return vim.api.nvim_get_current_win()
- end,
- callback = function(_, minwid)
- vim.api.nvim_win_close(minwid, true)
+ hl = { fg = "orange", bold = true },
+ utils.surround({ "[", "]" }, nil, {
+ provider = function()
+ return vim.fn.reg_recording()
end,
- name = 'heirline_winbar_close_button',
+ hl = { fg = "green", bold = true },
+ }),
+ update = {
+ "RecordingEnter",
+ "RecordingLeave",
+ callback = vim.schedule_wrap(function()
+ vim.cmd("redrawstatus")
+ end),
},
- },
-}
+ }
-local Center = {
- fallthrough = false,
- {
- -- Hide the winbar for special buffers
+ local ShowCmd = {
+ condition = function()
+ return vim.o.cmdheight == 0
+ end,
+ provider = ":%3.5(%S%)",
+ update = { "CmdlineChanged" },
+ }
+
+ local cursor_location = {
+ { provider = "%1(%4l:%-3(%c%)%) %*", hl = { fg = colors.black, bold = true } },
+ }
+
+ local Ruler = { cursor_location }
+
+ local WordCount = {
condition = function()
return conditions.buffer_matches({
- buftype = { 'terminal', 'nofile', 'prompt', 'help', 'quickfix' },
- filetype = { 'dap-ui', 'NvimTree', '^git.*', 'fugitive', 'dashboard' },
+ filetype = {
+ "markdown",
+ "txt",
+ "vimwiki",
+ },
})
end,
- init = function()
- vim.opt_local.winbar = nil
+ Space,
+ {
+ provider = function()
+ local ok, wordcount = pcall(vim.fn.wordcount)
+ return ok and wordcount.words and ("W:%d"):format(wordcount.words) or ""
+ end,
+ update = { "CursorMoved", "CursorMovedI", "InsertEnter", "TextChanged", "TextChangedI" },
+ },
+ }
+
+ local WorkDir = {
+ init = function(self)
+ local is_local = vim.fn.haslocaldir(0) == 1
+ self.icon = (is_local and "l" or "g") .. " " .. Icons.File.directory
+ local cwd = vim.fn.getcwd(0)
+ self.cwd = vim.fn.fnamemodify(cwd, ":~")
end,
- },
- {
- -- A special winbar for terminals
- condition = function()
- return conditions.buffer_matches({ buftype = { 'terminal' } })
+ hl = { fg = colors.blue, bold = true },
+ on_click = {
+ callback = function()
+ vim.cmd("Telescope find_files cwd=" .. vim.fn.getcwd(0))
+ end,
+ name = "heirline_workdir",
+ },
+ flexible = 1,
+ {
+ provider = function(self)
+ local trail = self.cwd:sub(-1) == "/" and "" or "/"
+ return self.icon .. " " .. self.cwd .. trail .. " "
+ end,
+ },
+ {
+ provider = function(self)
+ local cwd = vim.fn.pathshorten(self.cwd)
+ local trail = self.cwd:sub(-1) == "/" and "" or "/"
+ return self.icon .. " " .. cwd .. trail .. " "
+ end,
+ },
+ {
+ provider = function(self)
+ return self.icon .. " "
+ end,
+ },
+ }
+
+ -- Build FileNameBlock
+ FileNameBlock = utils.insert(
+ FileNameBlock,
+ FileIcon,
+ utils.insert(FileNameModifier, FileName),
+ unpack(FileFlags),
+ { provider = "%<" }
+ )
+
+ local FileInfoBlock = {
+ init = function(self)
+ self.filename = vim.api.nvim_buf_get_name(0)
end,
- FileType,
+ }
+
+ FileInfoBlock = utils.insert(
+ FileInfoBlock,
Space,
- --TerminalName,
- },
- {
- -- An inactive winbar for regular files
+ FileIcon,
+ FileType,
+ { provider = "%<" }
+ )
+
+ -- Create surrounded components with proper mode color functions
+ LeftSpace = utils.surround({ "", Icons.UI.right_separator }, function(self)
+ return self:mode_color()
+ end, { LeftSpace, hl = { fg = utils.get_highlight("statusline").bg, force = true } })
+
+ RightSpace = utils.surround({ Icons.UI.left_separator, "" }, function(self)
+ return self:mode_color()
+ end, { RightSpace, hl = { fg = utils.get_highlight("statusline").bg, force = true } })
+
+ LSPActive = utils.surround({ "", "" }, function(self)
+ return self:mode_color()
+ end, { Space, LSPActive, hl = { bg = colors.darkgray, force = true } })
+
+ FileInfoBlock = utils.surround({ "", "" }, function(self)
+ return self:mode_color()
+ end, { FileInfoBlock, Space, hl = { bg = colors.black, force = true } })
+
+ ViMode = utils.surround({ "", "" }, function(self)
+ return self:mode_color()
+ end, { ViMode, hl = { fg = colors.black, force = true } })
+
+ Ruler = utils.surround({ "", "" }, function(self)
+ return self:mode_color()
+ end, { Ruler, hl = { fg = colors.black, force = true } })
+
+ -- Statusline sections - FIXED: Removed duplicate LeftSpace from right section
+ local left = {
+ { RightSpace, hl = { bg = colors.nobg, force = true } },
+ { ViMode, hl = { bg = utils.get_highlight("statusline").bg, bold = false } },
+ { LeftSpace, hl = { bg = colors.nobg, force = true } },
+ { Space, hl = { bg = colors.nobg, force = true } },
+ { FileNameBlock, hl = { bg = colors.nobg, force = true } },
+ { Space, hl = { bg = colors.nobg, force = true } },
+ { Git, hl = { bg = colors.nobg, force = true } },
+ }
+
+ local middle = {
+ { Align, hl = { bg = colors.nobg, force = true } },
+ { Align, hl = { bg = colors.nobg, force = true } },
+ }
+
+ -- FIXED: Right section now has proper sequence without duplicate LeftSpace
+ local right = {
+ { Diagnostics, hl = { bg = colors.nobg, force = true } },
+ { Space, hl = { bg = colors.nobg, force = true } },
+ { LSPActive, hl = { bg = colors.nobg, force = true } },
+ { Space, hl = { bg = colors.nobg, force = true } },
+ { FileInfoBlock, hl = { bg = colors.nobg, force = true } },
+ { RightSpace, hl = { bg = colors.nobg, force = true } },
+ { Ruler, hl = { fg = utils.get_highlight("statusline").bg, bold = false } },
+ { LeftSpace, hl = { bg = colors.nobg, force = true } },
+ }
+
+ local sections = { left, middle, right }
+ local DefaultStatusline = { sections }
+
+ -- Special statuslines for inactive/special buffers
+ local specialleft = {
+ { RightSpace, hl = { bg = colors.nobg, force = true } },
+ { ViMode, hl = { bg = utils.get_highlight("statusline").bg, bold = false } },
+ { LeftSpace, hl = { bg = colors.nobg, force = true } },
+ }
+
+ local specialmiddle = {
+ { Align, hl = { bg = colors.nobg, force = true } },
+ { Align, hl = { bg = colors.nobg, force = true } },
+ }
+
+ local specialright = {
+ { RightSpace, hl = { bg = colors.nobg, force = true } },
+ { Ruler, hl = { fg = utils.get_highlight("statusline").bg, bold = false } },
+ { LeftSpace, hl = { bg = colors.nobg, force = true } },
+ }
+
+ local specialsections = { specialleft, specialmiddle, specialright }
+
+ local InactiveStatusline = {
+ condition = conditions.is_not_active,
+ specialsections,
+ }
+
+ local SpecialStatusline = {
condition = function()
- return not conditions.is_active()
+ return conditions.buffer_matches({
+ buftype = { "nofile", "prompt", "help", "quickfix" },
+ filetype = { "^git.*", "fugitive", "dashboard" },
+ })
end,
- --utils.surround({ "", "" }, colors.nobg, { FileIcon, { WinbarFileName, hl = { fg = colors.gray } }, FileFlags } ),
- utils.surround({ '', '' }, colors.nobg, { WinbarFileNameBlock }),
- },
- -- A winbar for regular files
- utils.surround({ '', '' }, colors.nobg, { FileNameBlock }),
-}
-
---local WinBar = { Align, Center, Align }
-local WinBar = { Space, Center }
-
--- TabLine
---local TablineBufnr = {
--- provider = function(self)
--- return tostring(self.bufnr) .. "."
--- end,
--- hl = { fg = colors.white, bold = false },
----- hl = "Comment",
---}
+ specialsections,
+ }
+
+ local TerminalStatusline = {
+ condition = function()
+ return conditions.buffer_matches({ buftype = { "terminal" } })
+ end,
+ specialsections,
+ }
+
+ -- FIXED: Main StatusLine with better mode handling
+ local StatusLine = {
+ static = {
+ mode_colors = {
+ n = colors.blue,
+ no = colors.blue,
+ nov = colors.blue,
+ noV = colors.blue,
+ ["no\22"] = colors.blue,
+ niI = colors.blue,
+ niR = colors.blue,
+ niV = colors.blue,
+ nt = colors.blue,
+ v = colors.purple,
+ vs = colors.purple,
+ V = colors.purple,
+ ["\22"] = colors.purple,
+ ["\22s"] = colors.purple,
+ s = colors.purple,
+ S = colors.purple,
+ ["\19"] = colors.purple,
+ i = colors.green,
+ ix = colors.green,
+ ic = colors.green,
+ R = colors.red,
+ Rc = colors.red,
+ Rx = colors.red,
+ Rv = colors.red,
+ Rvc = colors.red,
+ Rvx = colors.red,
+ c = colors.orange,
+ cv = colors.orange,
+ ce = colors.orange,
+ r = colors.red,
+ rm = colors.red,
+ ["r?"] = colors.red,
+ ["!"] = colors.orange,
+ t = colors.orange,
+ },
+ mode_color = function(self)
+ -- FIXED: Always get current mode to ensure updates
+ local mode = vim.fn.mode()
+ return self.mode_colors[mode] or colors.blue
+ end,
+ },
+ -- FIXED: Add update triggers to ensure statusline refreshes properly
+ update = {
+ "ModeChanged",
+ "BufEnter",
+ "WinEnter",
+ "WinLeave",
+ "BufWinEnter",
+ "CmdlineLeave",
+ callback = vim.schedule_wrap(function()
+ vim.cmd("redrawstatus")
+ end),
+ },
+ fallthrough = false,
+ SpecialStatusline,
+ TerminalStatusline,
+ InactiveStatusline,
+ DefaultStatusline,
+ }
+
+ -- WinBar components
+ local WinbarFileNameBlock = {
+ init = function(self)
+ self.filename = vim.api.nvim_buf_get_name(0)
+ end,
+ hl = { bg = colors.bg },
+ }
--- we redefine the filename component, as we probably only want the tail and not the relative path
-local TablineFileName = {
- provider = function(self)
- -- self.filename will be defined later, just keep looking at the example!
- local filename = self.filename
- filename = filename == '' and 'No Name' or vim.fn.fnamemodify(filename, ':t')
- return filename
- end,
- hl = function(self)
- return { fg = colors.white, bold = self.is_active or self.is_visible, italic = true }
- end,
-}
-
-local TablineFileFlags = {
- {
+ local WinbarFileName = {
provider = function(self)
- if vim.bo[self.bufnr].modified then
- return ' [+] '
+ local filename = vim.fn.fnamemodify(self.filename, ":.")
+ if filename == "" then
+ return "No Name"
+ end
+ if not conditions.width_percent_below(#filename, 0.25) then
+ filename = vim.fn.pathshorten(filename)
end
+ return filename
end,
- hl = { fg = colors.green },
- },
- {
- provider = function(self)
- if not vim.bo[self.bufnr].modifiable or vim.bo[self.bufnr].readonly then
- return '  '
+ hl = { fg = colors.gray, bold = false, bg = colors.bg },
+ }
+
+ WinbarFileNameBlock = utils.insert(
+ WinbarFileNameBlock,
+ FileIcon,
+ utils.insert(WinbarFileName),
+ unpack(FileFlags),
+ { provider = "%<" }
+ )
+
+ vim.api.nvim_create_autocmd("User", {
+ pattern = "HeirlineInitWinbar",
+ callback = function(args)
+ local buf = args.buf
+ local buftype = vim.tbl_contains({ "prompt", "nofile", "help", "quickfix" }, vim.bo[buf].buftype)
+ local filetype = vim.tbl_contains({ "gitcommit", "fugitive" }, vim.bo[buf].filetype)
+ if buftype or filetype then
+ vim.opt_local.winbar = nil
end
end,
- hl = { fg = 'orange' },
- },
-}
-
-local TablineFileIcon = {
- init = function(self)
- local filename = self.filename
- local extension = vim.fn.fnamemodify(filename, ':e')
- self.icon, self.icon_color = require('nvim-web-devicons').get_icon_color(filename, extension, { default = true })
- end,
- provider = function(self)
- return self.icon and (' ' .. self.icon .. ' ')
- end,
- hl = function(self)
- return { fg = self.icon_color }
- end,
-}
-
--- Here the filename block finally comes together
-local TablineFileNameBlock = {
- init = function(self)
- self.filename = vim.api.nvim_buf_get_name(self.bufnr)
- end,
- hl = function(self)
- if self.is_active then
- return 'TabLineSel'
- -- why not?
- --elseif not vim.api.nvim_buf_is_loaded(self.bufnr) then
- --return { fg = "gray", bg = colors.bg }
- else
- return 'TabLineFill'
- end
- end,
- on_click = {
- callback = function(_, minwid, _, button)
- if button == 'm' then -- close on mouse middle click
- vim.api.nvim_buf_delete(minwid, { force = false })
+ })
+
+ local On_click = {
+ minwid = function()
+ return vim.api.nvim_get_current_win()
+ end,
+ callback = function(_, minwid)
+ local winid = minwid
+ local buf = vim.api.nvim_win_get_buf(winid)
+ end,
+ }
+
+ local CloseButton = {
+ condition = function(self)
+ return not vim.bo.modified
+ end,
+ update = { "WinNew", "WinClosed", "BufEnter" },
+ { provider = " " },
+ {
+ provider = Icons.UI.close,
+ hl = { fg = "gray" },
+ On_click = {
+ minwid = function()
+ return vim.api.nvim_get_current_win()
+ end,
+ callback = function(_, minwid)
+ vim.api.nvim_win_close(minwid, true)
+ end,
+ name = "heirline_winbar_close_button",
+ },
+ },
+ }
+
+ local Center = {
+ fallthrough = false,
+ {
+ condition = function()
+ return conditions.buffer_matches({
+ buftype = { "terminal", "nofile", "prompt", "help", "quickfix" },
+ filetype = { "dap-ui", "NvimTree", "^git.*", "fugitive", "dashboard" },
+ })
+ end,
+ init = function()
+ vim.opt_local.winbar = nil
+ end,
+ },
+ {
+ condition = function()
+ return conditions.buffer_matches({ buftype = { "terminal" } })
+ end,
+ FileType,
+ Space,
+ },
+ {
+ condition = function()
+ return not conditions.is_active()
+ end,
+ utils.surround({ "", "" }, colors.nobg, { WinbarFileNameBlock }),
+ },
+ utils.surround({ "", "" }, colors.nobg, { FileNameBlock }),
+ }
+
+ local WinBar = { Space, Center }
+
+ -- Tabline components
+ local TablineFileIcon = {
+ init = function(self)
+ local filename = self.filename
+ local extension = vim.fn.fnamemodify(filename, ":e")
+
+ local has_nerd_fonts = vim.g.statusline_has_nerd_fonts
+ if has_nerd_fonts == nil then
+ if vim.fn.has('unix') == 1 and vim.fn.executable('fc-list') == 1 then
+ local handle = io.popen('fc-list | grep -i nerd')
+ local result = handle:read('*a')
+ handle:close()
+ has_nerd_fonts = result ~= ""
+ else
+ has_nerd_fonts = false
+ end
+ vim.g.statusline_has_nerd_fonts = has_nerd_fonts
+ end
+
+ if has_nerd_fonts then
+ self.icon, self.icon_color = require("nvim-web-devicons").get_icon_color(filename, extension, { default = true })
else
- vim.api.nvim_win_set_buf(0, minwid)
+ self.icon = ""
+ self.icon_color = colors.blue
+
+ if vim.fn.isdirectory(filename) == 1 then
+ self.icon = "[DIR]"
+ else
+ local file_icon = Icons.File[extension:lower()] or Icons.File.default
+ if type(file_icon) == "table" then
+ self.icon = file_icon[1] or Icons.File.default
+ else
+ self.icon = file_icon
+ end
+ end
+ end
+
+ if self.icon ~= "" then
+ self.icon = self.icon .. " "
end
end,
- minwid = function(self)
- return self.bufnr
+ provider = function(self)
+ return self.icon or ""
+ end,
+ hl = function(self)
+ return { fg = self.icon_color or colors.blue }
+ end,
+ }
+
+ local TablineFileName = {
+ provider = function(self)
+ local filename = vim.fn.fnamemodify(self.filename, ":t")
+ if filename == "" then
+ return "[No Name]"
+ end
+ return filename
+ end,
+ }
+
+ local TablineFileFlags = {
+ {
+ condition = function(self)
+ return vim.api.nvim_buf_get_option(self.bufnr, "modified")
+ end,
+ provider = "%X " .. Icons.Indicator.plus .. " %X",
+ hl = { fg = "green" },
+ },
+ {
+ condition = function(self)
+ return not vim.api.nvim_buf_get_option(self.bufnr, "modifiable") or vim.api.nvim_buf_get_option(self.bufnr, "readonly")
+ end,
+ provider = function()
+ if vim.bo.readonly then
+ return " " .. Icons.UI.lock
+ end
+ return ""
+ end,
+ hl = { fg = "orange" },
+ },
+ }
+
+ local TablineFileNameBlock = {
+ init = function(self)
+ self.filename = vim.api.nvim_buf_get_name(self.bufnr)
+ end,
+ hl = function(self)
+ if self.is_active then
+ return "TabLineSel"
+ else
+ return "TabLineFill"
+ end
end,
- name = 'heirline_tabline_buffer_callback',
- },
- --TablineBufnr,
- TablineFileIcon,
- TablineFileName,
- TablineFileFlags,
-}
-
--- a nice "x" button to close the buffer
-local TablineCloseButton = {
- condition = function(self)
- return not vim.api.nvim_buf_get_option(self.bufnr, 'modified')
- end,
- { provider = ' ' },
- {
- provider = ' ',
- --hl = { fg = "red", bg = colors.bg },
- hl = { fg = colors.red },
on_click = {
- callback = function(_, minwid)
- vim.api.nvim_buf_delete(minwid, { force = false })
+ callback = function(_, minwid, _, button)
+ if button == "m" then
+ vim.api.nvim_buf_delete(minwid, { force = false })
+ else
+ vim.api.nvim_win_set_buf(0, minwid)
+ end
end,
minwid = function(self)
return self.bufnr
end,
- name = 'heirline_tabline_close_buffer_callback',
+ name = "heirline_tabline_buffer_callback",
},
- },
-}
-
--- The final touch!
-local TablineBufferBlock = utils.surround({ '', '' }, function(self)
- --local TablineBufferBlock = utils.surround({ "█", "█" }, function(self)
- if self.is_active then
- return utils.get_highlight('TabLineSel').bg
- else
- return utils.get_highlight('TabLineFill').bg
- end
-end, { Tab, TablineFileNameBlock, TablineCloseButton })
-
-local BufferLine = utils.make_buflist(
- TablineBufferBlock,
- { provider = ' ', hl = { fg = colors.white } }, -- left truncation, optional (defaults to "<")
- { provider = ' ', hl = { fg = colors.white } } -- right trunctation, also optional (defaults to ...... yep, ">")
--- by the way, open a lot of buffers and try clicking them ;)
-)
--- TabList
-local Tabpage = {
- provider = function(self)
- return '%' .. self.tabnr .. 'T ' .. self.tabnr .. ' %T'
- end,
- hl = function(self)
- if not self.is_active then
- return 'TabLineFill'
+ TablineFileIcon,
+ TablineFileName,
+ TablineFileFlags,
+ }
+
+ local TablineCloseButton = {
+ condition = function(self)
+ return not vim.api.nvim_buf_get_option(self.bufnr, "modified")
+ end,
+ { provider = " " },
+ {
+ provider = "" .. Icons.UI.close .. " %X",
+ hl = { fg = colors.red },
+ on_click = {
+ callback = function(_, minwid)
+ vim.api.nvim_buf_delete(minwid, { force = false })
+ end,
+ minwid = function(self)
+ return self.bufnr
+ end,
+ name = "heirline_tabline_close_buffer_callback",
+ },
+ },
+ }
+
+ local TablineBufferBlock = utils.surround({ "", "" }, function(self)
+ if self.is_active then
+ return utils.get_highlight("TabLineSel").bg
else
- return 'TabLineSel'
+ return utils.get_highlight("TabLineFill").bg
end
- end,
-}
-
-local TabpageClose = {
- provider = '%999X  %X',
- --hl = "TabLine",
- hl = { fg = colors.red, bg = colors.bg },
-}
-
-local TabPages = {
- -- only show this component if there's 2 or more tabpages
- condition = function()
- return #vim.api.nvim_list_tabpages() >= 2
- end,
- {
- provider = '%=',
- },
- utils.make_tablist(Tabpage),
- TabpageClose,
-}
-
--- TabLineOffset
-local TabLineOffset = {
- condition = function(self)
- local win = vim.api.nvim_tabpage_list_wins(0)[1]
- local bufnr = vim.api.nvim_win_get_buf(win)
- self.winid = win
-
- if vim.api.nvim_buf_get_option(bufnr, 'filetype') == 'NvimTree' then
- self.title = 'NvimTree'
- return true
+ end, { Tab, TablineFileNameBlock, TablineCloseButton })
+
+ local BufferLine = utils.make_buflist(
+ TablineBufferBlock,
+ { provider = Icons.Symbol.arrow_left, hl = { fg = colors.gray } },
+ { provider = Icons.Symbol.arrow_right, hl = { fg = colors.gray } }
+ )
+
+ local Tabpage = {
+ provider = function(self)
+ return "%" .. self.tabnr .. "T " .. self.tabnr .. " %T"
+ end,
+ hl = function(self)
+ return self.is_active and "TabLineSel" or "TabLineFill"
+ end,
+ }
+
+ local TabpageClose = {
+ provider = "%999X " .. Icons.UI.close .. " %X",
+ hl = { fg = colors.red, bg = colors.bg },
+ }
+
+ local TabPages = {
+ condition = function()
+ return #vim.api.nvim_list_tabpages() >= 2
+ end,
+ { provider = "%=" },
+ utils.make_tablist(Tabpage),
+ TabpageClose,
+ }
+
+ local TabLineOffset = {
+ condition = function(self)
+ local win = vim.api.nvim_tabpage_list_wins(0)[1]
+ local bufnr = vim.api.nvim_win_get_buf(win)
+ self.winid = win
+
+ if vim.api.nvim_buf_get_option(bufnr, "filetype") == "NvimTree" then
+ self.title = "NvimTree"
+ return true
+ end
+ end,
+ provider = function(self)
+ local title = self.title
+ local width = vim.api.nvim_win_get_width(self.winid)
+ local pad = math.ceil((width - #title) / 2)
+ return string.rep(" ", pad) .. title .. string.rep(" ", pad)
+ end,
+ hl = { fg = colors.white, bg = "#333842", bold = true },
+ }
+
+ local TabLine = {
+ TabLineOffset,
+ BufferLine,
+ TabPages,
+ }
+
+ -- Buffer navigation functions
+ local function get_bufs()
+ return vim.tbl_filter(function(bufnr)
+ return vim.api.nvim_buf_is_loaded(bufnr) and vim.bo[bufnr].buflisted
+ end, vim.api.nvim_list_bufs())
+ end
+
+ local function goto_buf(index)
+ local bufs = get_bufs()
+ if index > #bufs then
+ index = #bufs
end
- end,
- provider = function(self)
- local title = self.title
- local width = vim.api.nvim_win_get_width(self.winid)
- local pad = math.ceil((width - #title) / 2)
- return string.rep(' ', pad) .. title .. string.rep(' ', pad)
- end,
- hl = { fg = colors.white, bg = '#333842', bold = true },
-
- --hl = function(self)
- -- if vim.api.nvim_get_current_win() == self.winid then
- -- return 'TablineSel'
- -- else
- -- return 'TablineFill'
- -- end
- --end,
-}
-
-local TabLine = {
- TabLineOffset,
- BufferLine,
- TabPages,
-}
-
-require('heirline').setup({
- statusline = StatusLine,
- winbar = WinBar,
- tabline = TabLine,
- --statuscolumn = StatusColumn
-})
-
--- Yep, with heirline we're driving manual!
-vim.o.showtabline = 2
-vim.cmd([[au FileType * if index(['wipe', 'delete', 'unload'], &bufhidden) >= 0 | set nobuflisted | endif]])
-
-local function get_bufs()
- return vim.tbl_filter(function(bufnr)
- return vim.api.nvim_buf_is_loaded(bufnr) and vim.bo[bufnr].buflisted
- end, vim.api.nvim_list_bufs())
-end
+ vim.api.nvim_win_set_buf(0, bufs[index])
+ end
-local function goto_buf(index)
- local bufs = get_bufs()
- if index > #bufs then
- index = #bufs
+ local function add_key(key, index)
+ vim.keymap.set("n", "<A-" .. key .. ">", function()
+ goto_buf(index)
+ end, { noremap = true, silent = true })
end
- vim.api.nvim_win_set_buf(0, bufs[index])
-end
-local function addKey(key, index)
- vim.keymap.set('', '<A-' .. key .. '>', function()
- goto_buf(index)
- end, { noremap = true, silent = true })
-end
+ for i = 1, 9 do
+ add_key(i, i)
+ end
+ add_key("0", 10)
+
+ vim.o.showtabline = 2
+ vim.cmd([[au FileType * if index(['wipe', 'delete', 'unload'], &bufhidden) >= 0 | set nobuflisted | endif]])
+
+ -- FIXED: Add proper autocmds for better statusline updates
+ local augroup = vim.api.nvim_create_augroup("HeirlineStatusline", { clear = true })
+
+ -- Force statusline refresh on mode changes and buffer events
+ vim.api.nvim_create_autocmd({
+ "ModeChanged",
+ "BufEnter",
+ "BufWinEnter",
+ "WinEnter",
+ "WinLeave",
+ "CmdlineLeave",
+ "TermEnter",
+ "TermLeave"
+ }, {
+ group = augroup,
+ callback = function()
+ vim.schedule(function()
+ if vim.o.laststatus > 0 then
+ vim.cmd("redrawstatus!")
+ end
+ end)
+ end,
+ })
+
+ -- Final heirline setup
+ heirline.setup({
+ statusline = StatusLine,
+ winbar = WinBar,
+ tabline = TabLine,
+ opts = {
+ disable_winbar_cb = function(args)
+ local buf = args.buf
+ if not vim.api.nvim_buf_is_valid(buf) then
+ return true
+ end
+
+ local buftype = vim.tbl_contains(
+ { "prompt", "nofile", "help", "quickfix" },
+ vim.bo[buf].buftype
+ )
+ local filetype = vim.tbl_contains(
+ { "gitcommit", "fugitive" },
+ vim.bo[buf].filetype
+ )
+ return buftype or filetype
+ end,
+ }
+ })
-for i = 1, 9 do
- addKey(i, i)
end
-addKey('0', 10)
+
+return M