Skip to content

Commit

Permalink
fix: store paths in g:BufferlinePositions (#780)
Browse files Browse the repository at this point in the history
This fixes an issue where restoring the custom sort order from a saved session doesn't work correctly most of the time, because it stores buffer ids in g:BufferlinePositions which are not preserved by :mksession. The fix is to store the full paths of the buffers instead. vim.json is required to stringify the list as :mksession only stores string/number values.
  • Loading branch information
AThePeanut4 authored Jun 28, 2023
1 parent fe77474 commit 2f391fd
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 28 deletions.
2 changes: 1 addition & 1 deletion lua/bufferline.lua
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ local function setup_autocommands(conf)
api.nvim_create_autocmd("SessionLoadPost", {
pattern = "*",
group = BUFFERLINE_GROUP,
callback = function() state.restore_positions() end,
callback = function() state.custom_sort = utils.restore_positions() end,
})
end
if not options.always_show_bufferline then
Expand Down
20 changes: 4 additions & 16 deletions lua/bufferline/commands.lua
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -269,9 +257,9 @@ end
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)
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

Expand Down
10 changes: 0 additions & 10 deletions lua/bufferline/state.lua
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
local M = {}

local lazy = require("bufferline.lazy")
local constants = lazy.require("bufferline.constants") ---@module "bufferline.constants"
local utils = lazy.require("bufferline.utils") ---@module "bufferline.utils"

-----------------------------------------------------------------------------//
Expand All @@ -21,15 +20,6 @@ local state = {
right_offset_size = 0,
}

function M.restore_positions()
local str = vim.g[constants.positions_key]
if not str then return str end
-- these are converted to strings when stored
-- so have to be converted back before usage
local ids = vim.split(str, ",")
if ids and #ids > 0 then state.custom_sort = vim.tbl_map(tonumber, ids) end
end

---@param list bufferline.Component[]
---@return bufferline.Component[]
local function filter_invisible(list)
Expand Down
2 changes: 1 addition & 1 deletion lua/bufferline/types.lua
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@
---@field is_picking boolean
---@field visible_components bufferline.Component[]
---@field __components bufferline.Component[]
---@field custom_sort number[]
---@field custom_sort number[]?
---@field left_offset_size number
---@field right_offset_size number

Expand Down
24 changes: 24 additions & 0 deletions lua/bufferline/utils/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,30 @@ function M.notify(msg, level, opts)
vim.schedule(function() vim.notify(msg, level, nopts) end)
end

---@return number[]?
function M.restore_positions()
local str = vim.g[constants.positions_key]
local ok, paths = pcall(vim.json.decode, str)
if not ok or type(paths) ~= "table" or #paths == 0 then return nil end
local ids = vim.tbl_map(function(path)
local escaped = vim.fn.fnameescape(path)
return vim.fn.bufnr("^" .. escaped .. "$" --[[@as integer]])
end, paths)
return vim.tbl_filter(function(id) return id ~= -1 end, ids)
end

---@param ids number[]
function M.save_positions(ids)
local paths = vim.tbl_map(function(id) return vim.api.nvim_buf_get_name(id) end, ids)
vim.g[constants.positions_key] = vim.json.encode(paths)
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
Expand Down
47 changes: 47 additions & 0 deletions tests/utils_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,51 @@ describe("Utils tests", function()
local truncated = utils.truncate_name("filename.md.md", 13)
assert.is_equal(truncated, "filename.md" .. constants.ELLIPSIS)
end)

it("should save/restore positions correctly", function()
-- remove existing buffers
vim.cmd("silent %bwipeout!")

local names = { "c.txt", "a.txt", "d.txt", "e.txt", "b.txt" }
local bufs = {}
for _, name in ipairs(names) do
vim.cmd.edit(name)
bufs[name] = api.nvim_get_current_buf()
end

local ids = {
bufs["a.txt"],
bufs["b.txt"],
bufs["c.txt"],
bufs["d.txt"],
bufs["e.txt"],
}

utils.save_positions(ids)

assert.same(utils.restore_positions(), ids)

-- restore_positions should not return invalid bufids

vim.cmd("bwipeout! " .. bufs["c.txt"])

ids = {
bufs["a.txt"],
bufs["b.txt"],
bufs["d.txt"],
bufs["e.txt"],
}
assert.same(utils.restore_positions(), ids)

vim.g[constants.positions_key] = '["INVALID_PATH"]'
assert.same(utils.restore_positions(), {})

-- empty or invalid JSON should return nil

vim.g[constants.positions_key] = "[]"
assert.is_equal(utils.restore_positions(), nil)

vim.g[constants.positions_key] = ""
assert.is_equal(utils.restore_positions(), nil)
end)
end)

0 comments on commit 2f391fd

Please sign in to comment.