nvim-ide icon indicating copy to clipboard operation
nvim-ide copied to clipboard

Issues closing Nvim/Buffers

Open prdanelli opened this issue 2 years ago • 56 comments

I've found a few "issues" while testing and i'm adding them individually so as to keep the discussions focused.

  • Just noticed :q on a buffer, which would normally close Nvim, seems to close the buffers, but keeps the left and right panels open, which need to be individually closed.

  • Closing a buffer, with the panels open, means that the panels take up the entire viewport, then when I try to move to another buffer, it opens inside one of the IDE panels.

prdanelli avatar Nov 27 '22 17:11 prdanelli

Same issue here, I think what we're asking for is, when closing a window, if the only remaining windows are all from nvim-ide, then close Neovim completely.

mrjones2014 avatar Nov 27 '22 17:11 mrjones2014

At the moment, the bigger issue for me is that when you close the buffer with the panels open, bnext opens the panel in the to left panel.

prdanelli avatar Nov 27 '22 17:11 prdanelli

The panels are normal Neovim windows. So use ":qa" instead. Just like if you had multiple neovim windows open and wanted to quit.

ldelossa avatar Nov 27 '22 18:11 ldelossa

edit: https://github.com/ldelossa/nvim-ide/issues/14#issuecomment-1328509373

distek avatar Nov 27 '22 20:11 distek

Ohhh this is cute. Hmm maybe this is worth adding to the WorkspaceController on start? Hide it behind a config flag like "auto_close" or something?

ldelossa avatar Nov 27 '22 22:11 ldelossa

Yeah, same idea as how nvim-tree has an option to close Neovim if it’s the last window

mrjones2014 avatar Nov 27 '22 22:11 mrjones2014

I think the one I wrote there could be a bit more robust. Also I'm starting to think QuitPre might be the wrong event to track. I can't actually :qa anymore unless it's strictly one remaining window + the panels.

distek avatar Nov 27 '22 22:11 distek

Yeah I think you want to use WinClosed

mrjones2014 avatar Nov 27 '22 23:11 mrjones2014

With WinClosed, it will trigger on any window being closed, including completion windows, Noice's prompts, etc.

I'm not really sure what the best way to handle this is.

distek avatar Nov 28 '22 00:11 distek

You could still catch the WinClosed event, but when you catch it, list all windows, search thru them, and if the only buffers you find in those Windows start with "component://" you can quit vim at that point.

Its a brute force approach but i think it will work.

ldelossa avatar Nov 28 '22 03:11 ldelossa

This seems to work as expected:

vim.api.nvim_create_autocmd("WinClosed", {
    pattern = {
        "*"
    },

    callback = function()
        vim.cmd("wincmd c")

        local stdBufCount = 0
        for _, v in ipairs(vim.api.nvim_list_wins()) do
            local name = vim.fn.bufname(vim.api.nvim_win_get_buf(v))

            if string.find(name, "component://*") then
                goto continue
            end

            stdBufCount = stdBufCount + 1

            ::continue::
        end

        if stdBufCount == 0 then
            vim.cmd("qa")
        end
    end
})

Basically what ldelossa was saying.

distek avatar Nov 28 '22 04:11 distek

Why do you do the:

        vim.cmd("wincmd c")

When "WinClosed" fires the window it matches for is already closed, why do you do the additional close of a window there?

ldelossa avatar Nov 28 '22 04:11 ldelossa

The autocmd actually triggers before the window is closed, which I wasn't expecting. Found that by placing a print statement in after the loop; It print's before closing the window. I'm assuming the idea is you run some cleanup tasks for the window you're closing? Regardless, the issue was the window was still open, so looping over the current windows was still resulting in a normal window being present. Because of that, I would have to close one of the panel windows (being the only ones left) for it to actually close neovim.

The way it is right now, it functions the way I'd expect :q to work. If you test it and it doesn't work that way, let me know as I might have something weird lurking in my config.

distek avatar Nov 28 '22 13:11 distek

Aha! I must have read the docs wrong.

Okay, and it appears that the event is not recursive. So I dont think youll fire an event from this event, for the same window being closed, which is good 👌

ldelossa avatar Nov 28 '22 13:11 ldelossa

I actually think you read the documentation correctly. I'm looking through the nvim source though and it looks like it fires the event before it actually closes the window?: https://github.com/neovim/neovim/blob/b2bb3973d9c7f25acfead2718d74fcf5b1e4551e/src/nvim/window.c#L2776

I might be reading it incorrectly.

Anyway, it might be up to nvim-ide to track the current state and if the only windows open are the panels, then close neovim/the current tab.

distek avatar Nov 28 '22 14:11 distek

If your autocmd there works reliably, you can shove it right into the "WorkspaceController" init function I believe: https://github.com/ldelossa/nvim-ide/blob/e81fbd084f10b92bb6a4f62155e0fa1edbee9c7e/lua/ide/workspaces/workspace_controller.lua#L227

Guard it with a config flag, and lets see how that goes?

ldelossa avatar Nov 28 '22 14:11 ldelossa

Let me test it more at work today and I'll give you a yay or nay on that.

distek avatar Nov 28 '22 14:11 distek

Sounds great.

ldelossa avatar Nov 28 '22 14:11 ldelossa

Broke almost immediately.

Played with it throughout the day when I could, came up with this:

vim.api.nvim_create_autocmd("WinEnter", {
    pattern = {
        "*"
    },

    callback = function()
        local winCount = 0
        local compWinCount = 0
        for _, v in ipairs(vim.api.nvim_list_wins()) do
            local name = vim.fn.bufname(vim.api.nvim_win_get_buf(v))

            if string.find(name, "component://*") then
                compWinCount = compWinCount + 1
            end

            winCount = winCount + 1
        end

        if stdBufCount == compWinCount then
            vim.cmd("qa")
        end
    end
})

Using WinEnter made sense since when you're quitting in the last "normal" window, it will close the window (as we talked about) and will run after the :q. I think this is the best route. My only concern: Though it doesn't seem to slow anything down when moving between windows, it is running two separate commands + a loop. If it gets integrated in to the codebase, we could probably instead use the .is_open() to get which/how many panels are open, and then just compare that to the number returned by #vim.api.nvim_list_wins()

distek avatar Nov 29 '22 00:11 distek

Hmm, maybe this is a bit more optimized? Basically, just try to quit ASAP when you see a window that's not a "component://" window.

vim.api.nvim_create_autocmd("WinEnter", {
    pattern = {
        "*"
    },

    callback = function()
       -- optimize exit
      local name = vim.fn.bufname(vim.api.nvim_win_get_buf(v))
      if not string.find(name, "component://*") then
          return
      end
      
      -- we are in a component win, search other wins to see if there are 
      -- any non-component wins.
      for _, v in ipairs(vim.api.nvim_list_wins()) do
            local name = vim.fn.bufname(vim.api.nvim_win_get_buf(v))
            if not string.find(name, "component://*") then
                return
            end
        end
      
        -- only component wins left, exit
        vim.cmd("qa")
    end
})

ldelossa avatar Nov 29 '22 01:11 ldelossa

You could keep track of and then check if the number of nvim-ide windows is equal to the total number of windows

mrjones2014 avatar Nov 29 '22 02:11 mrjones2014

You could keep track of and then check if the number of nvim-ide windows is equal to the total number of windows

That's what I was doing in the last iteration, but I think ldelossa has an idea there so this is my likely final iteration:

vim.api.nvim_create_autocmd("WinEnter", {
    pattern = {
        "*"
    },

    callback = function()
        if not string.find(vim.fn.bufname(vim.api.nvim_win_get_buf(0)), "component://*") then
            return
        end

        for _, v in ipairs(vim.api.nvim_list_wins()) do
            if not string.find(vim.fn.bufname(vim.api.nvim_win_get_buf(v)), "component://*") then
                return
            end
        end

        vim.cmd("qa")
    end
})

distek avatar Nov 29 '22 04:11 distek

This doesn't work for me

mrjones2014 avatar Nov 29 '22 12:11 mrjones2014

Odd, works on my end. What happens for you?

ldelossa avatar Nov 29 '22 13:11 ldelossa

It just doesn't seem to do anything. Can we put the autocmds into an augroup so I can inspect just that one augroup to further debug?

mrjones2014 avatar Nov 29 '22 13:11 mrjones2014

@mrjones2014 Not sure if this is your issue but I had to recompile with Packer to get everything working correctly. Also after changing auto_close to false (to test that it would behave as expected)

Edit: I do think an augroup would be a good idea.

distek avatar Nov 29 '22 13:11 distek

Hmm, I tried changing auto_close = false then :PackerCompile then change back auto_close = true then :PackerCompile and still no luck.

mrjones2014 avatar Nov 29 '22 13:11 mrjones2014

Have to ask, just to be sure, but did you make sure you're back on main?

distek avatar Nov 29 '22 14:11 distek

I'm using my fork currently since I'm working on a PR, but I synced the fork with the main repo and merged main into my branch, so the changes are definitely there. I can see the changes in my local codebase.

mrjones2014 avatar Nov 29 '22 14:11 mrjones2014

I still can't get this working 🤔

mrjones2014 avatar Nov 29 '22 19:11 mrjones2014