Skip to content

Commit

Permalink
feat(update/after): add update and after fields.
Browse files Browse the repository at this point in the history
  • Loading branch information
rebelot committed May 30, 2022
1 parent 1f3b296 commit 3188e22
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 17 deletions.
40 changes: 27 additions & 13 deletions cookbook.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,18 +135,18 @@ Each component may contain _any_ of the following fields:
- `on_click`:
- Type: `table` with the following fields:
- `callback`: (vim/)lua function to be called on mouse click(s). The function
has the signature `function(self, winid, minwid, nclicks, button)`
(see `:h 'statusline'` description for `@`). If a `string` is provided,
it is interpreted as the _raw_ function name (`v:lua.` is not prepended)
of an already defined function accessible from vim global scope.
Type: `function` or `string`.
has the signature `function(self, winid, minwid, nclicks, button)`
(see `:h 'statusline'` description for `@`). If a `string` is provided,
it is interpreted as the _raw_ function name (`v:lua.` is not prepended)
of an already defined function accessible from vim global scope.
Type: `function` or `string`.
- `name`: the global name the function will be registered with.
It is not required when `callback` is a `string`. Type: `string` or `function -> string`.
It is not required when `callback` is a `string`. Type: `string` or `function -> string`.
- `update`: whether the function should be registered even if
it already exists in the global namespace.
This is useful for dynamically registering different callbacks.
Omit this field if you are registering only one function.
Type: `boolean` (optional).
it already exists in the global namespace.
This is useful for dynamically registering different callbacks.
Omit this field if you are registering only one function.
Type: `boolean` (optional).
- Description: Specify a function to be called when clicking on the component (including its progeny);
Lua functions are automatically registered in the global scope with the name provided
by the `name` field. Arguments passed to the function are the same described
Expand All @@ -158,14 +158,23 @@ Each component may contain _any_ of the following fields:
If `update` is `true`, the callback will be (re-)registered
at each evaluation cycle. Note 1: be careful of the arguments passed to the callback,
you may often prefer wrapping a 'third-party' functions rather than passing their
reference as is. Note 2: the callback is ___not___ executed in the context
reference as is. Note 2: the callback is **_not_** executed in the context
of the window/buffer the component belongs to, but in the context of the
_actual_ current window and buffer. Use `winid` parameter to retrieve
information about the current buffer from a callback.
Be careful when accessing `self` attributes that were set depending
on the local buffer/window the component is displayed into from
within the callback, as they are shared between all representation
of the _same_ component.
Please see the recipes to learn _how to propagate information about
the window/buffer the clicked component belongs to_.
- `update`:
- Type: `function(self) -> boolean` or `string` or `table<string>`.
- Description: Control when the component should be updated or return a per-window
cached value.
If `update` is a function, the component will be updated whenever the function
return value is `true`; else, a `string` or a `table` of strings will be
interpreted as autocommand event names that should trigger the component evaluation.
- `{...}`:
- Type: `list`
- Description: The component progeny. Each item of the list is a component
Expand All @@ -187,6 +196,11 @@ Each component may contain _any_ of the following fields:
example, you can compute some values that will be accessed from other
functions within the component genealogy (even "global" statusline
variables).
- `after`:
- Type: `function(self) -> any`
- Description: This function is called after the component has evaluated all of its
children and can be used to alter the state of the component before it returns
the output string `self.stl`.
- `static`:
- Type: `table`
- Description: This is a container for static variables, that is, variables
Expand All @@ -211,8 +225,8 @@ There are two distinct phases in the life of a StatusLine object component: its
_creation_ (instantiation) and its _evaluation_. When creating the "blueprint"
tables, the user instructs the actual constructor on the attributes and methods
of the component. The fields `static` and `restrict` will have a meaning only
during the instantiation phase, while `condition`, `init`, `hl`, `provider` and
`pick_child` are evaluated (in this order) every time the statusline is
during the instantiation phase, while `condition`, `update`, `init`, `hl`,
`on_click`, `provider` and `pick_child` are evaluated (in this order) every time the statusline is
refreshed.

Confused yet? Don't worry, everything will come together in the [Recipes](#recipes) examples.
Expand Down
15 changes: 15 additions & 0 deletions lua/heirline/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,19 @@ function M.get_highlights()
return require("heirline.highlights").get_highlights()
end

local au_ids = {}

function M.get_au_ids()
return au_ids
end

function M.clear_autocommands()
for i = 1, #au_ids, 1 do
local au_id = table.remove(au_ids)
vim.api.nvim_del_autocmd(au_id)
end
end

function M.load()
vim.g.qf_disable_statusline = true
vim.o.statusline = "%{%v:lua.require'heirline'.eval_statusline()%}"
Expand All @@ -19,6 +32,8 @@ function M.load()
end

function M.setup(statusline, winbar)
M.clear_autocommands()
M.reset_highlights()
M.statusline = StatusLine:new(statusline)
if winbar then
M.winbar = StatusLine:new(winbar)
Expand Down
71 changes: 67 additions & 4 deletions lua/heirline/statusline.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,11 @@ local default_restrict = {
condition = true,
restrict = true,
pick_child = true,
after = true,
on_click = true,
update = true,
stl = true,
_win_stl = true,
}

local StatusLine = {
Expand All @@ -20,17 +24,30 @@ function StatusLine:new(child, index)
child = child or {}
local new = {}

if type(child.hl) == "function" then
new.hl = child.hl
elseif type(child.hl) == "table" then
new.hl = vim.tbl_extend("keep", child.hl, {})
if child.hl then
local hl_type = type(child.hl)
if hl_type == "function" then
new.hl = child.hl
elseif hl_type == "table" then
new.hl = vim.tbl_extend("keep", child.hl, {})
end
end

if child.update then
local update_type = type(child.update)
if vim.tbl_contains({ "function", "string" }, update_type) then
new.update = child.update
elseif update_type == "table" then
new.update = vim.tbl_extend("keep", child.update, {})
end
end

new.condition = child.condition
new.pick_child = child.pick_child and vim.tbl_exend("keep", child.pick_child, {})
new.init = child.init
new.provider = child.provider
new.stop_when = child.stop_when
new.after = child.after
new.on_click = child.on_click and vim.tbl_extend("keep", child.on_click, {})
new.restrict = child.restrict and vim.tbl_extend("keep", child.restrict, {})

Expand Down Expand Up @@ -132,11 +149,49 @@ local function register_global_function(component)
return "v:lua." .. func_name
end

local function update_autocmd(component)
local events = component.update
local id = vim.api.nvim_create_autocmd(events, {
callback = function()
component._unlock_from_au = true
end,
desc = "Heirline update au for " .. vim.inspect(component.id),
})
component._update_autocmd = true
table.insert(require("heirline").get_au_ids(), id)
end

function StatusLine:eval()
if self.condition and not self:condition() then
-- self.stl = ''
return ""
end

if self.update then
self._locked = true

if type(self.update) == "function" then
self._locked = not self:update()
elseif not self._update_autocmd then
update_autocmd(self)
end

if self._unlock_from_au or not self._locked then
self._win_stl = nil -- clear per-window cached stl
end

if self._locked then
local win_stl = self:get_win_attr("_win_stl")
if win_stl then
return win_stl
end
end

if self._unlock_from_au then
self._unlock_from_au = false
end
end

if self.init then
self:init()
end
Expand Down Expand Up @@ -185,6 +240,14 @@ function StatusLine:eval()

self.stl = table.concat(stl, "")

if self.after then
self:after()
end

if self.update then
self:set_win_attr("_win_stl", self.stl)
end

return self.stl
end

Expand Down

0 comments on commit 3188e22

Please sign in to comment.