Skip to content

Commit

Permalink
fix(winfixbuf): avoid windows that have winfixbuf when opening buff…
Browse files Browse the repository at this point in the history
…ers (#1390)
  • Loading branch information
pysan3 committed Mar 13, 2024
1 parent 5bde916 commit d8b4687
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 45 deletions.
4 changes: 2 additions & 2 deletions lua/neo-tree.lua
Expand Up @@ -17,7 +17,7 @@ M.ensure_config = function()
end
end

M.get_prior_window = function(ignore_filetypes)
M.get_prior_window = function(ignore_filetypes, ignore_winfixbuf)
ignore_filetypes = ignore_filetypes or {}
local ignore = utils.list_to_dict(ignore_filetypes)
ignore["neo-tree"] = true
Expand All @@ -32,7 +32,7 @@ M.get_prior_window = function(ignore_filetypes)
local last_win = wins[win_index]
if type(last_win) == "number" then
local success, is_valid = pcall(vim.api.nvim_win_is_valid, last_win)
if success and is_valid then
if success and is_valid and not (ignore_winfixbuf and utils.is_winfixbuf(last_win)) then
local buf = vim.api.nvim_win_get_buf(last_win)
local ft = vim.api.nvim_buf_get_option(buf, "filetype")
local bt = vim.api.nvim_buf_get_option(buf, "buftype") or "normal"
Expand Down
19 changes: 10 additions & 9 deletions lua/neo-tree/setup/init.lua
Expand Up @@ -197,8 +197,17 @@ M.buffer_enter_event = function()
end
last_buffer_enter_filetype = vim.bo.filetype

-- For all others, make sure another buffer is not hijacking our window
-- ..but not if the position is "current"
local prior_buf = vim.fn.bufnr("#")
if prior_buf < 1 then
return
end
local prior_type = vim.api.nvim_buf_get_option(prior_buf, "filetype")

-- there is nothing more we want to do with floating windows
if utils.is_floating() then
-- but when prior_type is neo-tree we might need to redirect buffer somewhere else.
if utils.is_floating() and prior_type ~= "neo-tree" then
return
end

Expand All @@ -207,14 +216,6 @@ M.buffer_enter_event = function()
return
end

-- For all others, make sure another buffer is not hijacking our window
-- ..but not if the position is "current"
local prior_buf = vim.fn.bufnr("#")
if prior_buf < 1 then
return
end
local winid = vim.api.nvim_get_current_win()
local prior_type = vim.api.nvim_buf_get_option(prior_buf, "filetype")
if prior_type == "neo-tree" then
local success, position = pcall(vim.api.nvim_buf_get_var, prior_buf, "neo_tree_position")
if not success then
Expand Down
17 changes: 8 additions & 9 deletions lua/neo-tree/ui/renderer.lua
Expand Up @@ -164,15 +164,12 @@ M.close = function(state, focus_prior_window)
state.winid = nil
end
local bufnr = utils.get_value(state, "bufnr", 0, true)
if bufnr > 0 and vim.api.nvim_buf_is_valid(bufnr) then
state.bufnr = nil
local success, err = pcall(vim.api.nvim_buf_delete, bufnr, { force = true })
if not success and err:match("E523") then
vim.schedule_wrap(function()
vim.api.nvim_buf_delete(bufnr, { force = true })
end)()
state.bufnr = nil
vim.schedule(function()
if bufnr > 0 and vim.api.nvim_buf_is_valid(bufnr) then
vim.api.nvim_buf_delete(bufnr, { force = true })
end
end
end)
return window_existed
end

Expand Down Expand Up @@ -1040,7 +1037,9 @@ M.acquire_window = function(state)
M.position.save(state)
end)
win:on({ "BufDelete" }, function()
win:unmount()
vim.schedule(function ()
win:unmount()
end)
end, { once = true })
end

Expand Down
70 changes: 50 additions & 20 deletions lua/neo-tree/utils/init.lua
Expand Up @@ -508,6 +508,14 @@ M.is_floating = function(win_id)
return false
end

M.is_winfixbuf = function(win_id)
if vim.fn.exists("&winfixbuf") == 1 then
win_id = win_id or vim.api.nvim_get_current_win()
return vim.api.nvim_get_option_value("winfixbuf", { win = win_id })
end
return false
end

---Evaluates the value of <afile>, which comes from an autocmd event, and determines if it
---is a valid file or some sort of utility buffer like quickfix or neo-tree itself.
---@param afile string The path or relative path to the file.
Expand Down Expand Up @@ -565,7 +573,7 @@ M.map = function(tbl, fn)
return t
end

M.get_appropriate_window = function(state)
M.get_appropriate_window = function(state, ignore_winfixbuf)
-- Avoid triggering autocommands when switching windows
local eventignore = vim.o.eventignore
vim.o.eventignore = "all"
Expand All @@ -579,7 +587,7 @@ M.get_appropriate_window = function(state)
local ignore = M.list_to_dict(ignore_ft)
ignore["neo-tree"] = true
if nt.config.open_files_in_last_window then
local prior_window = nt.get_prior_window(ignore)
local prior_window = nt.get_prior_window(ignore, ignore_winfixbuf)
if prior_window > 0 then
local success = pcall(vim.api.nvim_set_current_win, prior_window)
if success then
Expand All @@ -601,6 +609,9 @@ M.get_appropriate_window = function(state)
if ignore[vim.bo.filetype] or ignore[bt] or M.is_floating() then
attempts = attempts + 1
vim.cmd("wincmd w")
elseif ignore_winfixbuf and M.is_winfixbuf() then
attempts = attempts + 1
vim.cmd("wincmd w")
else
suitable_window_found = true
end
Expand Down Expand Up @@ -642,6 +653,28 @@ M.resolve_width = function(width)
return math.floor(width)
end

M.force_new_split = function(current_position, escaped_path)
local result, err
local split_command = "vsplit"
-- respect window position in user config when Neo-tree is the only window
if current_position == "left" then
split_command = "rightbelow vs"
elseif current_position == "right" then
split_command = "leftabove vs"
end
if escaped_path == M.escape_path_for_cmd("[No Name]") then
-- vim's default behavior is to overwrite [No Name] buffers.
-- We need to split first and then open the path to workaround this behavior.
result, err = pcall(vim.cmd, split_command)
if result then
vim.cmd.edit(escaped_path)
end
else
result, err = pcall(vim.cmd, split_command .. " " .. escaped_path)
end
return result, err
end

---Open file in the appropriate window.
---@param state table The state of the source
---@param path string The file to open
Expand All @@ -654,7 +687,8 @@ M.open_file = function(state, path, open_cmd, bufnr)
if bufnr <= 0 then
bufnr = nil
else
local buf_cmd_lookup = { edit = "b", e = "b", split = "sb", sp = "sb", vsplit = "vert sb", vs = "vert sb" }
local buf_cmd_lookup =
{ edit = "b", e = "b", split = "sb", sp = "sb", vsplit = "vert sb", vs = "vert sb" }
local cmd_for_buf = buf_cmd_lookup[open_cmd]
if cmd_for_buf then
open_cmd = cmd_for_buf
Expand Down Expand Up @@ -692,28 +726,24 @@ M.open_file = function(state, path, open_cmd, bufnr)
width = M.get_value(state, "window.width", 40, false)
width = M.resolve_width(width)
end

local split_command = "vsplit"
-- respect window position in user config when Neo-tree is the only window
if state.current_position == "left" then
split_command = "rightbelow vs"
elseif state.current_position == "right" then
split_command = "leftabove vs"
end
if path == "[No Name]" then
result, err = pcall(vim.cmd, split_command)
if result then
vim.cmd("b" .. bufnr)
end
else
result, err = pcall(vim.cmd, split_command .. " " .. escaped_path)
end

result, err = M.force_new_split(state.current_position, escaped_path)
vim.api.nvim_win_set_width(winid, width)
else
result, err = pcall(vim.cmd, open_cmd .. " " .. bufnr_or_path)
end
end
if not result and string.find(err or "", "winfixbuf") and M.is_winfixbuf() then
local winid, is_neo_tree_window = M.get_appropriate_window(state, true)
-- Rescan window list to find a window that is not winfixbuf.
-- If found, retry executing command in that window,
-- otherwise, all windows are either neo-tree or winfixbuf so we make a new split.
if not is_neo_tree_window and not M.is_winfixbuf(winid) then
vim.api.nvim_set_current_win(winid)
result, err = pcall(vim.cmd, open_cmd .. " " .. bufnr_or_path)
else
result, err = M.force_new_split(state.current_position, escaped_path)
end
end
if result or err == "Vim(edit):E325: ATTENTION" then
-- fixes #321
vim.api.nvim_buf_set_option(0, "buflisted", true)
Expand Down
7 changes: 6 additions & 1 deletion tests/neo-tree/command/command_current_spec.lua
Expand Up @@ -15,6 +15,11 @@ local run_in_current_command = function(command, expected_tree_node)
end
end

local run_close_command = function(command)
vim.cmd(command)
u.wait_for(function() end, { interval = 200, timeout = 200 })
end

describe("Command", function()
local test = u.fs.init_test({
items = {
Expand Down Expand Up @@ -71,7 +76,7 @@ describe("Command", function()
run_in_current_command(cmd, testfile)

-- toggle CLOSE
vim.cmd(cmd)
run_close_command(cmd)
verify.window_handle_is(tree_winid)
verify.buf_name_is(testfile)
end)
Expand Down
13 changes: 9 additions & 4 deletions tests/neo-tree/command/command_spec.lua
Expand Up @@ -39,6 +39,11 @@ local run_show_command = function(command, expected_tree_node)
end, "Expected to see a new window without focusing it.")
end

local run_close_command = function(command)
vim.cmd(command)
u.wait_for(function() end, { interval = 200, timeout = 200 })
end

describe("Command", function()
local test = u.fs.init_test({
items = {
Expand Down Expand Up @@ -87,7 +92,7 @@ describe("Command", function()
local tree_winid = vim.api.nvim_get_current_win()

-- toggle CLOSE
vim.cmd(cmd)
run_close_command(cmd)
verify.window_handle_is_not(tree_winid)
verify.buf_name_is(testfile)

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

-- toggle CLOSE
vim.cmd("Neotree float reveal toggle")
run_close_command("Neotree float reveal toggle")
verify.window_handle_is_not(tree_winid)
verify.buf_name_is(testfile)

Expand Down Expand Up @@ -158,7 +163,7 @@ describe("Command", function()

verify.after(500, function()
-- toggle CLOSE
vim.cmd(cmd)
run_close_command(cmd)

-- toggle OPEN
u.editfile(topfile)
Expand Down Expand Up @@ -189,7 +194,7 @@ describe("Command", function()
run_focus_command("Neotree reveal", baz)
local expected_tree_node = baz
-- toggle CLOSE
vim.cmd(cmd)
run_close_command(cmd)

verify.after(500, function()
-- toggle OPEN
Expand Down

0 comments on commit d8b4687

Please sign in to comment.