diff --git a/lua/bufferline.lua b/lua/bufferline.lua index 7bc28950..ab437f97 100644 --- a/lua/bufferline.lua +++ b/lua/bufferline.lua @@ -54,16 +54,10 @@ local function bufferline() local is_tabline = config:is_tabline() local components = is_tabline and tabpages.get_components(state) or buffers.get_components(state) - -- NOTE: keep track of the previous state so it can be used for sorting - -- specifically to position newly opened buffers next to the buffer that was previously open - local prev_idx, prev_components = state.current_element_index, state.components - local function sorter(list) - return sorters.sort(list, { - current_index = prev_idx, - prev_components = prev_components, - custom_sort = state.custom_sort, - }) + -- the user has manually sorted the buffers don't try to re-sort them + if state.custom_sort then return list end + return sorters.sort(list) end local _, current_idx = utils.find(function(component) return component:current() end, components) diff --git a/lua/bufferline/buffers.lua b/lua/bufferline/buffers.lua index 25f695c5..0f48bbd1 100644 --- a/lua/bufferline/buffers.lua +++ b/lua/bufferline/buffers.lua @@ -12,25 +12,68 @@ local M = {} local api = vim.api ---- sorts buf_names in place, but doesn't add/remove any values +--- sorts buf_nums in place, according to state.custom_sort --- @param buf_nums number[] ---- @param sorted number[] ---- @return number[] -local function get_updated_buffers(buf_nums, sorted) - if not sorted then return buf_nums end - local nums = { unpack(buf_nums) } +--- @param state bufferline.State +local function sort_buffers(buf_nums, state) + local opts = config.options + local sort_by = opts.sort_by + local sorted = state.custom_sort or {} local reverse_lookup_sorted = utils.tbl_reverse_lookup(sorted) - --- a comparator that sorts buffers by their position in sorted - local sort_by_sorted = function(buf_id_1, buf_id_2) - local buf_1_rank = reverse_lookup_sorted[buf_id_1] - local buf_2_rank = reverse_lookup_sorted[buf_id_2] - if not buf_1_rank then return false end - if not buf_2_rank then return true end - return buf_1_rank < buf_2_rank + local comp + if sort_by == "insert_at_end" then + -- a comparator that sorts buffers by their position in sorted + -- will also ensure any new buffers are placed at the end + comp = function(buf_a, buf_b) + local a_index = reverse_lookup_sorted[buf_a] + local b_index = reverse_lookup_sorted[buf_b] + if a_index and b_index then + return a_index < b_index + elseif a_index and not b_index then + return true + elseif not a_index and b_index then + return false + end + return buf_a < buf_b + end + elseif sort_by == "insert_after_current" then + local current_index = state.current_element_index or 1 + -- a comparator that sorts buffers by their position in sorted + -- will also ensure any new buffers are placed after the current buffer + comp = function(buf_a, buf_b) + local a_index = reverse_lookup_sorted[buf_a] + local b_index = reverse_lookup_sorted[buf_b] + if a_index and b_index then + -- If both buffers are either before or after (inclusive) the current buffer, respect the sorted order. + if (a_index - current_index) * (b_index - current_index) >= 0 then return a_index < b_index end + return a_index < current_index + elseif a_index and not b_index then + return a_index <= current_index + elseif not a_index and b_index then + return current_index < b_index + end + return buf_a < buf_b + end + else + -- if there's no custom sort, we have nothing to do + if not state.custom_sort then return end + + -- a comparator that sorts buffers by their position in sorted + comp = function(buf_id_1, buf_id_2) + local buf_1_rank = reverse_lookup_sorted[buf_id_1] + local buf_2_rank = reverse_lookup_sorted[buf_id_2] + if not buf_1_rank then return false end + if not buf_2_rank then return true end + return buf_1_rank < buf_2_rank + end end - table.sort(nums, sort_by_sorted) - return nums + + table.sort(buf_nums, comp) + + -- save the new custom sort + state.custom_sort = buf_nums + if opts.persist_buffer_sort then utils.save_positions(state.custom_sort) end end ---Filter the buffers to show based on the user callback passed in @@ -54,7 +97,7 @@ function M.get_components(state) local buf_nums = utils.get_valid_buffers() local filter = options.custom_filter buf_nums = filter and apply_buffer_filter(buf_nums, filter) or buf_nums - buf_nums = get_updated_buffers(buf_nums, state.custom_sort) + sort_buffers(buf_nums, state) pick.reset() duplicates.reset() diff --git a/lua/bufferline/commands.lua b/lua/bufferline/commands.lua index 7bea9f95..da4f8bd6 100644 --- a/lua/bufferline/commands.lua +++ b/lua/bufferline/commands.lua @@ -8,25 +8,13 @@ local utils = lazy.require("bufferline.utils") ---@module "bufferline.utils" local config = lazy.require("bufferline.config") ---@module "bufferline.config" local groups = lazy.require("bufferline.groups") ---@module "bufferline.groups" local sorters = lazy.require("bufferline.sorters") ---@module "bufferline.sorters" -local constants = lazy.require("bufferline.constants") ---@module "bufferline.constants" local pick = lazy.require("bufferline.pick") ---@module "bufferline.pick" local M = {} -local positions_key = constants.positions_key - local fmt = string.format local api = vim.api ----@param ids number[] -local function save_positions(ids) vim.g[positions_key] = table.concat(ids, ",") end - ---- @param elements bufferline.TabElement[] ---- @return number[] -local function get_ids(elements) - return vim.tbl_map(function(item) return item.id end, elements) -end - --- open the current element ---@param id number local function open_element(id) @@ -170,9 +158,9 @@ function M.move_to(to_index, from_index) local destination_buf = state.components[next_index] state.components[next_index] = item state.components[index] = destination_buf - state.custom_sort = get_ids(state.components) + state.custom_sort = utils.get_ids(state.components) local opts = config.options - if opts.persist_buffer_sort then save_positions(state.custom_sort) end + if opts.persist_buffer_sort then utils.save_positions(state.custom_sort) end ui.refresh() end end @@ -268,10 +256,10 @@ end --- @param sort_by (string|function)? function M.sort_by(sort_by) if next(state.components) == nil then return utils.notify("Unable to find elements to sort, sorry", "warn") end - sorters.sort(state.components, { sort_by = sort_by }) - state.custom_sort = get_ids(state.components) + sorters.sort(state.components, sort_by) + state.custom_sort = utils.get_ids(state.components) local opts = config.options - if opts.persist_buffer_sort then save_positions(state.custom_sort) end + if opts.persist_buffer_sort then utils.save_positions(state.custom_sort) end ui.refresh() end diff --git a/lua/bufferline/models.lua b/lua/bufferline/models.lua index f7f5a6db..e4927a77 100644 --- a/lua/bufferline/models.lua +++ b/lua/bufferline/models.lua @@ -196,14 +196,6 @@ end function Buffer:current() return api.nvim_get_current_buf() == self.id end ---- If the buffer is already part of state then it is existing ---- otherwise it is new ----@param components bufferline.TabElement[] ----@return boolean -function Buffer:previously_opened(components) - return utils.find(function(component) return component.id == self.id end, components) ~= nil -end - --- Find and return the index of the matching buffer (by id) in the list in state ---@param components bufferline.TabElement[] function Buffer:find_index(components) @@ -212,9 +204,6 @@ function Buffer:find_index(components) end end ----@param components bufferline.TabElement[] -function Buffer:newly_opened(components) return not self:previously_opened(components) end - function Buffer:visible() return fn.bufwinnr(self.id) > 0 end --- @param depth integer diff --git a/lua/bufferline/sorters.lua b/lua/bufferline/sorters.lua index 47ae376e..014671f3 100644 --- a/lua/bufferline/sorters.lua +++ b/lua/bufferline/sorters.lua @@ -78,55 +78,13 @@ local function sort_by_tabs(a, b) return buf_a_tabnr < buf_b_tabnr end ----@param components bufferline.TabElement[] ----@return bufferline.Sorter -local sort_by_new_after_existing = function(components) - return function(item_a, item_b) - if item_a:newly_opened(components) and item_b:previously_opened(components) then - return false - elseif item_a:previously_opened(components) and item_b:newly_opened(components) then - return true - end - return item_a.id < item_b.id - end -end - ----@param prev_components bufferline.TabElement[] ----@return bufferline.Sorter -local sort_by_new_after_current = function(prev_components, current_index) - return function(item_a, item_b) - local a_index = item_a:find_index(prev_components) - local a_is_new = item_a:newly_opened(prev_components) - local b_index = item_b:find_index(prev_components) - local b_is_new = item_b:newly_opened(prev_components) - current_index = current_index or 1 - if not a_is_new and not b_is_new then - -- If both buffers are either before or after (inclusive) the current buffer, respect the current order. - if (a_index - current_index) * (b_index - current_index) >= 0 then return a_index < b_index end - return a_index < current_index - elseif not a_is_new and b_is_new then - return a_index <= current_index - elseif a_is_new and not b_is_new then - return current_index < b_index - end - return item_a.id < item_b.id - end -end - --- sorts a list of buffers in place --- @param elements bufferline.TabElement[] ---- @param opts bufferline.SorterOptions -function M.sort(elements, opts) - opts = opts or {} - local sort_by = opts.sort_by or config.options.sort_by - -- the user has manually sorted the buffers don't try to re-sort them - if opts.custom_sort then return elements end +--- @param sort_by (string|function)? +function M.sort(elements, sort_by) + sort_by = sort_by or config.options.sort_by if sort_by == "none" then return elements - elseif sort_by == "insert_after_current" then - table.sort(elements, sort_by_new_after_current(opts.prev_components, opts.current_index)) - elseif sort_by == "insert_at_end" then - table.sort(elements, sort_by_new_after_existing(opts.prev_components)) elseif sort_by == "extension" then table.sort(elements, sort_by_extension) elseif sort_by == "directory" then diff --git a/lua/bufferline/types.lua b/lua/bufferline/types.lua index f7aeb4eb..5362c394 100644 --- a/lua/bufferline/types.lua +++ b/lua/bufferline/types.lua @@ -157,8 +157,6 @@ ---@field public ancestor bufferline.AncestorSearch ---@field public __ancestor bufferline.AncestorSearch ---@field public find_index fun(Buffer, BufferlineState): integer? ----@field public newly_opened fun(Buffer, BufferlineState): boolean ----@field public previously_opened fun(Buffer, BufferlineState): boolean ---@alias bufferline.ComponentsByGroup (bufferline.Group | bufferline.Component[])[] @@ -221,9 +219,3 @@ ---@field right_offset_size number ---@alias bufferline.Sorter fun(buf_a: bufferline.Buffer, buf_b: bufferline.Buffer): boolean - ----@class bufferline.SorterOptions ----@field sort_by (string|function)? ----@field current_index integer? ----@field custom_sort boolean? ----@field prev_components bufferline.TabElement[] diff --git a/lua/bufferline/utils/init.lua b/lua/bufferline/utils/init.lua index 4c06c08c..f5c96e32 100644 --- a/lua/bufferline/utils/init.lua +++ b/lua/bufferline/utils/init.lua @@ -154,6 +154,17 @@ function M.notify(msg, level, opts) vim.schedule(function() vim.notify(msg, level, nopts) end) end +local positions_key = constants.positions_key + +---@param ids number[] +function M.save_positions(ids) vim.g[positions_key] = table.concat(ids, ",") end + +--- @param elements bufferline.TabElement[] +--- @return number[] +function M.get_ids(elements) + return vim.tbl_map(function(item) return item.id end, elements) +end + ---Get an icon for a filetype using either nvim-web-devicons or vim-devicons ---if using the lua plugin this also returns the icon's highlights ---@param opts bufferline.IconFetcherOpts