Skip to content

Commit 2f391fd

Browse files
authored
fix: store paths in g:BufferlinePositions (#780)
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.
1 parent fe77474 commit 2f391fd

6 files changed

Lines changed: 77 additions & 28 deletions

File tree

lua/bufferline.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ local function setup_autocommands(conf)
116116
api.nvim_create_autocmd("SessionLoadPost", {
117117
pattern = "*",
118118
group = BUFFERLINE_GROUP,
119-
callback = function() state.restore_positions() end,
119+
callback = function() state.custom_sort = utils.restore_positions() end,
120120
})
121121
end
122122
if not options.always_show_bufferline then

lua/bufferline/commands.lua

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,13 @@ local utils = lazy.require("bufferline.utils") ---@module "bufferline.utils"
88
local config = lazy.require("bufferline.config") ---@module "bufferline.config"
99
local groups = lazy.require("bufferline.groups") ---@module "bufferline.groups"
1010
local sorters = lazy.require("bufferline.sorters") ---@module "bufferline.sorters"
11-
local constants = lazy.require("bufferline.constants") ---@module "bufferline.constants"
1211
local pick = lazy.require("bufferline.pick") ---@module "bufferline.pick"
1312

1413
local M = {}
1514

16-
local positions_key = constants.positions_key
17-
1815
local fmt = string.format
1916
local api = vim.api
2017

21-
---@param ids number[]
22-
local function save_positions(ids) vim.g[positions_key] = table.concat(ids, ",") end
23-
24-
--- @param elements bufferline.TabElement[]
25-
--- @return number[]
26-
local function get_ids(elements)
27-
return vim.tbl_map(function(item) return item.id end, elements)
28-
end
29-
3018
--- open the current element
3119
---@param id number
3220
local function open_element(id)
@@ -170,9 +158,9 @@ function M.move_to(to_index, from_index)
170158
local destination_buf = state.components[next_index]
171159
state.components[next_index] = item
172160
state.components[index] = destination_buf
173-
state.custom_sort = get_ids(state.components)
161+
state.custom_sort = utils.get_ids(state.components)
174162
local opts = config.options
175-
if opts.persist_buffer_sort then save_positions(state.custom_sort) end
163+
if opts.persist_buffer_sort then utils.save_positions(state.custom_sort) end
176164
ui.refresh()
177165
end
178166
end
@@ -269,9 +257,9 @@ end
269257
function M.sort_by(sort_by)
270258
if next(state.components) == nil then return utils.notify("Unable to find elements to sort, sorry", "warn") end
271259
sorters.sort(state.components, { sort_by = sort_by })
272-
state.custom_sort = get_ids(state.components)
260+
state.custom_sort = utils.get_ids(state.components)
273261
local opts = config.options
274-
if opts.persist_buffer_sort then save_positions(state.custom_sort) end
262+
if opts.persist_buffer_sort then utils.save_positions(state.custom_sort) end
275263
ui.refresh()
276264
end
277265

lua/bufferline/state.lua

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
local M = {}
22

33
local lazy = require("bufferline.lazy")
4-
local constants = lazy.require("bufferline.constants") ---@module "bufferline.constants"
54
local utils = lazy.require("bufferline.utils") ---@module "bufferline.utils"
65

76
-----------------------------------------------------------------------------//
@@ -21,15 +20,6 @@ local state = {
2120
right_offset_size = 0,
2221
}
2322

24-
function M.restore_positions()
25-
local str = vim.g[constants.positions_key]
26-
if not str then return str end
27-
-- these are converted to strings when stored
28-
-- so have to be converted back before usage
29-
local ids = vim.split(str, ",")
30-
if ids and #ids > 0 then state.custom_sort = vim.tbl_map(tonumber, ids) end
31-
end
32-
3323
---@param list bufferline.Component[]
3424
---@return bufferline.Component[]
3525
local function filter_invisible(list)

lua/bufferline/types.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,7 @@
216216
---@field is_picking boolean
217217
---@field visible_components bufferline.Component[]
218218
---@field __components bufferline.Component[]
219-
---@field custom_sort number[]
219+
---@field custom_sort number[]?
220220
---@field left_offset_size number
221221
---@field right_offset_size number
222222

lua/bufferline/utils/init.lua

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,30 @@ function M.notify(msg, level, opts)
154154
vim.schedule(function() vim.notify(msg, level, nopts) end)
155155
end
156156

157+
---@return number[]?
158+
function M.restore_positions()
159+
local str = vim.g[constants.positions_key]
160+
local ok, paths = pcall(vim.json.decode, str)
161+
if not ok or type(paths) ~= "table" or #paths == 0 then return nil end
162+
local ids = vim.tbl_map(function(path)
163+
local escaped = vim.fn.fnameescape(path)
164+
return vim.fn.bufnr("^" .. escaped .. "$" --[[@as integer]])
165+
end, paths)
166+
return vim.tbl_filter(function(id) return id ~= -1 end, ids)
167+
end
168+
169+
---@param ids number[]
170+
function M.save_positions(ids)
171+
local paths = vim.tbl_map(function(id) return vim.api.nvim_buf_get_name(id) end, ids)
172+
vim.g[constants.positions_key] = vim.json.encode(paths)
173+
end
174+
175+
--- @param elements bufferline.TabElement[]
176+
--- @return number[]
177+
function M.get_ids(elements)
178+
return vim.tbl_map(function(item) return item.id end, elements)
179+
end
180+
157181
---Get an icon for a filetype using either nvim-web-devicons or vim-devicons
158182
---if using the lua plugin this also returns the icon's highlights
159183
---@param opts bufferline.IconFetcherOpts

tests/utils_spec.lua

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,51 @@ describe("Utils tests", function()
1818
local truncated = utils.truncate_name("filename.md.md", 13)
1919
assert.is_equal(truncated, "filename.md" .. constants.ELLIPSIS)
2020
end)
21+
22+
it("should save/restore positions correctly", function()
23+
-- remove existing buffers
24+
vim.cmd("silent %bwipeout!")
25+
26+
local names = { "c.txt", "a.txt", "d.txt", "e.txt", "b.txt" }
27+
local bufs = {}
28+
for _, name in ipairs(names) do
29+
vim.cmd.edit(name)
30+
bufs[name] = api.nvim_get_current_buf()
31+
end
32+
33+
local ids = {
34+
bufs["a.txt"],
35+
bufs["b.txt"],
36+
bufs["c.txt"],
37+
bufs["d.txt"],
38+
bufs["e.txt"],
39+
}
40+
41+
utils.save_positions(ids)
42+
43+
assert.same(utils.restore_positions(), ids)
44+
45+
-- restore_positions should not return invalid bufids
46+
47+
vim.cmd("bwipeout! " .. bufs["c.txt"])
48+
49+
ids = {
50+
bufs["a.txt"],
51+
bufs["b.txt"],
52+
bufs["d.txt"],
53+
bufs["e.txt"],
54+
}
55+
assert.same(utils.restore_positions(), ids)
56+
57+
vim.g[constants.positions_key] = '["INVALID_PATH"]'
58+
assert.same(utils.restore_positions(), {})
59+
60+
-- empty or invalid JSON should return nil
61+
62+
vim.g[constants.positions_key] = "[]"
63+
assert.is_equal(utils.restore_positions(), nil)
64+
65+
vim.g[constants.positions_key] = ""
66+
assert.is_equal(utils.restore_positions(), nil)
67+
end)
2168
end)

0 commit comments

Comments
 (0)