feat: refresh lualine based on timer + winbar support. (#736)
* feat: refresh lualine based on timer. * fix config test * fix lag on win change issue * handle errors in timer callback * feat: add winbar support Pull in winbar changes form pr #689 and adapt them Co-authored-by: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> * make winbar disapear when winbar evals empty * only update stl of curwin with globalstatus * properly clear win local stl and wbr opts * add version guards for winbar feature * only add winbar if height > 1 * fix tests? * refresh lualine on ModeChanged event * ignore floating windows for refresh * properply restore options to previous state * fix stl not updating in cmd mode + some optimizations * fix tests on <nvim-0.7 * merge status_dispatch & winbar_dispatch + winbar support for extensions * fix globalstatus option not live updating * update docs * feat: allow disabling winbar and statusline separately * fix tests * fix: winbar some times oddly throwing errors about not having space in floating windows. This implements a temporary workaround the issue(https://github.com/neovim/neovim/issues/19464) until the bug in neovim gets fixed. Co-authored-by: Diego Fujii <android.mxdiego9@gmail.com>
This commit is contained in:
parent
8d956c1825
commit
53aa3d82d9
57
README.md
57
README.md
|
@ -121,9 +121,17 @@ require('lualine').setup {
|
|||
theme = 'auto',
|
||||
component_separators = { left = '', right = ''},
|
||||
section_separators = { left = '', right = ''},
|
||||
disabled_filetypes = {},
|
||||
disabled_filetypes = {
|
||||
statusline = {},
|
||||
winbar = {},
|
||||
},
|
||||
always_divide_middle = true,
|
||||
globalstatus = false,
|
||||
refresh = {
|
||||
statusline = 1000,
|
||||
tabline = 1000,
|
||||
winbar = 1000,
|
||||
}
|
||||
},
|
||||
sections = {
|
||||
lualine_a = {'mode'},
|
||||
|
@ -142,6 +150,8 @@ require('lualine').setup {
|
|||
lualine_z = {}
|
||||
},
|
||||
tabline = {},
|
||||
winbar = {},
|
||||
inactive_winbar = {},
|
||||
extensions = {}
|
||||
}
|
||||
```
|
||||
|
@ -336,13 +346,28 @@ options = {
|
|||
theme = 'auto', -- lualine theme
|
||||
component_separators = { left = '', right = '' },
|
||||
section_separators = { left = '', right = '' },
|
||||
disabled_filetypes = {}, -- Filetypes to disable lualine for.
|
||||
disabled_filetypes = { -- Filetypes to disable lualine for.
|
||||
statusline = {}, -- only ignores the ft for statusline.
|
||||
winbar = {}, -- only ignores the ft for winbar.
|
||||
},
|
||||
|
||||
always_divide_middle = true, -- When set to true, left sections i.e. 'a','b' and 'c'
|
||||
-- can't take over the entire statusline even
|
||||
-- if neither of 'x', 'y' or 'z' are present.
|
||||
|
||||
globalstatus = false, -- enable global statusline (have a single statusline
|
||||
-- at bottom of neovim instead of one for every window).
|
||||
-- This feature is only available in neovim 0.7 and higher.
|
||||
|
||||
refresh = { -- sets how often lualine should refreash it's contents (in ms)
|
||||
statusline = 1000, -- The refresh option sets minimum time that lualine tries
|
||||
tabline = 1000, -- to maintain between refresh. It's not guarantied if situation
|
||||
winbar = 1000 -- arises that lualine needs to refresh itself before this time
|
||||
-- it'll do it.
|
||||
|
||||
-- Also you can force lualine's refresh by calling refresh function
|
||||
-- like require('lualine').refresh()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
@ -689,6 +714,34 @@ tabline = {
|
|||
}
|
||||
```
|
||||
|
||||
### Winbar
|
||||
From neovim-0.8 you can customize your winbar with lualine.
|
||||
Winbar configuration is similar to statusline.
|
||||
```lua
|
||||
winbar = {
|
||||
lualine_a = {},
|
||||
lualine_b = {},
|
||||
lualine_c = {'filename'},
|
||||
lualine_x = {},
|
||||
lualine_y = {},
|
||||
lualine_z = {}
|
||||
}
|
||||
|
||||
inactive_winbar = {
|
||||
lualine_a = {},
|
||||
lualine_b = {},
|
||||
lualine_c = {'filename'},
|
||||
lualine_x = {},
|
||||
lualine_y = {},
|
||||
lualine_z = {}
|
||||
}
|
||||
```
|
||||
Just like statusline you can separately specify winbar for active and inactive
|
||||
windows. Any lualine component can be placed in winbar. All kinds of custom
|
||||
components supported in statusline are also suported for winbar too. In general
|
||||
You can treat winbar as another lualine statusline that just appears on top
|
||||
of windows instead of at bottom.
|
||||
|
||||
#### Buffers
|
||||
|
||||
Shows currently open buffers. Like bufferline . See
|
||||
|
|
244
lua/lualine.lua
244
lua/lualine.lua
|
@ -1,5 +1,7 @@
|
|||
-- Copyright (c) 2020-2021 hoob3rt
|
||||
-- MIT license, see LICENSE for more details.
|
||||
local M = {}
|
||||
|
||||
local lualine_require = require('lualine_require')
|
||||
local modules = lualine_require.lazy_require {
|
||||
highlight = 'lualine.highlight',
|
||||
|
@ -8,9 +10,20 @@ local modules = lualine_require.lazy_require {
|
|||
utils = 'lualine.utils.utils',
|
||||
utils_notices = 'lualine.utils.notices',
|
||||
config_module = 'lualine.config',
|
||||
nvim_opts = 'lualine.utils.nvim_opts'
|
||||
}
|
||||
local config -- Stores currently applied config
|
||||
local timers = {
|
||||
stl_timer = vim.loop.new_timer(),
|
||||
tal_timer = vim.loop.new_timer(),
|
||||
wb_timer = vim.loop.new_timer(),
|
||||
}
|
||||
|
||||
-- The events on which lualine redraws itself
|
||||
local default_refresh_events = 'WinEnter,BufEnter,SessionLoadPost,FileChangedShellPost,VimResized'
|
||||
if vim.fn.has('nvim-0.7') == 1 then -- utilize ModeChanged event introduced in 0.7
|
||||
default_refresh_events = default_refresh_events..',ModeChanged'
|
||||
end
|
||||
-- Helper for apply_transitional_separators()
|
||||
--- finds first applied highlight group after str_checked in status
|
||||
---@param status string : unprocessed statusline string
|
||||
|
@ -140,7 +153,7 @@ end
|
|||
--- component objects
|
||||
---@param is_focused boolean : whether being evaluated for focused window or not
|
||||
---@return string statusline string
|
||||
local statusline = modules.utils.retry_call_wrap(function(sections, is_focused)
|
||||
local statusline = modules.utils.retry_call_wrap(function(sections, is_focused, is_winbar)
|
||||
-- The sequence sections should maintain [SECTION_SEQUENCE]
|
||||
local section_sequence = { 'a', 'b', 'c', 'x', 'y', 'z' }
|
||||
local status = {}
|
||||
|
@ -167,7 +180,7 @@ local statusline = modules.utils.retry_call_wrap(function(sections, is_focused)
|
|||
end
|
||||
end
|
||||
end
|
||||
if applied_midsection_divider == false and config.options.always_divide_middle ~= false then
|
||||
if applied_midsection_divider == false and config.options.always_divide_middle ~= false and not is_winbar then
|
||||
-- When non of section x,y,z is present
|
||||
table.insert(status, modules.highlight.format_highlight('c', is_focused) .. '%=')
|
||||
end
|
||||
|
@ -182,15 +195,10 @@ end)
|
|||
-- TODO: change this so it uses a hash table instead of iteration over list
|
||||
-- to improve redraws. Add buftype / bufname for extensions
|
||||
-- or some kind of cond ?
|
||||
local function get_extension_sections(current_ft, is_focused)
|
||||
local function get_extension_sections(current_ft, is_focused, sec_name)
|
||||
for _, extension in ipairs(config.extensions) do
|
||||
for _, filetype in ipairs(extension.filetypes) do
|
||||
if current_ft == filetype then
|
||||
if is_focused == false and extension.inactive_sections then
|
||||
return extension.inactive_sections
|
||||
end
|
||||
return extension.sections
|
||||
end
|
||||
if vim.tbl_contains(extension.filetypes, current_ft) then
|
||||
return extension[(is_focused and '' or 'inactive_') .. sec_name]
|
||||
end
|
||||
end
|
||||
return nil
|
||||
|
@ -252,63 +260,192 @@ local function setup_theme()
|
|||
autocmd lualine OptionSet background lua require'lualine'.setup()]])
|
||||
end
|
||||
|
||||
---@alias StatusDispatchSecs
|
||||
---| 'sections'
|
||||
---| 'winbar'
|
||||
--- generates lualine.statusline & lualine.winbar function
|
||||
--- creates a closer that can draw sections of sec_name.
|
||||
---@param sec_name StatusDispatchSecs
|
||||
---@return function(focused:bool):string
|
||||
local function status_dispatch(sec_name)
|
||||
return function(focused)
|
||||
local retval
|
||||
local current_ft = vim.bo.filetype
|
||||
local is_focused = focused ~= nil and focused or modules.utils.is_focused()
|
||||
if vim.tbl_contains(config.options.disabled_filetypes[(sec_name == 'sections' and 'statusline' or sec_name)],
|
||||
current_ft) then
|
||||
-- disable on specific filetypes
|
||||
return ''
|
||||
end
|
||||
local extension_sections = get_extension_sections(current_ft, is_focused, sec_name)
|
||||
if extension_sections ~= nil then
|
||||
retval = statusline(extension_sections, is_focused, sec_name == 'winbar')
|
||||
else
|
||||
retval = statusline(config[(is_focused and '' or 'inactive_')..sec_name], is_focused, sec_name == 'winbar')
|
||||
end
|
||||
return retval
|
||||
end
|
||||
end
|
||||
|
||||
---@alias LualineRefreshOptsKind
|
||||
---| 'all'
|
||||
---| 'tabpage'
|
||||
---| 'window'
|
||||
---@alias LualineRefreshOptsPlace
|
||||
---| 'statusline'
|
||||
---| 'tabline'
|
||||
---| 'winbar'
|
||||
---@class LualineRefreshOpts
|
||||
---@field kind LualineRefreshOptsKind
|
||||
---@field place LualineRefreshOptsPlace[]
|
||||
---@field trigger 'autocmd'|'timer'|'unknown'
|
||||
--- Refresh contents of lualine
|
||||
---@param opts LualineRefreshOpts
|
||||
local function refresh(opts)
|
||||
if opts == nil then
|
||||
opts = {kind = 'tabpage', place = {'statusline', 'winbar', 'tabline'}, trigger='unknown'}
|
||||
end
|
||||
|
||||
-- workaround for https://github.com/neovim/neovim/issues/19464
|
||||
if (opts.trigger == 'autocmd'
|
||||
and vim.api.nvim_win_get_height(vim.api.nvim_get_current_win()) <= 1
|
||||
and vim.tbl_contains(opts.place, 'winbar')
|
||||
) then
|
||||
local id
|
||||
for index, value in ipairs(opts.place) do
|
||||
if value == 'winbar' then
|
||||
id = index
|
||||
break
|
||||
end
|
||||
end
|
||||
table.remove(opts.place, id)
|
||||
end
|
||||
|
||||
local wins = {}
|
||||
local old_actual_curwin = vim.g.actual_curwin
|
||||
vim.g.actual_curwin = vim.api.nvim_get_current_win()
|
||||
-- gather which windows needs update
|
||||
if opts.kind == 'all' then
|
||||
if vim.tbl_contains(opts.place, 'statusline')
|
||||
or vim.tbl_contains(opts.place, 'winbar') then
|
||||
wins = vim.tbl_filter(function (win)
|
||||
return vim.fn.win_gettype(win) ~= 'popup'
|
||||
end, vim.api.nvim_list_wins())
|
||||
end
|
||||
elseif opts.kind == 'tabpage' then
|
||||
if vim.tbl_contains(opts.place, 'statusline')
|
||||
or vim.tbl_contains(opts.place, 'winbar') then
|
||||
wins = vim.tbl_filter(function (win)
|
||||
return vim.fn.win_gettype(win) ~= 'popup'
|
||||
end, vim.api.nvim_tabpage_list_wins(0))
|
||||
end
|
||||
elseif opts.kind == 'window' then
|
||||
wins = {vim.api.nvim_get_current_win()}
|
||||
end
|
||||
|
||||
-- update them
|
||||
if vim.tbl_contains(opts.place, 'statusline') then
|
||||
for _, win in ipairs(wins) do
|
||||
modules.nvim_opts.set('statusline',
|
||||
vim.api.nvim_win_call(win, M.statusline), {window=win})
|
||||
end
|
||||
end
|
||||
if vim.tbl_contains(opts.place, 'winbar') then
|
||||
for _, win in ipairs(wins) do
|
||||
if vim.api.nvim_win_get_height(win) > 1 then
|
||||
modules.nvim_opts.set('winbar',
|
||||
vim.api.nvim_win_call(win, M.winbar), {window=win})
|
||||
end
|
||||
end
|
||||
end
|
||||
if vim.tbl_contains(opts.place, 'tabline') then
|
||||
modules.nvim_opts.set('tabline',
|
||||
vim.api.nvim_win_call(vim.api.nvim_get_current_win(), tabline),
|
||||
{global=true})
|
||||
end
|
||||
|
||||
-- call redraw
|
||||
if vim.tbl_contains(opts.place, 'statusline')
|
||||
or vim.tbl_contains(opts.place, 'winbar') then
|
||||
vim.cmd('redrawstatus')
|
||||
elseif vim.tbl_contains(opts.place, 'tabline') then
|
||||
vim.cmd('redrawtabline')
|
||||
end
|
||||
|
||||
vim.g.actual_curwin = old_actual_curwin
|
||||
end
|
||||
|
||||
--- Sets &tabline option to lualine
|
||||
local function set_tabline()
|
||||
vim.loop.timer_stop(timers.tal_timer)
|
||||
vim.cmd([[augroup lualine_tal_refresh | exe "autocmd!" | augroup END]])
|
||||
if next(config.tabline) ~= nil then
|
||||
vim.go.tabline = "%{%v:lua.require'lualine'.tabline()%}"
|
||||
vim.go.showtabline = 2
|
||||
elseif vim.go.tabline == "%{%v:lua.require'lualine'.tabline()%}" then
|
||||
vim.go.tabline = ''
|
||||
vim.go.showtabline = 1
|
||||
vim.loop.timer_start(timers.tal_timer, 0, config.options.refresh.tabline,
|
||||
modules.utils.timer_call(timers.stl_timer, 'lualine_tal_refresh', function ()
|
||||
refresh({kind='tabpage', place={'tabline'}, trigger='timer'})
|
||||
end, 3, "lualine: Failed to refresh tabline"))
|
||||
modules.utils.define_autocmd(default_refresh_events,
|
||||
'*', "call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['tabline'], 'trigger': 'autocmd'})",
|
||||
'lualine_tal_refresh')
|
||||
modules.nvim_opts.set('showtabline', 2, {global=true})
|
||||
else
|
||||
modules.nvim_opts.restore('tabline', {global=true})
|
||||
modules.nvim_opts.restore('showtabline', {global=true})
|
||||
end
|
||||
end
|
||||
|
||||
--- Sets &statusline option to lualine
|
||||
--- adds auto command to redraw lualine on VimResized event
|
||||
local function set_statusline()
|
||||
vim.loop.timer_stop(timers.stl_timer)
|
||||
vim.cmd([[augroup lualine_stl_refresh | exe "autocmd!" | augroup END]])
|
||||
if next(config.sections) ~= nil or next(config.inactive_sections) ~= nil then
|
||||
vim.cmd('autocmd lualine VimResized * redrawstatus')
|
||||
vim.go.statusline = "%{%v:lua.require'lualine'.statusline()%}"
|
||||
if config.options.globalstatus then
|
||||
vim.go.laststatus = 3
|
||||
modules.nvim_opts.set('laststatus', 3, {global=true})
|
||||
vim.loop.timer_start(timers.stl_timer, 0, config.options.refresh.statusline,
|
||||
modules.utils.timer_call(timers.stl_timer, 'lualine_stl_refresh', function ()
|
||||
refresh({kind='window', place={'statusline'}, trigger='timer'})
|
||||
end, 3, "lualine: Failed to refresh statusline"))
|
||||
modules.utils.define_autocmd(default_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(timers.stl_timer, 0, config.options.refresh.statusline,
|
||||
modules.utils.timer_call(timers.stl_timer, 'lualine_stl_refresh', function ()
|
||||
refresh({kind='tabpage', place={'statusline'}, trigger='timer'})
|
||||
end, 3, "lualine: Failed to refresh statusline"))
|
||||
modules.utils.define_autocmd(default_refresh_events,
|
||||
'*', "call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['statusline'], 'trigger': 'autocmd'})",
|
||||
'lualine_stl_refresh')
|
||||
end
|
||||
elseif vim.go.statusline == "%{%v:lua.require'lualine'.statusline()%}" then
|
||||
vim.go.statusline = ''
|
||||
if config.options.globalstatus then
|
||||
vim.go.laststatus = 2
|
||||
else
|
||||
modules.nvim_opts.restore('statusline', {global=true})
|
||||
for _, win in ipairs(vim.api.nvim_list_wins()) do
|
||||
modules.nvim_opts.restore('statusline', {window=win})
|
||||
end
|
||||
modules.nvim_opts.restore('laststatus', {global=true})
|
||||
end
|
||||
end
|
||||
|
||||
-- lualine.statusline function
|
||||
--- Draw correct statusline for current window
|
||||
---@param focused boolean : force the value of is_focused . Useful for debugging
|
||||
---@return string statusline string
|
||||
local function status_dispatch(focused)
|
||||
local retval
|
||||
local current_ft = vim.bo.filetype
|
||||
local is_focused = focused ~= nil and focused or modules.utils.is_focused()
|
||||
for _, ft in pairs(config.options.disabled_filetypes) do
|
||||
-- disable on specific filetypes
|
||||
if ft == current_ft then
|
||||
return ''
|
||||
--- Sets &winbar option to lualine
|
||||
local function set_winbar()
|
||||
vim.loop.timer_stop(timers.wb_timer)
|
||||
vim.cmd([[augroup lualine_wb_refresh | exe "autocmd!" | augroup END]])
|
||||
if next(config.winbar) ~= nil or next(config.inactive_winbar) ~= nil then
|
||||
vim.loop.timer_start(timers.stl_timer, 0, config.options.refresh.winbar,
|
||||
modules.utils.timer_call(timers.stl_timer, 'lualine_wb_refresh', function ()
|
||||
refresh({kind='tabpage', place={'winbar'}, trigger='timer'})
|
||||
end, 3, "lualine: Failed to refresh winbar"))
|
||||
modules.utils.define_autocmd(default_refresh_events,
|
||||
'*', "call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['winbar'], 'trigger': 'autocmd'})",
|
||||
'lualine_wb_refresh')
|
||||
elseif vim.fn.has('nvim-0.8') == 1 then
|
||||
modules.nvim_opts.restore('winbar', {global=true})
|
||||
for _, win in ipairs(vim.api.nvim_list_wins()) do
|
||||
modules.nvim_opts.restore('winbar', {window=win})
|
||||
end
|
||||
end
|
||||
local extension_sections = get_extension_sections(current_ft, is_focused)
|
||||
if is_focused then
|
||||
if extension_sections ~= nil then
|
||||
retval = statusline(extension_sections, is_focused)
|
||||
else
|
||||
retval = statusline(config.sections, is_focused)
|
||||
end
|
||||
else
|
||||
if extension_sections ~= nil then
|
||||
retval = statusline(extension_sections, is_focused)
|
||||
else
|
||||
retval = statusline(config.inactive_sections, is_focused)
|
||||
end
|
||||
end
|
||||
return retval
|
||||
end
|
||||
|
||||
-- lualine.setup function
|
||||
|
@ -332,14 +469,19 @@ local function setup(user_config)
|
|||
modules.loader.load_all(config)
|
||||
set_statusline()
|
||||
set_tabline()
|
||||
set_winbar()
|
||||
if package.loaded['lualine.utils.notices'] then
|
||||
modules.utils_notices.notice_message_startup()
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
M = {
|
||||
setup = setup,
|
||||
statusline = status_dispatch,
|
||||
statusline = status_dispatch('sections'),
|
||||
tabline = tabline,
|
||||
get_config = modules.config_module.get_config,
|
||||
refresh = refresh,
|
||||
winbar = status_dispatch('winbar'),
|
||||
}
|
||||
|
||||
return M
|
||||
|
|
|
@ -12,9 +12,17 @@ local config = {
|
|||
theme = 'auto',
|
||||
component_separators = { left = '', right = '' },
|
||||
section_separators = { left = '', right = '' },
|
||||
disabled_filetypes = {},
|
||||
disabled_filetypes = {
|
||||
statusline = {},
|
||||
winbar = {}
|
||||
},
|
||||
always_divide_middle = true,
|
||||
globalstatus = false,
|
||||
globalstatus = vim.go.laststatus == 3,
|
||||
refresh = {
|
||||
statusline = 1000,
|
||||
tabline = 1000,
|
||||
winbar = 1000,
|
||||
}
|
||||
},
|
||||
sections = {
|
||||
lualine_a = { 'mode' },
|
||||
|
@ -33,6 +41,8 @@ local config = {
|
|||
lualine_z = {},
|
||||
},
|
||||
tabline = {},
|
||||
winbar = {},
|
||||
inactive_winbar = {},
|
||||
extensions = {},
|
||||
}
|
||||
|
||||
|
@ -48,6 +58,24 @@ local function fix_separators(separators)
|
|||
return separators
|
||||
end
|
||||
|
||||
---copy raw disabled_filetypes to inner statusline & winbar tables.
|
||||
---@param disabled_filetypes table
|
||||
---@return table
|
||||
local function fix_disabled_filetypes(disabled_filetypes)
|
||||
if disabled_filetypes == nil then return end
|
||||
if disabled_filetypes.statusline == nil then
|
||||
disabled_filetypes.statusline = {}
|
||||
end
|
||||
if disabled_filetypes.winbar == nil then
|
||||
disabled_filetypes.winbar = {}
|
||||
end
|
||||
for k, disabled_ft in ipairs(disabled_filetypes) do
|
||||
table.insert(disabled_filetypes.statusline, disabled_ft)
|
||||
table.insert(disabled_filetypes.winbar, disabled_ft)
|
||||
disabled_filetypes[k] = nil
|
||||
end
|
||||
return disabled_filetypes
|
||||
end
|
||||
---extends config based on config_table
|
||||
---@param config_table table
|
||||
---@return table copy of config
|
||||
|
@ -73,15 +101,25 @@ local function apply_configuration(config_table)
|
|||
)
|
||||
config_table.options.globalstatus = false
|
||||
end
|
||||
if vim.fn.has('nvim-0.8') == 0 and (next(config_table.winbar or {}) or next(config_table.inactive_winbar or {})) then
|
||||
modules.utils_notices.add_notice(
|
||||
'### winbar\nSorry `winbar can only be used in neovim 0.8 or higher.\n'
|
||||
)
|
||||
config_table.winbar = {}
|
||||
config_table.inactive_winbar = {}
|
||||
end
|
||||
parse_sections('options')
|
||||
parse_sections('sections')
|
||||
parse_sections('inactive_sections')
|
||||
parse_sections('tabline')
|
||||
parse_sections('winbar')
|
||||
parse_sections('inactive_winbar')
|
||||
if config_table.extensions then
|
||||
config.extensions = utils.deepcopy(config_table.extensions)
|
||||
end
|
||||
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)
|
||||
return utils.deepcopy(config)
|
||||
end
|
||||
|
||||
|
|
|
@ -154,29 +154,22 @@ end
|
|||
---loads all the configs (active, inactive, tabline)
|
||||
---@param config table user config
|
||||
local function load_components(config)
|
||||
load_sections(config.sections, config.options)
|
||||
load_sections(config.inactive_sections, config.options)
|
||||
load_sections(config.tabline, config.options)
|
||||
local sec_names = {'sections', 'inactive_sections', 'tabline', 'winbar', 'inactive_winbar'}
|
||||
for _, section in ipairs(sec_names) do
|
||||
load_sections(config[section], config.options)
|
||||
end
|
||||
end
|
||||
|
||||
---loads all the extensions
|
||||
---@param config table user config
|
||||
local function load_extensions(config)
|
||||
local loaded_extensions = {}
|
||||
local sec_names = {'sections', 'inactive_sections', 'winbar', 'inactive_winbar'}
|
||||
for _, extension in pairs(config.extensions) do
|
||||
if type(extension) == 'string' then
|
||||
local ok, local_extension = pcall(require, 'lualine.extensions.' .. extension)
|
||||
if ok then
|
||||
local_extension = modules.utils.deepcopy(local_extension)
|
||||
load_sections(local_extension.sections, config.options)
|
||||
if local_extension.inactive_sections then
|
||||
load_sections(local_extension.inactive_sections, config.options)
|
||||
end
|
||||
if type(local_extension.init) == 'function' then
|
||||
local_extension.init()
|
||||
end
|
||||
table.insert(loaded_extensions, local_extension)
|
||||
else
|
||||
local ok
|
||||
ok, extension = pcall(require, 'lualine.extensions.' .. extension)
|
||||
if not ok then
|
||||
modules.notice.add_notice(string.format(
|
||||
[[
|
||||
### Extensions
|
||||
|
@ -185,11 +178,13 @@ Extension named `%s` was not found . Check if spelling is correct.
|
|||
extension
|
||||
))
|
||||
end
|
||||
elseif type(extension) == 'table' then
|
||||
end
|
||||
if type(extension) == 'table' then
|
||||
local local_extension = modules.utils.deepcopy(extension)
|
||||
load_sections(local_extension.sections, config.options)
|
||||
if local_extension.inactive_sections then
|
||||
load_sections(local_extension.inactive_sections, config.options)
|
||||
for _, section in ipairs(sec_names) do
|
||||
if local_extension[section] then
|
||||
load_sections(local_extension[section], config.options)
|
||||
end
|
||||
end
|
||||
if type(local_extension.init) == 'function' then
|
||||
local_extension.init()
|
||||
|
@ -205,6 +200,7 @@ end
|
|||
local function load_all(config)
|
||||
require('lualine.component')._reset_components()
|
||||
modules.fn_store.clear_fns()
|
||||
require('lualine.utils.nvim_opts').reset_cache()
|
||||
load_components(config)
|
||||
load_extensions(config)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,126 @@
|
|||
local M = {}
|
||||
|
||||
-- keeps backup of options that we cahge so we can restore it.
|
||||
-- format:
|
||||
-- options {
|
||||
-- global = <1> {
|
||||
-- name = {prev, set}
|
||||
-- },
|
||||
-- buffer = {
|
||||
-- buf1 = <1>,
|
||||
-- buf2 = <1>
|
||||
-- },
|
||||
-- window = {
|
||||
-- win1 = <1>,
|
||||
-- win2 = <1>
|
||||
-- }
|
||||
-- }
|
||||
---@class LualineNvimOptCacheOptStore
|
||||
---@field prev any
|
||||
---@field set any
|
||||
---@alias LualineNvimOptCacheOpt table<string, LualineNvimOptCacheOptStore>
|
||||
---@class LualineNvimOptCache
|
||||
---@field global LualineNvimOptCacheOpt[]
|
||||
---@field buffer table<number, LualineNvimOptCacheOpt[]>
|
||||
---@field window table<number, LualineNvimOptCacheOpt[]>
|
||||
---@type LualineNvimOptCache
|
||||
local options = {global={}, buffer={}, window={}}
|
||||
|
||||
-- helper function for M.set
|
||||
local function set_opt(name, val, getter_fn, setter_fn, cache_tbl)
|
||||
-- before nvim 0.7 nvim_win_get_option... didn't return default value when
|
||||
-- the option wasn't set instead threw error.
|
||||
-- So we need pcall (probably just for test)
|
||||
local ok, cur = pcall(getter_fn, name)
|
||||
if not ok then cur = nil end
|
||||
if cur == val then return end
|
||||
if cache_tbl[name] == nil then cache_tbl[name] = {} end
|
||||
if cache_tbl[name].set ~= cur then
|
||||
cache_tbl[name].prev = cur
|
||||
end
|
||||
cache_tbl[name].set = val
|
||||
setter_fn(name, val)
|
||||
end
|
||||
|
||||
-- set a option value
|
||||
---@param name string
|
||||
---@param val any
|
||||
---@param opts table|nil when table can be {global=true | buffer = bufnr | window = winnr}
|
||||
--- when nil it's treated as {global = true}
|
||||
function M.set(name, val, opts)
|
||||
if opts == nil or opts.global then
|
||||
set_opt(name, val, vim.api.nvim_get_option, vim.api.nvim_set_option, options.global)
|
||||
elseif opts.buffer then
|
||||
if options.buffer[opts.buffer] == nil then
|
||||
options.buffer[opts.buffer] = {}
|
||||
end
|
||||
set_opt(name, val, function (nm)
|
||||
return vim.api.nvim_buf_get_option(opts.buffer, nm)
|
||||
end, function (nm, vl)
|
||||
vim.api.nvim_buf_set_option(opts.buffer, nm, vl)
|
||||
end, options.buffer[opts.buffer])
|
||||
elseif opts.window then
|
||||
if options.window[opts.window] == nil then
|
||||
options.window[opts.window] = {}
|
||||
end
|
||||
set_opt(name, val, function (nm)
|
||||
return vim.api.nvim_win_get_option(opts.window, nm)
|
||||
end, function (nm, vl)
|
||||
vim.api.nvim_win_set_option(opts.window, nm, vl)
|
||||
end, options.window[opts.window])
|
||||
end
|
||||
end
|
||||
|
||||
-- resoters old value of option name
|
||||
---@param name string
|
||||
---@param opts table|nil same as M.set
|
||||
function M.restore(name, opts)
|
||||
if opts == nil or opts.global then
|
||||
if options.global[name] ~= nil and options.global[name].prev ~= nil then
|
||||
vim.api.nvim_set_option(name, options.global[name].prev)
|
||||
end
|
||||
elseif opts.buffer then
|
||||
if options.buffer[opts.buffer] ~= nil
|
||||
and options.buffer[opts.buffer][name] ~= nil
|
||||
and options.buffer[opts.buffer][name].prev ~= nil then
|
||||
vim.api.nvim_buf_set_option(opts.buffer, name, options.buffer[opts.buffer][name].prev)
|
||||
end
|
||||
elseif opts.window then
|
||||
if options.window[opts.window] ~= nil
|
||||
and options.window[opts.window][name] ~= nil
|
||||
and options.window[opts.window][name].prev ~= nil then
|
||||
vim.api.nvim_win_set_option(opts.window, name, options.window[opts.window][name].prev)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- returns cache for the option name
|
||||
---@param name string
|
||||
---@param opts table|nil same as M.set
|
||||
function M.get_cache(name, opts)
|
||||
if opts == nil or opts.global then
|
||||
if options.global[name] ~= nil and options.global[name].prev ~= nil then
|
||||
return options.global[name].prev
|
||||
end
|
||||
elseif opts.buffer then
|
||||
if options.buffer[opts.buffer] ~= nil
|
||||
and options.buffer[opts.buffer][name] ~= nil
|
||||
and options.buffer[opts.buffer][name].prev ~= nil then
|
||||
return options.buffer[opts.buffer][name].prev
|
||||
end
|
||||
elseif opts.window then
|
||||
if options.window[opts.window] ~= nil
|
||||
and options.window[opts.window][name] ~= nil
|
||||
and options.window[opts.window][name].prev ~= nil then
|
||||
return options.window[opts.window][name].prev
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- resets cache for options
|
||||
function M.reset_cache()
|
||||
options = {global={}, buffer={}, window={}}
|
||||
end
|
||||
|
||||
return M
|
|
@ -81,13 +81,14 @@ end
|
|||
---@param event string event name
|
||||
---@param pattern string event pattern
|
||||
---@param cmd string command to run on event
|
||||
function M.define_autocmd(event, pattern, cmd)
|
||||
---@param group string group name defaults to lualine
|
||||
function M.define_autocmd(event, pattern, cmd, group)
|
||||
if not cmd then
|
||||
cmd = pattern
|
||||
pattern = '*'
|
||||
end
|
||||
if not autocmd_is_defined(event, pattern, cmd) then
|
||||
vim.cmd(string.format('autocmd lualine %s %s %s', event, pattern, cmd))
|
||||
vim.cmd(string.format('autocmd %s %s %s %s', group or 'lualine', event, pattern, cmd))
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -183,4 +184,34 @@ function M.stl_escape(str)
|
|||
return str:gsub('%%', '%%%%')
|
||||
end
|
||||
|
||||
---A safe call inside a timmer
|
||||
---@param timer userdata
|
||||
---@param augroup string|nil autocmd group to reset too on error.
|
||||
---@param fn function
|
||||
---@param max_err integer
|
||||
---@param err_msg string
|
||||
---@return function a wraped fn that can be called inside a timer and that
|
||||
---stops the timer after max_err errors in calling fn
|
||||
function M.timer_call(timer, augroup, fn, max_err, err_msg)
|
||||
local err_cnt, ret = 0, nil
|
||||
max_err = max_err or 3
|
||||
return vim.schedule_wrap(function(...)
|
||||
if err_cnt > max_err then
|
||||
vim.loop.timer_stop(timer)
|
||||
if augroup then
|
||||
vim.cmd(string.format([[augroup %s | exe "autocmd!" | augroup END]], augroup))
|
||||
end
|
||||
error(err_msg..':\n'..tostring(ret))
|
||||
end
|
||||
local ok
|
||||
ok, ret = pcall(fn, ...)
|
||||
if ok then
|
||||
err_cnt = 0
|
||||
else
|
||||
err_cnt = err_cnt + 1
|
||||
end
|
||||
return ret
|
||||
end)
|
||||
end
|
||||
|
||||
return M
|
||||
|
|
|
@ -86,12 +86,12 @@ describe('config parsing', function()
|
|||
describe('disabled filetypes', function()
|
||||
it('default', function()
|
||||
local config = config_module.apply_configuration {}
|
||||
eq(config.options.disabled_filetypes, {})
|
||||
eq(config.options.disabled_filetypes, {statusline={}, winbar={}})
|
||||
end)
|
||||
it('custom', function()
|
||||
local config = { options = { disabled_filetypes = { 'lua' } } }
|
||||
config = config_module.apply_configuration(config)
|
||||
eq(config.options.disabled_filetypes, { 'lua' })
|
||||
eq(config.options.disabled_filetypes, {statusline={'lua'}, winbar={'lua'}})
|
||||
end)
|
||||
end)
|
||||
|
||||
|
|
|
@ -15,9 +15,17 @@ describe('Lualine', function()
|
|||
theme = 'gruvbox',
|
||||
component_separators = { left = '', right = '' },
|
||||
section_separators = { left = '', right = '' },
|
||||
disabled_filetypes = {},
|
||||
disabled_filetypes = {
|
||||
statusline = {},
|
||||
winbar = {},
|
||||
},
|
||||
always_divide_middle = true,
|
||||
globalstatus = false,
|
||||
refresh = {
|
||||
statusline = 1000,
|
||||
tabline = 1000,
|
||||
winbar = 1000,
|
||||
}
|
||||
},
|
||||
sections = {
|
||||
lualine_a = { 'mode' },
|
||||
|
@ -48,6 +56,8 @@ describe('Lualine', function()
|
|||
lualine_z = {},
|
||||
},
|
||||
tabline = {},
|
||||
winbar = {},
|
||||
inactive_winbar = {},
|
||||
extensions = {},
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue