BUG: Neo-tree Close_if_last_window Doesn't Work On Discard
Encountering issues with the close_if_last_window setting in Neo-tree when attempting to discard changes in Neovim? This article delves into a specific bug report concerning this functionality, offering insights and potential solutions. We'll explore the reported problem, the environment in which it occurs, and the steps to reproduce it. If you're struggling with Neo-tree not closing as expected when discarding changes, read on to understand the issue better and find possible workarounds.
Discussion category:
nvim-neo-tree, neo-tree.nvim
Did you check docs and existing issues?
- [x] I have read all the docs.
- [x] I have searched the existing issues.
- [x] I have searched the existing discussions.
Neovim Version (nvim -v)
v0.12.0
Operating System / Version
Ubuntu 24.04
Describe the Bug
The reported bug occurs when close_if_last_window is set to true in the Neo-tree configuration. When a file is modified and the user attempts to discard these changes and exit using the :q! command, Neo-tree fails to close. Instead, a warning message appears:
[Neo-tree WARN] Cannot close because one of the files is modified. Please save or discard changes
Subsequent attempts to exit with :q result in an error:
[Neo-tree ERROR] debounce filesystem_navigate error: .../nvim/site/pack/core/opt/nui.nvim/lua/nui/split/init.lua:169: Lua: .../nvim/site/pack/core/opt/nui.nvim/lua/nui/split/init.lua:170: BufEnter Autocommands for "*": Vim(split):E242
: Can't split a window while closing another
stack traceback:
[C]: in function 'nvim_command'
.../nvim/site/pack/core/opt/nui.nvim/lua/nui/split/init.lua:170: in function <.../nvim/site/pack/core/opt/nui.nvim/lua/nui/split/init.lua:169>
[C]: in function 'nvim_win_call'
.../nvim/site/pack/core/opt/nui.nvim/lua/nui/split/init.lua:169: in function '_open_window'
.../nvim/site/pack/core/opt/nui.nvim/lua/nui/split/init.lua:257: in function 'mount'
...pack/core/opt/neo-tree.nvim/lua/neo-tree/ui/renderer.lua:1192: in function 'acquire_window'
...ree.nvim/lua/neo-tree/sources/filesystem/lib/fs_scan.lua:627: in function 'get_items'
...t/neo-tree.nvim/lua/neo-tree/sources/filesystem/init.lua:176: in function '_navigate_internal'
...t/neo-tree.nvim/lua/neo-tree/sources/filesystem/init.lua:197: in function <...t/neo-tree.nvim/lua/neo-tree/sources/filesystem/init.lua:196>
[C]: in function 'pcall'
.../pack/core/opt/neo-tree.nvim/lua/neo-tree/utils/init.lua:125: in function 'debounce'
...t/neo-tree.nvim/lua/neo-tree/sources/filesystem/init.lua:196: in function 'navigate'
.../core/opt/neo-tree.nvim/lua/neo-tree/sources/manager.lua:615: in function 'navigate'
...ack/core/opt/neo-tree.nvim/lua/neo-tree/command/init.lua:194: in function 'do_show_or_focus'
...ack/core/opt/neo-tree.nvim/lua/neo-tree/command/init.lua:164: in function 'execute'
...ack/core/opt/neo-tree.nvim/lua/neo-tree/command/init.lua:171: in function '_command'
...vim/site/pack/core/opt/neo-tree.nvim/plugin/neo-tree.lua:7: in function <...vim/site/pack/core/opt/neo-tree.nvim/plugin/neo-tree.lua:
Screenshots, Traceback
No response
Steps to Reproduce
To reproduce this bug, follow these steps:
- Set
close_if_last_windowtotruein your Neo-tree configuration. - Open a file in Neovim.
- Toggle Neo-tree. The user in the bug report used
:Neotree filesystem show left. - Edit the opened file, making changes.
- Attempt to exit Neovim using the
:q!command to discard changes.
Expected Behavior
The expected behavior is that Neo-tree should close cleanly when :q! is used to discard changes, given that close_if_last_window is set to true. However, the bug prevents this from happening, leading to the aforementioned warning and error messages.
Diving Deep into the Neo-tree Bug: close_if_last_window
This section provides an in-depth analysis of the close_if_last_window bug within the Neo-tree Neovim plugin. We'll dissect the configuration, the precise steps to replicate the issue, and the expected versus actual behavior. Understanding these details is crucial for developers aiming to resolve the bug and for users seeking effective workarounds.
The core of the problem lies in the interaction between Neo-tree's close_if_last_window setting and Neovim's handling of unsaved changes. When close_if_last_window is enabled, Neo-tree is expected to automatically close when it's the last window open in Neovim. However, when there are unsaved changes in a buffer and the user attempts to quit with :q!, Neovim first issues a warning about the unsaved changes. Neo-tree seems to misinterpret this state, failing to properly close itself, leading to the warning message: [Neo-tree WARN] Cannot close because one of the files is modified. Please save or discard changes.
The subsequent attempt to quit with :q then triggers a more severe error, revealing a conflict in window management. The error message [Neo-tree ERROR] debounce filesystem_navigate error: ... Vim(split):E242: Can't split a window while closing another indicates that Neo-tree is trying to perform a window operation (likely related to its file system navigation) at the same time that Neovim is in the process of closing a window. This race condition leads to the crash and the inability to cleanly exit Neovim.
Understanding the Configuration:
The user's configuration, as provided, is straightforward:
vim.pack.add({
-- Tree
{
src = 'https://github.com/nvim-neo-tree/neo-tree.nvim',
version = vim.version.range('3')
},
'https://github.com/nvim-lua/plenary.nvim',
'https://github.com/MunifTanjim/nui.nvim',
'https://github.com/nvim-tree/nvim-web-devicons',
})
require('neo-tree').setup({
close_if_last_window = true
})
This configuration installs Neo-tree along with its dependencies (plenary.nvim, nui.nvim, and nvim-web-devicons) and then sets the close_if_last_window option to true. There are no other customizations that might be interfering with the expected behavior.
Reproducing the Issue:
The steps to reproduce the issue are clearly outlined:
- Ensure the configuration above is in place.
- Open Neovim and create or open a file.
- Open Neo-tree using
:Neotree filesystem show left. - Modify the file by adding or deleting content.
- Attempt to quit without saving using
:q!.
By following these steps, the bug should consistently manifest itself, producing the warning and error messages described earlier.
Expected vs. Actual Behavior:
The expected behavior is that Neo-tree should seamlessly close when the user discards changes and quits Neovim with :q!. The close_if_last_window setting is designed to automate this process, providing a cleaner and more intuitive user experience. The actual behavior, however, deviates significantly from this expectation. Neo-tree fails to close, displays a warning message, and ultimately throws an error that prevents Neovim from exiting cleanly. This discrepancy highlights the severity of the bug and its impact on usability.
Analyzing Potential Causes and Solutions for the Neo-tree close_if_last_window Bug
This section explores the underlying causes of the close_if_last_window bug in Neo-tree and proposes potential solutions and workarounds. We'll examine how Neo-tree interacts with Neovim's event loop, how it handles buffer states, and how these interactions might be leading to the observed errors.
One potential cause of the bug is a race condition between Neo-tree's window management logic and Neovim's buffer management. When :q! is executed, Neovim initiates a sequence of actions, including checking for unsaved changes and closing the current buffer. Neo-tree, in turn, might be attempting to close its window or perform other UI updates concurrently. If these operations are not properly synchronized, it could lead to the errors observed.
Another possible factor is the way Neo-tree handles the BufEnter autocommand. The error message BufEnter Autocommands for "*": Vim(split):E242: Can't split a window while closing another suggests that Neo-tree is attempting to split a window as a result of the BufEnter event, which is triggered when a new buffer is entered. However, if Neovim is in the process of closing the last buffer, this attempt to split a window could fail, leading to the error.
Potential Solutions and Workarounds:
- Synchronization Mechanisms: One potential solution is to introduce synchronization mechanisms to ensure that Neo-tree's window management operations are properly coordinated with Neovim's buffer management. This could involve using mutexes or other locking primitives to prevent concurrent access to shared resources.
- Event Handling Adjustments: Another approach is to modify the way Neo-tree handles the
BufEnterevent. Instead of immediately attempting to split a window, Neo-tree could check whether Neovim is in the process of closing the last buffer and defer the window split until later. - Conditional Closing Logic: The
close_if_last_windowlogic itself could be made more robust by adding checks to ensure that it only attempts to close Neo-tree when it is safe to do so. This could involve checking the number of open buffers and the state of the current buffer before attempting to close the window. - User Workarounds:
- Save or Discard Explicitly: Before quitting, users can explicitly save their changes with
:wor discard them with:e!. This avoids the warning message and allows Neo-tree to close cleanly. - Close Neo-tree Manually: Before quitting, users can manually close the Neo-tree window using
:Neotree close. This ensures that Neo-tree is closed before Neovim attempts to exit. - :qa! Using
:qa!to quit all buffers without saving appears to work without issues, although this might not be desirable in all situations.
- Save or Discard Explicitly: Before quitting, users can explicitly save their changes with
Code Snippets for Potential Solutions (Illustrative):
Note: These code snippets are illustrative and might require further refinement to be implemented correctly.
-- Example of conditional closing logic
local function close_neo_tree()
if vim.fn.winnr('{{content}}#39;) == 1 and vim.bo.modified == false then
require('neo-tree.command').execute({ action = 'close' })
end
end
-- Example of adjusting BufEnter event handling
vim.api.nvim_create_autocmd('BufEnter', {
pattern = '*',
callback = function()
if not vim.fn.exists('g:neo_tree_closing') then
-- Defer window split logic here
end
end
})
Configuration
vim.pack.add{
-- Tree
{
src = 'https://github.com/nvim-neo-tree/neo-tree.nvim',
version = vim.version.range('3')
},
'https://github.com/nvim-lua/plenary.nvim',
'https://github.com/MunifTanjim/nui.nvim',
'https://github.com/nvim-tree/nvim-web-devicons',
}
require('neo-tree').setup({
close_if_last_window = true
})
Conclusion
The close_if_last_window bug in Neo-tree presents a frustrating issue for users who expect the plugin to close automatically when discarding changes. While the exact cause of the bug remains uncertain, potential solutions involve synchronization mechanisms, event handling adjustments, and conditional closing logic. In the meantime, users can employ workarounds such as explicitly saving or discarding changes, manually closing Neo-tree, or using the :qa! command. Further investigation and development efforts are needed to fully resolve this bug and provide a seamless user experience.
For more information on NeoVim and its plugins, visit the NeoVim Official Website. This will allow you to keep up to date with all the latest features, bug fixes, and community discussions surrounding NeoVim.