Skip to content
This repository has been archived by the owner on Sep 20, 2023. It is now read-only.

Commit

Permalink
Merge pull request #234 from feline-nvim/develop
Browse files Browse the repository at this point in the history
v1.1
  • Loading branch information
famiu authored Mar 12, 2022
2 parents 7ddd3f0 + 3fb93a8 commit d8093d2
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 20 deletions.
26 changes: 26 additions & 0 deletions USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,32 @@ end

If you omit the provider value, it will be set to an empty string. A component with no provider or an empty provider may be useful for things like [applying a highlight to section gaps](#highlight-section-gaps) or just having an icon or separator as a component.

##### Update provider value using triggers

Sometimes the provider value has to do some heavy operations, which makes it undesirable to run the provider function every time the statusline is generated. Feline allows you to conditionally re-run the provider function by triggering an update to the provider string through either an autocmd or a function. Until the provider function is run again, the value from the previous execution of the provider function is used as the provider string.

Updating provider value through triggers is achieved through the `update` key in the `provider` table. `update` can be either a boolean value, a table or a function that returns a boolean value or a table. If it's a boolean value, then the provider will be updated if value is `true`. For example:

```lua
provider = {
name = 'my_provider',
-- Only update provider if there are less than 4 windows in the current tabpage
update = function()
return #vim.api.nvim_tabpage_list_wins(0) < 4
end
}
```

If it's a table, it must contain a list of autocmds that will trigger an update for the provider. For example:

```lua
provider = {
name = 'my_provider',
-- Only update provider if a window is closed or if a buffer is deleted
update = { 'WinClosed', 'BufDelete' }
}
```

#### Component name

A component can optionally be given a name. While the component is not required to have a name and the name is mostly useless, it can be used to check if the component has been [truncated](#truncation). To give a component a name, just set its `name` value to a `string`, shown below:
Expand Down
37 changes: 37 additions & 0 deletions doc/feline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,43 @@ with no provider or an empty provider may be useful for things like
|feline-applying-a-highlight-to-section-gaps| or just having an icon or
separator as a component.

UPDATE PROVIDER VALUE USING TRIGGERS

Sometimes the provider value has to do some heavy operations, which makes it
undesirable to run the provider function every time the statusline is
generated. Feline allows you to conditionally re-run the provider function by
triggering an update to the provider string through either an autocmd or a
function. Until the provider function is run again, the value from the previous
execution of the provider function is used as the provider string.

Updating provider value through triggers is achieved through the `update` key
in the `provider` table. `update` can be either a boolean value, a table or a
function that returns a boolean value or a table. If it’s a boolean value,
then the provider will be updated if value is `true`. For example:

>
provider = {
name = 'my_provider',
-- Only update provider if there are less than 4 windows in the current tabpage
update = function()
return #vim.api.nvim_tabpage_list_wins(0) < 4
end
}
<


If it’s a table, it must contain a list of autocmds that will trigger an
update for the provider. For example:

>
provider = {
name = 'my_provider',
-- Only update provider if a window is closed or if a buffer is deleted
update = { 'WinClosed', 'BufDelete' }
}
<


*feline-Component-name*

Component name A component can optionally be given a
Expand Down
143 changes: 126 additions & 17 deletions lua/feline/generator.lua
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ local bo = vim.bo
local api = vim.api

local feline = require('feline')
local utils = require('feline.utils')

local M = {
-- Cached highlights
Expand All @@ -12,6 +13,15 @@ local M = {
component_hidden = {},
}

-- Cached provider strings for providers that are updated through a trigger
local provider_cache = {}

-- Cached provider strings for short providers that are updated through a trigger
local short_provider_cache = {}

-- Flags to check if the autocmd for a provider update trigger has been created
local provider_autocmd = {}

-- Return true if any pattern in tbl matches provided value
local function find_pattern_match(tbl, val)
return tbl and next(vim.tbl_filter(function(pattern)
Expand Down Expand Up @@ -231,7 +241,7 @@ local function parse_icon(icon, parent_hl, is_component_empty)
end

-- Parse component provider to return the provider string and default icon
local function parse_provider(provider, component)
local function parse_provider(provider, component, is_short, winid, section_nr, component_nr)
local str = ''
local icon

Expand All @@ -242,11 +252,17 @@ local function parse_provider(provider, component)
else
str = provider
end
-- If provider is a function, just evaluate it normally
elseif type(provider) == 'function' then

return str, icon
end

-- If provider is a function, just evaluate it normally
if type(provider) == 'function' then
str, icon = provider(component)
-- If provider is a table, get the provider name and opts and evaluate the provider
elseif type(provider) == 'table' then
local provider_fn, provider_opts

if not provider.name then
api.nvim_err_writeln("Provider table doesn't have the provider name")
elseif type(provider.name) ~= 'string' then
Expand All @@ -256,7 +272,85 @@ local function parse_provider(provider, component)
elseif not feline.providers[provider.name] then
api.nvim_err_writeln(string.format("Provider with name '%s' doesn't exist", provider.name))
else
str, icon = feline.providers[provider.name](component, provider.opts or {})
provider_fn = feline.providers[provider.name]
provider_opts = provider.opts or {}
end

local update = evaluate_if_function(provider.update)

if update == nil then
str, icon = provider_fn(component, provider_opts)
else
local provider_cache_tbl

if is_short then
provider_cache_tbl = short_provider_cache
else
provider_cache_tbl = provider_cache
end

-- Initialize provider cache tables
if not provider_cache[winid] then
provider_cache[winid] = {}
end

if not provider_cache[winid][section_nr] then
provider_cache[winid][section_nr] = {}
end

if not short_provider_cache[winid] then
short_provider_cache[winid] = {}
end

if not short_provider_cache[winid][section_nr] then
short_provider_cache[winid][section_nr] = {}
end

-- If `update` is true or provider string is not cached, call the provider function
-- and cache it
-- Use == true for comparison to prevent the condition being true if `update` is a table
if update == true or not provider_cache_tbl[winid][section_nr][component_nr] then
local cache_str, cache_icon = provider_fn(component, provider_opts)

provider_cache_tbl[winid][section_nr][component_nr] = {
str = cache_str,
icon = cache_icon,
}
end

-- If `update` is a table, it means that the provider update is triggered through
-- autocmds
if type(update) == 'table' then
-- Initialize autocmd table structure
if not provider_autocmd[winid] then
provider_autocmd[winid] = {}
end

if not provider_autocmd[winid][section_nr] then
provider_autocmd[winid][section_nr] = {}
end

-- If an autocmd hasn't been created for the provider update trigger, create it
if not provider_autocmd[winid][section_nr][component_nr] then
provider_autocmd[winid][section_nr][component_nr] = true

utils.create_augroup({
{
table.concat(update, ','),
'*',
string.format(
'lua require("feline.generator").trigger_provider_update(%d, %d, %d)',
winid,
section_nr,
component_nr
),
},
}, 'feline', true)
end
end

str = provider_cache_tbl[winid][section_nr][component_nr].str
icon = provider_cache_tbl[winid][section_nr][component_nr].icon
end
end

Expand All @@ -269,7 +363,7 @@ local function parse_provider(provider, component)
return str, icon
end

local function parse_component(component, use_short_provider)
local function parse_component(component, use_short_provider, winid, section_nr, component_nr)
local enabled

if component.enabled ~= nil then
Expand Down Expand Up @@ -305,7 +399,7 @@ local function parse_component(component, use_short_provider)
end

if provider then
str, icon = parse_provider(provider, component)
str, icon = parse_provider(provider, component, use_short_provider, winid, section_nr, component_nr)
else
str = ''
end
Expand All @@ -324,21 +418,21 @@ end
-- Wrapper around parse_component that handles any errors that happen while parsing the components
-- and points to the location of the component in case of any errors
local function parse_component_handle_errors(
winid,
component,
use_short_provider,
statusline_type,
component_section,
component_number
winid,
section_nr,
component_nr,
statusline_type
)
local ok, result = pcall(parse_component, component, use_short_provider)
local ok, result = pcall(parse_component, component, use_short_provider, winid, section_nr, component_nr)

if not ok then
api.nvim_err_writeln(
string.format(
"Feline: error while processing component number %d on section %d of type '%s' for window %d: %s",
component_number,
component_section,
section_nr,
component_nr,
statusline_type,
winid,
result
Expand All @@ -362,6 +456,11 @@ local function get_component_width(component_str)
return api.nvim_eval_statusline(component_str, eval_statusline_opts).width
end

function M.trigger_provider_update(winid, section_nr, component_nr)
provider_cache[winid][section_nr][component_nr] = nil
short_provider_cache[winid][section_nr][component_nr] = nil
end

-- Generate statusline by parsing all components and return a string
function M.generate_statusline(is_active)
local components
Expand Down Expand Up @@ -435,7 +534,7 @@ function M.generate_statusline(is_active)
M.component_hidden[winid][component.name] = false
end

local component_str = parse_component_handle_errors(winid, component, false, statusline_type, i, j)
local component_str = parse_component_handle_errors(component, false, winid, i, j, statusline_type)
local component_width = get_component_width(component_str)

component_strs[i][j] = component_str
Expand Down Expand Up @@ -468,12 +567,12 @@ function M.generate_statusline(is_active)

if component.short_provider then
local component_str = parse_component_handle_errors(
winid,
component,
true,
statusline_type,
winid,
section,
number
number,
statusline_type
)

local component_width = get_component_width(component_str)
Expand Down Expand Up @@ -536,4 +635,14 @@ function M.generate_statusline(is_active)
return table.concat(section_strs, '%=')
end

-- Clear statusline generator state in order to do a clean reinitialization of it
function M.clear_state()
M.highlights = {}
M.component_hidden = {}
M.component_truncated = {}
provider_cache = {}
short_provider_cache = {}
provider_autocmd = {}
end

return M
6 changes: 6 additions & 0 deletions lua/feline/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ function M.setup(config)
M.force_inactive = config.force_inactive
M.disable = config.disable

-- Unload providers in case they were loaded before to prevent custom providers from old
-- configuration being cached
package.loaded['feline.providers'] = nil
M.providers = require('feline.providers')

-- Register custom providers
Expand Down Expand Up @@ -214,6 +217,9 @@ function M.setup(config)
-- Ensures custom quickfix statusline isn't loaded
g.qf_disable_statusline = true

-- Clear statusline generator state
gen.clear_state()

-- Set the value of the statusline option to Feline's statusline generation function
opt.statusline = "%{%v:lua.require'feline'.statusline()%}"

Expand Down
1 change: 0 additions & 1 deletion lua/feline/providers/vi_mode.lua
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ local mode_alias = {
['niI'] = 'NORMAL',
['niR'] = 'NORMAL',
['niV'] = 'NORMAL',
['nt'] = 'NORMAL',
['v'] = 'VISUAL',
['V'] = 'LINES',
[''] = 'BLOCK',
Expand Down
7 changes: 5 additions & 2 deletions lua/feline/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,12 @@ local cmd = vim.api.nvim_command
local M = {}

-- Utility function to create augroups
function M.create_augroup(autocmds, name)
function M.create_augroup(autocmds, name, no_clear)
cmd('augroup ' .. name)
cmd('autocmd!')

if no_clear == nil or no_clear == false then
cmd('autocmd!')
end

for _, autocmd in ipairs(autocmds) do
cmd('autocmd ' .. table.concat(autocmd, ' '))
Expand Down

0 comments on commit d8093d2

Please sign in to comment.