COOKBOOK.md

June 20, 2026 Β· View on GitHub

Cookbook is a collection of recipes that demonstrate how to use various features of the software. Each recipe provides step-by-step instructions, code examples, and explanations to help you understand and implement specific functionalities.

βš™οΈ Component Fields

⚠️ Important Notes (Read Before Creating Components)

Important about function fields to make the cache work properly:

  • Function fields should be pure functions without side effects.
  • They should only depend on their input parameters and not have any up-values.
  • The up-values allowed are only global variables such as vim, package, require.
  • The tips to remove up-values:
    • Move the up-value inside the function.
    • If you are using a module, require it inside the function.
    • If you are using a global variable, use vim or package directly inside the function.

Example of a pure function:

local api = vim.api -- Not allowed, as it's an up-value you need to move it inside the function like below
local builtin = require("witch-line.builtin") -- Not allowed, as it's an up-value you need to move it inside the function like below


local component = {
  id = "identifier",
  update = function(self, session_id)
    local builtin = require("witch-line.builtin") -- Allowed, as it's inside the function
    local api = vim.api -- Allowed, as it's a global API call
    return api.nvim_buf_get_name(0) -- Depends only on the current buffer
  end,
}

🎣 Hooks to Access Component Data

WitchLine provides some hooks to access data in module witch-line.core.manager.hook.

  • use_static(comp): Access the static field of the component or from referenced component.
  • use_context(comp, session_id): Access the context field of the component or from referenced component for the given session.
  • use_event_info(comp, session_id): Access the data event that triggered the update for the component in the given session. The result is the argument passed to the event callback in vim.api.nvim_create_autocmd.

πŸ”‘ Global Accessible Fields

  • static:
TypeDescription
tableA table that holds static data for the component.
nilNo static data for the component.

Description: A table that holds static data for the component. It can be used to store configuration or other immutable values of component.

When a component has a static field or reference static from another component, Then the user can access the static field by using the hook require("witch-line.core.manager.hook").use_static(comp).

Tricks:

  • If you ensure that the component has a static field by self not referencing other components, then you can use the self.static directly in any function of the component like init, update, etc for better performance.

Example:

local component = {
    static = {
        config_value = true,
        another_value = "example",
        icon = "⚑"
    },
    update = function(self, session_id)
        return self.static.icon .. " updated text" -- Using self.static directly
    end,
    init = function(self, session_id)
      local hook = require("witch-line.core.manager.hook") -- Use hook to access static
      local static = hook.use_static(self)
      print(static.config_value) -- true
    end
}
  • context:

    Type:Description
    tableA table that holds dynamic data for the component.
    fun(self): tableA function that returns a table context

    Description: A table or a function that holds dynamic data for the component. It can be used to store values that can change frequently and are reactive.

    When a component has a context field or reference context from another component, Then the user can access the context field by using the hook require("witch-line.core.manager.hook").use_context(comp, session_id).

    Tricks:

    • If you ensure that the context is same for all sessions by self not referencing other components (usually when context is a static table or a string path), then you can use the self.context directly in any function of the component like init, update, etc for better performance.

    Example:

    • Type: table
    local component = {
      context = {
          dynamic_value = 42,
          another_dynamic_value = "dynamic"
      },
      update = function(self, session_id)
          local hook = require("witch-line.core.manager.hook") -- Use hook to access context
          local ctx = hook.use_context(self, session_id)
    
          -- You can also use self.context directly if you ensure that context is same for all sessions
          -- local ctx = self.context
    
          return "Dynamic Value: " .. ctx.dynamic_value
      end
    }
    
    • Type: fun(self) -> table
    local component = {
      context = function(self)
          return {
              dynamic_value = math.random(1, 100), -- Random value between 1 and 100
              another_dynamic_value = os.date("%Y-%m-%d %H:%M:%S") -- Current date and time
          }
      end,
      update = function(self, session_id)
          local hook = require("witch-line.core.manager.hook") -- Use hook to access context
          local ctx = hook.use_context(self, session_id)
          return "Dynamic Value: " .. ctx.dynamic_value
      end
    }
    

🧰 Basic Fields

  • id: (Very Important)

    Type: string

    Description: A unique identifier for the component. It's allow an component to be referenced by other components. The id must be different from default components provided by WitchLine. You can see the list of default ids in the Default Components section.

    Example:

    local component = {
        id = "my_component"
    }
    
  • lazy:

    TypeDescription
    trueThe component will be loaded lazily.
    falseThe component will be loaded immediately.
    nilThe component will be loaded lazily (default value).

    Description: A flag that indicates whether the component should be loaded lazily. If set to true, the component will only be loaded when it is needed, which can help improve performance. If not provided, the component will be loaded lazily.

    Example:

    local component = {
        lazy = true
    }
    
  • version:

    Type: number | string | nil

    Description: The version of the component. This can be used to manage cache manually. If the version is changed, the component will be reloaded even if it is cached. This is useful when you want to manually invalidate the cache for a component. If not provided, the cache will use all the fields of the component to determine if it needs to be reloaded (This will be slower).

    Example:

    local component = {
        version = 2
    }
    
  • events:

    • Alias: Component.SpecialEvent
FieldTypeDescription
[integer]stringEvent name (e.g., "BufEnter", "InsertLeave"). Each entry in the array represents an event.
once?boolean(Optional) If true, the event triggers only once.
pattern?string | string[](Optional) A pattern or list of patterns the event should match (e.g., "*.lua").
remove_when?function (Optional) Remove this special event if remove_when return true.
  • events type
TypeDescription
string[]A list of events that the component listens to.
stringA single event that the component listens to.
Component.SpecialEvent[]A list of detailed special event definitions with extra options such as pattern or once.
nilThe component will not listen to any events (default value).

Description: A list of events that the component listens to. When any of these events are triggered, the component will be updated. If not provided, the component will not listen to any events. Type :h autocmd-events in Neovim to see the list of available events.

Combine with reference: This field can be combine with ref.events. The component will be updated if events is triggered or the reference component's events triggered.

Syntax: "EventName pattern1,pattern2" or {"EventName", ...}

Example:

  • Type: string[], Component.SpecialEvent[]
local component = {
    events = {
        "BufEnter",
        "User VeryLazy,LazyLoad",
        "BufEnter *lua,*js",
        {
            "User",
            pattern = { "VeryLazy", "LazyLoad" },
        },
        {
            "CursorHold", "CursorHoldI",
            once = true,
        }
    }
}
  • Type: string
local component = {
    events = "BufEnter"
}
  • timing:

    TypeDescription
    trueThe component will be updated every 1000 milliseconds (default debounce time).
    numberThe component will be updated every specified number of milliseconds.
    nilThe component will not rely on timer-based updates (default value).

    Description: The time in milliseconds to debounce updates for the component. If set to true, it will use a default debounce time of 1000 milliseconds. If not provided, the component will not rely on timer-based updates.

    Combine with reference: This field can be combine with ref.timing field. The component will be triggered update if on time or the reference component on time.

    Example:

    • Type: true
    local component = {
        timing = true -- Default debounce time of 1000ms
    }
    
    • Type: number
    local component = {
        timing = 500 -- Debounce time of 500ms
    }
    
  • win_individual:

    Type: boolean

    Description: If set to true, the component will be updated for each window individually. If not provided, the component will not be updated for each window individually.

    Example:

    local component = {
    	id = "test",
    	win_individual = true,
    	lazy = false,
    	update = function(self, sid)
          -- When the component in the window with filetype NvimTree
          -- The result will be "nvim_tree"
          -- Else the result will be "test"
          -- Each window has individual value
    		local filetype = vim.bo.filetype
    		if filetype == "NvimTree" then
    			return "nvim_tree"
    		end
    		return "test"
    	end,
    
  • temp:

    Type: any

    Description: Any temporary data that you want to store in the component instance. The data inside this field will not be cached, so you need to set them in any function like init, update, etc. This is useful for storing state or other information that should not persist across Neovim restarts. If the value is not a table, the temp field will be removed when restarting Neovim. If is a table, then the table will be emptied when restarting Neovim.

    Example:

    local component = {
        -- Empty table to hold temporary datas. The datas inside this table will not be cached so you need to set them in any function like init or update.
        temp = {},
        -- If a temp is not a table, the temp field will be removed when restarting neovim.
        -- or temp = "",
        init = function(self, session_id)
          -- If temp is a table, and you set temp as a component field then you can do something like this to initialize the temp.current_state values because the temp field is still be a empty table when restarting neovim.
          self.temp.current_state = "initial"
    
          -- But if temp is not a table, then you need to set it like this.
          -- The temp field will be removed when restarting neovim, so you need to set it in init or any function that is called when the component is created.
          -- self.temp = "initial"
        end,
        update = function(self, session_id)
            -- You can use self.temp.current_state here
            return "Current State: " .. (self.temp.current_state or "unknown")
        end}
    
  • auto_theme:

    Type: boolean | fun(self, session_id): boolean

    Description: If set to true, the style of the component will be automatically updated based on the current theme. This will not work if user set auto_theme to false in setup function.

    Example:

    local component = {
        auto_theme = true -- The style of the component will be automatically updated based on the current theme
    }
    
  • flexible:

    Type: number | nil

    Description: A priority value that determines how the component behaves when there is limited space in the status line. If the total width of all components exceeds the available space, components with higher flexible values will be truncated or hidden first. If not provided, the component will not be flexible and will always be displayed in full.

    Example:

    local component = {
        flexible = 2 -- Higher priority for truncation or hiding
    }
    
  • padding:

    Alias: PaddingFunc : fun(self, session_id): number | nil

    TypeDescription
    numberThe padding applied to both sides of the component
    PaddingFuncA function that returns the padding for both sides, or a table with left and right fields.
    { left: number | PaddingFunc | nil, right: number | PaddingFunc | nil }A table with left and right fields to specify different padding for each side.

    Description: The padding to be applied to the component. It can be a number, a function, or a table with left and right fields. If not provided, a default padding of 1 space will be applied to both sides of the component.

    • Note: Padding is applied inside the separator if separator is provided. For example, if padding = 1 and separator = "|", the output will be "| text |".

    Example:

    • Type: number
    local component = {
        padding = 2 -- Adds 2 spaces on both sides
    }
    
    • Type: PaddingFunc
    local component = {
        padding = function(self, session_id)
            return 3 -- Adds 3 spaces on both sides
        end
    }
    
    • Type: { left: number, right: number }
    local component = {
        padding = { left = 1, right = 2 } -- Adds 1 space on the left and 2 spaces on the right
    }
    
    • Type: { left: PaddingFunc, right: PaddingFunc }
    local component = {
        padding = {
            left = function(self, session_id) return 1 end,
            right = function(self, session_id) return 2 end
        } -- Adds 1 space on the left and 2 spaces on the right
    }
    
    • Type: nil
    local component = {
        padding = nil -- Adds 1 space on both sides (default behavior)
    }
    
  • init:

    TypeDescription
    fun(self, session_id): nilA function that initializes the component. It is called once when the component is created.

    Description: A function that initializes the component. It is called once when the component is created right after the component is managed by WitchLine. This is usually use for create custom update logic for the component when events or timing is not enough.

    Example:

    • Type: fun(self, session_id): nil
    local parent = {
        id = "parent",
        init = function(self, session_id)
        end
    }
    
    
    • Some tricks:
      local component = {
          id = "parent",
          init = function(self, session_id)
              local hook = require("witch-line.core.manager.hook")
              local static = hook.use_static(self) -- Use hook to access static
              local ctx = hook.use_context(self, session_id) -- Use hook to access context
    
                -- You can set static values here
              static.icon = "⚑"
    
                -- You can also set ctx values here if ctx is a static value and not a function like a table
              ctx.some_value = 42
    
              -- You can also add autocmds here
              vim.api.nvim_create_autocmd("BufWritePost", {
                  pattern = "*",
                  callback = function()
                      -- This will trigger an update for the component when the event is fired
                      require("witch-line.core.handler").refresh_comp_graph(self)
                  end
              })
            end,
            update = function(self, session_id)
              local hook = require("witch-line.core.manager.hook")
              local static = hook.use_static(self) -- Use hook to access static
              return static.icon .. " updated text"
            end
    
      }
    
      -- Then when you want to create a child component and update at the same time with parent then you
      -- can use ref like this.
      -- Just ensure that the parent is call `require("witch-line.core.handler").refresh_component_graph`
      local child = {
          id = "child",
          ref = {
              events = "parent"
          },
          update = function()
              return "child"
          end
      }
    
  • style:

    Alias: ThemeAwareStyle: This is inherited from vim.api.keyset.highlight with new field:

    Type:

    TypeDescription
    stringA highlight group name to be applied to the component.
    ThemeAwareStyleA static highlight group to be applied to the component.
    nilNo specific highlight group will be applied (default behavior).
    fun(self, session_id): string | ThemeAwareStyleA function that returns a highlight group. This can be used to create dynamic styles based on the current state of the component.

    Description: The highlight style to be applied to the component.

    Example:

    • Type: ThemeAwareStyle
    local component = {
        style = {
            fg = "#ffffff",
            bg = "#000000",
            auto_theme = false -- disabled auto theme only for style
        }
    }
    
    • Type: fun(self, session_id) -> ThemeAwareStyle
    local component = {
        style = function(self, session_id)
          local static = require("witch-line.core.manager.hook").use_static(self) -- Use hook to access static
          if static.config_value then
              return { fg = "#00ff00" } -- Green text if config_value is true
          else
              return { fg = "#ff0000" } -- Red text if config_value is false
          end
        end
    }
    
  • pre_update:

    Type: fun(self, session_id) -> nil

    Description: A function that is called before the component is updated. It is called every time the component needs to be rerendered, right before the update function is called. This can be used to perform any necessary actions or calculations before the component is updated.

    Example:

    local component = {
      pre_update = function(self, session_id)
              -- Pre-update code here
      end
    }
    
  • update: Type: string|nil|fun(self, session_id): string|nil , CompStyle|nil

    Description: A string or a function that updates the component. It is called every time the component needs to be rerendered. It should return the text to be displayed and the highlight properties to be applied.

    The reason for the second return value is to allow dynamic highlights based on the current state of the component. Although we have the style field to define style, but sometimes the style needs to change based on the value, and this allows for that flexibility.

    Example:

    local component = {
        update = function(self, session_id)
            -- Update code here
            return "updated text", { fg = "#ffffff", bg = "#000000" } -- Return text and highlight
        end
    }
    
  • post_update:

    Type: fun(self, session_id) -> nil

    Description: A function that is called after the component is updated. It is called every time the component is rerendered, right after the update function is called. This can be used to perform any necessary actions or calculations after the component is updated.

    Example:

    local component = {
        post_update = function(self, session_id)
            -- Post-update code here
        end
    }
    
  • min_screen_width:

    TypeDescription
    numberThe minimum screen width required for the component to be displayed.
    fun(self, session_id): numberA function that returns the minimum screen width required for the component to be displayed.
    nilNo minimum screen width requirement (default behavior).

    Description: The minimum screen width required for the component to be displayed. If the screen width is less than this value, the component will not be rendered. This can be used to hide components on smaller screens or when there is not enough space to display them properly.

    Combine with reference: This field can be combine with ref.min_screen_width. After combination, the component will be hide when screen zoom in below min_screen_width or the same behavior from reference component

    Example:

    • Type: number
    local component = {
        min_screen_width = 80 -- Component will only be displayed if screen width is at least 80
    }
    
    • Type: fun(self, session_id) -> number
    local component = {
        min_screen_width = function(self, session_id)
            return session_id and 100 or 50 -- Dynamic minimum screen width based on session_id
        end
    }
    
  • hidden:

    TypeDescription
    booleanA flag that determines whether the component is hidden or not.
    fun(self, session_id): booleanA function that returns a boolean to determine whether the component is hidden or not.
    nilThe component will not be hidden (default behavior).

    Description: A flag that determines whether the component is hidden or not. If set to true, the component will not be rendered. This can be used to conditionally hide components based on certain criteria.

    Combine with reference: This field can be combine with ref.hidden field. The component will be hide when the component.hidden return true or the reference component's hidden field return true

  • Example:

    • Type: boolean
    local component = {
      hidden = true -- Component will always be hidden
    }
    
    • Type: fun(self, session_id) -> boolean
    local component = {
        hidden = function(self, session_id)
            local static = require("witch-line.core.manager.hook").use_static(self) -- Use hook to access static
            return static.config_value -- Dynamic hiding based on config_value
        end
    }
    
  • left:

    TypeDescription
    nilNo left separator will be used (default behavior).
    stringA static string to be used as the left separator of the component.
    fun(self, session_id): stringA function that returns a string to be used as the left separator of the component.

    Description: The left separator of the component.

    Example:

    • Type: string
    local component = {
      -- semi circle separator
      left = "β¦…" -- Static left part
    }
    
    • Type: fun(self, session_id) -> string
    local component = {
      left = function(self, session_id)
          local static = require("witch-line.core.manager.hook").use_static(self) -- Use hook to access static
          return static.icon .. " β¦…" -- Dynamic left part based on static values
      end
    }
    
  • right:

    TypeDescription
    stringA static string to be used as the right separator of the component.
    fun(self, session_id): string | nilA function that returns a string to be used as the right separator of the component.
    nilNo right separator will be used (default behavior).

Description: The right separator of the component.

Example:

  • Type: string
local component = {
    -- right semi circle separator
    right = "⦆" -- Static right part
}
  • Type: fun(self, session_id) -> string
local component = {
    right = function(self, session_id)
        local static = require("witch-line.core.manager.hook").use_static(self) -- Use hook to access static
        return static.icon .. " ⦆" -- Dynamic right part based on static values
    end
}
  • left_style:

    Alias:

    • SepStyle : 0 | 1 | 2 | 3
    Values:Description
    0Inherit from component style
    1The foreground color of the separator is the foreground color of the component, and the background color of the separator is NONE.
    2The foreground color of the separator is the background color of the component, and the background color of the separator is NONE.
    3The foreground color of the separator is the background color of the component, and the background color of the separator is the foreground color of the component.
    • ThemeAwareStyle: This is inherited from vim.api.keyset.highlight with new field:

    Type:

    TypeDescription
    SepStyleA predefined style based on the component's style.
    ThemeAwareStyleA static highlight group to be applied to the left part of the component.
    nilDefaults to SepStyle.SepBg (uses the component's background color as the separator's foreground).
    fun(self, session_id): ThemeAwareStyle | SepStyle | string | nilA function that returns a highlight group. This can be used to create dynamic styles based on the current state of the component.
    stringA highlight group name to be applied to the left part of the component.

    Description: The highlight style to be applied to the left part of the component.

    Example:

    • Type: SepStyle
    local component = {
        left_style = 1
    }
    
    • Type: ThemeAwareStyle
    local component = {
        left_style = {
            fg = "#ffffff",
            bg = "#000000",
            auto_theme = false -- disabled auto theme only for left_style
        }
    }
    
    • Type: fun(self, session_id) -> ThemeAwareStyle
    local component = {
        left_style = function(self, session_id)
            if static.config_value then
                return { fg = "#00ff00" } -- Green text if config_value is true
            else
                return { fg = "#ff0000" } -- Red text if config_value is false
            end
        end
    }
    
    • Type: fun(self, session_id) -> SepStyle
    local component = {
        left_style = function(self, session_id)
            if self.config_value then
                return 1 -- Use SepStyle 1 if config_value is true
            else
                return 2 -- Use SepStyle 2 if config_value is false
            end
        end
    }
    
    • Type: string
      local component = {
          left_style = "MyHighlightGroup" -- Use a custom highlight group
      }
    
  • right_style:

    Alias:

    • SepStyle : 0 | 1 | 2 | 3
    Values:Description
    0Inherit from component style
    1The foreground color of the separator is the foreground color of the component, and the background color of the separator is NONE.
    2The foreground color of the separator is the background color of the component, and the background color of the separator is NONE.
    3The foreground color of the separator is the background color of the component, and the background color of the separator is the foreground color of the component.
    • ThemeAwareStyle: This is inherited from vim.api.keyset.highlight with new field:

    Type:

    TypeDescription
    SepStyleA predefined style based on the component's style.
    ThemeAwareStyleA static highlight group to be applied to the right part of the component.
    nilNo specific highlight group will be applied to the right part (default behavior).
    fun(self, session_id): ThemeAwareStyle | SepStyle | string | nilA function that returns a highlight group. This can be used to create dynamic styles based on the current state of the component.
    stringA highlight group name to be applied to the right part of the component.

    Description: The highlight style to be applied to the right part of the component. It can be a static highlight group or a function that returns a highlight group. If not provided, the default highlight group will be used.

    Example:

    • Type: SepStyle
    local component = {
        right_style = 1
    }
    
    • Type: ThemeAwareStyle
    local component = {
        right_style = {
            fg = "#ffffff",
            bg = "#000000",
            auto_theme = false -- disabled auto theme only for right_style
        }
    }
    
    • Type: fun(self, session_id) -> ThemeAwareStyle
    local component = {
        right_style = function(self, session_id)
            if static.config_value then
                return { fg = "#00ff00" } -- Green text if config_value is true
            else
                return { fg = "#ff0000" } -- Red text if config_value is false
            end
        end
    }
    
    • Type: fun(self, session_id) -> SepStyle
        local component = {
            right_style = function(self, session_id)
                if static.config_value then
                    return 1 -- Use SepStyle 1 if config_value is true
                else
                    return 2 -- Use SepStyle 2 if config_value is false
                end
            end
        }
    
  • on_click:

    Alias: OnClickFunc : fun(self: ManagedComponent, minwid: 0, click_times: number, mouse_button: "l"|"r"|"m", modifier_pressed: "s"|"c"|"a"|"m"): nil

    TypeDescription
    nilThe component will not have any click handler (default behavior).
    stringThe name of a global function to be called when the component is clicked.
    OnClickFuncA function that will be called when the component is clicked.
    {name: string, callback: OnClickFunc}A table with name and callback fields to define a named click handler.

    Description: A function name or a function that is called when the component is clicked. It can be a string representing the name of a global function, a function itself, or a table with name and callback fields. If it's a table, the name field is used to identify the click handler, and the callback field is the function that will be called when the component is clicked.

    The function accepts the following parameters:

    ParameterTypeDescription
    selfManagedComponentThe component instance.
    minwidnumberThe window number where the component was clicked.
    click_timesnumberThe number of clicks (1 for single click, 2 for double click, etc.).
    mouse_button"l" | "r" | "m"The mouse button that was clicked ("l" for left, "r" for right, "m" for middle).
    modifier_pressed"s" | "c" | "a" | "m"The modifier key that was pressed ("s" for Shift, "c" for Control, "a" for Alt, "m" for Meta).

    Example:

    local component = {
        on_click = function(self, minwid, click_times, mouse_button, modifier_pressed)
            -- Click handling code here
            print("Component clicked with button: " .. mouse_button .. ", clicks: " .. click_times)
        end
    }
    

πŸ”— Referencing Fields

An component can reference other components for some of its fields. This allows for reusing common configurations and creating more complex components by combining simpler ones. The following fields can reference other components:

  • inherit:

    Type: CompId | nil

    Description: The id of another component to inherit fields from. The fields of the inherited component will be merged with the fields of the current component. If a field is defined in both components, the value from the current component will take precedence. This allows for creating base components that can be extended by other components.

    The component inherited from another component will be updated when the parent component is updated. This means that if the parent component changes, the child component will also reflect those changes. If the parent is hidden, the child component will also be hidden.

    Example:

    local base_component = {
        id = "base_component",
        events = {"BufEnter"},
        timing = true,
        style = { fg = "#ffffff", bg = "#000000" },
        padding = 1,
        update = function(self, session_id)
            return "Base Component"
        end
    }
    
    local child_component = {
        -- So the child will update on BufEnter event, update every 1000ms (default timing for true),
        -- and have the same style and padding as the base component.
        id = "child_component",
        inherit = "base_component",
        update = function(self, session_id)
            return "Child Component"
        end
    }
    
    
  • ref:

    Type: table

    Description: A table that maps field names to component IDs. When a field is not found locally or via inherit, the ref[key] is checked as a fallback. Any field name can be used as a key β€” the entries below are the most common.

    Some ref keys also create dependency graph links (see the Dependency column), meaning the current component is re-rendered whenever the referenced component updates.

    FieldTypeValue BehaviorDependency
    eventsCompId | CompId[]β€” (no value fallback)Linked as Event dep
    timingCompId | CompId[]β€” (no value fallback)Linked as Timer dep
    styleCompIdFallback for the style fieldβ€”
    left_styleCompIdFallback for the left_style fieldβ€”
    right_styleCompIdFallback for the right_style fieldβ€”
    leftCompIdFallback for the left fieldβ€”
    rightCompIdFallback for the right fieldβ€”
    staticCompIdFallback for the static fieldβ€”
    contextCompIdFallback for the context fieldβ€”
    hiddenCompId | CompId[]Fallback for the hidden fieldLinked as Visible dep
    min_screen_widthCompId | CompId[]Fallback for the min_screen_width fieldLinked as Visible dep

    Example:

    local event_component = {
        id = "event_component",
        events = {"BufEnter", "CursorHold"},
        static = { event_info = "Event Info" },
        update = function(self, session_id)
            return "Event Component"
        end
    }
    local style_component = {
        id = "style_component",
        style = { fg = "#00ff00", bg = "#000000" },
        update = function(self, session_id)
            return "Style Component"
        end
    }
    local main_component = {
        id = "main_component",
        ref = {
            static = "event_component", -- The main component will have static values from event_component, In this case static = { event_info = "Event Info" }
            events = "event_component", -- The main component will update on BufEnter and CursorHold events. In this case, the main component will update when event_component updates.
            style = "style_component"    -- The main component will have the style defined in style_component, In this case fg = "#00ff00", bg = "#000000"
        },
        update = function(self, session_id)
            -- static.event_info is available here because we referenced static from event_component
            return "Main Component"
        end
    }
    

βš™οΈ Pipeline Overview

When designing a component in Witch-Line, the resolution of a field or behavior follows a strict pipeline:

Local β†’ Inherit β†’ Reference

  1. Local β€” The value or method defined directly on the current component instance.
  2. Inherit β€” If no local value exists, the component’s parent class (or ancestor chain) is checked.
    • If the value is a function, it is executed with self pointing to the current component.
  3. Reference β€” If neither local nor inherited value exists, the reference component (another instance that the current component points to) is checked.
    • If the value is a function, it is executed with self pointing to the referenced component.

This pipeline is recursive, and Witch-Line ensures correct self binding during each step:

  • Inheritance calls β†’ self = current component
  • Reference calls β†’ self = referenced component

πŸ” Differences Between Reference and Inheritance

Most developers are already familiar with inheritance:

A child component inherits logic and values from its ancestors.

But reference works differently β€” it’s not ownership, it’s delegation. Let’s illustrate it with a simple analogy πŸ‘‡

🧠 Analogy
  1. You and your friend are preparing for an exam.
  2. You can’t solve a math problem, so you ask your friend for help.
  3. Your friend says: β€œI’ll share my solution in a Google Document and send you a link.”
  4. You open the link, see the solution, but you can’t edit it β€” it’s read-only.

That’s how reference works:

A component can use another component’s computed result, but cannot modify or override it.

The shared component executes its logic by itself; the referencing component just receives and uses the result.


🧩 Example

local Shared = {
    id = "A",
    static = {
        A = "A",
        B = "B"
    },
    style = function(self, sid)
        -- self here is Shared, not Comp
        local static = require("witch-line.core.manager.hook").use_static(self)
        if static.A == "A" then
            return { fg = "#ffffff" }
        else
            return { fg = "#000000" }
        end
    end
}

local Comp = {
    id = "B",
    static = {
        A = "B",
    },
    ref = {
        style = "A"
    }
}

  • You guess what's happen when style of Comp is called.
  • Answer is: Comp will not call any style function. It's just read the result from shared component and use it directly. The style function called with the self passed is the Shared component instead of Comp
  • If it's inherit Shared it's a new story. The Comp will call style with self is Comp

Tips

  • Use ref as much as possible instead of inherit for performance
  • Don't combine ref and inherit unless you ensure how it's really executed
  • Try to create less than 2 level of ancestor for manipulation.
  • Remember lookup pipeline : Local > Inherit > Ref

πŸš€ Advanced Fields

  • {...}:

    Type: Component[]

    Description: A list of child components that will be rendered inside the current component. This allows for creating nested components and more complex layouts. The child components will be set inherit by the id of the parent component, so they will inherit fields from the parent component unless they override them. This is useful for creating groups of components that share common configurations.

    Example:

    local parent = {
        id = "parent_component",
        timing = true,
        style = { fg = "#ffffff", bg = "#000000" },
        padding = 1,
    
    
        {
            id = "child_component_1",
            -- So the child will have the same style and padding as the parent component.
            -- The child will also update every 1000ms (default timing for true).
            update = function(self, session_id)
                return "Child 2"
            end
        },
        {
            id = "child_component_2",
            -- This child will also have the same style as the parent component, but will override the padding.
            -- The child will also update every 1000ms (default timing for true).
            padding = 2, -- This child will override the padding of the parent component.
            update = function(self, session_id)
                return "Child 3"
            end
        }
    
    }
    

πŸ§ͺ Component Function Lifecycle

  • init : Called once when the component is created. After WitchLine manages the component. (Such as setting up autocmds, etc.)
  • pre_update : Called every time the component needs to be update, right before calling min_screen_width -> hidden -> update functions.
  • min_screen_width : Called every time the component needs to be update, right after calling pre_update function, right before calling hidden function.
  • hidden : Called every time the component needs to be update, right after calling min_screen_width function, right before calling update function.
  • update : Called every time the component needs to be update, right after calling hidden function, to get the content of the component. Padding is applied right after this.
  • padding : Called every time the component needs to be update, right after calling update function, to get the padding of the component.
  • style : Called every time the component updated successfully, after calling update function, right before setting the value in the statusline, to get the style of the component.
  • left : Called after the style is resolved, to get the left side decoration of the component.
  • right : Called after left, to get the right side decoration of the component.
  • left_style : Called after left, to get the style of the left decoration.
  • right_style : Called after right, to get the style of the right decoration.
  • on_click : The click handler is registered in the statusline after all decorations are resolved.
  • post_update : Called at the very end of the update cycle, after all values and styles are set.