Skip to content

Commit dd2364d

Browse files
authored
feat(#2826): allow only one window with nvim-tree buffer per tab (#3174)
* feat(#2826): allow only one window with nvim-tree buffer per tab * feat(#2826): remove globals.BUFNR_BY_TABID * Revert "feat(#2826): remove globals.BUFNR_BY_TABID" This reverts commit 2651f9b. * feat(#2826): remove unused View.winid_by_tabid * feat(#2826): add feature gate experimental.close_other_windows_in_tab
1 parent 9a05b9e commit dd2364d

File tree

1 file changed

+72
-28
lines changed

1 file changed

+72
-28
lines changed

lua/nvim-tree/explorer/view.lua

Lines changed: 72 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ local Class = require("nvim-tree.classic")
2020
---@field private padding integer
2121
-- TODO multi-instance replace with single members
2222
---@field private bufnr_by_tabid table<integer, integer>
23-
---@field private winid_by_tabid table<integer, integer>
2423
local View = Class:extend()
2524

2625
---@class View
@@ -39,7 +38,6 @@ function View:new(args)
3938
self.side = (self.explorer.opts.view.side == "right") and "right" or "left"
4039
self.live_filter = { prev_focused_node = nil, }
4140
self.bufnr_by_tabid = {}
42-
self.winid_by_tabid = {}
4341

4442
self.winopts = {
4543
relativenumber = self.explorer.opts.view.relativenumber,
@@ -78,36 +76,49 @@ local BUFFER_OPTIONS = {
7876
{ name = "swapfile", value = false },
7977
}
8078

79+
---@private
80+
---@param data table
81+
---@param bufnr integer
82+
function View:log_event(data, bufnr)
83+
log.line("dev", "View %s\
84+
bufnr = %s\
85+
vim.api.nvim_get_current_tabpage() = %s\
86+
vim.api.nvim_get_current_win() = %s\
87+
self.bufnr_by_tabid = %s\
88+
globals.BUFNR_BY_TABID = %s\
89+
globals.WINID_BY_TABID = %s\
90+
vim.fn.win_findbuf(bufnr) = %s\
91+
data = %s\
92+
vim.v.event = %s",
93+
data.event,
94+
bufnr,
95+
vim.api.nvim_get_current_tabpage(),
96+
vim.api.nvim_get_current_win(),
97+
vim.inspect(self.bufnr_by_tabid, { newline = "" }),
98+
vim.inspect(globals.BUFNR_BY_TABID, { newline = "" }),
99+
vim.inspect(globals.WINID_BY_TABID, { newline = "" }),
100+
vim.inspect(vim.fn.win_findbuf(bufnr), { newline = "" }),
101+
vim.inspect(data, { newline = "" }),
102+
vim.inspect(vim.v.event, { newline = "" })
103+
)
104+
end
105+
81106
---Buffer local autocommands to track state, deleted on buffer wipeout
82107
---@private
83108
---@param bufnr integer
84109
function View:create_autocmds(bufnr)
85-
-- clear bufnr and winid
86110
-- eject buffer opened in the nvim-tree window and create a new buffer
87111
vim.api.nvim_create_autocmd("BufWipeout", {
88112
group = self.explorer.augroup_id,
89113
buffer = bufnr,
90114
callback = function(data)
91-
log.line("dev",
92-
"View BufWipeout\n bufnr = %s\n data.buf = %s\n self.bufnr_by_tabid = %s\n self.winid_by_tabid = %s",
93-
bufnr,
94-
data.buf,
95-
vim.inspect(self.bufnr_by_tabid, { newline = "" }),
96-
vim.inspect(self.winid_by_tabid, { newline = "" }),
97-
vim.inspect(data, { newline = "" })
98-
)
115+
self:log_event(data, bufnr)
99116

100117
-- clear the tab's buffer
101118
self.bufnr_by_tabid = vim.tbl_map(function(b)
102119
return b ~= bufnr and b or nil
103120
end, self.bufnr_by_tabid)
104121

105-
-- clear the tab's window(s)
106-
local winids = vim.fn.win_findbuf(bufnr)
107-
self.winid_by_tabid = vim.tbl_map(function(winid)
108-
return not vim.tbl_contains(winids, winid) and winid or nil
109-
end, self.winid_by_tabid)
110-
111122
if self.explorer.opts.actions.open_file.eject then
112123
self:prevent_buffer_override()
113124
else
@@ -116,26 +127,59 @@ function View:create_autocmds(bufnr)
116127
end,
117128
})
118129

119-
-- set winid
120-
vim.api.nvim_create_autocmd("BufWinEnter", {
130+
-- not fired when entering the first window, only subsequent event such as following a split
131+
-- does fire on :tabnew for _any_ buffer
132+
vim.api.nvim_create_autocmd("WinEnter", {
121133
group = self.explorer.augroup_id,
122134
buffer = bufnr,
123135
callback = function(data)
124-
local tabid = vim.api.nvim_get_current_tabpage()
136+
self:log_event(data, bufnr)
125137

126-
log.line("dev",
127-
"View BufWinEnter\n bufnr = %s\n data.buf = %s\n self.bufnr_by_tabid = %s\n self.winid_by_tabid = %s",
128-
bufnr,
129-
data.buf,
130-
vim.inspect(self.bufnr_by_tabid, { newline = "" }),
131-
vim.inspect(self.winid_by_tabid, { newline = "" })
132-
)
138+
-- ignore other buffers
139+
if data.buf ~= bufnr then
140+
return
141+
end
133142

134-
self.winid_by_tabid[tabid] = vim.fn.bufwinid(data.buf) -- first on current tabpage
143+
-- ignore other tabs
144+
-- this event is fired on a a :tabnew window, even though the buffer isn't actually present
145+
local tabid_cur = vim.api.nvim_get_current_tabpage()
146+
if self.bufnr_by_tabid[tabid_cur] ~= bufnr then
147+
return
148+
end
149+
150+
-- close other windows in this tab
151+
self:close_other_windows(bufnr)
135152
end,
136153
})
137154
end
138155

156+
---Close any other windows containing this buffer and setup current window
157+
---Feature gated behind experimental.close_other_windows_in_tab
158+
---@param bufnr integer
159+
function View:close_other_windows(bufnr)
160+
if not self.explorer.opts.experimental.close_other_windows_in_tab then
161+
return
162+
end
163+
164+
-- are there any other windows containing bufnr?
165+
local winids_buf = vim.fn.win_findbuf(bufnr)
166+
if #winids_buf <= 1 then
167+
return
168+
end
169+
170+
-- close all other windows
171+
local winid_cur = vim.api.nvim_get_current_win()
172+
for _, winid in ipairs(winids_buf) do
173+
if winid ~= winid_cur then
174+
pcall(vim.api.nvim_win_close, winid, false)
175+
end
176+
end
177+
178+
-- setup current window, it may be new e.g. split
179+
self:set_window_options_and_buffer()
180+
self:resize()
181+
end
182+
139183
-- TODO multi-instance remove this; delete buffers rather than retaining them
140184
---@private
141185
---@param bufnr integer

0 commit comments

Comments
 (0)