Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 48 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,22 @@ require('lualine').setup {
always_show_tabline = true,
globalstatus = false,
refresh = {
statusline = 100,
tabline = 100,
winbar = 100,
statusline = 1000,
tabline = 1000,
winbar = 1000,
refresh_time = 16, -- ~60fps
events = {
'WinEnter',
'BufEnter',
'BufWritePost',
'SessionLoadPost',
'FileChangedShellPost',
'VimResized',
'Filetype',
'CursorMoved',
'CursorMovedI',
'ModeChanged',
},
}
},
sections = {
Expand Down Expand Up @@ -400,8 +413,20 @@ options = {
statusline = 100, -- The refresh option sets minimum time that lualine tries
tabline = 100, -- to maintain between refresh. It's not guarantied if situation
winbar = 100 -- arises that lualine needs to refresh itself before this time
-- it'll do it.

-- it'll do it.
refresh_time = 16, -- ~60fps the time after which refresh queue is processed. Mininum refreshtime for lualine
events = { -- The auto command events at which lualine refreshes
'WinEnter',
'BufEnter',
'BufWritePost',
'SessionLoadPost',
'FileChangedShellPost',
'VimResized',
'Filetype',
'CursorMoved',
'CursorMovedI',
'ModeChanged',
},
-- Also you can force lualine's refresh by calling refresh function
-- like require('lualine').refresh()
}
Expand Down Expand Up @@ -1014,8 +1039,24 @@ So you can simply do
require('lualine').refresh()
```

Avoid calling `lualine.refresh` inside components. Since components are evaluated
during refresh, calling refresh while refreshing can have undesirable effects.
Also, note by default when you call refresh a refresh event is queued in lualine.
It desn't refresh event immidiately. It'll refresh on next refresh check pass.
By default this time is set to 16ms to match 60fps. This duration can be configured
with `options.refresh.refresh_time` option. If you want to bypass the refresh queue
and want lualine to process the refresh immmidiately call refresh with `force=true`
parameter set like this.
```lua
require('lualine').refresh({
force = true, -- do an immidiate refresh
scope = 'tabpage', -- scope of refresh all/tabpage/window
place = { 'statusline', 'winbar', 'tabline' }, -- lualine segment ro refresh.
})
```
Practically, speaking this is almost never needed. Also you should avoid calling
`lualine.refresh` with `force` inside components. Since components are
evaluated during refresh, calling refresh while refreshing can have undesirable
effects.


### Disabling lualine

Expand Down
81 changes: 80 additions & 1 deletion lua/lualine.lua
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ local timers = {
stl_timer = vim.loop.new_timer(),
tal_timer = vim.loop.new_timer(),
wb_timer = vim.loop.new_timer(),
refresh_check_timer = vim.loop.new_timer(),
halt_stl_refresh = false, -- mutex ?
halt_tal_refresh = false,
halt_wb_refresh = false,
Expand Down Expand Up @@ -333,6 +334,13 @@ local function should_ignore_focus(user_config, win)
end
end

local refresh_event_queue = {
has_events = false,
statusline = {},
tabline = {},
winbar = {},
}

---@alias LualineRefreshOptsKind
---| 'all'
---| 'tabpage'
Expand All @@ -344,7 +352,9 @@ end
---@class LualineRefreshOpts
---@field scope LualineRefreshOptsKind
---@field place LualineRefreshOptsPlace[]
---@field trigger 'timer' | 'init' |'unknown'
---@field trigger 'timer' | 'init' | 'autocmd' |'unknown'
---@field queued boolean the refresh even was queue and queue is now being processed
---@field force boolean force refresh now instead of queuing
--- Refresh contents of lualine
---@param opts LualineRefreshOpts
local function refresh(opts)
Expand All @@ -357,6 +367,14 @@ local function refresh(opts)
trigger = 'unknown',
})

if not opts.queued and not opts.force then
for _, place in ipairs(opts.place) do
refresh_event_queue['has_events'] = true
refresh_event_queue[place] = vim.tbl_extend('force', opts, { place = { place }, queued = true })
end
return
end

local wins = {}
local old_actual_curwin = vim.g.actual_curwin

Expand Down Expand Up @@ -449,6 +467,7 @@ end
local function set_tabline(hide)
vim.loop.timer_stop(timers.tal_timer)
timers.halt_tal_refresh = true
vim.cmd([[augroup lualine_tal_refresh | exe "autocmd!" | augroup END]])
if not hide and next(config.tabline) ~= nil then
vim.loop.timer_start(
timers.tal_timer,
Expand All @@ -458,6 +477,13 @@ local function set_tabline(hide)
refresh { scope = 'tabpage', place = { 'tabline' }, trigger = 'timer' }
end, 3, 'lualine: Failed to refresh tabline')
)
modules.utils.define_autocmd(
table.concat(config.options.refresh.events, ','),
'*',
"call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['tabline'], 'trigger': 'autocmd'})",
'lualine_tal_refresh'
)

modules.nvim_opts.set('showtabline', config.options.always_show_tabline and 2 or 1, { global = true })
timers.halt_tal_refresh = false
vim.schedule(function()
Expand All @@ -471,12 +497,43 @@ local function set_tabline(hide)
end
end

local function check_refresh()
if not refresh_event_queue.has_events then
return
end
refresh_event_queue.has_events = nil

for place, refresh_cmd in pairs(refresh_event_queue) do
if type(refresh_cmd) == 'table' and refresh_cmd.queued == true then
refresh(refresh_cmd)
refresh_event_queue[place] = {}
end
end
end

local function set_refresh_checker()
vim.loop.timer_stop(timers.refresh_check_timer)
vim.loop.timer_start(
timers.refresh_check_timer,
0,
config.options.refresh.refresh_time,
modules.utils.timer_call(
timers.refresh_check_timer,
'lualine_refresh_check',
check_refresh,
3,
'lualine: Failed to refresh statusline'
)
)
end

--- Sets &statusline option to lualine
--- adds auto command to redraw lualine on VimResized event
---@param hide boolean|nil if should hide statusline
local function set_statusline(hide)
vim.loop.timer_stop(timers.stl_timer)
timers.halt_stl_refresh = true
vim.cmd([[augroup lualine_stl_refresh | exe "autocmd!" | augroup END]])
if not hide and (next(config.sections) ~= nil or next(config.inactive_sections) ~= nil) then
modules.nvim_opts.set('statusline', '%#lualine_transparent#', { global = true })
if config.options.globalstatus then
Expand All @@ -489,6 +546,12 @@ local function set_statusline(hide)
refresh { scope = 'window', place = { 'statusline' }, trigger = 'timer' }
end, 3, 'lualine: Failed to refresh statusline')
)
modules.utils.define_autocmd(
table.concat(config.options.refresh.events, ','),
'*',
"call v:lua.require'lualine'.refresh({'kind': 'window', 'place': ['statusline'], 'trigger': 'autocmd'})",
'lualine_stl_refresh'
)
else
modules.nvim_opts.set('laststatus', 2, { global = true })
vim.loop.timer_start(
Expand All @@ -499,6 +562,13 @@ local function set_statusline(hide)
refresh { scope = 'tabpage', place = { 'statusline' }, trigger = 'timer' }
end, 3, 'lualine: Failed to refresh statusline')
)

modules.utils.define_autocmd(
table.concat(config.options.refresh.events, ','),
'*',
"call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['statusline'], 'trigger': 'autocmd'})",
'lualine_stl_refresh'
)
end
timers.halt_stl_refresh = false
vim.schedule(function()
Expand All @@ -524,6 +594,7 @@ end
local function set_winbar(hide)
vim.loop.timer_stop(timers.wb_timer)
timers.halt_wb_refresh = true
vim.cmd([[augroup lualine_wb_refresh | exe "autocmd!" | augroup END]])
if not hide and (next(config.winbar) ~= nil or next(config.inactive_winbar) ~= nil) then
vim.loop.timer_start(
timers.wb_timer,
Expand All @@ -533,6 +604,13 @@ local function set_winbar(hide)
refresh { scope = 'tabpage', place = { 'winbar' }, trigger = 'timer' }
end, 3, 'lualine: Failed to refresh winbar')
)
modules.utils.define_autocmd(
table.concat(config.options.refresh.events, ','),
'*',
"call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['winbar'], 'trigger': 'autocmd'})",
'lualine_wb_refresh'
)

timers.halt_wb_refresh = false
vim.schedule(function()
-- imediately refresh upon load.
Expand Down Expand Up @@ -613,6 +691,7 @@ local function setup(user_config)
set_statusline()
set_tabline()
set_winbar()
set_refresh_checker()
end
if package.loaded['lualine.utils.notices'] then
modules.utils_notices.notice_message_startup()
Expand Down
31 changes: 28 additions & 3 deletions lua/lualine/config.lua
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,22 @@ local config = {
always_show_tabline = true,
globalstatus = vim.go.laststatus == 3,
refresh = {
statusline = 100,
tabline = 100,
winbar = 100,
statusline = 1000,
tabline = 1000,
winbar = 1000,
refresh_time = 16, -- ~60fps
events = {
'WinEnter',
'BufEnter',
'BufWritePost',
'SessionLoadPost',
'FileChangedShellPost',
'VimResized',
'Filetype',
'CursorMoved',
'CursorMovedI',
'ModeChanged',
},
},
},
sections = {
Expand All @@ -48,6 +61,17 @@ local config = {
extensions = {},
}

--- Ensure sure user config doesn't disable lualine refresh completely
---@param refresh_options table | nil
---@return table | nil
local function fix_refresh_timer(refresh_options)
if refresh_options == nil then return end
if refresh_options.refresh_time <= 0 then
refresh_options.refresh_time = 16
end
return refresh_options
end

--- change separator format 'x' to {left='x', right='x'}
---@param separators string|table
---@return table
Expand Down Expand Up @@ -118,6 +142,7 @@ local function apply_configuration(config_table)
if config_table.extensions then
config.extensions = utils.deepcopy(config_table.extensions)
end
config.options.refresh = fix_refresh_timer(config.options.refresh)
config.options.section_separators = fix_separators(config.options.section_separators)
config.options.component_separators = fix_separators(config.options.component_separators)
config.options.disabled_filetypes = fix_disabled_filetypes(config.options.disabled_filetypes)
Expand Down
19 changes: 16 additions & 3 deletions tests/spec/lualine_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,22 @@ describe('Lualine', function()
always_show_tabline = true,
globalstatus = false,
refresh = {
statusline = 100,
tabline = 100,
winbar = 100,
statusline = 1000,
tabline = 1000,
winbar = 1000,
refresh_time = 16, -- ~60fps
events = {
'WinEnter',
'BufEnter',
'BufWritePost',
'SessionLoadPost',
'FileChangedShellPost',
'VimResized',
'Filetype',
'CursorMoved',
'CursorMovedI',
'ModeChanged',
},
},
},
sections = {
Expand Down