From e9b935ccd6bdc95fb7a8b3718aa268659aa3965a Mon Sep 17 00:00:00 2001 From: Camille Dejoye Date: Sat, 26 Mar 2022 14:31:40 +0100 Subject: [PATCH] feat: add windows component (#595) Co-authored-by: shadmansaleh <13149513+shadmansaleh@users.noreply.github.com> --- README.md | 38 ++++++++++++ doc/lualine.txt | 39 ++++++++++++ lua/lualine.lua | 4 +- lua/lualine/components/buffers/buffer.lua | 36 ++++++++--- lua/lualine/components/buffers/init.lua | 61 +++++++++++------- lua/lualine/components/windows/init.lua | 75 +++++++++++++++++++++++ lua/lualine/components/windows/window.lua | 35 +++++++++++ tests/spec/lualine_spec.lua | 49 +++++++++++++++ 8 files changed, 305 insertions(+), 32 deletions(-) create mode 100644 lua/lualine/components/windows/init.lua create mode 100644 lua/lualine/components/windows/window.lua diff --git a/README.md b/README.md index e67aedd..f2648e4 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,7 @@ sections = {lualine_a = {'mode'}} - `mode` (vim mode) - `progress` (%progress in file) - `tabs` (shows currently available tabs) +- `windows` (shows currently available windows) #### Custom components @@ -597,6 +598,43 @@ sections = { } ``` +#### windows component options + +```lua +sections = { + lualine_a = { + { + 'windows', + show_filename_only = true, -- Shows shortened relative path when set to false. + show_modified_status = true, -- Shows indicator when the window is modified. + + mode = 0, -- 0: Shows window name + -- 1: Shows window index (bufnr) + -- 2: Shows window name + window index (bufnr) + + max_length = vim.o.columns * 2 / 3, -- Maximum width of windows component, + -- it can also be a function that returns + -- the value of `max_length` dynamically. + filetype_names = { + TelescopePrompt = 'Telescope', + dashboard = 'Dashboard', + packer = 'Packer', + fzf = 'FZF', + alpha = 'Alpha' + }, -- Shows specific window name for that filetype ( { `filetype` = `window_name`, ... } ) + + disabled_buftypes = { 'quickfix', 'prompt' }, -- Hide a window if its buffer's type is disabled + + windows_color = { + -- Same values as the general color option can be used here. + active = 'lualine_{section}_normal', -- Color for active window. + inactive = 'lualine_{section}_inactive', -- Color for inactive window. + }, + } + } +} +``` + --- ### Tabline diff --git a/doc/lualine.txt b/doc/lualine.txt index 08a9704..41f97c2 100644 --- a/doc/lualine.txt +++ b/doc/lualine.txt @@ -240,6 +240,7 @@ CHANGING COMPONENTS IN LUALINE SECTIONS ~ - `mode` (vim mode) - `progress` (%progress in file) - `tabs` (shows currently available tabs) +- `windows` (shows currently available windows) *lualine-Custom-components* @@ -621,6 +622,44 @@ Component specific options These are options that are available on < + *lualine-windows-component-options* + +> + sections = { + lualine_a = { + { + 'windows', + show_filename_only = true, -- Shows shortened relative path when set to false. + show_modified_status = true, -- Shows indicator when the window is modified. + + mode = 0, -- 0: Shows window name + -- 1: Shows window index (bufnr) + -- 2: Shows window name + window index (bufnr) + + max_length = vim.o.columns * 2 / 3, -- Maximum width of windows component, + -- it can also be a function that returns + -- the value of `max_length` dynamically. + filetype_names = { + TelescopePrompt = 'Telescope', + dashboard = 'Dashboard', + packer = 'Packer', + fzf = 'FZF', + alpha = 'Alpha' + }, -- Shows specific window name for that filetype ( { `filetype` = `window_name`, ... } ) + + disabled_buftypes = { 'quickfix', 'prompt' }, -- Hide a window if its buffer's type is disabled + + windows_color = { + -- Same values as the general color option can be used here. + active = 'lualine_{section}_normal', -- Color for active window. + inactive = 'lualine_{section}_inactive', -- Color for inactive window. + }, + } + } + } +< + + ------------------------------------------------------------------------------ TABLINE ~ diff --git a/lua/lualine.lua b/lua/lualine.lua index b49f76d..0bc3b0f 100644 --- a/lua/lualine.lua +++ b/lua/lualine.lua @@ -277,8 +277,8 @@ local function set_statusline() end -- lualine.statusline function ---- Draw correct statusline for current winwow ----@param focused boolean : force the vale of is_focuased . useful for debugginf +--- 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 diff --git a/lua/lualine/components/buffers/buffer.lua b/lua/lualine/components/buffers/buffer.lua index 2f9fa06..8af11a3 100644 --- a/lua/lualine/components/buffers/buffer.lua +++ b/lua/lualine/components/buffers/buffer.lua @@ -15,6 +15,10 @@ function Buffer:init(opts) self:get_props() end +function Buffer:is_current() + return vim.api.nvim_get_current_buf() == self.bufnr +end + ---setup icons, modified status for buffer function Buffer:get_props() self.file = modules.utils.stl_escape(vim.api.nvim_buf_get_name(self.bufnr)) @@ -48,6 +52,13 @@ function Buffer:get_props() end end +---returns line configured for handling mouse click +---@param name string +---@return string +function Buffer:configure_mouse_click(name) + return string.format('%%%s@LualineSwitchBuffer@%s%%T', self.bufnr, name) +end + ---returns rendered buffer ---@return string function Buffer:render() @@ -59,19 +70,13 @@ function Buffer:render() if self.ellipse then -- show elipsis name = '...' else - if self.options.mode == 0 then - name = string.format('%s%s%s', self.icon, name, self.modified_icon) - elseif self.options.mode == 1 then - name = string.format('%s %s%s', self.bufnr, self.icon, self.modified_icon) - else - name = string.format('%s %s%s%s', self.bufnr, self.icon, name, self.modified_icon) - end + name = self:apply_mode(name) end name = Buffer.apply_padding(name, self.options.padding) self.len = vim.fn.strchars(name) -- setup for mouse clicks - local line = string.format('%%%s@LualineSwitchBuffer@%s%%T', self.bufnr, name) + local line = self:configure_mouse_click(name) -- apply highlight line = modules.highlight.component_format_highlight(self.highlights[(self.current and 'active' or 'inactive')]) .. line @@ -119,6 +124,9 @@ function Buffer:name() elseif self.buftype == 'terminal' then local match = string.match(vim.split(self.file, ' ')[1], 'term:.*:(%a+)') return match ~= nil and match or vim.fn.fnamemodify(vim.env.SHELL, ':t') + elseif self.buftype == 'quickfix' then + local is_loclist = 0 ~= vim.fn.getloclist(0, { filewinid = 1 }).filewinid + return is_loclist and 'Location list' or 'Quickfix List' elseif vim.fn.isdirectory(self.file) == 1 then return vim.fn.fnamemodify(self.file, ':p:.') elseif self.file == '' then @@ -139,4 +147,16 @@ function Buffer.apply_padding(str, padding) return string.rep(' ', l_padding) .. str .. string.rep(' ', r_padding) end +function Buffer:apply_mode(name) + if self.options.mode == 0 then + return string.format('%s%s%s', self.icon, name, self.modified_icon) + end + + if self.options.mode == 1 then + return string.format('%s %s%s', self.bufnr, self.icon, self.modified_icon) + end + + return string.format('%s %s%s%s', self.bufnr, self.icon, name, self.modified_icon) +end + return Buffer diff --git a/lua/lualine/components/buffers/init.lua b/lua/lualine/components/buffers/init.lua index 0d1ea38..964c4fa 100644 --- a/lua/lualine/components/buffers/init.lua +++ b/lua/lualine/components/buffers/init.lua @@ -48,31 +48,48 @@ function M:init(options) inactive = get_hl('lualine_' .. options.self.section, false), } self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) - self.highlights = { - active = highlight.create_component_highlight_group( - self.options.buffers_color.active, - 'buffers_active', - self.options, - false - ), - inactive = highlight.create_component_highlight_group( - self.options.buffers_color.inactive, - 'buffers_inactive', - self.options, - false - ), + if self.options.component_name == 'buffers' then + self.highlights = { + active = highlight.create_component_highlight_group( + self.options.buffers_color.active, + 'buffers_active', + self.options, + false + ), + inactive = highlight.create_component_highlight_group( + self.options.buffers_color.inactive, + 'buffers_inactive', + self.options, + false + ), + } + end +end + +function M:new_buffer(bufnr) + bufnr = bufnr or vim.api.nvim_get_current_buf() + + return Buffer:new { + bufnr = bufnr, + options = self.options, + highlights = self.highlights, } end +function M:buffers() + local buffers = {} + for b = 1, vim.fn.bufnr('$') do + if vim.fn.buflisted(b) ~= 0 and vim.api.nvim_buf_get_option(b, 'buftype') ~= 'quickfix' then + buffers[#buffers + 1] = self:new_buffer(b) + end + end + + return buffers +end + function M:update_status() local data = {} - local buffers = {} - for b = 1, vim.fn.bufnr('$') do - if vim.fn.buflisted(b) ~= 0 and vim.api.nvim_buf_get_option(b, 'buftype') ~= 'quickfix' then - buffers[#buffers + 1] = Buffer { bufnr = b, options = self.options, highlights = self.highlights } - end - end - local current_bufnr = vim.api.nvim_get_current_buf() + local buffers = self:buffers() local current = -2 -- mark the first, last, current, before current, after current buffers -- for rendering @@ -83,7 +100,7 @@ function M:update_status() buffers[#buffers].last = true end for i, buffer in ipairs(buffers) do - if buffer.bufnr == current_bufnr then + if buffer:is_current() then buffer.current = true current = i end @@ -112,7 +129,7 @@ function M:update_status() -- start drawing from current buffer and draw left and right of it until -- all buffers are drawn or max_length has been reached. if current == -2 then - local b = Buffer { bufnr = vim.api.nvim_get_current_buf(), options = self.options, highlights = self.highlights } + local b = self:new_buffer() b.current = true if self.options.self.section < 'x' then b.last = true diff --git a/lua/lualine/components/windows/init.lua b/lua/lualine/components/windows/init.lua new file mode 100644 index 0000000..56af7eb --- /dev/null +++ b/lua/lualine/components/windows/init.lua @@ -0,0 +1,75 @@ +local Window = require('lualine.components.windows.window') +local M = require('lualine.components.buffers'):extend() +local highlight = require('lualine.highlight') + +local default_options = { + disabled_filetypes = {}, + disabled_buftypes = { 'quickfix', 'prompt' }, +} + +function M:init(options) + options.buffers_color = nil -- bufers_color isn't windpws option. + M.super.init(self, options) + + self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) + self.options.windows_color = vim.tbl_deep_extend('keep', self.options.windows_color or {}, self.options.buffers_color) + self.options.buffers_color = nil -- this is the default value of colors generated by parent bufferes component. + + self.highlights = { + active = highlight.create_component_highlight_group( + self.options.windows_color.active, + 'windows_active', + self.options, + false + ), + inactive = highlight.create_component_highlight_group( + self.options.windows_color.inactive, + 'windows_inactive', + self.options, + false + ), + } +end + +function M:new_buffer(winnr) + winnr = winnr or vim.api.nvim_get_current_win() + + return Window:new { + winnr = winnr, + options = self.options, + highlights = self.highlights, + } +end + +--- Override to only return buffers shown in the windows of the current tab +function M:buffers() + local tabnr = vim.api.nvim_get_current_tabpage() + local buffers = {} + + for _, winnr in ipairs(vim.api.nvim_tabpage_list_wins(tabnr)) do + if not self:should_hide(winnr) then + buffers[#buffers + 1] = self:new_buffer(winnr) + end + end + + return buffers +end + +function M:should_hide(winnr) + local bufnr = vim.api.nvim_win_get_buf(winnr) + local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype') + local buftype = vim.api.nvim_buf_get_option(bufnr, 'buftype') + local is_filetype_disabled = vim.tbl_contains(self.options.disabled_filetypes, filetype) + local is_buftype_disabled = vim.tbl_contains(self.options.disabled_buftypes, buftype) + local is_floating = '' ~= vim.api.nvim_win_get_config(winnr).relative + + return is_floating or is_buftype_disabled or is_filetype_disabled +end + +vim.cmd([[ + function! LualineSwitchWindow(win_number, mouseclicks, mousebutton, modifiers) + execute a:win_number . 'wincmd w' + endfunction +]]) + +return M diff --git a/lua/lualine/components/windows/window.lua b/lua/lualine/components/windows/window.lua new file mode 100644 index 0000000..73f7301 --- /dev/null +++ b/lua/lualine/components/windows/window.lua @@ -0,0 +1,35 @@ +local Window = require('lualine.components.buffers.buffer'):extend() + +---intialize a new buffer from opts +---@param opts table +function Window:init(opts) + assert(opts.winnr, 'Cannot create Window without winnr') + opts.bufnr = vim.api.nvim_win_get_buf(opts.winnr) + + Window.super.init(self, opts) + + self.winnr = opts.winnr + self.win_number = vim.api.nvim_win_get_number(self.winnr) +end + +function Window:is_current() + return vim.api.nvim_get_current_win() == self.winnr +end + +function Window:apply_mode(name) + if self.options.mode == 0 then + return string.format('%s%s%s', self.icon, name, self.modified_icon) + end + + if self.options.mode == 1 then + return string.format('%s %s%s', self.win_number, self.icon, self.modified_icon) + end + + return string.format('%s %s%s%s', self.win_number, self.icon, name, self.modified_icon) +end + +function Window:configure_mouse_click(name) + return string.format('%%%s@LualineSwitchWindow@%s%%T', self.win_number, name) +end + +return Window diff --git a/tests/spec/lualine_spec.lua b/tests/spec/lualine_spec.lua index 82661c4..dc0d8df 100644 --- a/tests/spec/lualine_spec.lua +++ b/tests/spec/lualine_spec.lua @@ -752,6 +752,55 @@ describe('Lualine', function() ]===]) end) end) + + describe('windows component', function() + it('works', function() + local conf = vim.deepcopy(tab_conf) + conf.tabline.lualine_a = { { 'windows', max_length = 1e3, mode = 2, icons_enabled = false } } + vim.cmd('e ' .. 'a.txt') + vim.cmd('tabe ' .. 'b.txt') + vim.cmd('vsplit ' .. 'c.txt') + vim.cmd('tabe ' .. 'd.txt') + require('lualine').setup(conf) + require('lualine').statusline() + tabline:expect([===[ + highlights = { + 1: lualine_a_windows_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_windows_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 d.txt } + {2:} + {3: }| + ]===]) + + vim.cmd('tabprev') + tabline:expect([===[ + highlights = { + 1: lualine_a_windows_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_windows_active_to_lualine_a_windows_inactive = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_a_windows_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } + 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 c.txt } + {2:} + {3: 2 b.txt } + {4: }| + ]===]) + + vim.cmd('tabprev') + tabline:expect([===[ + highlights = { + 1: lualine_a_windows_active = { bg = "#a89984", bold = true, fg = "#282828" } + 2: lualine_transitional_lualine_a_windows_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } + } + |{1: 1 a.txt } + {2:} + {3: }| + ]===]) + end) + end) end) describe('diagnostics', function()