Skip to content

Commit

Permalink
feat: using folke/snacks.nvim can preserve window layouts on deletes
Browse files Browse the repository at this point in the history
Issue
=====

When yazi deletes a file, yazi.nvim also closes that buffer. However,
the default implementation in neovim is to also close the window/split
if one is currently open. This is distracting and annoying for users
(me).

Solution
========

If the https://github.com/folke/snacks.nvim plugin is installed,
yazi.nvim uses that to delete the buffer while preserving the window
layout.

If it's not installed, the old (annoying) behavior is used.

In the future, snacks.nvim might come a required dependency for
yazi.nvim.
  • Loading branch information
mikavilpas committed Dec 7, 2024
1 parent 74019bf commit 27bf004
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,16 +34,20 @@ describe("reading events", () => {
it("can read 'trash' events and close an open buffer when its file was trashed", () => {
// NOTE: trash means moving a file to the trash, not deleting it permanently

cy.startNeovim().then((dir) => {
cy.startNeovim({
filename: { openInVerticalSplits: ["initial-file.txt", "file2.txt"] },
}).then((dir) => {
// the default file should already be open
cy.contains(dir.contents["initial-file.txt"].name)
cy.contains("If you see this text, Neovim is ready!")
cy.contains("Hello")

// modify the buffer to make sure it works even if the buffer is modified
cy.typeIntoTerminal("ccchanged{esc}")

// start yazi
// start yazi and wait for it to display contents
cy.typeIntoTerminal("{upArrow}")
cy.contains("subdirectory" satisfies MyTestDirectoryFile)

// start file deletion
cy.typeIntoTerminal("d")
Expand All @@ -59,22 +63,40 @@ describe("reading events", () => {
// have closed the buffer
cy.contains(dir.contents["initial-file.txt"].name).should("not.exist")
cy.contains("If you see this text, Neovim is ready").should("not.exist")

// make sure two windows are open. The test environment uses snacks.nvim
// which should make sure the window layout is preserved when closing
// the deleted buffer. The default in neovim is to also close the
// window.
cy.runExCommand({ command: `echo winnr("$")` }).then((result) => {
expect(result.value).to.match(/2/)
})
})
})

it("can read 'delete' events and close an open buffer when its file was deleted", () => {
// NOTE: delete means permanently deleting a file (not moving it to the trash)

cy.startNeovim().then((dir) => {
cy.startNeovim({
filename: { openInVerticalSplits: ["initial-file.txt", "file2.txt"] },
}).then((dir) => {
// the default file should already be open
cy.contains(dir.contents["initial-file.txt"].name)
cy.contains("If you see this text, Neovim is ready!")
cy.contains("Hello")

// make sure If you see this text, Neovim is ready! is in the correct
// buffer so that we are editing the correct buffer in this test
cy.runExCommand({ command: "echo expand('%')" }).then((result) => {
expect(result.value).to.match(/initial-file.txt$/)
})

// modify the buffer to make sure it works even if the buffer is modified
cy.typeIntoTerminal("ccchanged{esc}")

// start yazi
// start yazi and wait for it to display contents
cy.typeIntoTerminal("{upArrow}")
cy.contains("subdirectory" satisfies MyTestDirectoryFile)

// start file deletion
cy.typeIntoTerminal("D")
Expand All @@ -90,6 +112,14 @@ describe("reading events", () => {
// have closed the buffer
cy.get(dir.contents["initial-file.txt"].name).should("not.exist")
cy.contains("If you see this text, Neovim is ready").should("not.exist")

// make sure two windows are open. The test environment uses snacks.nvim
// which should make sure the window layout is preserved when closing
// the deleted buffer. The default in neovim is to also close the
// window.
cy.runExCommand({ command: `echo winnr("$")` }).then((result) => {
expect(result.value).to.match(/2/)
})
})
})
})
Expand Down
3 changes: 3 additions & 0 deletions integration-tests/cypress/support/tui-sandbox.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,9 @@ declare global {

runLuaCode(input: LuaCodeClientInput): Chainable<RunLuaCodeOutput>

/** Run an ex command in neovim.
* @example "echo expand('%:.')" current file, relative to the cwd
*/
runExCommand(input: ExCommandClientInput): Chainable<RunExCommandOutput>
}
}
Expand Down
1 change: 1 addition & 0 deletions integration-tests/test-environment/.config/nvim/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ local plugins = {
{ "nvim-telescope/telescope.nvim", lazy = true },
{ "catppuccin/nvim", name = "catppuccin", priority = 1000 },
{ "https://github.com/MagicDuck/grug-far.nvim", opts = {} },
{ "folke/snacks.nvim", opts = {} },
}
require("lazy").setup({ spec = plugins })

Expand Down
2 changes: 1 addition & 1 deletion lua/yazi/event_handling/yazi_event_handling.lua
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ function M.process_delete_event(event, remaining_events)
deleted_buffers[#deleted_buffers + 1] = buffer

vim.schedule(function()
vim.api.nvim_buf_delete(buffer.bufnr, { force = true })
utils.bufdelete(buffer.bufnr)
lsp_delete.file_deleted(buffer.path.filename)
end)
end
Expand Down
11 changes: 11 additions & 0 deletions lua/yazi/utils.lua
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,17 @@ function M.is_buffer_open(path)
return false
end

function M.bufdelete(bufnr)
local ok, bufdelete = pcall(function()
return require("snacks.bufdelete")
end)
if ok then
return bufdelete.delete({ buf = bufnr, force = true })
else
vim.api.nvim_buf_delete(bufnr, { force = true })
end
end

---@param instruction RenameableBuffer
---@return nil
function M.rename_or_close_buffer(instruction)
Expand Down

0 comments on commit 27bf004

Please sign in to comment.