nvim-tree.lua icon indicating copy to clipboard operation
nvim-tree.lua copied to clipboard

`nvim-tree` allows the creation of files over `sshfs`, but they can't be accessed

Open futurisold opened this issue 1 year ago • 11 comments

Description

I'm using nosduco/remote-sshfs.nvim to work remotely over sshfs.

nvim-tree doesn't show anything from the mounted folder on the server, however, you can perform edit operations from nvim-tree directly, such as creating a new file (see image, I basically pressed a on top of the folder in nvim-tree panel).

I checked on server and the file was indeed created.

Thanks

Neovim version

NVIM v0.10.0
Build type: Release
LuaJIT 2.1.1716656478


"nvim-tree.lua": { "branch": "master", "commit": "517e4fbb9ef3c0986da7047f44b4b91a2400f93c" }

Operating system and version

macOS Sonoma 14.5 (23F79)

Windows variant

No response

nvim-tree version

{ "branch": "master", "commit": "517e4fbb9ef3c0986da7047f44b4b91a2400f93c" }

Clean room replication

vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1

vim.cmd [[set runtimepath=$VIMRUNTIME]]
vim.cmd [[set packpath=/tmp/nvt-min/site]]
local package_root = "/tmp/nvt-min/site/pack"
local install_path = package_root .. "/packer/start/packer.nvim"
local function load_plugins()
  require("packer").startup {
    {
    "wbthomason/packer.nvim",
    "nvim-tree/nvim-tree.lua",
    "nvim-tree/nvim-web-devicons",
    -- ADD PLUGINS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE
    'nosduco/remote-sshfs.nvim',
    "nvim-telescope/telescope.nvim", branch = "0.1.x",
    "nvim-lua/plenary.nvim",
    },

    config = {
      package_root = package_root,
      compile_path = install_path .. "/plugin/packer_compiled.lua",
      display = { non_interactive = true },
    },
  }
end
if vim.fn.isdirectory(install_path) == 0 then
  print "Installing nvim-tree and dependencies."
  vim.fn.system { "git", "clone", "--depth=1", "https://github.com/wbthomason/packer.nvim", install_path }
end
load_plugins()
require("packer").sync()
vim.cmd [[autocmd User PackerComplete ++once echo "Ready!" | lua setup()]]
vim.opt.termguicolors = true
vim.opt.cursorline = true

-- MODIFY NVIM-TREE SETTINGS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE
_G.setup = function()
    require("nvim-tree").setup {}
    require('remote-sshfs').setup {
      connections = {
        ssh_configs = { -- which ssh configs to parse for hosts list
          vim.fn.expand "$HOME" .. "/.ssh/config",
          "/etc/ssh/ssh_config",
        },
        sshfs_args = { -- arguments to pass to the sshfs command
          "-o reconnect",
          "-o auto_cache",
          "-o Ciphers=aes128-ctr",
          "-o ConnectTimeout=5",
          "-C",
          "-o cache_timeout=60",
          "-o cache=yes",
        },
      },
      mounts = {
        base_dir = vim.fn.expand "$HOME" .. "/.sshfs/", -- base directory for mount points
        unmount_on_exit = true, -- run sshfs as foreground, will unmount on vim exit
      },
      handlers = {
        on_connect = {
          change_dir = true, -- when connected change vim working directory to mount point
        },
        on_disconnect = {
          clean_mount_folders = false, -- remove mount point folder on disconnect/unmount
        },
        on_edit = {}, -- not yet implemented
      },
      ui = {
        select_prompts = false, -- not yet implemented
        confirm = {
          connect = true, -- prompt y/n when host is selected to connect to
          change_dir = false, -- prompt y/n to change working directory on connection (only applicable if handlers.on_connect.change_dir is enabled)
        },
      },
      log = {
        enable = false, -- enable logging
        truncate = false, -- truncate logs
        types = { -- enabled log types
          all = false,
          util = false,
          handler = false,
          sshfs = false,
        },
      },
    }
    require('telescope').setup {}

end

-- UNCOMMENT this block for diagnostics issues, substituting pattern and cmd as appropriate.
-- Requires diagnostics.enable = true in setup.
--[[
vim.api.nvim_create_autocmd("FileType", {
  pattern = "lua",
  callback = function()
    vim.lsp.start { cmd = { "lua-language-server" } }
  end,
})
]]

Steps to reproduce

1. nvim -nu /tmp/nvt-min.lua
2. :RemoteSSHFSConnect (pick server; have it listed in your ~/.ssh/config)
3. :NvimTreeOpen
4. expand the mounted folder (should be ~ on your server)

Expected behavior

List all files, directories, normal navigation as one would have locally.

Actual behavior

image

futurisold avatar Jun 05 '24 09:06 futurisold

Initial thoughts: this may be a file system notification issue. Does the folder show after you manually refresh R or restart vim?

I don't have any knowledge about ssh file systems however I imagine that libuv would have issues. It's definitely not something that nvim-tree is aware of or can handle.

Suggestion: raise an issue with nosduco/remote-sshfs.nvim and see what ideas they have then we can talk further. I notice they copied our log subsystem :)

alex-courtis avatar Jun 07 '24 04:06 alex-courtis

Thanks, @alex-courtis. Unfortunately, no, I can only create files directly from nvim-tree explorer. I will try to check on their side. Will close this and will reopen if necessarry.

futurisold avatar Jun 07 '24 06:06 futurisold

Upon further digging, this is definitely a nvim-tree issue.

The issue arises solely with the nvim-tree enabled, even when using the most basic clean configuration suggested by you (&pasted below), while mounting the folder with sshfs then nvim --clean [ mounted dir ] works.

vim.g.loaded_netrw = 1
vim.g.loaded_netrwPlugin = 1

vim.cmd [[set runtimepath=$VIMRUNTIME]]
vim.cmd [[set packpath=/tmp/nvt-min/site]]
local package_root = "/tmp/nvt-min/site/pack"
local install_path = package_root .. "/packer/start/packer.nvim"
local function load_plugins()
  require("packer").startup {
    {
    "wbthomason/packer.nvim",
    "nvim-tree/nvim-tree.lua",
    "nvim-tree/nvim-web-devicons",
    },

    config = {
      package_root = package_root,
      compile_path = install_path .. "/plugin/packer_compiled.lua",
      display = { non_interactive = true },
    },
  }
end
if vim.fn.isdirectory(install_path) == 0 then
  print "Installing nvim-tree and dependencies."
  vim.fn.system { "git", "clone", "--depth=1", "https://github.com/wbthomason/packer.nvim", install_path }
end
load_plugins()
require("packer").sync()
vim.cmd [[autocmd User PackerComplete ++once echo "Ready!" | lua setup()]]
vim.opt.termguicolors = true
vim.opt.cursorline = true

-- MODIFY NVIM-TREE SETTINGS THAT ARE _NECESSARY_ FOR REPRODUCING THE ISSUE
_G.setup = function()
    require("nvim-tree").setup {}
end

-- UNCOMMENT this block for diagnostics issues, substituting pattern and cmd as appropriate.
-- Requires diagnostics.enable = true in setup.
--[[
vim.api.nvim_create_autocmd("FileType", {
  pattern = "lua",
  callback = function()
    vim.lsp.start { cmd = { "lua-language-server" } }
  end,
})
]]

futurisold avatar Jun 11 '24 08:06 futurisold

Hi, facing the same issue. I have successfully used this plugin for fuse mounted file system using sshfs before, so it broke in some recent commit.

rag-hav avatar Jun 15 '24 03:06 rag-hav

Thank you, I can reproduce this with sshfs and default nvim-tree config. The directories are seen by nvim-tree, but not enumerated.

Top level contains only directories: in this case baz and foo are seen, 2 is not. This behaviour is nondeterministic - sometimes baz and foo are not seen.

: ; find .
.
./baz
./baz/2
./foo
./foo/bar
./foo/bar/1
[2024-06-15 14:12:29] [profile] START core init /home/alex/src/nvim-tree/r/2794/sfs
[2024-06-15 14:12:29] [profile] START git toplevel git_dir /home/alex/src/nvim-tree/r/2794/sfs
[2024-06-15 14:12:29] [profile] END   git toplevel git_dir /home/alex/src/nvim-tree/r/2794/sfs 3ms
[2024-06-15 14:12:29] [profile] START explore init /home/alex/src/nvim-tree/r/2794/sfs
[2024-06-15 14:12:29] [profile] START explore populate_children /home/alex/src/nvim-tree/r/2794/sfs/baz
[2024-06-15 14:12:29] [profile] END   explore populate_children /home/alex/src/nvim-tree/r/2794/sfs/baz 1ms
[2024-06-15 14:12:29] [profile] START explore populate_children /home/alex/src/nvim-tree/r/2794/sfs/foo
[2024-06-15 14:12:29] [profile] END   explore populate_children /home/alex/src/nvim-tree/r/2794/sfs/foo 1ms
[2024-06-15 14:12:29] [profile] END   explore init /home/alex/src/nvim-tree/r/2794/sfs 2ms
[2024-06-15 14:12:29] [profile] END   core init /home/alex/src/nvim-tree/r/2794/sfs 7ms
[2024-06-15 14:12:29] [profile] START view open
[2024-06-15 14:12:29] [profile] END   view open 4ms
[2024-06-15 14:12:29] [profile] START draw
[2024-06-15 14:12:29] [profile] END   draw 0ms

open baz

[2024-06-15 14:12:34] [profile] START git toplevel git_dir /home/alex/src/nvim-tree/r/2794/sfs/baz
[2024-06-15 14:12:34] [profile] END   git toplevel git_dir /home/alex/src/nvim-tree/r/2794/sfs/baz 5ms
[2024-06-15 14:12:34] [profile] START explore init /home/alex/src/nvim-tree/r/2794/sfs/baz
[2024-06-15 14:12:34] [profile] START explore populate_children /home/alex/src/nvim-tree/r/2794/sfs/baz/2
[2024-06-15 14:12:34] [profile] END   explore populate_children /home/alex/src/nvim-tree/r/2794/sfs/baz/2 0ms
[2024-06-15 14:12:34] [profile] END   explore init /home/alex/src/nvim-tree/r/2794/sfs/baz 0ms
[2024-06-15 14:12:34] [profile] START draw
[2024-06-15 14:12:34] [profile] END   draw 0ms

Add a file 0 next to foo, no change.

alex-courtis avatar Jun 15 '24 04:06 alex-courtis

git bisect shows this to be the first bad commit

2d97059661c83787372c8c003e743c984ba3ac50

rag-hav avatar Jun 15 '24 04:06 rag-hav

It appears to be the fs_scandir_next that is failing. fs_stat seems reliable:

---@param handle uv.uv_fs_t
---@param cwd string
---@param node Node
---@param git_status table
local function populate_children(handle, cwd, node, git_status)
  local node_ignored = explorer_node.is_git_ignored(node)
  local nodes_by_path = utils.bool_record(node.nodes, "absolute_path")
  local filter_status = filters.prepare(git_status)
  while true do
    local name, t, err = vim.loop.fs_scandir_next(handle)
    log.line("dev", "child next %s %s %s", name, t, err)

    if not name then
      break
    end

    local abs = utils.path_join { cwd, name }

    local res, ok, errr = vim.loop.fs_stat(abs)
    log.line("dev", "child stat %s %s %s", res, ok, errr)
[2024-06-15 14:39:32] [profile] START core init /home/alex/src/nvim-tree/r/2794/sfs
[2024-06-15 14:39:32] [watcher] Watcher:new '/home/alex/src/nvim-tree/r/2794/sfs' nil
[2024-06-15 14:39:32] [watcher] Event:new '/home/alex/src/nvim-tree/r/2794/sfs'
[2024-06-15 14:39:32] [watcher] Event:start '/home/alex/src/nvim-tree/r/2794/sfs'
[2024-06-15 14:39:32] [profile] START git toplevel git_dir /home/alex/src/nvim-tree/r/2794/sfs
[2024-06-15 14:39:32] [profile] END   git toplevel git_dir /home/alex/src/nvim-tree/r/2794/sfs 2ms
[2024-06-15 14:39:32] [profile] START explore init /home/alex/src/nvim-tree/r/2794/sfs
[2024-06-15 14:39:32] [dev] child next baz nil nil
[2024-06-15 14:39:32] [dev] child stat table: 0x7b6359192200 nil nil
[2024-06-15 14:39:32] [profile] START explore populate_children /home/alex/src/nvim-tree/r/2794/sfs/baz
[2024-06-15 14:39:32] [profile] END   explore populate_children /home/alex/src/nvim-tree/r/2794/sfs/baz 0ms
[2024-06-15 14:39:32] [dev] child next foo nil nil
[2024-06-15 14:39:32] [dev] child stat table: 0x7b63591937e8 nil nil
[2024-06-15 14:39:32] [profile] START explore populate_children /home/alex/src/nvim-tree/r/2794/sfs/foo
[2024-06-15 14:39:32] [profile] END   explore populate_children /home/alex/src/nvim-tree/r/2794/sfs/foo 0ms
[2024-06-15 14:39:32] [dev] child next nil nil nil
[2024-06-15 14:39:32] [dev] nodes_by_path {}
[2024-06-15 14:39:32] [profile] END   explore init /home/alex/src/nvim-tree/r/2794/sfs 0ms
[2024-06-15 14:39:32] [profile] END   core init /home/alex/src/nvim-tree/r/2794/sfs 4ms
[2024-06-15 14:39:32] [profile] START view open
[2024-06-15 14:39:32] [profile] END   view open 4ms
[2024-06-15 14:39:32] [profile] START draw
[2024-06-15 14:39:32] [profile] END   draw 0ms

alex-courtis avatar Jun 15 '24 04:06 alex-courtis

git bisect shows this to be the first bad commit

2d97059

Very interesting, it does look like the type detection was changed there.

Are you interested in creating a pull request to fix this one @rag-hav ? I reckon you're the best one to find real world test cases ;)

alex-courtis avatar Jun 15 '24 04:06 alex-courtis

Will look into it, but I don't really have any experience with this.

rag-hav avatar Jun 15 '24 04:06 rag-hav

Thanks mate! Dev and contribution docs.

alex-courtis avatar Jun 16 '24 01:06 alex-courtis

Out of curiousity, I checked out a previous commit, and found that this bug is actually a regression. I will run a git bisect to get the first bad commit.

Edit: according to git bisect, commit 2d97059 is the first bad commit.

ydalton avatar Jul 09 '24 12:07 ydalton

Should be fixed in #2893. Waiting review and merge.

mxple avatar Sep 01 '24 22:09 mxple

Thank you so much for fixing this. Now however, nvim-tree is extremely slow when using it on an sshfs mount, possibly because it is scanning the file system every time you open nvim tree. Is there a way to optimize this? Perhaps you could assume that when working on a mount, the current machine is the only one working on the file system and hence there is no need to update it every time?

Thiggel avatar Sep 09 '24 13:09 Thiggel

@Thiggel I believe the slowness you are experiencing is network latency and not nvim-tree. Could you test the mount using a file explorer or ls/cd/cat to see if there is indeed network lag? I personally have not experienced slowness but I can investigate if the problem persists.

mxple avatar Sep 09 '24 13:09 mxple

@mxple I have had a similar "lag" only when using R to reload the entire tree. Otherwise it was rather performant and enjoyable.

gerritwellecke avatar Sep 09 '24 13:09 gerritwellecke

@Thiggel Unfortunately there's not much we can do here - nvim-tree has to look at each file.

Perhaps you could assume that when working on a mount, the current machine is the only one working on the file system and hence there is no need to update it every time?

You might like to experiment with filesystem watchers: disabling or enabling (default)

:help nvim-tree.filesystem_watchers.enable

You can be targeted about it, disabling them for specific directories:

:help nvim-tree.filesystem_watchers.ignore_dirs

alex-courtis avatar Sep 14 '24 02:09 alex-courtis

@mxple I've had to revert #2920 this change as there was an issue with symlinks:

For symlinks: vim.loop.fs_scandir_next returns a type "link" vim.loop.fs_stat returns a type "file"

I'd be most grateful if we could work together to create a complete solution.

alex-courtis avatar Sep 22 '24 05:09 alex-courtis

Hi @alex-courtis . Luckily, it seems there is a pretty simple fix where we replace fs_stat with fs_lstat in init.lua. I've tested it and it works. However, I noticed that a recent commit has broken sshfs compatibility. Running a bisect, it seems commit cd9c6db77f7c8b348dfa14d805ea28a69e5e718f is the first bad commit.

I believe I have fixed both issues in (#2922). Please test and review.

mxple avatar Sep 22 '24 06:09 mxple

However, I noticed that a recent commit has broken sshfs compatibility. Running a bisect, it seems commit cd9c6db is the first bad commit.

Apologies, yes, that was a bad refactor/rebase.

alex-courtis avatar Sep 22 '24 23:09 alex-courtis