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',
|
theme = 'auto',
|
||||||
component_separators = { left = '', right = ''},
|
component_separators = { left = '', right = ''},
|
||||||
section_separators = { left = '', right = ''},
|
section_separators = { left = '', right = ''},
|
||||||
disabled_filetypes = {},
|
disabled_filetypes = {
|
||||||
|
statusline = {},
|
||||||
|
winbar = {},
|
||||||
|
},
|
||||||
always_divide_middle = true,
|
always_divide_middle = true,
|
||||||
globalstatus = false,
|
globalstatus = false,
|
||||||
|
refresh = {
|
||||||
|
statusline = 1000,
|
||||||
|
tabline = 1000,
|
||||||
|
winbar = 1000,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
sections = {
|
sections = {
|
||||||
lualine_a = {'mode'},
|
lualine_a = {'mode'},
|
||||||
|
@ -142,6 +150,8 @@ require('lualine').setup {
|
||||||
lualine_z = {}
|
lualine_z = {}
|
||||||
},
|
},
|
||||||
tabline = {},
|
tabline = {},
|
||||||
|
winbar = {},
|
||||||
|
inactive_winbar = {},
|
||||||
extensions = {}
|
extensions = {}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -336,13 +346,28 @@ options = {
|
||||||
theme = 'auto', -- lualine theme
|
theme = 'auto', -- lualine theme
|
||||||
component_separators = { left = '', right = '' },
|
component_separators = { left = '', right = '' },
|
||||||
section_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'
|
always_divide_middle = true, -- When set to true, left sections i.e. 'a','b' and 'c'
|
||||||
-- can't take over the entire statusline even
|
-- can't take over the entire statusline even
|
||||||
-- if neither of 'x', 'y' or 'z' are present.
|
-- if neither of 'x', 'y' or 'z' are present.
|
||||||
|
|
||||||
globalstatus = false, -- enable global statusline (have a single statusline
|
globalstatus = false, -- enable global statusline (have a single statusline
|
||||||
-- at bottom of neovim instead of one for every window).
|
-- at bottom of neovim instead of one for every window).
|
||||||
-- This feature is only available in neovim 0.7 and higher.
|
-- 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
|
#### Buffers
|
||||||
|
|
||||||
Shows currently open buffers. Like bufferline . See
|
Shows currently open buffers. Like bufferline . See
|
||||||
|
|
244
lua/lualine.lua
244
lua/lualine.lua
|
@ -1,5 +1,7 @@
|
||||||
-- Copyright (c) 2020-2021 hoob3rt
|
-- Copyright (c) 2020-2021 hoob3rt
|
||||||
-- MIT license, see LICENSE for more details.
|
-- MIT license, see LICENSE for more details.
|
||||||
|
local M = {}
|
||||||
|
|
||||||
local lualine_require = require('lualine_require')
|
local lualine_require = require('lualine_require')
|
||||||
local modules = lualine_require.lazy_require {
|
local modules = lualine_require.lazy_require {
|
||||||
highlight = 'lualine.highlight',
|
highlight = 'lualine.highlight',
|
||||||
|
@ -8,9 +10,20 @@ local modules = lualine_require.lazy_require {
|
||||||
utils = 'lualine.utils.utils',
|
utils = 'lualine.utils.utils',
|
||||||
utils_notices = 'lualine.utils.notices',
|
utils_notices = 'lualine.utils.notices',
|
||||||
config_module = 'lualine.config',
|
config_module = 'lualine.config',
|
||||||
|
nvim_opts = 'lualine.utils.nvim_opts'
|
||||||
}
|
}
|
||||||
local config -- Stores currently applied config
|
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()
|
-- Helper for apply_transitional_separators()
|
||||||
--- finds first applied highlight group after str_checked in status
|
--- finds first applied highlight group after str_checked in status
|
||||||
---@param status string : unprocessed statusline string
|
---@param status string : unprocessed statusline string
|
||||||
|
@ -140,7 +153,7 @@ end
|
||||||
--- component objects
|
--- component objects
|
||||||
---@param is_focused boolean : whether being evaluated for focused window or not
|
---@param is_focused boolean : whether being evaluated for focused window or not
|
||||||
---@return string statusline string
|
---@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]
|
-- The sequence sections should maintain [SECTION_SEQUENCE]
|
||||||
local section_sequence = { 'a', 'b', 'c', 'x', 'y', 'z' }
|
local section_sequence = { 'a', 'b', 'c', 'x', 'y', 'z' }
|
||||||
local status = {}
|
local status = {}
|
||||||
|
@ -167,7 +180,7 @@ local statusline = modules.utils.retry_call_wrap(function(sections, is_focused)
|
||||||
end
|
end
|
||||||
end
|
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
|
-- When non of section x,y,z is present
|
||||||
table.insert(status, modules.highlight.format_highlight('c', is_focused) .. '%=')
|
table.insert(status, modules.highlight.format_highlight('c', is_focused) .. '%=')
|
||||||
end
|
end
|
||||||
|
@ -182,15 +195,10 @@ end)
|
||||||
-- TODO: change this so it uses a hash table instead of iteration over list
|
-- TODO: change this so it uses a hash table instead of iteration over list
|
||||||
-- to improve redraws. Add buftype / bufname for extensions
|
-- to improve redraws. Add buftype / bufname for extensions
|
||||||
-- or some kind of cond ?
|
-- 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 _, extension in ipairs(config.extensions) do
|
||||||
for _, filetype in ipairs(extension.filetypes) do
|
if vim.tbl_contains(extension.filetypes, current_ft) then
|
||||||
if current_ft == filetype then
|
return extension[(is_focused and '' or 'inactive_') .. sec_name]
|
||||||
if is_focused == false and extension.inactive_sections then
|
|
||||||
return extension.inactive_sections
|
|
||||||
end
|
|
||||||
return extension.sections
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
return nil
|
return nil
|
||||||
|
@ -252,63 +260,192 @@ local function setup_theme()
|
||||||
autocmd lualine OptionSet background lua require'lualine'.setup()]])
|
autocmd lualine OptionSet background lua require'lualine'.setup()]])
|
||||||
end
|
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
|
--- Sets &tabline option to lualine
|
||||||
local function set_tabline()
|
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
|
if next(config.tabline) ~= nil then
|
||||||
vim.go.tabline = "%{%v:lua.require'lualine'.tabline()%}"
|
vim.loop.timer_start(timers.tal_timer, 0, config.options.refresh.tabline,
|
||||||
vim.go.showtabline = 2
|
modules.utils.timer_call(timers.stl_timer, 'lualine_tal_refresh', function ()
|
||||||
elseif vim.go.tabline == "%{%v:lua.require'lualine'.tabline()%}" then
|
refresh({kind='tabpage', place={'tabline'}, trigger='timer'})
|
||||||
vim.go.tabline = ''
|
end, 3, "lualine: Failed to refresh tabline"))
|
||||||
vim.go.showtabline = 1
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Sets &statusline option to lualine
|
--- Sets &statusline option to lualine
|
||||||
--- adds auto command to redraw lualine on VimResized event
|
--- adds auto command to redraw lualine on VimResized event
|
||||||
local function set_statusline()
|
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
|
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
|
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
|
end
|
||||||
elseif vim.go.statusline == "%{%v:lua.require'lualine'.statusline()%}" then
|
else
|
||||||
vim.go.statusline = ''
|
modules.nvim_opts.restore('statusline', {global=true})
|
||||||
if config.options.globalstatus then
|
for _, win in ipairs(vim.api.nvim_list_wins()) do
|
||||||
vim.go.laststatus = 2
|
modules.nvim_opts.restore('statusline', {window=win})
|
||||||
end
|
end
|
||||||
|
modules.nvim_opts.restore('laststatus', {global=true})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- lualine.statusline function
|
--- Sets &winbar option to lualine
|
||||||
--- Draw correct statusline for current window
|
local function set_winbar()
|
||||||
---@param focused boolean : force the value of is_focused . Useful for debugging
|
vim.loop.timer_stop(timers.wb_timer)
|
||||||
---@return string statusline string
|
vim.cmd([[augroup lualine_wb_refresh | exe "autocmd!" | augroup END]])
|
||||||
local function status_dispatch(focused)
|
if next(config.winbar) ~= nil or next(config.inactive_winbar) ~= nil then
|
||||||
local retval
|
vim.loop.timer_start(timers.stl_timer, 0, config.options.refresh.winbar,
|
||||||
local current_ft = vim.bo.filetype
|
modules.utils.timer_call(timers.stl_timer, 'lualine_wb_refresh', function ()
|
||||||
local is_focused = focused ~= nil and focused or modules.utils.is_focused()
|
refresh({kind='tabpage', place={'winbar'}, trigger='timer'})
|
||||||
for _, ft in pairs(config.options.disabled_filetypes) do
|
end, 3, "lualine: Failed to refresh winbar"))
|
||||||
-- disable on specific filetypes
|
modules.utils.define_autocmd(default_refresh_events,
|
||||||
if ft == current_ft then
|
'*', "call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['winbar'], 'trigger': 'autocmd'})",
|
||||||
return ''
|
'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
|
||||||
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
|
end
|
||||||
|
|
||||||
-- lualine.setup function
|
-- lualine.setup function
|
||||||
|
@ -332,14 +469,19 @@ local function setup(user_config)
|
||||||
modules.loader.load_all(config)
|
modules.loader.load_all(config)
|
||||||
set_statusline()
|
set_statusline()
|
||||||
set_tabline()
|
set_tabline()
|
||||||
|
set_winbar()
|
||||||
if package.loaded['lualine.utils.notices'] then
|
if package.loaded['lualine.utils.notices'] then
|
||||||
modules.utils_notices.notice_message_startup()
|
modules.utils_notices.notice_message_startup()
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
M = {
|
||||||
setup = setup,
|
setup = setup,
|
||||||
statusline = status_dispatch,
|
statusline = status_dispatch('sections'),
|
||||||
tabline = tabline,
|
tabline = tabline,
|
||||||
get_config = modules.config_module.get_config,
|
get_config = modules.config_module.get_config,
|
||||||
|
refresh = refresh,
|
||||||
|
winbar = status_dispatch('winbar'),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return M
|
||||||
|
|
|
@ -12,9 +12,17 @@ local config = {
|
||||||
theme = 'auto',
|
theme = 'auto',
|
||||||
component_separators = { left = '', right = '' },
|
component_separators = { left = '', right = '' },
|
||||||
section_separators = { left = '', right = '' },
|
section_separators = { left = '', right = '' },
|
||||||
disabled_filetypes = {},
|
disabled_filetypes = {
|
||||||
|
statusline = {},
|
||||||
|
winbar = {}
|
||||||
|
},
|
||||||
always_divide_middle = true,
|
always_divide_middle = true,
|
||||||
globalstatus = false,
|
globalstatus = vim.go.laststatus == 3,
|
||||||
|
refresh = {
|
||||||
|
statusline = 1000,
|
||||||
|
tabline = 1000,
|
||||||
|
winbar = 1000,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
sections = {
|
sections = {
|
||||||
lualine_a = { 'mode' },
|
lualine_a = { 'mode' },
|
||||||
|
@ -33,6 +41,8 @@ local config = {
|
||||||
lualine_z = {},
|
lualine_z = {},
|
||||||
},
|
},
|
||||||
tabline = {},
|
tabline = {},
|
||||||
|
winbar = {},
|
||||||
|
inactive_winbar = {},
|
||||||
extensions = {},
|
extensions = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,6 +58,24 @@ local function fix_separators(separators)
|
||||||
return separators
|
return separators
|
||||||
end
|
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
|
---extends config based on config_table
|
||||||
---@param config_table table
|
---@param config_table table
|
||||||
---@return table copy of config
|
---@return table copy of config
|
||||||
|
@ -73,15 +101,25 @@ local function apply_configuration(config_table)
|
||||||
)
|
)
|
||||||
config_table.options.globalstatus = false
|
config_table.options.globalstatus = false
|
||||||
end
|
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('options')
|
||||||
parse_sections('sections')
|
parse_sections('sections')
|
||||||
parse_sections('inactive_sections')
|
parse_sections('inactive_sections')
|
||||||
parse_sections('tabline')
|
parse_sections('tabline')
|
||||||
|
parse_sections('winbar')
|
||||||
|
parse_sections('inactive_winbar')
|
||||||
if config_table.extensions then
|
if config_table.extensions then
|
||||||
config.extensions = utils.deepcopy(config_table.extensions)
|
config.extensions = utils.deepcopy(config_table.extensions)
|
||||||
end
|
end
|
||||||
config.options.section_separators = fix_separators(config.options.section_separators)
|
config.options.section_separators = fix_separators(config.options.section_separators)
|
||||||
config.options.component_separators = fix_separators(config.options.component_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)
|
return utils.deepcopy(config)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -154,29 +154,22 @@ end
|
||||||
---loads all the configs (active, inactive, tabline)
|
---loads all the configs (active, inactive, tabline)
|
||||||
---@param config table user config
|
---@param config table user config
|
||||||
local function load_components(config)
|
local function load_components(config)
|
||||||
load_sections(config.sections, config.options)
|
local sec_names = {'sections', 'inactive_sections', 'tabline', 'winbar', 'inactive_winbar'}
|
||||||
load_sections(config.inactive_sections, config.options)
|
for _, section in ipairs(sec_names) do
|
||||||
load_sections(config.tabline, config.options)
|
load_sections(config[section], config.options)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
---loads all the extensions
|
---loads all the extensions
|
||||||
---@param config table user config
|
---@param config table user config
|
||||||
local function load_extensions(config)
|
local function load_extensions(config)
|
||||||
local loaded_extensions = {}
|
local loaded_extensions = {}
|
||||||
|
local sec_names = {'sections', 'inactive_sections', 'winbar', 'inactive_winbar'}
|
||||||
for _, extension in pairs(config.extensions) do
|
for _, extension in pairs(config.extensions) do
|
||||||
if type(extension) == 'string' then
|
if type(extension) == 'string' then
|
||||||
local ok, local_extension = pcall(require, 'lualine.extensions.' .. extension)
|
local ok
|
||||||
if ok then
|
ok, extension = pcall(require, 'lualine.extensions.' .. extension)
|
||||||
local_extension = modules.utils.deepcopy(local_extension)
|
if not ok then
|
||||||
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
|
|
||||||
modules.notice.add_notice(string.format(
|
modules.notice.add_notice(string.format(
|
||||||
[[
|
[[
|
||||||
### Extensions
|
### Extensions
|
||||||
|
@ -185,11 +178,13 @@ Extension named `%s` was not found . Check if spelling is correct.
|
||||||
extension
|
extension
|
||||||
))
|
))
|
||||||
end
|
end
|
||||||
elseif type(extension) == 'table' then
|
end
|
||||||
|
if type(extension) == 'table' then
|
||||||
local local_extension = modules.utils.deepcopy(extension)
|
local local_extension = modules.utils.deepcopy(extension)
|
||||||
load_sections(local_extension.sections, config.options)
|
for _, section in ipairs(sec_names) do
|
||||||
if local_extension.inactive_sections then
|
if local_extension[section] then
|
||||||
load_sections(local_extension.inactive_sections, config.options)
|
load_sections(local_extension[section], config.options)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
if type(local_extension.init) == 'function' then
|
if type(local_extension.init) == 'function' then
|
||||||
local_extension.init()
|
local_extension.init()
|
||||||
|
@ -205,6 +200,7 @@ end
|
||||||
local function load_all(config)
|
local function load_all(config)
|
||||||
require('lualine.component')._reset_components()
|
require('lualine.component')._reset_components()
|
||||||
modules.fn_store.clear_fns()
|
modules.fn_store.clear_fns()
|
||||||
|
require('lualine.utils.nvim_opts').reset_cache()
|
||||||
load_components(config)
|
load_components(config)
|
||||||
load_extensions(config)
|
load_extensions(config)
|
||||||
end
|
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 event string event name
|
||||||
---@param pattern string event pattern
|
---@param pattern string event pattern
|
||||||
---@param cmd string command to run on event
|
---@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
|
if not cmd then
|
||||||
cmd = pattern
|
cmd = pattern
|
||||||
pattern = '*'
|
pattern = '*'
|
||||||
end
|
end
|
||||||
if not autocmd_is_defined(event, pattern, cmd) then
|
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
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -183,4 +184,34 @@ function M.stl_escape(str)
|
||||||
return str:gsub('%%', '%%%%')
|
return str:gsub('%%', '%%%%')
|
||||||
end
|
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
|
return M
|
||||||
|
|
|
@ -86,12 +86,12 @@ describe('config parsing', function()
|
||||||
describe('disabled filetypes', function()
|
describe('disabled filetypes', function()
|
||||||
it('default', function()
|
it('default', function()
|
||||||
local config = config_module.apply_configuration {}
|
local config = config_module.apply_configuration {}
|
||||||
eq(config.options.disabled_filetypes, {})
|
eq(config.options.disabled_filetypes, {statusline={}, winbar={}})
|
||||||
end)
|
end)
|
||||||
it('custom', function()
|
it('custom', function()
|
||||||
local config = { options = { disabled_filetypes = { 'lua' } } }
|
local config = { options = { disabled_filetypes = { 'lua' } } }
|
||||||
config = config_module.apply_configuration(config)
|
config = config_module.apply_configuration(config)
|
||||||
eq(config.options.disabled_filetypes, { 'lua' })
|
eq(config.options.disabled_filetypes, {statusline={'lua'}, winbar={'lua'}})
|
||||||
end)
|
end)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
|
@ -15,9 +15,17 @@ describe('Lualine', function()
|
||||||
theme = 'gruvbox',
|
theme = 'gruvbox',
|
||||||
component_separators = { left = '', right = '' },
|
component_separators = { left = '', right = '' },
|
||||||
section_separators = { left = '', right = '' },
|
section_separators = { left = '', right = '' },
|
||||||
disabled_filetypes = {},
|
disabled_filetypes = {
|
||||||
|
statusline = {},
|
||||||
|
winbar = {},
|
||||||
|
},
|
||||||
always_divide_middle = true,
|
always_divide_middle = true,
|
||||||
globalstatus = false,
|
globalstatus = false,
|
||||||
|
refresh = {
|
||||||
|
statusline = 1000,
|
||||||
|
tabline = 1000,
|
||||||
|
winbar = 1000,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
sections = {
|
sections = {
|
||||||
lualine_a = { 'mode' },
|
lualine_a = { 'mode' },
|
||||||
|
@ -48,6 +56,8 @@ describe('Lualine', function()
|
||||||
lualine_z = {},
|
lualine_z = {},
|
||||||
},
|
},
|
||||||
tabline = {},
|
tabline = {},
|
||||||
|
winbar = {},
|
||||||
|
inactive_winbar = {},
|
||||||
extensions = {},
|
extensions = {},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue