mirror of
https://github.com/neovim/neovim.git
synced 2026-11-02 09:49:02 -06:00
Merge #41112 from erdivartanovich/dir-failed-open-recovery
This commit is contained in:
2 files changed
+82
-8
No files matched your search
@@ -26,6 +26,7 @@ local M = {}
|
||||
|
||||
---@class (private) nvim.dir.State
|
||||
---@field gen integer
|
||||
---@field err? string
|
||||
---@field provider? nvim.dir.Provider
|
||||
|
||||
local listing_group = api.nvim_create_augroup('nvim.dir.listing', { clear = false })
|
||||
@@ -212,7 +213,8 @@ function load(buf, name, provider, restore_view, setup, select)
|
||||
local list_gen = state.gen + 1
|
||||
state.gen = list_gen
|
||||
vim.b[buf].nvim_dir = state
|
||||
-- Discard the listing state if the initial load fails, but preserve an existing listing on reload failure.
|
||||
-- Render an empty listing (retried by "R" or :edit) if the initial load fails,
|
||||
-- but preserve an existing listing on reload failure.
|
||||
local first_render = state.provider == nil
|
||||
local done = false
|
||||
|
||||
@@ -229,10 +231,12 @@ function load(buf, name, provider, restore_view, setup, select)
|
||||
end
|
||||
if err ~= nil then
|
||||
notify_error(err)
|
||||
if first_render then
|
||||
close_listing(buf)
|
||||
if not first_render then
|
||||
current_state.err = err
|
||||
vim.b[buf].nvim_dir = current_state
|
||||
return
|
||||
end
|
||||
return
|
||||
entries = {}
|
||||
end
|
||||
---@cast entries nvim.dir.Entry[]
|
||||
|
||||
@@ -248,7 +252,7 @@ function load(buf, name, provider, restore_view, setup, select)
|
||||
if select and api.nvim_get_current_buf() == buf then
|
||||
select_entry(select)
|
||||
end
|
||||
current_state.provider = provider
|
||||
current_state.err, current_state.provider = err, provider
|
||||
vim.b[buf].nvim_dir = current_state
|
||||
|
||||
if not setup then
|
||||
@@ -264,6 +268,10 @@ function load(buf, name, provider, restore_view, setup, select)
|
||||
|
||||
local ok, call_err = pcall(provider.list, buf, name, on_list) ---@type boolean, any
|
||||
if not ok then
|
||||
if done then
|
||||
-- The handler already ran and would ignore this error: propagate it.
|
||||
error(call_err, 0)
|
||||
end
|
||||
-- Route provider exceptions through the list handler so failures share one cleanup path.
|
||||
on_list(tostring(call_err), nil)
|
||||
end
|
||||
|
||||
@@ -235,8 +235,13 @@ describe('nvim.dir', function()
|
||||
|
||||
exec_lua(function()
|
||||
require('nvim.dir').open(0, 'custom://error', {
|
||||
list = function(_, _, cb)
|
||||
cb('simulated error')
|
||||
list = function(buf, _, cb)
|
||||
vim.b[buf].custom_fail = (vim.b[buf].custom_fail or 0) + 1
|
||||
if vim.b[buf].custom_fail == 1 then
|
||||
cb('simulated error')
|
||||
else
|
||||
cb(nil, { { name = 'recovered.txt', dir = false } })
|
||||
end
|
||||
end,
|
||||
open = function() end,
|
||||
open_parent = function() end,
|
||||
@@ -244,7 +249,67 @@ describe('nvim.dir', function()
|
||||
end)
|
||||
|
||||
ok(exec_capture('messages'):find('simulated error', 1, true) ~= nil)
|
||||
eq(false, exec_lua([[return vim.b.nvim_dir ~= nil]]))
|
||||
-- A failed initial load renders an empty listing that "R" can retry.
|
||||
eq('simulated error', exec_lua([[return vim.b.nvim_dir.err]]))
|
||||
eq({ '' }, lines())
|
||||
eq('nowrite', bufopt('buftype'))
|
||||
eq(false, bufopt('modifiable'))
|
||||
feed('R')
|
||||
poke_eventloop()
|
||||
eq({ 'recovered.txt' }, lines())
|
||||
eq(vim.NIL, exec_lua([[return vim.b.nvim_dir.err]]))
|
||||
end)
|
||||
|
||||
it('failed directory open does not repeat the error on every BufEnter', function()
|
||||
t.skip(t.is_os('win'), 'directory permissions are POSIX-only')
|
||||
t.skip(vim.uv.getuid() == 0, 'root ignores directory permissions')
|
||||
n.clear({ args = { '--clean' } })
|
||||
make_fixture()
|
||||
local locked = root .. '/locked'
|
||||
t.mkdir(locked)
|
||||
finally(function()
|
||||
vim.uv.fs_chmod(locked, tonumber('755', 8))
|
||||
end)
|
||||
vim.uv.fs_chmod(locked, 0)
|
||||
|
||||
edit(root)
|
||||
api.nvim_win_set_cursor(0, { line_of('locked/'), 0 })
|
||||
feed('<CR>')
|
||||
poke_eventloop()
|
||||
local function count_errors()
|
||||
local _, n_errors = exec_capture('messages'):gsub('EACCES', '')
|
||||
return n_errors
|
||||
end
|
||||
eq(1, count_errors())
|
||||
ok(exec_lua([[return vim.b.nvim_dir.err]]):find('EACCES', 1, true) ~= nil)
|
||||
eq('nowrite', bufopt('buftype'))
|
||||
edit(file)
|
||||
feed('<C-^>')
|
||||
poke_eventloop()
|
||||
eq(1, count_errors())
|
||||
vim.uv.fs_chmod(locked, tonumber('755', 8))
|
||||
t.write_file(locked .. '/inside.txt', 'inside', true)
|
||||
feed('R')
|
||||
poke_eventloop()
|
||||
eq({ 'inside.txt' }, lines())
|
||||
end)
|
||||
|
||||
it('does not swallow errors raised after the list callback', function()
|
||||
n.clear({ args = { '--clean' } })
|
||||
|
||||
local err = exec_lua(function()
|
||||
local ok_open, e = pcall(require('nvim.dir').open, 0, 'custom://late-error', {
|
||||
list = function(_, _, cb)
|
||||
cb(nil, { { name = 'file.txt', dir = false } })
|
||||
error('late provider error')
|
||||
end,
|
||||
open = function() end,
|
||||
open_parent = function() end,
|
||||
})
|
||||
return not ok_open and tostring(e) or nil
|
||||
end)
|
||||
ok(err ~= nil and err:find('late provider error', 1, true) ~= nil)
|
||||
eq({ 'file.txt' }, lines())
|
||||
end)
|
||||
|
||||
it('reloads custom listing providers', function()
|
||||
@@ -587,6 +652,7 @@ describe('nvim.dir', function()
|
||||
poke_eventloop()
|
||||
|
||||
ok(exec_capture('messages'):find('ENOENT', 1, true) ~= nil)
|
||||
ok(exec_lua([[return vim.b.nvim_dir.err]]):find('ENOENT', 1, true) ~= nil)
|
||||
assert_directory(subdir)
|
||||
end)
|
||||
|
||||
|
||||
Reference in new issue
Block a user