diff --git a/.luarc.json b/.luarc.json index 24c625c9..2de2470f 100644 --- a/.luarc.json +++ b/.luarc.json @@ -4,5 +4,15 @@ "assign-type-mismatch", "cast-local-type", "missing-parameter" + ], + "Lua.diagnostics.globals": [ + "vim", + "P", + "describe", + "it", + "before_each", + "after_each", + "packer_plugins", + "___bufferline_private" ] } \ No newline at end of file diff --git a/README.md b/README.md index ddadcb8c..1d5c2e8f 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ It was inspired by a screenshot of DOOM Emacs using [centaur tabs](https://githu ## Requirements -- Neovim 0.7+ +- Neovim 0.8+ - A patched font (see [nerd fonts](https://github.com/ryanoasis/nerd-fonts)) - A colorscheme (either your custom highlight or a maintained one somewhere) @@ -53,7 +53,7 @@ If you'd like to use an older version of the plugin compatible with nvim-0.6.1 a ```lua -- using packer.nvim -use {'akinsho/bufferline.nvim', tag = "v2.*", requires = 'kyazdani42/nvim-web-devicons'} +use {'akinsho/bufferline.nvim', tag = "v3.*", requires = 'kyazdani42/nvim-web-devicons'} ``` **Vimscript** @@ -61,7 +61,7 @@ use {'akinsho/bufferline.nvim', tag = "v2.*", requires = 'kyazdani42/nvim-web-de ```vim Plug 'kyazdani42/nvim-web-devicons' " Recommended (for coloured icons) " Plug 'ryanoasis/vim-devicons' Icons without colours -Plug 'akinsho/bufferline.nvim', { 'tag': 'v2.*' } +Plug 'akinsho/bufferline.nvim', { 'tag': 'v3.*' } ``` ## Usage @@ -117,10 +117,8 @@ see: `:h bufferline-styling` **NOTE**: this is only available for _neovim 0.8+ (nightly) ONLY_ and is still **experimental** - ![hover-event-preview](https://user-images.githubusercontent.com/22454918/189106657-163b0550-897c-42c8-a571-d899bdd69998.gif) - see `:help bufferline-hover-events` for more information on configuration --- diff --git a/doc/bufferline.txt b/doc/bufferline.txt index 76bd7ce4..9bf68122 100644 --- a/doc/bufferline.txt +++ b/doc/bufferline.txt @@ -26,6 +26,7 @@ Mappings...............................: |bufferline-mappings| Highlights.............................: |bufferline-highlights| Mouse actions..........................: |bufferline-mouse-actions| Custom areas...........................: |bufferline-custom-areas| +Working with Elements..................: |bufferline-working-with-elements| Issues.................................: |bufferline-issues| @@ -499,7 +500,7 @@ are: * `BufferLineCloseLeft` - close all visible buffers to the left of the current buffer -NOTE: these commands will skip unwritten buffers, you can add `!` to close without writing. +These commands apply the configured `close_command` to each of the corresponding buffers. ============================================================================== CUSTOM-FUNCTIONS *bufferline-functions* @@ -1067,6 +1068,39 @@ to be shown in a list of tables. For example: Please note that this function will be called a lot and should be as inexpensive as possible so it does not block rendering the tabline. +============================================================================== +WORKING WITH ELEMENTS *bufferline-working-with-elements* + +Bufferline exposes some information about the buffers it shows will allow you +to implement your own custom functionality. Note that this will not include any +buffers that are filtered out of bufferline, making it handy for writing +functions that need to ignore special buffers. + +The output has the following structure: + +> + { + mode = "tabs" -- depends on your config setting for mode + elements = { + {id = 1, name = "hi.txt", path = "/tmp/folder/hi.txt"}, + -- and so on for all open buffers + } + } +< + +Here's an example that will let you close all open buffers. + +> + function close_all_buffers () + for _, e in ipairs(bufferline.get_elements().elements) do + vim.schedule(function() + vim.cmd("bd ".. e.id) + end) + end + end +< + + ============================================================================== COLORSCHEME DEVELOPMENT *bufferline-colorscheme-development* diff --git a/lua/bufferline.lua b/lua/bufferline.lua index 276ff5e9..0be6bb02 100644 --- a/lua/bufferline.lua +++ b/lua/bufferline.lua @@ -24,6 +24,10 @@ local highlights = lazy.require("bufferline.highlights") --- @module "bufferline.hover" local hover = lazy.require("bufferline.hover") +-- @v:lua@ in the tabline only supports global functions, so this is +-- the only way to add click handlers without autoloaded vimscript functions +_G.___bufferline_private = _G.___bufferline_private or {} -- to guard against reloads + local api = vim.api local positions_key = constants.positions_key @@ -36,11 +40,9 @@ local M = { cycle = commands.cycle, sort_by = commands.sort_by, pick_buffer = commands.pick, - handle_close = commands.handle_close, - handle_click = commands.handle_click, + get_elements = commands.get_elements, close_with_pick = commands.close_with_pick, close_in_direction = commands.close_in_direction, - handle_group_click = commands.handle_group_click, -- @deprecate go_to_buffer = commands.go_to, sort_buffers_by = commands.sort_by, @@ -227,16 +229,8 @@ local function setup_commands() cmd("BufferLinePickClose", function() M.close_buffer_with_pick() end, {}) cmd("BufferLineCycleNext", function() M.cycle(1) end, {}) cmd("BufferLineCyclePrev", function() M.cycle(-1) end, {}) - cmd( - "BufferLineCloseRight", - function(opts) M.close_in_direction("right", opts.bang) end, - { bang = true } - ) - cmd( - "BufferLineCloseLeft", - function(opts) M.close_in_direction("left", opts.bang) end, - { bang = true } - ) + cmd("BufferLineCloseRight", function() M.close_in_direction("right") end, {}) + cmd("BufferLineCloseLeft", function() M.close_in_direction("left") end, {}) cmd("BufferLineMoveNext", function() M.move(1) end, {}) cmd("BufferLineMovePrev", function() M.move(-1) end, {}) cmd("BufferLineSortByExtension", function() M.sort_buffers_by("extension") end, {}) diff --git a/lua/bufferline/commands.lua b/lua/bufferline/commands.lua index 6a29e608..569239dc 100644 --- a/lua/bufferline/commands.lua +++ b/lua/bufferline/commands.lua @@ -45,16 +45,6 @@ local function open_element(id) end end ----@param id number -local function delete_element(id, force) - force = vim.F.if_nil(force, false) - if config:is_tabline() then - vim.cmd("tabclose " .. id) - else - api.nvim_buf_delete(id, { force = force }) - end -end - ---Get the current element i.e. tab or buffer ---@return number local function get_current_element() @@ -79,18 +69,27 @@ local function handle_user_command(command, id) end ---@param position number -function M.handle_group_click(position) +local function handle_group_click(position) groups.toggle_hidden(position) ui.refresh() end ---@param id number -function M.handle_close(id) +local function handle_close(id) local options = config.options local close = options.close_command handle_user_command(close, id) end +---@param id number +local function delete_element(id) + if config:is_tabline() then + vim.cmd("tabclose " .. id) + else + handle_close(id) + end +end + ---@param id number function M.handle_win_click(id) local win_id = vim.fn.bufwinid(id) @@ -105,7 +104,7 @@ local cmds = { ---Handler for each type of mouse click ---@param id number ---@param button string -function M.handle_click(id, button) +local function handle_click(id, _, button) local options = config.options if id then handle_user_command(options[cmds[button]], id) end end @@ -121,7 +120,9 @@ end function M.pick() pick.choose_then(open_element) end function M.close_with_pick() - pick.choose_then(function(id) M.handle_close(id) end) + pick.choose_then(function(id) + handle_close(id) + end) end --- Open a element based on it's visible position in the list @@ -190,10 +191,19 @@ function M.cycle(direction) open_element(item.id) end +function M.get_elements() + return { + mode = config.options.mode, + elements = vim.tbl_map(function(elem) + return {id = elem.id, name = elem.name, path = elem.path} + end, state.components) + } +end + ---@alias Direction "'left'" | "'right'" ---Close all elements to the left or right of the current buffer ---@param direction Direction -function M.close_in_direction(direction, force) +function M.close_in_direction(direction) local index = M.get_current_element_index(state) if not index then return end local length = #state.components @@ -203,9 +213,10 @@ function M.close_in_direction(direction, force) local start = direction == "left" and 1 or index + 1 local _end = direction == "left" and index - 1 or length for _, item in ipairs(vim.list_slice(state.components, start, _end)) do - delete_element(item.id, force) + delete_element(item.id) end end + ui.refresh() end --- sorts all elements @@ -221,4 +232,8 @@ function M.sort_by(sort_by) ui.refresh() end +_G.___bufferline_private.handle_close = handle_close +_G.___bufferline_private.handle_click = handle_click +_G.___bufferline_private.handle_group_click = handle_group_click + return M diff --git a/lua/bufferline/groups.lua b/lua/bufferline/groups.lua index 003fcac0..98b15ae6 100644 --- a/lua/bufferline/groups.lua +++ b/lua/bufferline/groups.lua @@ -104,7 +104,7 @@ function separator.tab(group, hls, count) local hl = hls.fill.hl_group local indicator_hl = hls.buffer.hl_group local indicator = { - { higlight = hl, text = padding }, + { highlight = hl, text = padding }, { highlight = indicator_hl, text = padding .. group.name .. count .. padding }, { highlight = hl, text = padding }, } diff --git a/lua/bufferline/ui.lua b/lua/bufferline/ui.lua index 8822c6a9..d2eb9b74 100644 --- a/lua/bufferline/ui.lua +++ b/lua/bufferline/ui.lua @@ -163,10 +163,8 @@ end ---@param id number ---@param component Segment function M.make_clickable(func_name, id, component) - -- v:lua does not support function references in vimscript so - -- the only way to implement this is using autoload vimscript functions component.attr = component.attr or {} - component.attr.prefix = "%" .. id .. "@nvim_bufferline#" .. func_name .. "@" + component.attr.prefix = "%" .. id .. "@v:lua.___bufferline_private." .. func_name .. "@" -- the %X works as a closing label. @see :h tabline component.attr.suffix = "%X" return component @@ -301,8 +299,10 @@ local function get_close_icon(buf_id, context) end local buffer_close_icon = options.buffer_close_icon local close_button_hl = context.current_highlights.close_button - if not options.show_buffer_close_icons then return end - return M.make_clickable("handle_close_buffer", buf_id, { + if not options.show_buffer_close_icons then + return + end + return M.make_clickable("handle_close", buf_id, { text = buffer_close_icon, highlight = close_button_hl, }) diff --git a/tests/bufferline_spec.lua b/tests/bufferline_spec.lua index c92d7059..ff5664ad 100644 --- a/tests/bufferline_spec.lua +++ b/tests/bufferline_spec.lua @@ -149,7 +149,7 @@ describe("Bufferline tests:", function() left_mouse_command = "vertical sbuffer %d", }, }) - bufferline.handle_click(bufnum, "l") + ___bufferline_private.handle_click(bufnum, nil, "l") vim.wait(10) assert.is_equal(#vim.api.nvim_list_wins(), 2) end) @@ -161,7 +161,7 @@ describe("Bufferline tests:", function() middle_mouse_command = function(bufid) vim.bo[bufid].filetype = "test" end, }, }) - bufferline.handle_click(bufnum, "m") + ___bufferline_private.handle_click(bufnum, nil, "m") assert.is_equal(vim.bo[bufnum].filetype, "test") end) @@ -172,7 +172,7 @@ describe("Bufferline tests:", function() right_mouse_command = "setfiletype egg", }, }) - bufferline.handle_click(bufnum, "r") + ___bufferline_private.handle_click(bufnum, nil, "r") vim.wait(10) assert.is_equal(vim.bo.filetype, "egg") end) @@ -186,7 +186,7 @@ describe("Bufferline tests:", function() close_command = function(bufid) count = count + bufid end, }, }) - bufferline.handle_close(bufnum) + ___bufferline_private.handle_close(bufnum) assert.is_equal(count, expected) end) end) @@ -194,7 +194,13 @@ describe("Bufferline tests:", function() -- FIXME: nvim_bufferline() needs to be manually called describe("commands - ", function() it("should close buffers to the right of the current buffer", function() - bufferline.setup() + bufferline.setup({ + options = { + close_command = function(bufid) + vim.api.nvim_buf_delete(bufid, { force = true }) + end + } + }) vim.cmd("file! a.txt") vim.cmd("edit b.txt") vim.cmd("edit c.txt") @@ -209,7 +215,13 @@ describe("Bufferline tests:", function() end) it("should close buffers to the left of the current buffer", function() - bufferline.setup() + bufferline.setup({ + options = { + close_command = function(bufid) + vim.api.nvim_buf_delete(bufid, { force = true }) + end + } + }) vim.cmd("edit! a.txt") vim.cmd("edit b.txt") vim.cmd("edit c.txt") @@ -225,23 +237,6 @@ describe("Bufferline tests:", function() bufs = vim.api.nvim_list_bufs() assert.is_equal(1, #bufs) end) - - it("should close buffers in direction, but skip unwritten ones", function() - bufferline.setup() - vim.cmd("edit a.txt") - vim.cmd("edit b.txt") - vim.cmd("edit c.txt") - vim.cmd("edit d.txt") - vim.cmd("edit e.txt") - vim.api.nvim_put({ "some text" }, "", true, true) - local unwritten_buf = vim.api.nvim_get_current_buf() - nvim_bufferline() - - vim.cmd("edit c.txt") - local ok, err = pcall(bufferline.close_in_direction, "right", false) - assert.is_false(ok) - assert.is_truthy(err:match("Failed to unload buffer")) - end) end) describe("Theme - ", function()