stevearc / resession.nvim

A replacement for mksession with a better API
MIT License
178 stars 13 forks source link

How to persist window focus? #5

Closed oblitzitate closed 7 months ago

oblitzitate commented 1 year ago

I attempted to make a neo-tree extension adapted from the quickfix example. When I load the session, it always seems to focus on the neo-tree window rather than the last focused window. How do I resolve this?

(fn M.is_win_supported [winid bufnr]
  (= (vim.api.nvim_buf_get_option bufnr "filetype") "neo-tree"))

(fn M.load_win [winid config]
  (vim.api.nvim_set_current_win winid)
  (vim.cmd "Neotree")
  (vim.api.nvim_win_close winid true)
  (vim.api.nvim_get_current_win))
stevearc commented 1 year ago

I'm not sure why that would be happening. You can see that we call set_winlayout (which does the window restoration and calls into extensions) on this line https://github.com/stevearc/resession.nvim/blob/6bda7fdebd5d685e7b45da408df1842c947d02f5/lua/resession/init.lua#L460

And right below it, after we restore all of the tabs, we set the current window https://github.com/stevearc/resession.nvim/blob/6bda7fdebd5d685e7b45da408df1842c947d02f5/lua/resession/init.lua#L466-L468

You can try adding some debug statements around there to make sure curwin isn't nil, that it's the window you expect, etc. It's possible that there's something running after the load function that causes the focus to shift to NeoTree. This could be a deferred NeoTree function, or possibly some other autocmd in your config. One thing that could help is testing with an absolutely minimal init.lua that only installs resession and neo-tree. That would rule out the last possibility.

oblitzitate commented 1 year ago

I disabled everything except for resession.nvim and neo-tree.nvim yet still get the same issue.

I also tried to set the current buffer to Neotree instead of opening a new Neotree sidebar and closing the previous window, BUT the larger content window ends up being filled with a Neo-tree buffer (when it should be the content file), while the sidebar window ends up blank (when it should be Neo-tree)

(fn M.load_win [winid config]
  (vim.api.nvim_set_current_win winid)
  (vim.cmd "Neotree current")
  winid)

On save: 2023-02-12-16:18:44-screenshot

On load (I moved the cursor down so don't mind that): 2023-02-12-16:16:32-screenshot

stevearc commented 1 year ago

Well here is your problem. If you source this file:

local win = vim.api.nvim_get_current_win()
vim.cmd("Neotree")
vim.api.nvim_set_current_win(win)

The neo-tree window ends up focused. There is some autocmd firing after it opens that causes it to grab the focus, even after you've set it back to a different window. Theoretically you can open neotree with :Neotree action=show and it won't grab focus, but what that actually does is attempt to set the focus back to the initial window after opening it. The trouble is that it uses the window id that was already closed, and throws an error.

In theory, this should work:

local win = vim.api.nvim_get_current_win()
vim.cmd('vsplit')
vim.cmd('enew')
vim.cmd("Neotree position=current")
vim.api.nvim_set_current_win(win)

But sourcing it produces the same bad behavior you've seen. I think that the :Neotree command has some defer or delay built into it, so instead of running immediately it runs after some delay, at which point it's in the wrong window and has the wrong context. You could try posting this in the neo-tree repo and see if they have a fix for you, but this is well beyond the scope of resession at this point.

oblitzitate commented 1 year ago

I think that the :Neotree command has some defer or delay built into it, so instead of running immediately it runs after some delay, at which point it's in the wrong window and has the wrong context.

That may be the case.

You could try posting this in the neo-tree repo and see if they have a fix for you, but this is well beyond the scope of resession at this point.

I'm encountering a somewhat related (though not the same) issue with another sidebar plugin that is aerial.nvim. I don't think this issue will be specific to neo-tree.

My proposal would be to allow the user more control on loading the extensions as a whole (not just individually) once the normal buffers are loaded. Like maybe pass the user a table that maps all the extension winids to their relevant info (e.g. extension type and saved data), so the user can handle extension loading as a whole themselves? For the extension type, maybe the user can return it in the is_win_supported method instead of just returning true or false? In short, you'd still be able to use the standard on_load_win API for non-problematic extensions, but then afterwards, you could use an API the gives you the extension winid mappings to handle yourselves.

To avoid conflicting with non-problematic extensions that use on_load_win, maybe you can pass the info that the particular extension winid has already been loaded in the extension winid mappings? Or maybe you don't pass the already-loaded extensions but instead just pass the remaining extension winid mappings?

stevearc commented 1 year ago

I don't like the idea of adding an entirely new API just for extensions that can't load properly. If a plugin requires hacks to properly restore a window, either the plugin needs to be fixed or the hacks should be added as part of the extension. I don't want to upstream the hacks into the architecture of resession. You can already defer the execution of the logic with vim.defer_fn, or by adding a post_load hook.

I think that this is specific to Neo-tree. I haven't encountered any issues with the aerial extension, and if you have then please file the bug so I can fix it.