Skip to content

Commit

Permalink
fix: crash when renaming to an open buffer
Browse files Browse the repository at this point in the history
This could happen in the following case:

- open files A and B
- open yazi
- rename A to B
- quit yazi

It would cause a crash when processing events, causing an error to be
printed to the user, and stopping the processing of events.
  • Loading branch information
mikavilpas committed Apr 18, 2024
1 parent 47fff41 commit 0ff9086
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 12 deletions.
3 changes: 2 additions & 1 deletion lua/yazi.lua
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ function M.yazi(config, path)

utils.on_yazi_exited(prev_win, win, yazi_buffer, config)

event_handling.process_events_emitted_from_yazi(config)
local events = utils.read_events_file(config.events_file_path)
event_handling.process_events_emitted_from_yazi(events)
end,
})

Expand Down
20 changes: 9 additions & 11 deletions lua/yazi/event_handling.lua
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,17 @@ function M.process_delete_event(event, remaining_events)
end
end

---@param rename_event YaziEventDataRenameOrMove
---@param rename_or_move_event YaziEventDataRenameOrMove
---@return RenameableBuffer[] "instructions for renaming the buffers (command pattern)"
function M.get_buffers_that_need_renaming_after_yazi_exited(rename_event)
function M.get_buffers_that_need_renaming_after_yazi_exited(
rename_or_move_event
)
local open_buffers = utils.get_open_buffers()

---@type table<integer, RenameableBuffer>
local renamed_buffers = {}

local event = rename_event
local event = rename_or_move_event
for _, buffer in ipairs(open_buffers) do
if buffer:matches_exactly(event.from) then
buffer:rename(event.to)
Expand All @@ -52,29 +54,25 @@ function M.get_buffers_that_need_renaming_after_yazi_exited(rename_event)
return vim.tbl_values(renamed_buffers)
end

---@param config YaziConfig
function M.process_events_emitted_from_yazi(config)
---@param events YaziEvent[]
function M.process_events_emitted_from_yazi(events)
-- process events emitted from yazi
local events = utils.read_events_file(config.events_file_path)

for i, event in ipairs(events) do
if event.type == 'rename' then
---@cast event YaziRenameEvent
local rename_instructions =
M.get_buffers_that_need_renaming_after_yazi_exited(event.data)
for _, instruction in ipairs(rename_instructions) do
vim.api.nvim_buf_set_name(instruction.bufnr, instruction.path.filename)
utils.rename_or_close_buffer(instruction)
end
elseif event.type == 'move' then
---@cast event YaziMoveEvent
for _, item in ipairs(event.data.items) do
local rename_instructions =
M.get_buffers_that_need_renaming_after_yazi_exited(item)
for _, instruction in ipairs(rename_instructions) do
vim.api.nvim_buf_set_name(
instruction.bufnr,
instruction.path.filename
)
utils.rename_or_close_buffer(instruction)
end
end
elseif event.type == 'delete' then
Expand Down
25 changes: 25 additions & 0 deletions lua/yazi/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,31 @@ function M.get_open_buffers()
return open_buffers
end

---@param path string
---@return boolean
function M.is_buffer_open(path)
local open_buffers = M.get_open_buffers()
for _, buffer in ipairs(open_buffers) do
if buffer:matches_exactly(path) then
return true
end
end

return false
end

---@param instruction RenameableBuffer
---@return nil
function M.rename_or_close_buffer(instruction)
-- If the target buffer is already open in neovim, just close the old buffer.
-- It causes an error to try to rename to a buffer that's already open.
if M.is_buffer_open(instruction.path.filename) then
vim.api.nvim_buf_delete(instruction.bufnr, {})
else
vim.api.nvim_buf_set_name(instruction.bufnr, instruction.path.filename)
end
end

---@param prev_win integer
---@param floating_window_id integer
---@param floating_window_buffer integer
Expand Down
60 changes: 60 additions & 0 deletions tests/yazi/rename_spec.lua
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
local assert = require('luassert')
local event_handling = require('yazi.event_handling')
local utils = require('yazi.utils')

describe('get_buffers_that_need_renaming_after_yazi_exited', function()
before_each(function()
Expand Down Expand Up @@ -73,3 +74,62 @@ describe('get_buffers_that_need_renaming_after_yazi_exited', function()
assert.is_equal(vim.tbl_count(rename_instructions), 0)
end)
end)

describe('process_events_emitted_from_yazi', function()
before_each(function()
-- clear all buffers
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
vim.api.nvim_buf_delete(buf, { force = true })
end
end)

it('closes a buffer that was renamed to another open buffer', function()
vim.fn.bufadd('/my-tmp/file1')
vim.fn.bufadd('/my-tmp/file2')

---@type YaziRenameEvent
local event = {
type = 'rename',
data = {
from = '/my-tmp/file1',
to = '/my-tmp/file2',
},
timestamp = '2021-09-01T00:00:00Z',
id = '123',
}

event_handling.process_events_emitted_from_yazi({ event })

local open_buffers = utils.get_open_buffers()
for _, buffer in ipairs(open_buffers) do
assert.is_not_equal('/my-tmp/file1', buffer.path.filename)
end
end)

it('closes a buffer that was moved to another open buffer', function()
vim.fn.bufadd('/my-tmp/file1')
vim.fn.bufadd('/my-tmp/file2')

---@type YaziMoveEvent
local event = {
type = 'move',
id = '123',
timestamp = '2021-09-01T00:00:00Z',
data = {
items = {
{
from = '/my-tmp/file1',
to = '/my-tmp/file2',
},
},
},
}

event_handling.process_events_emitted_from_yazi({ event })

local open_buffers = utils.get_open_buffers()
for _, buffer in ipairs(open_buffers) do
assert.is_not_equal('/my-tmp/file1', buffer.path.filename)
end
end)
end)

0 comments on commit 0ff9086

Please sign in to comment.