From 73bb6a8a5e3fa20336e27159df4b9112b33398bc Mon Sep 17 00:00:00 2001 From: Barrett Ruth <62671086+barrettruth@users.noreply.github.com> Date: Mon, 13 Jul 2026 14:10:19 -0500 Subject: [PATCH] fix(dir): let 3P dir-browser plugin rename the dir buffer #40529 Problem: `FileType directory` fires *before* the `BufEnter` path where `dir.lua` populates the buffer, in order to allow a 3P dir-browser plugin to handle the event and (optionally) rename the buffer to e.g. `example:///tmp/foo/`. But currently, `open_buffer()` continues after `filetypedetect BufRead` as if the original directory buffer is *still* current and valid, which may fall through to the built-in `nvim.dir` path after a 3P plugin ALREADY handled the directory. Solution: Keep a buf ref around the early `filetypedetect BufRead` call for directory buffers. If the `FileType directory` handler switches away from/deletes the original directory buffer, stop the open path instead of continuing into the built-in `nvim.dir` flow. --- src/nvim/buffer.c | 5 +++++ test/functional/plugin/dir_spec.lua | 30 +++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index 8f10c93173..ea1c93f786 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -413,7 +413,12 @@ int open_buffer(bool read_stdin, exarg_T *eap, int flags_arg) && after_pathsep(curbuf->b_ffname, curbuf->b_ffname + strlen(curbuf->b_ffname))) { if (augroup_exists("filetypedetect")) { + bufref_T bufref; + set_bufref(&bufref, curbuf); do_doautocmd("filetypedetect BufRead", false, NULL); + if (!bufref_valid(&bufref) || curbuf != old_curbuf.br_buf || aborting()) { + return FAIL; + } } } diff --git a/test/functional/plugin/dir_spec.lua b/test/functional/plugin/dir_spec.lua index cd35948cb6..1c6cd01d73 100644 --- a/test/functional/plugin/dir_spec.lua +++ b/test/functional/plugin/dir_spec.lua @@ -105,6 +105,36 @@ describe('nvim.dir', function() line_of('alpha.txt') end) + it('3P dir-browser can handle `FileType directory` event and rename buf', function() + make_fixture() + n.clear({ args_rm = { '-u' } }) + exec_lua(function() + _G.nvim_dir_loaded_in_filetype = nil + vim.api.nvim_create_autocmd('FileType', { + pattern = 'directory', + callback = function(ev) + _G.nvim_dir_loaded_in_filetype = package.loaded['nvim.dir'] ~= nil + local dir = vim.api.nvim_buf_get_name(ev.buf) + local browser = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_name(browser, 'example://' .. dir) + vim.api.nvim_buf_set_lines(browser, 0, -1, false, { + 'plugin browser for: ' .. dir, + }) + vim.api.nvim_set_current_buf(browser) + vim.api.nvim_buf_delete(ev.buf, { force = true }) + end, + }) + end) + + edit(root) + + eq(false, exec_lua('return _G.nvim_dir_loaded_in_filetype')) + eq('example://' .. root .. '/', api.nvim_buf_get_name(0)) + eq({ 'plugin browser for: ' .. root .. '/' }, lines()) + eq(false, exec_lua([[return package.loaded['nvim.dir'] ~= nil]])) + eq('', exec_capture('messages')) + end) + it('triggers nested autocmds when opening directory buffers', function() make_fixture()