Skip to content

Commit 7a5eb10

Browse files
n1ckisthereufolke
andauthored
fix(explorer): mounted directories being detected as non-directories in Tree:expand (#2053)
## Description In certain filesystems — such as bind mounts, NFS, FUSE, or other mounted paths — uv.fs_scandir_next() may return a nil type for valid directories. This causes the snacks.nvim explorer to treat mounted folders as files, making them unexpandable in the UI. This patch adds a fallback to resolve the correct type when fs_scandir_next() returns nil. It uses uv.fs_stat() first, and falls back to vim.fn.isdirectory() if needed. This ensures that mounted directories are properly marked as navigable. Code change summary: - Inside Tree:expand, we now check t == nil - If so, we call uv.fs_stat() to get the true type - If fs_stat fails, we fallback to vim.fn.isdirectory - Final dir = true logic remains intact Impact: - Fixes a long-standing bug in mounted directories under /mnt, FUSE, etc. - Only runs extra checks when needed (no performance hit for regular files) - No changes elsewhere — clean, isolated patch ## Related Issue(s) - Fixes #2036 ## Screenshots before: <img width="1907" height="946" alt="image" src="https://github.com/user-attachments/assets/15e1b8a5-c999-49e8-8ab5-1d23c60e4969" /> after: <img width="343" height="902" alt="Screenshot_20250715_222920" src="https://github.com/user-attachments/assets/c7ca2fa0-e0cc-4e3d-a4bb-5345538408f7" /> --------- Co-authored-by: Folke Lemaitre <[email protected]>
1 parent 68da653 commit 7a5eb10

File tree

4 files changed

+18
-2
lines changed

4 files changed

+18
-2
lines changed

lua/snacks/explorer/tree.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ function Tree:expand(node)
141141
if not name then
142142
break
143143
end
144+
t = t or Snacks.util.path_type(node.path .. "/" .. name)
144145
found[name] = true
145146
local child = self:child(node, name, t)
146147
child.type = t

lua/snacks/picker/preview.lua

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ function M.directory(ctx)
1313
ctx.preview:set_title(ctx.item.title or name)
1414
local ls = {} ---@type {file:string, type:"file"|"directory"}[]
1515
for file, t in vim.fs.dir(ctx.item.file) do
16+
t = t or Snacks.util.path_type(ctx.item.file .. "/" .. file)
1617
ls[#ls + 1] = { file = file, type = t }
1718
end
1819
ctx.preview:set_lines(vim.split(string.rep("\n", #ls), "\n"))

lua/snacks/picker/util/init.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,7 @@ function M.dir(item)
454454
local path = type(item) == "table" and M.path(item) or item
455455
---@cast path string
456456
path = svim.fs.normalize(path)
457-
return vim.fn.isdirectory(path) == 1 and path or vim.fs.dirname(path)
457+
return Snacks.util.path_type(path) == "directory" and path or vim.fs.dirname(path)
458458
end
459459

460460
---@param paths string[]
@@ -476,7 +476,7 @@ function M.copy_path(from, to)
476476
Snacks.notify.error(("File `%s` does not exist"):format(from))
477477
return
478478
end
479-
if vim.fn.isdirectory(from) == 1 then
479+
if Snacks.util.path_type(from) == "directory" then
480480
M.copy_dir(from, to)
481481
else
482482
M.copy_file(from, to)

lua/snacks/util/init.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -458,4 +458,18 @@ function M.parse(parser, range, on_parse)
458458
end
459459
end
460460

461+
--- Better validation to check if path is a dir or a file
462+
---@param path string
463+
---@return "directory"|"file"
464+
function M.path_type(path)
465+
local stat = uv.fs_stat(path)
466+
if stat and stat.type then
467+
return stat.type
468+
end
469+
if vim.fn.isdirectory(path) == 1 then
470+
return "directory"
471+
end
472+
return "file"
473+
end
474+
461475
return M

0 commit comments

Comments
 (0)