heirline.nvim

repository·master·Indexed 23 days ago

https://github.com/rebelot/heirline.nvim

A highly flexible, Lua-based framework for building Neovim statuslines, winbars, tablines, and statuscolumns. Unlike most statusline plugins, it provides no default configuration, acting instead as an API for users to build their own UI using recursive inheritance. Key features include conditional rendering, highlight propagation, update triggers, interactivity via clickable components, and dynamic resizing.

Tokens
14.9K
Snippets
26
Records
44
Agent score
79%

What's inside heirline.nvim

  1. Overview of heirline.nvim

    master

    heirline.nvim is a Neovim plugin designed for rendering statusline, winbar, tabline, and statuscolumn format strings. Unlike most statusline plugins, it provides no default statusline; it acts as an API for you to build your own using Lua. It is built around recursive inheritance to ensure high performance and modularity.

    Key capabilities include:

    • Conditionals: Create components that react to filetype, buftype, or buffer names.
    • Highlight propagation: Easily surround components with separators or apply dynamic coloring to groups.
    • Modularity: Reusable components that behave according to their position in the component tree.
    • Update triggers: Re-evaluate components based on specific autocommand events or conditions.
    • Interactivity: Support for clickable components via Lua callbacks.
    • Dynamic resizing: Control how components occupy available space.
    • Full control: Hooks to manage the evaluation cycle.
  2. Use flexible components to adjust output based on space

    master

    Flexible components automatically adjust their output depending on the available visible space in a window's statusline. You define them by adding a flexible field to a component, which accepts an integer representing its priority.

    Priority Rules

    • Higher priority: Last to contract, first to expand.
    • Lower priority: First to contract, last to expand.
    • Same priority: Contract or expand simultaneously.

    Nesting Rules

    • When nesting flexible components, the priority of nested components is ignored; only the outermost priority determines the expansion/contraction order.
    • To ensure different priorities for separate groups of nested components (siblings), the difference between their priorities should be at least 1 + #(nested levels). Using large numbers to separate priority groups is a safe way to manage this.
    local nest_madness = {
        { flexible = 1, -- first root
            a,
            { flexible = true, -- nested components priority is ignored!
                b,
                { flexible = true, c, d },
                e
            },
            f
        },
        { provider = "%=" },
        { flexible = 4, -- second root (priority 4 is > 1 + 3 levels)
            a,
            { flexible = true,
                b,
                { flexible = true, c, d },
                e
            },
            f
        },
    }
  3. Understand the StatusLine life cycle

    master

    A component goes through two distinct phases:

    1. Creation (Instantiation): The 'blueprint' table is processed. Fields like static and restrict are applied here.
    2. Evaluation: This happens every time the statusline is refreshed. The following fields are executed in this specific order: condition $\rightarrow$ init $\rightarrow$ hl $\rightarrow$ provider $\rightarrow$ pick_child.
  4. Understand the core concept of StatusLine objects

    master

    In heirline, everything is a StatusLine object. There is no distinction between the final statusline and its individual components in terms of how they are defined.

    Instead of manually creating StatusLine objects, you define Lua tables that act as blueprints. These tables are passed to the setup() function, which deep-copies them to create the actual live objects.

    Component Nesting

    • Components: The nested tables used to build your UI.
    • Children: A component inside another component. Children inherit the fields of their parents.
    • Nesting: There is no limit to how deep components can be nested.

    To make configuration manageable, it is recommended to define simple components as separate variables and then assemble them into a larger structure.

    ```lua
    -- Define simple components first
    local Component1 = { ... }
    local Sub1 = { ... }
    local Component2 = { ... }
    
    -- Assemble them into a blueprint
    local statusline = {
        ...,
        {Component1, Sub1},
        Component2,
    }
    
    -- Pass the blueprint to setup
    require("heirline").setup({
        statusline = statusline
    })
    ```埋
  5. Define component fields in heirline.nvim

    master
    Components are the building blocks of your statusline. A component is a table containing various fields that define its content, appearance, and behavior. When defining functions within a component, they are executed in the context of the buffer and window the statusline belongs to. You can access the actual current buffer and window indices via vim.g.actual_curbuf and vim.g.actual_curwin.
  6. Create a Bufferline using make_buflist

    master

    You can render a bufferline in the tabline by using the utils.make_buflist(buffer_component) utility.

    When creating the buffer_component (the abstract component used to render each individual buffer), the component automatically inherits the following fields:

    • self.bufnr <integer>: The buffer number of the listed buffer.
    • self.is_active <bool>: Whether the buffer is shown in the current window.
    • self.is_visible <bool>: Whether the buffer is shown in the current tab.

    Important: Because make_buflist renders all listed buffers (not just the one in the current window), you must use self.bufnr to retrieve buffer-specific information (like filename or modified status) instead of relying on global window state.

    local TablineBufnr = {
        provider = function(self)
            return tostring(self.bufnr) .. ". "
        end,
        hl = "Comment",
    }
    
    -- ... other components ...
    
    local BufferLine = utils.make_buflist(
        TablineBufferBlock,
        { provider = "", hl = { fg = "gray" } }, -- left truncation
        { provider = "", hl = { fg = "gray" } }  -- right truncation
    )
  7. Change background colors based on Vi Mode

    master

    To implement dynamic background colors that change based on the current Neovim mode (e.g., different colors for Normal, Insert, and Visual modes), you can define a global utility function within your StatusLine's static table. This function can then be accessed by individual components via self:mode_color().

    local StatusLines = {
        -- ... other components
        static = {
            mode_colors_map = {
                n = "red",
                i = "green",
                v = "cyan",
                V = "cyan",
                ["\22"] = "cyan",
                c = "orange",
                s = "purple",
                S = "purple",
                ["\19"] = "purple",
                R = "orange",
                r = "orange",
                ["!"] = "red",
                t = "green",
            },
            mode_color = function(self)
                local mode = conditions.is_active() and vim.fn.mode() or "n"
                return self.mode_colors_map[mode]
            end,
        },
    }
    
    local ViMode = {
        static = {
            mode_names = { ... }
        },
        provider = function(self)
            return " %2(" .. self.mode_names[vim.fn.mode(1)] .. "%)"
        end,
        hl = function(self)
            local color = self:mode_color() -- Accesses the static method
            return { fg = color, bold = true }
        end,
    }
  8. Configure StatusLine, WinBar, and TabLine via setup()

    master

    The heirline.setup() function is used to initialize the plugin. You provide Lua tables as blueprints for different UI elements.

    Supported UI elements include:

    • statusline: The main statusline at the bottom.
    • winbar: An optional bar at the top of each window.
    • tabline: The bar used for buffer/tab management.
    • statuscolumn: The column to the left of the text area.

    Once setup() is called, the live objects can be accessed via require('heirline').statusline, require('heirline').winbar, etc. Modifying these live objects will reflect in real time on your UI.

  9. Create conditional statuslines using fallthrough

    master

    You can define multiple statusline configurations and use the condition field to select the appropriate one.

    Key Concepts

    • condition: A function that returns true or false. If a component's condition is met, it is used.
    • fallthrough = false: When set on a parent component (like a list of statuslines), it stops the evaluation at the first component whose condition evaluates to true. This acts like a switch statement with break.
    • Ordering: Statusline conditions are evaluated sequentially. You should order them from strictest to loosest conditions.

    Best Practice

    Always end a short statusline with %= (the Align component) to fill the remaining space with the same background color.

    local StatusLines = {
        hl = function()
            if conditions.is_active() then
                return "StatusLine"
            else
                return "StatusLineNC"
            end
        end,
    
        -- the first statusline with no condition, or which condition returns true is used.
        -- it's like a switch case with breaks to stop fallthrough.
        fallthrough = false,
    
        SpecialStatusline, TerminalStatusline, InactiveStatusline, DefaultStatusline,
    }
  10. Install heirline.nvim using Packer

    master

    You can install heirline.nvim using your preferred plugin manager. If using Packer, it is recommended to optionally lazy-load the plugin on the UiEnter event to ensure that colorschemes and other required plugins are fully loaded before heirline initializes.

    use({
        "rebelot/heirline.nvim",
        -- You can optionally lazy-load heirline on UiEnter
        -- to make sure all required plugins and colorschemes are loaded before setup
        -- event = "UiEnter",
        config = function()
            require("heirline").setup({...})
        end
    })
  11. Update statusline colors automatically on ColorScheme change

    master

    To ensure your statusline colors update automatically when you switch Neovim colorschemes, you should wrap your color definitions in a function and use the utils.on_colorscheme utility. This function handles the boilerplate of re-evaluating your color aliases.

    1. Define a function (e.g., setup_colors) that returns a table of color aliases using utils.get_highlight(group).
    2. Use utils.on_colorscheme(setup_colors) within a ColorScheme autocommand to refresh the colors.
    local function setup_colors()
        return {
            bright_bg = utils.get_highlight("Folded").bg,
            bright_fg = utils.get_highlight("Folded").fg,
            red = utils.get_highlight("DiagnosticError").fg,
            dark_red = utils.get_highlight("DiffDelete").bg,
            green = utils.get_highlight("String").fg,
            blue = utils.get_highlight("Function").fg,
            gray = utils.get_highlight("NonText").fg,
            orange = utils.get_highlight("Constant").fg,
            purple = utils.get_highlight("Statement").fg,
            cyan = utils.get_highlight("Special").fg,
            diag_warn = utils.get_highlight("DiagnosticWarn").fg,
            diag_error = utils.get_highlight("DiagnosticError").fg,
            diag_hint = utils.get_highlight("DiagnosticHint").fg,
            diag_info = utils.get_highlight("DiagnosticInfo").fg,
            git_del = utils.get_highlight("diffDeleted").fg,
            git_add = utils.get_highlight("diffAdded").fg,
            git_change = utils.get_highlight("diffChanged").fg,
        }
    end
    
    -- Use an autocommand to trigger the update
    vim.api.nvim_create_augroup("Heirline", { clear = true })
    vim.api.nvim_create_autocmd("ColorScheme", {
        callback = function()
            utils.on_colorscheme(setup_colors)
        end,
        group = "Heirline",
    })
  12. Configure setup options in heirline

    master

    Setup options can be passed via the config.opts table during initialization.

    Available Options:

    | Option | Type | Description | | --- | function(args)->boolean | A callback to disable the winbar on a per-buffer/window basis. args is the table passed to autocommand callbacks. | | disable_winbar_cb | | | | colors | table or function->table | Define color name aliases. Values can be a hex string or an integer (24-bit or 8-bit depending on termguicolors). | | colors | | |