Skip to content

Commit 5c209e5

Browse files
Geliocseickel
authored andcommitted
fix: delete old buffers on rename/move
When renaming or moving files/directories, neo-tree used to change the name of renamed buffers while retaining the same buffer numbers. It did not delete the old buffers and open new ones for editing. This ended up crashing Neovim in the following scenario: * a `BufWritePre` autocmd was created to call `vim.lsp.buf.format({ async = false })` * there was an LSP client registered for the buffer * the buffer was opened and then put in the background (e.g. by `:Neotree position=current`) * the file was renamed/moved in neo-tree The operation that caused the Neovim crash was the `silent! write!` that neo-tree executes on the buffer right after it is renamed. During that write, synchronous formatting is attempted in the `BufWritePre` autocmd. My assumption is that the LSP server does not get a notification about the file rename and sends some invalid formatting information back to Neovim. Neovim cannot interpret it and crashes. The crash does not happen if the renamed buffer is currently visible, rather than being hidden (e.g. when the renamed buffer is in one window and neo-tree is opened in a vertical split). The approach to handling file renamed used in this commit is the same as used in the `vim.lsp.util.rename` [0]. Instead of reusing the buffers in Neovim before and after rename, neo-tree deletes old buffers for the file paths before the rename, and opens new buffers for files after the rename. If old buffers were shown in some Neovim window, the code opens the new buffers in these windows. The code that handles modified buffers during rename calls `silent! write!` on the new buffers. This no longer crashes Neovim. The downside is that LSP clients are not attached at the moment `write!` is called, which means the files after the rename will not be formatted after neo-tree saves the modified content. Overall, this seems like a lesser evil compared to Neovim crashing on each rename. Related to #308 [0]: https://github.com/neovim/neovim/blob/234b8c5f3d57294dda06dbc6c1760e5983bd2c19/runtime/lua/vim/lsp/util.lua#L751-L775
1 parent b6d9af5 commit 5c209e5

File tree

1 file changed

+22
-5
lines changed

1 file changed

+22
-5
lines changed

Diff for: lua/neo-tree/sources/filesystem/lib/fs_actions.lua

+22-5
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,18 @@ local function clear_buffer(path)
3939
end
4040
end
4141

42+
---Opens new_buf in each window that has old_buf currently open.
43+
---Useful during file rename.
44+
---@param old_buf number
45+
---@param new_buf number
46+
local function replace_buffer_in_windows(old_buf, new_buf)
47+
for _, win in ipairs(vim.api.nvim_list_wins()) do
48+
if vim.api.nvim_win_get_buf(win) == old_buf then
49+
vim.api.nvim_win_set_buf(win, new_buf)
50+
end
51+
end
52+
end
53+
4254
local function rename_buffer(old_path, new_path)
4355
local force_save = function()
4456
vim.cmd("silent! write!")
@@ -54,15 +66,21 @@ local function rename_buffer(old_path, new_path)
5466
new_buf_name = new_path .. buf_name:sub(#old_path + 1)
5567
end
5668
if utils.truthy(new_buf_name) then
57-
vim.api.nvim_buf_set_name(buf, new_buf_name)
58-
-- Force write to avoid E13 error
69+
local new_buf = vim.fn.bufadd(new_buf_name)
70+
vim.fn.bufload(new_buf)
71+
vim.api.nvim_buf_set_option(new_buf, "buflisted", true)
72+
replace_buffer_in_windows(buf, new_buf)
73+
5974
if vim.api.nvim_buf_get_option(buf, "buftype") == "" then
6075
local modified = vim.api.nvim_buf_get_option(buf, "modified")
6176
if modified then
77+
local old_buffer_lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
78+
vim.api.nvim_buf_set_lines(new_buf, 0, -1, false, old_buffer_lines)
79+
6280
local msg = buf_name .. " has been modified. Save under new name? (y/n) "
6381
inputs.confirm(msg, function(confirmed)
6482
if confirmed then
65-
vim.api.nvim_buf_call(buf, force_save)
83+
vim.api.nvim_buf_call(new_buf, force_save)
6684
log.trace("Force saving renamed buffer with changes")
6785
else
6886
vim.cmd("echohl WarningMsg")
@@ -72,10 +90,9 @@ local function rename_buffer(old_path, new_path)
7290
vim.cmd("echohl NONE")
7391
end
7492
end)
75-
else
76-
vim.api.nvim_buf_call(buf, force_save)
7793
end
7894
end
95+
vim.api.nvim_buf_delete(buf, { force = true })
7996
end
8097
end
8198
end

0 commit comments

Comments
 (0)