From 6d11f9f50817020cf634903a235628cabe0c56f8 Mon Sep 17 00:00:00 2001 From: Shadman <13149513+shadmansaleh@users.noreply.github.com> Date: Fri, 29 Jul 2022 16:54:31 +0600 Subject: [PATCH] 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 --- README.md | 10 +++- doc/lualine.txt | 10 +++- lua/lualine.lua | 74 +++++++++++++++++++++++++---- lua/lualine/components/location.lua | 4 +- lua/lualine/components/progress.lua | 10 +++- lua/lualine/config.lua | 1 + tests/spec/component_spec.lua | 14 +++++- tests/spec/lualine_spec.lua | 52 ++++++++++---------- 8 files changed, 134 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index a762471..19a59f6 100644 --- a/README.md +++ b/README.md @@ -125,6 +125,7 @@ require('lualine').setup { statusline = {}, winbar = {}, }, + ignore_focus = {}, always_divide_middle = true, globalstatus = false, refresh = { @@ -337,7 +338,7 @@ Values set here are treated as default for other options that work in component level. 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 the option value in component. @@ -351,6 +352,13 @@ options = { 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' -- can't take over the entire statusline even -- if neither of 'x', 'y' or 'z' are present. diff --git a/doc/lualine.txt b/doc/lualine.txt index d4db421..ae33925 100644 --- a/doc/lualine.txt +++ b/doc/lualine.txt @@ -109,6 +109,7 @@ For more information, check out `:help lua-heredoc`. statusline = {}, winbar = {}, }, + ignore_focus = {}, always_divide_middle = true, globalstatus = false, 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 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 can still overwrite defaults set in option table by specifying the option value in component. @@ -361,6 +362,13 @@ in component. 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' -- can't take over the entire statusline even -- if neither of 'x', 'y' or 'z' are present. diff --git a/lua/lualine.lua b/lua/lualine.lua index ba376a7..847f8d6 100644 --- a/lua/lualine.lua +++ b/lua/lualine.lua @@ -19,6 +19,9 @@ local timers = { wb_timer = vim.loop.new_timer(), } +local last_focus = {} +local refresh_real_curwin + -- The events on which lualine redraws itself 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 @@ -187,7 +190,7 @@ end) --- check if any extension matches the filetype and return proper sections ---@param current_ft string : filetype name of current file ---@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 -- TODO: change this so it uses a hash table instead of iteration over list -- to improve redraws. Add buftype / bufname for extensions @@ -195,7 +198,11 @@ end) local function get_extension_sections(current_ft, is_focused, sec_name) for _, extension in ipairs(config.extensions) do 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 return nil @@ -267,7 +274,9 @@ end local function status_dispatch(sec_name) return function(focused) 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() if vim.tbl_contains( @@ -299,7 +308,7 @@ end ---@class LualineRefreshOpts ---@field kind LualineRefreshOptsKind ---@field place LualineRefreshOptsPlace[] ----@field trigger 'autocmd'|'autocmd_redired|timer'|'unknown' +---@field trigger 'autocmd'|'autocmd_redired'|'timer'|'unknown' --- Refresh contents of lualine ---@param opts LualineRefreshOpts local function refresh(opts) @@ -324,7 +333,50 @@ local function refresh(opts) local wins = {} 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 if opts.kind == 'all' 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 elseif opts.kind == 'window' then - wins = { vim.api.nvim_get_current_win() } + wins = { curwin } end -- update them if vim.tbl_contains(opts.place, 'statusline') then 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 }) if stl_cur or stl_last then modules.nvim_opts.set('statusline', stl_cur, { window = win }) @@ -354,8 +407,9 @@ local function refresh(opts) end if vim.tbl_contains(opts.place, 'winbar') then 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 - 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 }) if wbr_cur or wbr_last then modules.nvim_opts.set('winbar', wbr_cur, { window = win }) @@ -364,7 +418,8 @@ local function refresh(opts) end end 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 }) if tbl_cur or tbl_last then modules.nvim_opts.set('tabline', tbl_cur, { global = true }) @@ -372,6 +427,7 @@ local function refresh(opts) end vim.g.actual_curwin = old_actual_curwin + refresh_real_curwin = nil end --- Sets &tabline option to lualine diff --git a/lua/lualine/components/location.lua b/lua/lualine/components/location.lua index 3c46790..413bf9e 100644 --- a/lua/lualine/components/location.lua +++ b/lua/lualine/components/location.lua @@ -1,7 +1,9 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local function location() - return '%3l:%-2v' + local line = vim.fn.line('.') + local col = vim.fn.col('.') + return string.format('%3d:%-2d', line, col) end return location diff --git a/lua/lualine/components/progress.lua b/lua/lualine/components/progress.lua index 7946a6c..5611fbe 100644 --- a/lua/lualine/components/progress.lua +++ b/lua/lualine/components/progress.lua @@ -1,7 +1,15 @@ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. 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 return progress diff --git a/lua/lualine/config.lua b/lua/lualine/config.lua index dce1070..5ee6e04 100644 --- a/lua/lualine/config.lua +++ b/lua/lualine/config.lua @@ -16,6 +16,7 @@ local config = { statusline = {}, winbar = {}, }, + ignore_focus = {}, always_divide_middle = true, globalstatus = vim.go.laststatus == 3, refresh = { diff --git a/tests/spec/component_spec.lua b/tests/spec/component_spec.lua index b21dc56..4b7d1e9 100644 --- a/tests/spec/component_spec.lua +++ b/tests/spec/component_spec.lua @@ -400,7 +400,12 @@ describe('Location component', function() component_separators = { left = '', right = '' }, 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) @@ -410,7 +415,12 @@ describe('Progress component', function() component_separators = { left = '', right = '' }, 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) diff --git a/tests/spec/lualine_spec.lua b/tests/spec/lualine_spec.lua index f433c7d..6ba5d5f 100644 --- a/tests/spec/lualine_spec.lua +++ b/tests/spec/lualine_spec.lua @@ -19,6 +19,7 @@ describe('Lualine', function() statusline = {}, winbar = {}, }, + ignore_focus = {}, always_divide_middle = true, globalstatus = false, refresh = { @@ -81,12 +82,12 @@ describe('Lualine', function() {3:  master } {4:} {5: [No Name] } - {5: } + {5: } {5:  } {4:} - {3: 100% } + {3: Top } {2:} - {1: 0:1 }| + {1: 1:1 }| ]===]) end) @@ -97,7 +98,7 @@ describe('Lualine', function() } |{1: [No Name] } {1: } - {1: 0:1 }| + {1: 1:1 }| ]===]) end) @@ -120,12 +121,12 @@ describe('Lualine', function() |{1:  master } {2:} {3: [No Name] } - {3: } + {3: } {3:  } {2:} - {1: 100% } + {1: Top } {4:} - {5: 0:1 }| + {5: 1:1 }| ]===]) end) @@ -141,10 +142,10 @@ describe('Lualine', function() |{1: NORMAL } {2:  master } {3: [No Name] } - {3: } + {3: } {3:  } - {2: 100% } - {1: 0:1 }| + {2: Top } + {1: 1:1 }| ]===]) end) @@ -171,12 +172,12 @@ describe('Lualine', function() {3:  master } {4:} {5: [No Name] } - {5: } + {5: } {5:  } {4:} - {3: 100% } + {3: Top } {2:} - {1: 0:1 } + {1: 1:1 } {1: test_comp2 }| ]===]) @@ -196,12 +197,12 @@ describe('Lualine', function() {3:  master } {4:} {5: [No Name] } - {5: } + {5: } {5:  } {4:} - {3: 100% } + {3: Top } {2:} - {1: 0:1 } + {1: 1:1 } {1: test_comp2 }| ]===]) end) @@ -241,10 +242,10 @@ describe('Lualine', function() |{1: NORMAL } {2: master } {3: [No Name] } - {3: } + {3: } {3: unix } - {2: 100% } - {1: 0:1 }| + {2: Top } + {1: 1:1 }| ]===]) end) @@ -322,14 +323,13 @@ describe('Lualine', function() {3:  master } {4:} {5: [No Name] } - {5: } + {5: } {5:  } {4:} - {3: 100% } + {3: Top } {2:} - {1: 0:1 }| + {1: 1:1 }| ]===]) - vim.bo.ft = 'test_ft2' statusline:expect([===[ highlights = { @@ -403,12 +403,12 @@ describe('Lualine', function() {3:  master } {4:} {5: [No Name] } - {5: } + {5: } {5:  } {4:} - {3: 100% } + {3: Top } {2:} - {1: 0:1 }| + {1: 1:1 }| ]===]) end) describe('tabs component', function()