Skip to content

Commit c494994

Browse files
feat(tabs): add tab.sync options (#1698)
* Sync closing of nvim-tree across tabs * chore: remove vim.* "requires" * Sync closing of nvim-tree across tabs * Fix api.close calls * Fix issue from merge * Implement changes * Finish todos and add close_all_tabs * silently refactor options, add doc * fix vinegar example * Refactor close to work with tabid * Close nvim tree if last buffer * close and abandon all tabs on subsequent setup calls Co-authored-by: Alexander Courtis <[email protected]>
1 parent 1837751 commit c494994

File tree

7 files changed

+120
-43
lines changed

7 files changed

+120
-43
lines changed

doc/nvim-tree-lua.txt

+29-12
Original file line numberDiff line numberDiff line change
@@ -177,8 +177,6 @@ Subsequent calls to setup will replace the previous configuration.
177177
ignore_buffer_on_setup = false,
178178
open_on_setup = false,
179179
open_on_setup_file = false,
180-
open_on_tab = false,
181-
ignore_buf_on_tab_change = {},
182180
sort_by = "name",
183181
root_dirs = {},
184182
prefer_startup_root = false,
@@ -360,6 +358,13 @@ Subsequent calls to setup will replace the previous configuration.
360358
prefix = "[FILTER]: ",
361359
always_show_folders = true,
362360
},
361+
tab = {
362+
sync = {
363+
open = false,
364+
close = false,
365+
ignore = {},
366+
},
367+
},
363368
log = {
364369
enable = false,
365370
truncate = false,
@@ -417,10 +422,6 @@ You can use this option if you don't want the tree to open
417422
in some scenarios (eg using vim startify).
418423
Type: {string}, Default: `{}`
419424

420-
*nvim-tree.ignore_buf_on_tab_change*
421-
List of filetypes or buffer names that will prevent `open_on_tab` to open.
422-
Type: {string}, Default: `{}`
423-
424425
*nvim-tree.auto_reload_on_write*
425426
Reloads the explorer every time a buffer is written to.
426427
Type: `boolean`, Default: `true`
@@ -430,11 +431,6 @@ Creating a file when the cursor is on a closed folder will set the
430431
path to be inside the closed folder, otherwise the parent folder.
431432
Type: `boolean`, Default: `false`
432433

433-
*nvim-tree.open_on_tab*
434-
Opens the tree automatically when switching tabpage or opening a new tabpage
435-
if the tree was previously open.
436-
Type: `boolean`, Default: `false`
437-
438434
*nvim-tree.sort_by*
439435
Changes how files within the same directory are sorted.
440436
Can be one of `name`, `case_sensitive`, `modification_time`, `extension` or a
@@ -1002,6 +998,26 @@ The filter can be cleared with the `F` key by default.
1002998
Whether to filter folders or not.
1003999
Type: `boolean`, Default: `true`
10041000

1001+
*nvim-tree.tab*
1002+
Configuration for tab behaviour.
1003+
1004+
*nvim-tree.tab.sync*
1005+
Configuration for syncing nvim-tree across tabs.
1006+
1007+
*nvim-tree.tab.sync.open* (previously `nvim-tree.open_on_tab`)
1008+
Opens the tree automatically when switching tabpage or opening a new
1009+
tabpage if the tree was previously open.
1010+
Type: `boolean`, Default: `false`
1011+
1012+
*nvim-tree.tab.sync.close*
1013+
Closes the tree across all tabpages when the tree is closed.
1014+
Type: `boolean`, Default: `false`
1015+
1016+
*nvim-tree.tab.sync.ignore* (previously `nvim-tree.ignore_buf_on_tab_change`)
1017+
List of filetypes or buffer names on new tab that will prevent
1018+
|nvim-tree.tab.sync.open| and |nvim-tree.tab.sync.close|
1019+
Type: {string}, Default: `{}`
1020+
10051021
*nvim-tree.notify*
10061022
Configuration for notification.
10071023

@@ -1074,8 +1090,9 @@ You can easily implement a toggle using this too:
10741090
>
10751091
local function toggle_replace()
10761092
local view = require"nvim-tree.view"
1093+
local api = require"nvim-tree.api"
10771094
if view.is_visible() then
1078-
view.close()
1095+
api.tree.close()
10791096
else
10801097
require"nvim-tree".open_replacing_current_buffer()
10811098
end

lua/nvim-tree.lua

+14-10
Original file line numberDiff line numberDiff line change
@@ -116,11 +116,11 @@ function M.open_replacing_current_buffer(cwd)
116116
require("nvim-tree.actions.finders.find-file").fn(bufname)
117117
end
118118

119-
function M.tab_change()
119+
function M.tab_enter()
120120
if view.is_visible { any_tabpage = true } then
121121
local bufname = vim.api.nvim_buf_get_name(0)
122122
local ft = vim.api.nvim_buf_get_option(0, "ft")
123-
for _, filter in ipairs(M.config.ignore_buf_on_tab_change) do
123+
for _, filter in ipairs(M.config.tab.sync.ignore) do
124124
if bufname:match(filter) ~= nil or ft:match(filter) ~= nil then
125125
return
126126
end
@@ -360,8 +360,8 @@ local function setup_autocommands(opts)
360360
})
361361
end
362362

363-
if opts.open_on_tab then
364-
create_nvim_tree_autocmd("TabEnter", { callback = vim.schedule_wrap(M.tab_change) })
363+
if opts.tab.sync.open then
364+
create_nvim_tree_autocmd("TabEnter", { callback = vim.schedule_wrap(M.tab_enter) })
365365
end
366366
if opts.hijack_cursor then
367367
create_nvim_tree_autocmd("CursorMoved", {
@@ -455,8 +455,6 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
455455
ignore_buffer_on_setup = false,
456456
open_on_setup = false,
457457
open_on_setup_file = false,
458-
open_on_tab = false,
459-
ignore_buf_on_tab_change = {},
460458
sort_by = "name",
461459
root_dirs = {},
462460
prefer_startup_root = false,
@@ -638,6 +636,13 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
638636
prefix = "[FILTER]: ",
639637
always_show_folders = true,
640638
},
639+
tab = {
640+
sync = {
641+
open = false,
642+
close = false,
643+
ignore = {},
644+
},
645+
},
641646
notify = {
642647
threshold = vim.log.levels.INFO,
643648
},
@@ -737,7 +742,6 @@ function M.setup(conf)
737742
_config.open_on_setup_file = opts.open_on_setup_file
738743
_config.ignore_buffer_on_setup = opts.ignore_buffer_on_setup
739744
_config.ignore_ft_on_setup = opts.ignore_ft_on_setup
740-
_config.ignore_buf_on_tab_change = opts.ignore_buf_on_tab_change
741745
_config.hijack_directories = opts.hijack_directories
742746
_config.hijack_directories.enable = _config.hijack_directories.enable and netrw_disabled
743747

@@ -772,9 +776,9 @@ function M.setup(conf)
772776
setup_vim_commands()
773777
end
774778

775-
if M.setup_called and view.is_visible() then
776-
view.close()
777-
view.abandon_current_window()
779+
if M.setup_called then
780+
view.close_all_tabs()
781+
view.abandon_all_windows()
778782
end
779783

780784
if M.setup_called and core.get_explorer() ~= nil then

lua/nvim-tree/api.lua

+2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ end
1818
Api.tree.open = require("nvim-tree").open
1919
Api.tree.toggle = require("nvim-tree").toggle
2020
Api.tree.close = require("nvim-tree.view").close
21+
Api.tree.close_in_this_tab = require("nvim-tree.view").close_this_tab_only
22+
Api.tree.close_in_all_tabs = require("nvim-tree.view").close_all_tabs
2123
Api.tree.focus = require("nvim-tree").focus
2224
Api.tree.reload = require("nvim-tree.actions.reloaders.reloaders").reload_explorer
2325
Api.tree.change_root = require("nvim-tree").change_dir

lua/nvim-tree/legacy.lua

+5
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,11 @@ local function refactored(opts)
289289
-- 2022/06/20
290290
utils.move_missing_val(opts, "update_focused_file", "update_cwd", opts, "update_focused_file", "update_root")
291291
utils.move_missing_val(opts, "", "update_cwd", opts, "", "sync_root_with_cwd")
292+
293+
-- 2022/11/07
294+
utils.move_missing_val(opts, "", "open_on_tab", opts, "tab.sync", "open", false)
295+
utils.move_missing_val(opts, "", "open_on_tab", opts, "tab.sync", "close")
296+
utils.move_missing_val(opts, "", "ignore_buf_on_tab_change", opts, "tab.sync", "ignore")
292297
end
293298

294299
local function removed(opts)

lua/nvim-tree/lib.lua

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ function M.open(cwd)
117117
core.init(cwd or vim.loop.cwd())
118118
end
119119
if should_hijack_current_buf() then
120-
view.close()
120+
view.close_this_tab_only()
121121
view.open_in_current_win()
122122
renderer.draw()
123123
else

lua/nvim-tree/utils.lua

+21-10
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
local Iterator = require "nvim-tree.iterators.node-iterator"
2+
local notify = require "nvim-tree.notify"
23

34
local M = {
45
debouncers = {},
@@ -266,24 +267,32 @@ function M.table_create_missing(tbl, path)
266267
return t
267268
end
268269

269-
-- Move a value from src to dst if value is nil on dst
270-
-- @param src to copy from
271-
-- @param src_path dot separated string of sub-tables
272-
-- @param src_pos value pos
273-
-- @param dst to copy to
274-
-- @param dst_path dot separated string of sub-tables, created when missing
275-
-- @param dst_pos value pos
276-
function M.move_missing_val(src, src_path, src_pos, dst, dst_path, dst_pos)
270+
--- Move a value from src to dst if value is nil on dst.
271+
--- Remove value from src
272+
--- @param src table to copy from
273+
--- @param src_path string dot separated string of sub-tables
274+
--- @param src_pos string value pos
275+
--- @param dst table to copy to
276+
--- @param dst_path string dot separated string of sub-tables, created when missing
277+
--- @param dst_pos string value pos
278+
--- @param remove boolean default true
279+
function M.move_missing_val(src, src_path, src_pos, dst, dst_path, dst_pos, remove)
280+
if remove == nil then
281+
remove = true
282+
end
283+
277284
local ok, err = pcall(vim.validate, {
278285
src = { src, "table" },
279286
src_path = { src_path, "string" },
280287
src_pos = { src_pos, "string" },
281288
dst = { dst, "table" },
282289
dst_path = { dst_path, "string" },
283290
dst_pos = { dst_pos, "string" },
291+
remove = { remove, "boolean" },
284292
})
285293
if not ok then
286-
M.notify.warn("move_missing_val: " .. (err or "invalid arguments"))
294+
notify.warn("move_missing_val: " .. (err or "invalid arguments"))
295+
return
287296
end
288297

289298
for pos in string.gmatch(src_path, "([^%.]+)%.*") do
@@ -304,7 +313,9 @@ function M.move_missing_val(src, src_path, src_pos, dst, dst_path, dst_pos)
304313
dst[dst_pos] = src_val
305314
end
306315

307-
src[src_pos] = nil
316+
if remove then
317+
src[src_pos] = nil
318+
end
308319
end
309320

310321
function M.format_bytes(bytes)

lua/nvim-tree/view.lua

+48-10
Original file line numberDiff line numberDiff line change
@@ -179,21 +179,21 @@ local function switch_buf_if_last_buf()
179179
end
180180

181181
-- save_tab_state saves any state that should be preserved across redraws.
182-
local function save_tab_state()
183-
local tabpage = vim.api.nvim_get_current_tabpage()
184-
M.View.cursors[tabpage] = vim.api.nvim_win_get_cursor(M.get_winnr())
182+
local function save_tab_state(tabnr)
183+
local tabpage = tabnr or vim.api.nvim_get_current_tabpage()
184+
M.View.cursors[tabpage] = vim.api.nvim_win_get_cursor(M.get_winnr(tabpage))
185185
end
186186

187-
function M.close()
188-
if not M.is_visible() then
187+
local function close(tabpage)
188+
if not M.is_visible { tabpage = tabpage } then
189189
return
190190
end
191-
save_tab_state()
191+
save_tab_state(tabpage)
192192
switch_buf_if_last_buf()
193-
local tree_win = M.get_winnr()
193+
local tree_win = M.get_winnr(tabpage)
194194
local current_win = vim.api.nvim_get_current_win()
195-
for _, win in pairs(vim.api.nvim_list_wins()) do
196-
if tree_win ~= win and vim.api.nvim_win_get_config(win).relative == "" then
195+
for _, win in pairs(vim.api.nvim_tabpage_list_wins(tabpage)) do
196+
if vim.api.nvim_win_get_config(win).relative == "" then
197197
local prev_win = vim.fn.winnr "#" -- this tab only
198198
if tree_win == current_win and prev_win > 0 then
199199
vim.api.nvim_set_current_win(vim.fn.win_getid(prev_win))
@@ -207,6 +207,24 @@ function M.close()
207207
end
208208
end
209209

210+
function M.close_this_tab_only()
211+
close(vim.api.nvim_get_current_tabpage())
212+
end
213+
214+
function M.close_all_tabs()
215+
for tabpage, _ in pairs(M.View.tabpages) do
216+
close(tabpage)
217+
end
218+
end
219+
220+
function M.close()
221+
if M.View.tab.sync.close then
222+
M.close_all_tabs()
223+
else
224+
M.close_this_tab_only()
225+
end
226+
end
227+
210228
function M.open(options)
211229
if M.is_visible() then
212230
return
@@ -308,10 +326,29 @@ end
308326
function M.abandon_current_window()
309327
local tab = vim.api.nvim_get_current_tabpage()
310328
BUFNR_PER_TAB[tab] = nil
311-
M.View.tabpages[tab].winnr = nil
329+
if M.View.tabpages[tab] then
330+
M.View.tabpages[tab].winnr = nil
331+
end
332+
end
333+
334+
function M.abandon_all_windows()
335+
for tab, _ in pairs(vim.api.nvim_list_tabpages()) do
336+
BUFNR_PER_TAB[tab] = nil
337+
if M.View.tabpages[tab] then
338+
M.View.tabpages[tab].winnr = nil
339+
end
340+
end
312341
end
313342

314343
function M.is_visible(opts)
344+
if opts and opts.tabpage then
345+
if M.View.tabpages[opts.tabpage] == nil then
346+
return false
347+
end
348+
local winnr = M.View.tabpages[opts.tabpage].winnr
349+
return winnr and vim.api.nvim_win_is_valid(winnr)
350+
end
351+
315352
if opts and opts.any_tabpage then
316353
for _, v in pairs(M.View.tabpages) do
317354
if v.winnr and vim.api.nvim_win_is_valid(v.winnr) then
@@ -450,6 +487,7 @@ function M.setup(opts)
450487
M.View.height = options.height
451488
M.View.initial_width = get_size()
452489
M.View.hide_root_folder = options.hide_root_folder
490+
M.View.tab = opts.tab
453491
M.View.preserve_window_proportions = options.preserve_window_proportions
454492
M.View.winopts.number = options.number
455493
M.View.winopts.relativenumber = options.relativenumber

0 commit comments

Comments
 (0)