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

Commit d8093d2

Browse files
authored
Merge pull request #234 from feline-nvim/develop
v1.1
2 parents 7ddd3f0 + 3fb93a8 commit d8093d2

File tree

6 files changed

+200
-20
lines changed

6 files changed

+200
-20
lines changed

USAGE.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,32 @@ end
164164

165165
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.
166166

167+
##### Update provider value using triggers
168+
169+
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.
170+
171+
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:
172+
173+
```lua
174+
provider = {
175+
name = 'my_provider',
176+
-- Only update provider if there are less than 4 windows in the current tabpage
177+
update = function()
178+
return #vim.api.nvim_tabpage_list_wins(0) < 4
179+
end
180+
}
181+
```
182+
183+
If it's a table, it must contain a list of autocmds that will trigger an update for the provider. For example:
184+
185+
```lua
186+
provider = {
187+
name = 'my_provider',
188+
-- Only update provider if a window is closed or if a buffer is deleted
189+
update = { 'WinClosed', 'BufDelete' }
190+
}
191+
```
192+
167193
#### Component name
168194

169195
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:

doc/feline.txt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,43 @@ with no provider or an empty provider may be useful for things like
236236
|feline-applying-a-highlight-to-section-gaps| or just having an icon or
237237
separator as a component.
238238

239+
UPDATE PROVIDER VALUE USING TRIGGERS
240+
241+
Sometimes the provider value has to do some heavy operations, which makes it
242+
undesirable to run the provider function every time the statusline is
243+
generated. Feline allows you to conditionally re-run the provider function by
244+
triggering an update to the provider string through either an autocmd or a
245+
function. Until the provider function is run again, the value from the previous
246+
execution of the provider function is used as the provider string.
247+
248+
Updating provider value through triggers is achieved through the `update` key
249+
in the `provider` table. `update` can be either a boolean value, a table or a
250+
function that returns a boolean value or a table. If it’s a boolean value,
251+
then the provider will be updated if value is `true`. For example:
252+
253+
>
254+
provider = {
255+
name = 'my_provider',
256+
-- Only update provider if there are less than 4 windows in the current tabpage
257+
update = function()
258+
return #vim.api.nvim_tabpage_list_wins(0) < 4
259+
end
260+
}
261+
<
262+
263+
264+
If it’s a table, it must contain a list of autocmds that will trigger an
265+
update for the provider. For example:
266+
267+
>
268+
provider = {
269+
name = 'my_provider',
270+
-- Only update provider if a window is closed or if a buffer is deleted
271+
update = { 'WinClosed', 'BufDelete' }
272+
}
273+
<
274+
275+
239276
*feline-Component-name*
240277

241278
Component name A component can optionally be given a

lua/feline/generator.lua

Lines changed: 126 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local bo = vim.bo
22
local api = vim.api
33

44
local feline = require('feline')
5+
local utils = require('feline.utils')
56

67
local M = {
78
-- Cached highlights
@@ -12,6 +13,15 @@ local M = {
1213
component_hidden = {},
1314
}
1415

16+
-- Cached provider strings for providers that are updated through a trigger
17+
local provider_cache = {}
18+
19+
-- Cached provider strings for short providers that are updated through a trigger
20+
local short_provider_cache = {}
21+
22+
-- Flags to check if the autocmd for a provider update trigger has been created
23+
local provider_autocmd = {}
24+
1525
-- Return true if any pattern in tbl matches provided value
1626
local function find_pattern_match(tbl, val)
1727
return tbl and next(vim.tbl_filter(function(pattern)
@@ -231,7 +241,7 @@ local function parse_icon(icon, parent_hl, is_component_empty)
231241
end
232242

233243
-- Parse component provider to return the provider string and default icon
234-
local function parse_provider(provider, component)
244+
local function parse_provider(provider, component, is_short, winid, section_nr, component_nr)
235245
local str = ''
236246
local icon
237247

@@ -242,11 +252,17 @@ local function parse_provider(provider, component)
242252
else
243253
str = provider
244254
end
245-
-- If provider is a function, just evaluate it normally
246-
elseif type(provider) == 'function' then
255+
256+
return str, icon
257+
end
258+
259+
-- If provider is a function, just evaluate it normally
260+
if type(provider) == 'function' then
247261
str, icon = provider(component)
248262
-- If provider is a table, get the provider name and opts and evaluate the provider
249263
elseif type(provider) == 'table' then
264+
local provider_fn, provider_opts
265+
250266
if not provider.name then
251267
api.nvim_err_writeln("Provider table doesn't have the provider name")
252268
elseif type(provider.name) ~= 'string' then
@@ -256,7 +272,85 @@ local function parse_provider(provider, component)
256272
elseif not feline.providers[provider.name] then
257273
api.nvim_err_writeln(string.format("Provider with name '%s' doesn't exist", provider.name))
258274
else
259-
str, icon = feline.providers[provider.name](component, provider.opts or {})
275+
provider_fn = feline.providers[provider.name]
276+
provider_opts = provider.opts or {}
277+
end
278+
279+
local update = evaluate_if_function(provider.update)
280+
281+
if update == nil then
282+
str, icon = provider_fn(component, provider_opts)
283+
else
284+
local provider_cache_tbl
285+
286+
if is_short then
287+
provider_cache_tbl = short_provider_cache
288+
else
289+
provider_cache_tbl = provider_cache
290+
end
291+
292+
-- Initialize provider cache tables
293+
if not provider_cache[winid] then
294+
provider_cache[winid] = {}
295+
end
296+
297+
if not provider_cache[winid][section_nr] then
298+
provider_cache[winid][section_nr] = {}
299+
end
300+
301+
if not short_provider_cache[winid] then
302+
short_provider_cache[winid] = {}
303+
end
304+
305+
if not short_provider_cache[winid][section_nr] then
306+
short_provider_cache[winid][section_nr] = {}
307+
end
308+
309+
-- If `update` is true or provider string is not cached, call the provider function
310+
-- and cache it
311+
-- Use == true for comparison to prevent the condition being true if `update` is a table
312+
if update == true or not provider_cache_tbl[winid][section_nr][component_nr] then
313+
local cache_str, cache_icon = provider_fn(component, provider_opts)
314+
315+
provider_cache_tbl[winid][section_nr][component_nr] = {
316+
str = cache_str,
317+
icon = cache_icon,
318+
}
319+
end
320+
321+
-- If `update` is a table, it means that the provider update is triggered through
322+
-- autocmds
323+
if type(update) == 'table' then
324+
-- Initialize autocmd table structure
325+
if not provider_autocmd[winid] then
326+
provider_autocmd[winid] = {}
327+
end
328+
329+
if not provider_autocmd[winid][section_nr] then
330+
provider_autocmd[winid][section_nr] = {}
331+
end
332+
333+
-- If an autocmd hasn't been created for the provider update trigger, create it
334+
if not provider_autocmd[winid][section_nr][component_nr] then
335+
provider_autocmd[winid][section_nr][component_nr] = true
336+
337+
utils.create_augroup({
338+
{
339+
table.concat(update, ','),
340+
'*',
341+
string.format(
342+
'lua require("feline.generator").trigger_provider_update(%d, %d, %d)',
343+
winid,
344+
section_nr,
345+
component_nr
346+
),
347+
},
348+
}, 'feline', true)
349+
end
350+
end
351+
352+
str = provider_cache_tbl[winid][section_nr][component_nr].str
353+
icon = provider_cache_tbl[winid][section_nr][component_nr].icon
260354
end
261355
end
262356

@@ -269,7 +363,7 @@ local function parse_provider(provider, component)
269363
return str, icon
270364
end
271365

272-
local function parse_component(component, use_short_provider)
366+
local function parse_component(component, use_short_provider, winid, section_nr, component_nr)
273367
local enabled
274368

275369
if component.enabled ~= nil then
@@ -305,7 +399,7 @@ local function parse_component(component, use_short_provider)
305399
end
306400

307401
if provider then
308-
str, icon = parse_provider(provider, component)
402+
str, icon = parse_provider(provider, component, use_short_provider, winid, section_nr, component_nr)
309403
else
310404
str = ''
311405
end
@@ -324,21 +418,21 @@ end
324418
-- Wrapper around parse_component that handles any errors that happen while parsing the components
325419
-- and points to the location of the component in case of any errors
326420
local function parse_component_handle_errors(
327-
winid,
328421
component,
329422
use_short_provider,
330-
statusline_type,
331-
component_section,
332-
component_number
423+
winid,
424+
section_nr,
425+
component_nr,
426+
statusline_type
333427
)
334-
local ok, result = pcall(parse_component, component, use_short_provider)
428+
local ok, result = pcall(parse_component, component, use_short_provider, winid, section_nr, component_nr)
335429

336430
if not ok then
337431
api.nvim_err_writeln(
338432
string.format(
339433
"Feline: error while processing component number %d on section %d of type '%s' for window %d: %s",
340-
component_number,
341-
component_section,
434+
section_nr,
435+
component_nr,
342436
statusline_type,
343437
winid,
344438
result
@@ -362,6 +456,11 @@ local function get_component_width(component_str)
362456
return api.nvim_eval_statusline(component_str, eval_statusline_opts).width
363457
end
364458

459+
function M.trigger_provider_update(winid, section_nr, component_nr)
460+
provider_cache[winid][section_nr][component_nr] = nil
461+
short_provider_cache[winid][section_nr][component_nr] = nil
462+
end
463+
365464
-- Generate statusline by parsing all components and return a string
366465
function M.generate_statusline(is_active)
367466
local components
@@ -435,7 +534,7 @@ function M.generate_statusline(is_active)
435534
M.component_hidden[winid][component.name] = false
436535
end
437536

438-
local component_str = parse_component_handle_errors(winid, component, false, statusline_type, i, j)
537+
local component_str = parse_component_handle_errors(component, false, winid, i, j, statusline_type)
439538
local component_width = get_component_width(component_str)
440539

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

469568
if component.short_provider then
470569
local component_str = parse_component_handle_errors(
471-
winid,
472570
component,
473571
true,
474-
statusline_type,
572+
winid,
475573
section,
476-
number
574+
number,
575+
statusline_type
477576
)
478577

479578
local component_width = get_component_width(component_str)
@@ -536,4 +635,14 @@ function M.generate_statusline(is_active)
536635
return table.concat(section_strs, '%=')
537636
end
538637

638+
-- Clear statusline generator state in order to do a clean reinitialization of it
639+
function M.clear_state()
640+
M.highlights = {}
641+
M.component_hidden = {}
642+
M.component_truncated = {}
643+
provider_cache = {}
644+
short_provider_cache = {}
645+
provider_autocmd = {}
646+
end
647+
539648
return M

lua/feline/init.lua

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,9 @@ function M.setup(config)
180180
M.force_inactive = config.force_inactive
181181
M.disable = config.disable
182182

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

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

220+
-- Clear statusline generator state
221+
gen.clear_state()
222+
217223
-- Set the value of the statusline option to Feline's statusline generation function
218224
opt.statusline = "%{%v:lua.require'feline'.statusline()%}"
219225

lua/feline/providers/vi_mode.lua

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ local mode_alias = {
1111
['niI'] = 'NORMAL',
1212
['niR'] = 'NORMAL',
1313
['niV'] = 'NORMAL',
14-
['nt'] = 'NORMAL',
1514
['v'] = 'VISUAL',
1615
['V'] = 'LINES',
1716
[''] = 'BLOCK',

lua/feline/utils.lua

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,12 @@ local cmd = vim.api.nvim_command
44
local M = {}
55

66
-- Utility function to create augroups
7-
function M.create_augroup(autocmds, name)
7+
function M.create_augroup(autocmds, name, no_clear)
88
cmd('augroup ' .. name)
9-
cmd('autocmd!')
9+
10+
if no_clear == nil or no_clear == false then
11+
cmd('autocmd!')
12+
end
1013

1114
for _, autocmd in ipairs(autocmds) do
1215
cmd('autocmd ' .. table.concat(autocmd, ' '))

0 commit comments

Comments
 (0)