Skip to content

Commit 0fb1425

Browse files
committed
feat: refresh lualine on auto-command events again
The basic idea is that we don't process refresh events just as they come. We queue them and process them every certain time quantum (by default 60fps). Under this, previous timers as well as autocmds all become refresh events. The event queue should be empty 99% of the time, there's a quick check for that. So, although this check is being performed quite often performance impact shouldn't be negligible. This should hopefully let us have a snappy experience with fewer refreshes, without flicker.
1 parent 0c6cca9 commit 0fb1425

File tree

3 files changed

+111
-7
lines changed

3 files changed

+111
-7
lines changed

lua/lualine.lua

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ local timers = {
1717
stl_timer = vim.loop.new_timer(),
1818
tal_timer = vim.loop.new_timer(),
1919
wb_timer = vim.loop.new_timer(),
20+
refresh_check_timer = vim.loop.new_timer(),
2021
halt_stl_refresh = false, -- mutex ?
2122
halt_tal_refresh = false,
2223
halt_wb_refresh = false,
@@ -333,6 +334,13 @@ local function should_ignore_focus(user_config, win)
333334
end
334335
end
335336

337+
local refresh_event_queue = {
338+
has_events = false,
339+
statusline = {},
340+
tabline = {},
341+
winbar = {},
342+
}
343+
336344
---@alias LualineRefreshOptsKind
337345
---| 'all'
338346
---| 'tabpage'
@@ -344,7 +352,8 @@ end
344352
---@class LualineRefreshOpts
345353
---@field scope LualineRefreshOptsKind
346354
---@field place LualineRefreshOptsPlace[]
347-
---@field trigger 'timer' | 'init' |'unknown'
355+
---@field trigger 'timer' | 'init' | 'autocmd' |'unknown'
356+
---@field queued boolean
348357
--- Refresh contents of lualine
349358
---@param opts LualineRefreshOpts
350359
local function refresh(opts)
@@ -357,6 +366,14 @@ local function refresh(opts)
357366
trigger = 'unknown',
358367
})
359368

369+
if not opts.queued then
370+
for _, place in ipairs(opts.place) do
371+
refresh_event_queue['has_events'] = true
372+
refresh_event_queue[place] = vim.tbl_extend('force', opts, { place = { place }, queued = true })
373+
end
374+
return
375+
end
376+
360377
local wins = {}
361378
local old_actual_curwin = vim.g.actual_curwin
362379

@@ -449,6 +466,7 @@ end
449466
local function set_tabline(hide)
450467
vim.loop.timer_stop(timers.tal_timer)
451468
timers.halt_tal_refresh = true
469+
vim.cmd([[augroup lualine_tal_refresh | exe "autocmd!" | augroup END]])
452470
if not hide and next(config.tabline) ~= nil then
453471
vim.loop.timer_start(
454472
timers.tal_timer,
@@ -458,6 +476,13 @@ local function set_tabline(hide)
458476
refresh { scope = 'tabpage', place = { 'tabline' }, trigger = 'timer' }
459477
end, 3, 'lualine: Failed to refresh tabline')
460478
)
479+
modules.utils.define_autocmd(
480+
table.concat(config.options.refresh.events, ','),
481+
'*',
482+
"call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['tabline'], 'trigger': 'autocmd'})",
483+
'lualine_tal_refresh'
484+
)
485+
461486
modules.nvim_opts.set('showtabline', config.options.always_show_tabline and 2 or 1, { global = true })
462487
timers.halt_tal_refresh = false
463488
vim.schedule(function()
@@ -471,12 +496,43 @@ local function set_tabline(hide)
471496
end
472497
end
473498

499+
local function check_refresh()
500+
if not refresh_event_queue.has_events then
501+
return
502+
end
503+
refresh_event_queue.has_events = nil
504+
505+
for place, refresh_cmd in pairs(refresh_event_queue) do
506+
if type(refresh_cmd) == 'table' and refresh_cmd.queued == true then
507+
refresh(refresh_cmd)
508+
refresh_event_queue[place] = {}
509+
end
510+
end
511+
end
512+
513+
local function set_refresh_checker()
514+
vim.loop.timer_stop(timers.refresh_check_timer)
515+
vim.loop.timer_start(
516+
timers.refresh_check_timer,
517+
0,
518+
config.options.refresh.check,
519+
modules.utils.timer_call(
520+
timers.refresh_check_timer,
521+
'lualine_refresh_check',
522+
check_refresh,
523+
3,
524+
'lualine: Failed to refresh statusline'
525+
)
526+
)
527+
end
528+
474529
--- Sets &statusline option to lualine
475530
--- adds auto command to redraw lualine on VimResized event
476531
---@param hide boolean|nil if should hide statusline
477532
local function set_statusline(hide)
478533
vim.loop.timer_stop(timers.stl_timer)
479534
timers.halt_stl_refresh = true
535+
vim.cmd([[augroup lualine_stl_refresh | exe "autocmd!" | augroup END]])
480536
if not hide and (next(config.sections) ~= nil or next(config.inactive_sections) ~= nil) then
481537
modules.nvim_opts.set('statusline', '%#lualine_transparent#', { global = true })
482538
if config.options.globalstatus then
@@ -489,6 +545,12 @@ local function set_statusline(hide)
489545
refresh { scope = 'window', place = { 'statusline' }, trigger = 'timer' }
490546
end, 3, 'lualine: Failed to refresh statusline')
491547
)
548+
modules.utils.define_autocmd(
549+
table.concat(config.options.refresh.events, ','),
550+
'*',
551+
"call v:lua.require'lualine'.refresh({'kind': 'window', 'place': ['statusline'], 'trigger': 'autocmd'})",
552+
'lualine_stl_refresh'
553+
)
492554
else
493555
modules.nvim_opts.set('laststatus', 2, { global = true })
494556
vim.loop.timer_start(
@@ -499,6 +561,13 @@ local function set_statusline(hide)
499561
refresh { scope = 'tabpage', place = { 'statusline' }, trigger = 'timer' }
500562
end, 3, 'lualine: Failed to refresh statusline')
501563
)
564+
565+
modules.utils.define_autocmd(
566+
table.concat(config.options.refresh.events, ','),
567+
'*',
568+
"call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['statusline'], 'trigger': 'autocmd'})",
569+
'lualine_stl_refresh'
570+
)
502571
end
503572
timers.halt_stl_refresh = false
504573
vim.schedule(function()
@@ -524,6 +593,7 @@ end
524593
local function set_winbar(hide)
525594
vim.loop.timer_stop(timers.wb_timer)
526595
timers.halt_wb_refresh = true
596+
vim.cmd([[augroup lualine_wb_refresh | exe "autocmd!" | augroup END]])
527597
if not hide and (next(config.winbar) ~= nil or next(config.inactive_winbar) ~= nil) then
528598
vim.loop.timer_start(
529599
timers.wb_timer,
@@ -533,6 +603,13 @@ local function set_winbar(hide)
533603
refresh { scope = 'tabpage', place = { 'winbar' }, trigger = 'timer' }
534604
end, 3, 'lualine: Failed to refresh winbar')
535605
)
606+
modules.utils.define_autocmd(
607+
table.concat(config.options.refresh.events, ','),
608+
'*',
609+
"call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['winbar'], 'trigger': 'autocmd'})",
610+
'lualine_wb_refresh'
611+
)
612+
536613
timers.halt_wb_refresh = false
537614
vim.schedule(function()
538615
-- imediately refresh upon load.
@@ -613,6 +690,7 @@ local function setup(user_config)
613690
set_statusline()
614691
set_tabline()
615692
set_winbar()
693+
set_refresh_checker()
616694
end
617695
if package.loaded['lualine.utils.notices'] then
618696
modules.utils_notices.notice_message_startup()

lua/lualine/config.lua

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,22 @@ local config = {
2121
always_show_tabline = true,
2222
globalstatus = vim.go.laststatus == 3,
2323
refresh = {
24-
statusline = 100,
25-
tabline = 100,
26-
winbar = 100,
24+
statusline = 1000,
25+
tabline = 1000,
26+
winbar = 1000,
27+
check = 16, -- ~60fps
28+
events = {
29+
'WinEnter',
30+
'BufEnter',
31+
'BufWritePost',
32+
'SessionLoadPost',
33+
'FileChangedShellPost',
34+
'VimResized',
35+
'Filetype',
36+
'CursorMoved',
37+
'CursorMovedI',
38+
'ModeChanged',
39+
},
2740
},
2841
},
2942
sections = {

tests/spec/lualine_spec.lua

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,22 @@ describe('Lualine', function()
2424
always_show_tabline = true,
2525
globalstatus = false,
2626
refresh = {
27-
statusline = 100,
28-
tabline = 100,
29-
winbar = 100,
27+
statusline = 1000,
28+
tabline = 1000,
29+
winbar = 1000,
30+
check = 16,
31+
events = {
32+
'WinEnter',
33+
'BufEnter',
34+
'BufWritePost',
35+
'SessionLoadPost',
36+
'FileChangedShellPost',
37+
'VimResized',
38+
'Filetype',
39+
'CursorMoved',
40+
'CursorMovedI',
41+
'ModeChanged',
42+
},
3043
},
3144
},
3245
sections = {

0 commit comments

Comments
 (0)