nvim-tree / nvim-tree.lua

A file explorer tree for neovim written in lua
Other
7.2k stars 609 forks source link

Previous window is not selected unless :NvimTreeFocus is used #2262

Open zhimsel opened 1 year ago

zhimsel commented 1 year ago

Description

From https://github.com/nvim-tree/nvim-tree.lua/issues/1985#issuecomment-1582896807

Is there really no way to open to the window that was previously active before selecting the nvim-tree window (regardless on navigation method)?

I have the same problem as @paulrouget, however I often use the mouse to start interacting with nvim-tree (to open a file, for instance). If I don't use :NvimTreeFocus, then it opens the file in the last window to have previously used :NvimTreeFocus (or presumably have been created by nvim-tree).

Would it be possible to use the lua equivalent of [:winnr(#)](https://neovim.io/doc/user/builtin.html#winnr()) (if it exists)?

Would it be possible to track the previous window by an event that fires every time you leave a window? Like maybe the WinLeave autocmd event? We could add another WinLeave autocmd similar to the one defined here, but using a negated pattern to ignore unwanted buffers like NvimTree_*.


TL;DR

This plugin can only track the "previous active window" when it's focused via :NvimTreeFocus. If the nvim-tree is focused by any other method, it will still refer to the window where :NvimTreeFocus was lat called, not the actual last window.

Neovim version

NVIM v0.9.0
Build type: Release
LuaJIT 2.1.0-beta3

Operating system and version

Linux 6.2.12-zen1-1-zen, MacOS 13.4

nvim-tree version

Latest master (034511714bacfadc5008e49f73fcef67e5613840 as of testing)

Minimal config

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
    },
    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({
    actions = {
      open_file = {
        window_picker = {
          enable = false
        },
      },
    },
  })
end

Steps to reproduce

Setup

Open nvim-tree and open at least 2 files in splits (vertical/horizontal doesn't seem to matter). In this example I'll open file1, file2, and file3 in vertical splits in order (1 on the left, 3 on the right).

  1. nvim -nu /tmp/nvt-min.lua
  2. :NvimTreeOpen
  3. Select file1 and press <C-v>
  4. :NvimTreeFocus
  5. Select file2 and press <C-v>
  6. :NvimTreeFocus
  7. Select file3 and press <C-v>

The bug

  1. With window 3 selected (as it should be after the last step above), focus nvim-tree with :NvimTreeFocus
  2. Open another file with <CR>. The file opens in window 3, as it was the last window selected.
  3. Focus window 1 (with file1 open) with any method of your choice (mouse, <C-w>h, whatever)
  4. Focus nvim-tree this time with a method that doesn't involve calling :NvimTreeFocus, like clicking the window with the mouse, or using <C-w>h to move the cursor to the nvim-tree window.
  5. Open a file that is not currently open in a window with <CR>

Expected behavior

I would expect the file to be opened in window 1 (where file1 used to be), as it was the last window selected.

Actual behavior

The file is opened in window 3 (where file3 was), as it was the window selected when :NvimTreeFocus was last called.

alex-courtis commented 1 year ago

target_winid tracking for the previous window was added ~2021 https://github.com/nvim-tree/nvim-tree.lua/pull/174

This is functional but incomplete, which explains this behaviour.

The # buffer is tracked per window so that will not be useful.

<c-w><c-p> can be used to switch to the previous window however I'm not seeing an obvious means of programatically finding the previously focused window.

It is tempting to remove target_winid however I must be mindful of Chesterton's fence

alex-courtis commented 1 year ago

We could manually track windows by tab, however this feels very wrong:

local log = require("nvim-tree.log")

local windows_by_tabpage = {
  previous = {},
  current = {},
}

local function remove_invalid(mapping)
  for t, w in pairs(mapping) do
    if not vim.api.nvim_tabpage_is_valid(t) or not vim.api.nvim_win_is_valid(w) then
      mapping[t] = nil
    end
  end
end

vim.api.nvim_create_autocmd({ "WinEnter", "WinLeave" }, {
  callback = function(d)
    local tabpage = vim.api.nvim_get_current_tabpage()
    local window = vim.api.nvim_get_current_win()

    -- set previous or current
    if d.event == "WinEnter" then
      windows_by_tabpage.current[tabpage] = window
    elseif d.event == "WinLeave" then
      windows_by_tabpage.previous[tabpage] = window
    end

    -- tidy closed windows/tabs
    remove_invalid(windows_by_tabpage.current)
    remove_invalid(windows_by_tabpage.previous)

    log.line("dev", "%s %s\n", d.event, vim.inspect(windows_by_tabpage))
  end,
})
zhimsel commented 1 year ago

To be honest, that doesn't feel "wrong" to me. It's a simple and effective (as far as I can tell) way to track the previous window. I haven't found a mechanism built into nvim that provides this function, probably because using these autocmds is easy and clean enough.

It's more thorough than only tracking the previous window when using :NvimTreeFocus.

zhimsel commented 1 year ago

I can attempt to put together a PR, if you could point me in the right directions for which parts of this plug-in are responsible for window tracking.

alex-courtis commented 1 year ago

I can attempt to put together a PR, if you could point me in the right directions for which parts of this plug-in are responsible for window tracking.

That would be fantastic!

  1. Add the tracking bits above to lib.lua. Call that from an autocommand in nvim-tree.lua
  2. Remove lib.target_winid. Any accessors should be changed to use the previous value.
  3. Find other places where we should also use the previous value.
bombela commented 11 months ago

I am not 100% sure if this bug is the same I am encountering.

:tabedit $somepath select a file to open, and NvimTree will focus the previous tab and ask me which window I want to replace with the file.

If before opening the file I execute :NvimTreeFocus then NvimTree will open the file in a vertical split.

After that, with another :tabedid $somepath, NvimTree will replace itself with the opened file (no vertical split, no focus on the previous tab). This last behavior is similar to the original netrw, and what I expect.

alex-courtis commented 11 months ago

I am not 100% sure if this bug is the same I am encountering.

That splitting behaviour does sound different however I would not be surprised if the above fix resolved it.

I can't reproduce, however there are nvim and nvim-tree settings that would affect this sort of behaviour.

Can you please provide a reproducer with exact steps as per clean room replication?

alex-courtis commented 7 months ago

See #2722