feat: allow lualine to ignore focus on specific filetypes. (#767)

* feat: allow lualine to ignore focus on specific filetypes.

closes #710

* fix: extensions on ingnored_focus

* perf: reuse api call results & use local variables instead of vim.g

* make location & progress components behave in ingnored filetypes

* fix: crash when last_focused win gets closed

* fix: location & progress related broken tests
This commit is contained in:
Shadman 2022-07-29 16:54:31 +06:00 committed by GitHub
parent 788805771c
commit 6d11f9f508
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 134 additions and 41 deletions

View File

@ -125,6 +125,7 @@ require('lualine').setup {
statusline = {}, statusline = {},
winbar = {}, winbar = {},
}, },
ignore_focus = {},
always_divide_middle = true, always_divide_middle = true,
globalstatus = false, globalstatus = false,
refresh = { refresh = {
@ -337,7 +338,7 @@ Values set here are treated as default for other options
that work in component level. that work in component level.
For example even though `icons_enabled` is a general component option. For example even though `icons_enabled` is a general component option.
you can set `icons_enabled` to `false` and icons will be disabled on all You can set `icons_enabled` to `false` and icons will be disabled on all
component. You can still overwrite defaults set in option table by specifying component. You can still overwrite defaults set in option table by specifying
the option value in component. the option value in component.
@ -351,6 +352,13 @@ options = {
winbar = {}, -- only ignores the ft for winbar. winbar = {}, -- only ignores the ft for winbar.
}, },
ignore_focus = {}, -- If current filetype is in this list it'll
-- always be drawn as inactive statusline
-- and the last window will be drawn as active statusline.
-- for example if you don't want statusline of
-- your file tree / sidebar window to have active
-- statusline you can add their filetypes here.
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.

View File

@ -109,6 +109,7 @@ For more information, check out `:help lua-heredoc`.
statusline = {}, statusline = {},
winbar = {}, winbar = {},
}, },
ignore_focus = {},
always_divide_middle = true, always_divide_middle = true,
globalstatus = false, globalstatus = false,
refresh = { refresh = {
@ -346,7 +347,7 @@ Global options These are `options` that are used in
Values set here are treated as default for other options that work in component Values set here are treated as default for other options that work in component
level. level.
For example even though `icons_enabled` is a general component option. you can For example even though `icons_enabled` is a general component option. You can
set `icons_enabled` to `false` and icons will be disabled on all component. You set `icons_enabled` to `false` and icons will be disabled on all component. You
can still overwrite defaults set in option table by specifying the option value can still overwrite defaults set in option table by specifying the option value
in component. in component.
@ -361,6 +362,13 @@ in component.
winbar = {}, -- only ignores the ft for winbar. winbar = {}, -- only ignores the ft for winbar.
}, },
ignore_focus = {}, -- If current filetype is in this list it'll
-- always be drawn as inactive statusline
-- and the last window will be drawn as active statusline.
-- for example if you don't want statusline of
-- your file tree / sidebar window to have active
-- statusline you can add their filetypes here.
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.

View File

@ -19,6 +19,9 @@ local timers = {
wb_timer = vim.loop.new_timer(), wb_timer = vim.loop.new_timer(),
} }
local last_focus = {}
local refresh_real_curwin
-- The events on which lualine redraws itself -- The events on which lualine redraws itself
local default_refresh_events = 'WinEnter,BufEnter,SessionLoadPost,FileChangedShellPost,VimResized,Filetype' local default_refresh_events = 'WinEnter,BufEnter,SessionLoadPost,FileChangedShellPost,VimResized,Filetype'
if vim.fn.has('nvim-0.7') == 1 then -- utilize ModeChanged event introduced in 0.7 if vim.fn.has('nvim-0.7') == 1 then -- utilize ModeChanged event introduced in 0.7
@ -187,7 +190,7 @@ end)
--- check if any extension matches the filetype and return proper sections --- check if any extension matches the filetype and return proper sections
---@param current_ft string : filetype name of current file ---@param current_ft string : filetype name of current file
---@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 table : (section_table) section config where components are replaced with ---@return table|nil : (section_table) section config where components are replaced with
--- component objects --- component objects
-- 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
@ -195,7 +198,11 @@ end)
local function get_extension_sections(current_ft, is_focused, sec_name) 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
if vim.tbl_contains(extension.filetypes, current_ft) then if vim.tbl_contains(extension.filetypes, current_ft) then
return extension[(is_focused and '' or 'inactive_') .. sec_name] if is_focused then
return extension[sec_name]
else
return extension['inactive_' .. sec_name] or extension[sec_name]
end
end end
end end
return nil return nil
@ -267,7 +274,9 @@ end
local function status_dispatch(sec_name) local function status_dispatch(sec_name)
return function(focused) return function(focused)
local retval local retval
local current_ft = vim.bo.filetype local current_ft = refresh_real_curwin
and vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(refresh_real_curwin), 'filetype')
or vim.bo.filetype
local is_focused = focused ~= nil and focused or modules.utils.is_focused() local is_focused = focused ~= nil and focused or modules.utils.is_focused()
if if
vim.tbl_contains( vim.tbl_contains(
@ -299,7 +308,7 @@ end
---@class LualineRefreshOpts ---@class LualineRefreshOpts
---@field kind LualineRefreshOptsKind ---@field kind LualineRefreshOptsKind
---@field place LualineRefreshOptsPlace[] ---@field place LualineRefreshOptsPlace[]
---@field trigger 'autocmd'|'autocmd_redired|timer'|'unknown' ---@field trigger 'autocmd'|'autocmd_redired'|'timer'|'unknown'
--- Refresh contents of lualine --- Refresh contents of lualine
---@param opts LualineRefreshOpts ---@param opts LualineRefreshOpts
local function refresh(opts) local function refresh(opts)
@ -324,7 +333,50 @@ local function refresh(opts)
local wins = {} local wins = {}
local old_actual_curwin = vim.g.actual_curwin local old_actual_curwin = vim.g.actual_curwin
vim.g.actual_curwin = vim.api.nvim_get_current_win()
-- ignore focus on filetypes listes in options.ignore_focus
local curwin = vim.api.nvim_get_current_win()
local curtab = vim.api.nvim_get_current_tabpage()
if last_focus[curtab] == nil or not vim.api.nvim_win_is_valid(last_focus[curtab]) then
if
not vim.tbl_contains(
config.options.ignore_focus,
vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(curwin), 'filetype')
)
then
last_focus[curtab] = curwin
else
local tab_wins = vim.api.nvim_tabpage_list_wins(curtab)
if #tab_wins == 1 then
last_focus[curtab] = curwin
else
local focusable_win = curwin
for _, win in ipairs(tab_wins) do
if
not vim.tbl_contains(
config.options.ignore_focus,
vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(win), 'filetype')
)
then
focusable_win = win
break
end
end
last_focus[curtab] = focusable_win
end
end
else
if
not vim.tbl_contains(
config.options.ignore_focus,
vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(curwin), 'filetype')
)
then
last_focus[curtab] = curwin
end
end
vim.g.actual_curwin = last_focus[curtab]
-- gather which windows needs update -- gather which windows needs update
if opts.kind == 'all' then if opts.kind == 'all' then
if vim.tbl_contains(opts.place, 'statusline') or vim.tbl_contains(opts.place, 'winbar') then if vim.tbl_contains(opts.place, 'statusline') or vim.tbl_contains(opts.place, 'winbar') then
@ -339,13 +391,14 @@ local function refresh(opts)
end, vim.api.nvim_tabpage_list_wins(0)) end, vim.api.nvim_tabpage_list_wins(0))
end end
elseif opts.kind == 'window' then elseif opts.kind == 'window' then
wins = { vim.api.nvim_get_current_win() } wins = { curwin }
end end
-- update them -- update them
if vim.tbl_contains(opts.place, 'statusline') then if vim.tbl_contains(opts.place, 'statusline') then
for _, win in ipairs(wins) do for _, win in ipairs(wins) do
local stl_cur = vim.api.nvim_win_call(win, M.statusline) refresh_real_curwin = config.options.globalstatus and last_focus[curtab] or win
local stl_cur = vim.api.nvim_win_call(refresh_real_curwin, M.statusline)
local stl_last = modules.nvim_opts.get_cache('statusline', { window = win }) local stl_last = modules.nvim_opts.get_cache('statusline', { window = win })
if stl_cur or stl_last then if stl_cur or stl_last then
modules.nvim_opts.set('statusline', stl_cur, { window = win }) modules.nvim_opts.set('statusline', stl_cur, { window = win })
@ -354,8 +407,9 @@ local function refresh(opts)
end end
if vim.tbl_contains(opts.place, 'winbar') then if vim.tbl_contains(opts.place, 'winbar') then
for _, win in ipairs(wins) do for _, win in ipairs(wins) do
refresh_real_curwin = config.options.globalstatus and last_focus[curtab] or win
if vim.api.nvim_win_get_height(win) > 1 then if vim.api.nvim_win_get_height(win) > 1 then
local wbr_cur = vim.api.nvim_win_call(win, M.winbar) local wbr_cur = vim.api.nvim_win_call(refresh_real_curwin, M.winbar)
local wbr_last = modules.nvim_opts.get_cache('winbar', { window = win }) local wbr_last = modules.nvim_opts.get_cache('winbar', { window = win })
if wbr_cur or wbr_last then if wbr_cur or wbr_last then
modules.nvim_opts.set('winbar', wbr_cur, { window = win }) modules.nvim_opts.set('winbar', wbr_cur, { window = win })
@ -364,7 +418,8 @@ local function refresh(opts)
end end
end end
if vim.tbl_contains(opts.place, 'tabline') then if vim.tbl_contains(opts.place, 'tabline') then
local tbl_cur = vim.api.nvim_win_call(vim.api.nvim_get_current_win(), tabline) refresh_real_curwin = curwin
local tbl_cur = vim.api.nvim_win_call(curwin, tabline)
local tbl_last = modules.nvim_opts.get_cache('tabline', { global = true }) local tbl_last = modules.nvim_opts.get_cache('tabline', { global = true })
if tbl_cur or tbl_last then if tbl_cur or tbl_last then
modules.nvim_opts.set('tabline', tbl_cur, { global = true }) modules.nvim_opts.set('tabline', tbl_cur, { global = true })
@ -372,6 +427,7 @@ local function refresh(opts)
end end
vim.g.actual_curwin = old_actual_curwin vim.g.actual_curwin = old_actual_curwin
refresh_real_curwin = nil
end end
--- Sets &tabline option to lualine --- Sets &tabline option to lualine

View File

@ -1,7 +1,9 @@
-- 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 function location() local function location()
return '%3l:%-2v' local line = vim.fn.line('.')
local col = vim.fn.col('.')
return string.format('%3d:%-2d', line, col)
end end
return location return location

View File

@ -1,7 +1,15 @@
-- 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 function progress() local function progress()
return '%3p%%' local cur = vim.fn.line('.')
local total = vim.fn.line('$')
if cur == 1 then
return 'Top'
elseif cur == total then
return 'Bot'
else
return math.floor(cur / total * 100) .. '%%'
end
end end
return progress return progress

View File

@ -16,6 +16,7 @@ local config = {
statusline = {}, statusline = {},
winbar = {}, winbar = {},
}, },
ignore_focus = {},
always_divide_middle = true, always_divide_middle = true,
globalstatus = vim.go.laststatus == 3, globalstatus = vim.go.laststatus == 3,
refresh = { refresh = {

View File

@ -400,7 +400,12 @@ describe('Location component', function()
component_separators = { left = '', right = '' }, component_separators = { left = '', right = '' },
padding = 0, padding = 0,
} }
assert_component('location', opts, '%3l:%-2v') assert_component('location', opts, ' 1:1 ')
vim.cmd('normal! 9o')
assert_component('location', opts, ' 10:1 ')
vim.api.nvim_win_set_cursor(0, {5, 0})
assert_component('location', opts, ' 5:1 ')
vim.cmd('bdelete!')
end) end)
end) end)
@ -410,7 +415,12 @@ describe('Progress component', function()
component_separators = { left = '', right = '' }, component_separators = { left = '', right = '' },
padding = 0, padding = 0,
} }
assert_component('progress', opts, '%3p%%') assert_component('progress', opts, 'Top')
vim.cmd('normal! 9o')
assert_component('progress', opts, 'Bot')
vim.api.nvim_win_set_cursor(0, {5, 0})
assert_component('progress', opts, '50%%')
vim.cmd('bdelete!')
end) end)
end) end)

View File

@ -19,6 +19,7 @@ describe('Lualine', function()
statusline = {}, statusline = {},
winbar = {}, winbar = {},
}, },
ignore_focus = {},
always_divide_middle = true, always_divide_middle = true,
globalstatus = false, globalstatus = false,
refresh = { refresh = {
@ -81,12 +82,12 @@ describe('Lualine', function()
{3: master } {3: master }
{4:} {4:}
{5: [No Name] } {5: [No Name] }
{5: } {5: }
{5: } {5: }
{4:} {4:}
{3: 100% } {3: Top }
{2:} {2:}
{1: 0:1 }| {1: 1:1 }|
]===]) ]===])
end) end)
@ -97,7 +98,7 @@ describe('Lualine', function()
} }
|{1: [No Name] } |{1: [No Name] }
{1: } {1: }
{1: 0:1 }| {1: 1:1 }|
]===]) ]===])
end) end)
@ -120,12 +121,12 @@ describe('Lualine', function()
|{1: master } |{1: master }
{2:} {2:}
{3: [No Name] } {3: [No Name] }
{3: } {3: }
{3: } {3: }
{2:} {2:}
{1: 100% } {1: Top }
{4:} {4:}
{5: 0:1 }| {5: 1:1 }|
]===]) ]===])
end) end)
@ -141,10 +142,10 @@ describe('Lualine', function()
|{1: NORMAL } |{1: NORMAL }
{2: master } {2: master }
{3: [No Name] } {3: [No Name] }
{3: } {3: }
{3: } {3: }
{2: 100% } {2: Top }
{1: 0:1 }| {1: 1:1 }|
]===]) ]===])
end) end)
@ -171,12 +172,12 @@ describe('Lualine', function()
{3: master } {3: master }
{4:} {4:}
{5: [No Name] } {5: [No Name] }
{5: } {5: }
{5: } {5: }
{4:} {4:}
{3: 100% } {3: Top }
{2:} {2:}
{1: 0:1 } {1: 1:1 }
{1: test_comp2 }| {1: test_comp2 }|
]===]) ]===])
@ -196,12 +197,12 @@ describe('Lualine', function()
{3: master } {3: master }
{4:} {4:}
{5: [No Name] } {5: [No Name] }
{5: } {5: }
{5: } {5: }
{4:} {4:}
{3: 100% } {3: Top }
{2:} {2:}
{1: 0:1 } {1: 1:1 }
{1: test_comp2 }| {1: test_comp2 }|
]===]) ]===])
end) end)
@ -241,10 +242,10 @@ describe('Lualine', function()
|{1: NORMAL } |{1: NORMAL }
{2: master } {2: master }
{3: [No Name] } {3: [No Name] }
{3: } {3: }
{3: unix } {3: unix }
{2: 100% } {2: Top }
{1: 0:1 }| {1: 1:1 }|
]===]) ]===])
end) end)
@ -322,14 +323,13 @@ describe('Lualine', function()
{3: master } {3: master }
{4:} {4:}
{5: [No Name] } {5: [No Name] }
{5: } {5: }
{5: } {5: }
{4:} {4:}
{3: 100% } {3: Top }
{2:} {2:}
{1: 0:1 }| {1: 1:1 }|
]===]) ]===])
vim.bo.ft = 'test_ft2' vim.bo.ft = 'test_ft2'
statusline:expect([===[ statusline:expect([===[
highlights = { highlights = {
@ -403,12 +403,12 @@ describe('Lualine', function()
{3: master } {3: master }
{4:} {4:}
{5: [No Name] } {5: [No Name] }
{5: } {5: }
{5: } {5: }
{4:} {4:}
{3: 100% } {3: Top }
{2:} {2:}
{1: 0:1 }| {1: 1:1 }|
]===]) ]===])
end) end)
describe('tabs component', function() describe('tabs component', function()