Skip to content

Commit d8b4687

Browse files
authored
fix(winfixbuf): avoid windows that have winfixbuf when opening buffers (nvim-neo-tree#1390)
1 parent 5bde916 commit d8b4687

File tree

6 files changed

+85
-45
lines changed

6 files changed

+85
-45
lines changed

lua/neo-tree.lua

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ M.ensure_config = function()
1717
end
1818
end
1919

20-
M.get_prior_window = function(ignore_filetypes)
20+
M.get_prior_window = function(ignore_filetypes, ignore_winfixbuf)
2121
ignore_filetypes = ignore_filetypes or {}
2222
local ignore = utils.list_to_dict(ignore_filetypes)
2323
ignore["neo-tree"] = true
@@ -32,7 +32,7 @@ M.get_prior_window = function(ignore_filetypes)
3232
local last_win = wins[win_index]
3333
if type(last_win) == "number" then
3434
local success, is_valid = pcall(vim.api.nvim_win_is_valid, last_win)
35-
if success and is_valid then
35+
if success and is_valid and not (ignore_winfixbuf and utils.is_winfixbuf(last_win)) then
3636
local buf = vim.api.nvim_win_get_buf(last_win)
3737
local ft = vim.api.nvim_buf_get_option(buf, "filetype")
3838
local bt = vim.api.nvim_buf_get_option(buf, "buftype") or "normal"

lua/neo-tree/setup/init.lua

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,17 @@ M.buffer_enter_event = function()
197197
end
198198
last_buffer_enter_filetype = vim.bo.filetype
199199

200+
-- For all others, make sure another buffer is not hijacking our window
201+
-- ..but not if the position is "current"
202+
local prior_buf = vim.fn.bufnr("#")
203+
if prior_buf < 1 then
204+
return
205+
end
206+
local prior_type = vim.api.nvim_buf_get_option(prior_buf, "filetype")
207+
200208
-- there is nothing more we want to do with floating windows
201-
if utils.is_floating() then
209+
-- but when prior_type is neo-tree we might need to redirect buffer somewhere else.
210+
if utils.is_floating() and prior_type ~= "neo-tree" then
202211
return
203212
end
204213

@@ -207,14 +216,6 @@ M.buffer_enter_event = function()
207216
return
208217
end
209218

210-
-- For all others, make sure another buffer is not hijacking our window
211-
-- ..but not if the position is "current"
212-
local prior_buf = vim.fn.bufnr("#")
213-
if prior_buf < 1 then
214-
return
215-
end
216-
local winid = vim.api.nvim_get_current_win()
217-
local prior_type = vim.api.nvim_buf_get_option(prior_buf, "filetype")
218219
if prior_type == "neo-tree" then
219220
local success, position = pcall(vim.api.nvim_buf_get_var, prior_buf, "neo_tree_position")
220221
if not success then

lua/neo-tree/ui/renderer.lua

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,15 +164,12 @@ M.close = function(state, focus_prior_window)
164164
state.winid = nil
165165
end
166166
local bufnr = utils.get_value(state, "bufnr", 0, true)
167-
if bufnr > 0 and vim.api.nvim_buf_is_valid(bufnr) then
168-
state.bufnr = nil
169-
local success, err = pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
170-
if not success and err:match("E523") then
171-
vim.schedule_wrap(function()
172-
vim.api.nvim_buf_delete(bufnr, { force = true })
173-
end)()
167+
state.bufnr = nil
168+
vim.schedule(function()
169+
if bufnr > 0 and vim.api.nvim_buf_is_valid(bufnr) then
170+
vim.api.nvim_buf_delete(bufnr, { force = true })
174171
end
175-
end
172+
end)
176173
return window_existed
177174
end
178175

@@ -1040,7 +1037,9 @@ M.acquire_window = function(state)
10401037
M.position.save(state)
10411038
end)
10421039
win:on({ "BufDelete" }, function()
1043-
win:unmount()
1040+
vim.schedule(function ()
1041+
win:unmount()
1042+
end)
10441043
end, { once = true })
10451044
end
10461045

lua/neo-tree/utils/init.lua

Lines changed: 50 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,14 @@ M.is_floating = function(win_id)
508508
return false
509509
end
510510

511+
M.is_winfixbuf = function(win_id)
512+
if vim.fn.exists("&winfixbuf") == 1 then
513+
win_id = win_id or vim.api.nvim_get_current_win()
514+
return vim.api.nvim_get_option_value("winfixbuf", { win = win_id })
515+
end
516+
return false
517+
end
518+
511519
---Evaluates the value of <afile>, which comes from an autocmd event, and determines if it
512520
---is a valid file or some sort of utility buffer like quickfix or neo-tree itself.
513521
---@param afile string The path or relative path to the file.
@@ -565,7 +573,7 @@ M.map = function(tbl, fn)
565573
return t
566574
end
567575

568-
M.get_appropriate_window = function(state)
576+
M.get_appropriate_window = function(state, ignore_winfixbuf)
569577
-- Avoid triggering autocommands when switching windows
570578
local eventignore = vim.o.eventignore
571579
vim.o.eventignore = "all"
@@ -579,7 +587,7 @@ M.get_appropriate_window = function(state)
579587
local ignore = M.list_to_dict(ignore_ft)
580588
ignore["neo-tree"] = true
581589
if nt.config.open_files_in_last_window then
582-
local prior_window = nt.get_prior_window(ignore)
590+
local prior_window = nt.get_prior_window(ignore, ignore_winfixbuf)
583591
if prior_window > 0 then
584592
local success = pcall(vim.api.nvim_set_current_win, prior_window)
585593
if success then
@@ -601,6 +609,9 @@ M.get_appropriate_window = function(state)
601609
if ignore[vim.bo.filetype] or ignore[bt] or M.is_floating() then
602610
attempts = attempts + 1
603611
vim.cmd("wincmd w")
612+
elseif ignore_winfixbuf and M.is_winfixbuf() then
613+
attempts = attempts + 1
614+
vim.cmd("wincmd w")
604615
else
605616
suitable_window_found = true
606617
end
@@ -642,6 +653,28 @@ M.resolve_width = function(width)
642653
return math.floor(width)
643654
end
644655

656+
M.force_new_split = function(current_position, escaped_path)
657+
local result, err
658+
local split_command = "vsplit"
659+
-- respect window position in user config when Neo-tree is the only window
660+
if current_position == "left" then
661+
split_command = "rightbelow vs"
662+
elseif current_position == "right" then
663+
split_command = "leftabove vs"
664+
end
665+
if escaped_path == M.escape_path_for_cmd("[No Name]") then
666+
-- vim's default behavior is to overwrite [No Name] buffers.
667+
-- We need to split first and then open the path to workaround this behavior.
668+
result, err = pcall(vim.cmd, split_command)
669+
if result then
670+
vim.cmd.edit(escaped_path)
671+
end
672+
else
673+
result, err = pcall(vim.cmd, split_command .. " " .. escaped_path)
674+
end
675+
return result, err
676+
end
677+
645678
---Open file in the appropriate window.
646679
---@param state table The state of the source
647680
---@param path string The file to open
@@ -654,7 +687,8 @@ M.open_file = function(state, path, open_cmd, bufnr)
654687
if bufnr <= 0 then
655688
bufnr = nil
656689
else
657-
local buf_cmd_lookup = { edit = "b", e = "b", split = "sb", sp = "sb", vsplit = "vert sb", vs = "vert sb" }
690+
local buf_cmd_lookup =
691+
{ edit = "b", e = "b", split = "sb", sp = "sb", vsplit = "vert sb", vs = "vert sb" }
658692
local cmd_for_buf = buf_cmd_lookup[open_cmd]
659693
if cmd_for_buf then
660694
open_cmd = cmd_for_buf
@@ -692,28 +726,24 @@ M.open_file = function(state, path, open_cmd, bufnr)
692726
width = M.get_value(state, "window.width", 40, false)
693727
width = M.resolve_width(width)
694728
end
695-
696-
local split_command = "vsplit"
697-
-- respect window position in user config when Neo-tree is the only window
698-
if state.current_position == "left" then
699-
split_command = "rightbelow vs"
700-
elseif state.current_position == "right" then
701-
split_command = "leftabove vs"
702-
end
703-
if path == "[No Name]" then
704-
result, err = pcall(vim.cmd, split_command)
705-
if result then
706-
vim.cmd("b" .. bufnr)
707-
end
708-
else
709-
result, err = pcall(vim.cmd, split_command .. " " .. escaped_path)
710-
end
711-
729+
result, err = M.force_new_split(state.current_position, escaped_path)
712730
vim.api.nvim_win_set_width(winid, width)
713731
else
714732
result, err = pcall(vim.cmd, open_cmd .. " " .. bufnr_or_path)
715733
end
716734
end
735+
if not result and string.find(err or "", "winfixbuf") and M.is_winfixbuf() then
736+
local winid, is_neo_tree_window = M.get_appropriate_window(state, true)
737+
-- Rescan window list to find a window that is not winfixbuf.
738+
-- If found, retry executing command in that window,
739+
-- otherwise, all windows are either neo-tree or winfixbuf so we make a new split.
740+
if not is_neo_tree_window and not M.is_winfixbuf(winid) then
741+
vim.api.nvim_set_current_win(winid)
742+
result, err = pcall(vim.cmd, open_cmd .. " " .. bufnr_or_path)
743+
else
744+
result, err = M.force_new_split(state.current_position, escaped_path)
745+
end
746+
end
717747
if result or err == "Vim(edit):E325: ATTENTION" then
718748
-- fixes #321
719749
vim.api.nvim_buf_set_option(0, "buflisted", true)

tests/neo-tree/command/command_current_spec.lua

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,11 @@ local run_in_current_command = function(command, expected_tree_node)
1515
end
1616
end
1717

18+
local run_close_command = function(command)
19+
vim.cmd(command)
20+
u.wait_for(function() end, { interval = 200, timeout = 200 })
21+
end
22+
1823
describe("Command", function()
1924
local test = u.fs.init_test({
2025
items = {
@@ -71,7 +76,7 @@ describe("Command", function()
7176
run_in_current_command(cmd, testfile)
7277

7378
-- toggle CLOSE
74-
vim.cmd(cmd)
79+
run_close_command(cmd)
7580
verify.window_handle_is(tree_winid)
7681
verify.buf_name_is(testfile)
7782
end)

tests/neo-tree/command/command_spec.lua

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,11 @@ local run_show_command = function(command, expected_tree_node)
3939
end, "Expected to see a new window without focusing it.")
4040
end
4141

42+
local run_close_command = function(command)
43+
vim.cmd(command)
44+
u.wait_for(function() end, { interval = 200, timeout = 200 })
45+
end
46+
4247
describe("Command", function()
4348
local test = u.fs.init_test({
4449
items = {
@@ -87,7 +92,7 @@ describe("Command", function()
8792
local tree_winid = vim.api.nvim_get_current_win()
8893

8994
-- toggle CLOSE
90-
vim.cmd(cmd)
95+
run_close_command(cmd)
9196
verify.window_handle_is_not(tree_winid)
9297
verify.buf_name_is(testfile)
9398

@@ -109,7 +114,7 @@ describe("Command", function()
109114
local tree_winid = vim.api.nvim_get_current_win()
110115

111116
-- toggle CLOSE
112-
vim.cmd("Neotree float reveal toggle")
117+
run_close_command("Neotree float reveal toggle")
113118
verify.window_handle_is_not(tree_winid)
114119
verify.buf_name_is(testfile)
115120

@@ -158,7 +163,7 @@ describe("Command", function()
158163

159164
verify.after(500, function()
160165
-- toggle CLOSE
161-
vim.cmd(cmd)
166+
run_close_command(cmd)
162167

163168
-- toggle OPEN
164169
u.editfile(topfile)
@@ -189,7 +194,7 @@ describe("Command", function()
189194
run_focus_command("Neotree reveal", baz)
190195
local expected_tree_node = baz
191196
-- toggle CLOSE
192-
vim.cmd(cmd)
197+
run_close_command(cmd)
193198

194199
verify.after(500, function()
195200
-- toggle OPEN

0 commit comments

Comments
 (0)