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 <alex@courtis.org>
This commit is contained in:
Wessel Blokzijl 2022-11-19 03:57:45 +01:00 committed by GitHub
parent 1837751efb
commit c49499413a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 120 additions and 43 deletions

View File

@ -177,8 +177,6 @@ Subsequent calls to setup will replace the previous configuration.
ignore_buffer_on_setup = false, ignore_buffer_on_setup = false,
open_on_setup = false, open_on_setup = false,
open_on_setup_file = false, open_on_setup_file = false,
open_on_tab = false,
ignore_buf_on_tab_change = {},
sort_by = "name", sort_by = "name",
root_dirs = {}, root_dirs = {},
prefer_startup_root = false, prefer_startup_root = false,
@ -360,6 +358,13 @@ Subsequent calls to setup will replace the previous configuration.
prefix = "[FILTER]: ", prefix = "[FILTER]: ",
always_show_folders = true, always_show_folders = true,
}, },
tab = {
sync = {
open = false,
close = false,
ignore = {},
},
},
log = { log = {
enable = false, enable = false,
truncate = false, truncate = false,
@ -417,10 +422,6 @@ You can use this option if you don't want the tree to open
in some scenarios (eg using vim startify). in some scenarios (eg using vim startify).
Type: {string}, Default: `{}` Type: {string}, Default: `{}`
*nvim-tree.ignore_buf_on_tab_change*
List of filetypes or buffer names that will prevent `open_on_tab` to open.
Type: {string}, Default: `{}`
*nvim-tree.auto_reload_on_write* *nvim-tree.auto_reload_on_write*
Reloads the explorer every time a buffer is written to. Reloads the explorer every time a buffer is written to.
Type: `boolean`, Default: `true` Type: `boolean`, Default: `true`
@ -430,11 +431,6 @@ Creating a file when the cursor is on a closed folder will set the
path to be inside the closed folder, otherwise the parent folder. path to be inside the closed folder, otherwise the parent folder.
Type: `boolean`, Default: `false` Type: `boolean`, Default: `false`
*nvim-tree.open_on_tab*
Opens the tree automatically when switching tabpage or opening a new tabpage
if the tree was previously open.
Type: `boolean`, Default: `false`
*nvim-tree.sort_by* *nvim-tree.sort_by*
Changes how files within the same directory are sorted. Changes how files within the same directory are sorted.
Can be one of `name`, `case_sensitive`, `modification_time`, `extension` or a 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.
Whether to filter folders or not. Whether to filter folders or not.
Type: `boolean`, Default: `true` Type: `boolean`, Default: `true`
*nvim-tree.tab*
Configuration for tab behaviour.
*nvim-tree.tab.sync*
Configuration for syncing nvim-tree across tabs.
*nvim-tree.tab.sync.open* (previously `nvim-tree.open_on_tab`)
Opens the tree automatically when switching tabpage or opening a new
tabpage if the tree was previously open.
Type: `boolean`, Default: `false`
*nvim-tree.tab.sync.close*
Closes the tree across all tabpages when the tree is closed.
Type: `boolean`, Default: `false`
*nvim-tree.tab.sync.ignore* (previously `nvim-tree.ignore_buf_on_tab_change`)
List of filetypes or buffer names on new tab that will prevent
|nvim-tree.tab.sync.open| and |nvim-tree.tab.sync.close|
Type: {string}, Default: `{}`
*nvim-tree.notify* *nvim-tree.notify*
Configuration for notification. Configuration for notification.
@ -1074,8 +1090,9 @@ You can easily implement a toggle using this too:
> >
local function toggle_replace() local function toggle_replace()
local view = require"nvim-tree.view" local view = require"nvim-tree.view"
local api = require"nvim-tree.api"
if view.is_visible() then if view.is_visible() then
view.close() api.tree.close()
else else
require"nvim-tree".open_replacing_current_buffer() require"nvim-tree".open_replacing_current_buffer()
end end

View File

@ -116,11 +116,11 @@ function M.open_replacing_current_buffer(cwd)
require("nvim-tree.actions.finders.find-file").fn(bufname) require("nvim-tree.actions.finders.find-file").fn(bufname)
end end
function M.tab_change() function M.tab_enter()
if view.is_visible { any_tabpage = true } then if view.is_visible { any_tabpage = true } then
local bufname = vim.api.nvim_buf_get_name(0) local bufname = vim.api.nvim_buf_get_name(0)
local ft = vim.api.nvim_buf_get_option(0, "ft") local ft = vim.api.nvim_buf_get_option(0, "ft")
for _, filter in ipairs(M.config.ignore_buf_on_tab_change) do for _, filter in ipairs(M.config.tab.sync.ignore) do
if bufname:match(filter) ~= nil or ft:match(filter) ~= nil then if bufname:match(filter) ~= nil or ft:match(filter) ~= nil then
return return
end end
@ -360,8 +360,8 @@ local function setup_autocommands(opts)
}) })
end end
if opts.open_on_tab then if opts.tab.sync.open then
create_nvim_tree_autocmd("TabEnter", { callback = vim.schedule_wrap(M.tab_change) }) create_nvim_tree_autocmd("TabEnter", { callback = vim.schedule_wrap(M.tab_enter) })
end end
if opts.hijack_cursor then if opts.hijack_cursor then
create_nvim_tree_autocmd("CursorMoved", { create_nvim_tree_autocmd("CursorMoved", {
@ -455,8 +455,6 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
ignore_buffer_on_setup = false, ignore_buffer_on_setup = false,
open_on_setup = false, open_on_setup = false,
open_on_setup_file = false, open_on_setup_file = false,
open_on_tab = false,
ignore_buf_on_tab_change = {},
sort_by = "name", sort_by = "name",
root_dirs = {}, root_dirs = {},
prefer_startup_root = false, prefer_startup_root = false,
@ -638,6 +636,13 @@ local DEFAULT_OPTS = { -- BEGIN_DEFAULT_OPTS
prefix = "[FILTER]: ", prefix = "[FILTER]: ",
always_show_folders = true, always_show_folders = true,
}, },
tab = {
sync = {
open = false,
close = false,
ignore = {},
},
},
notify = { notify = {
threshold = vim.log.levels.INFO, threshold = vim.log.levels.INFO,
}, },
@ -737,7 +742,6 @@ function M.setup(conf)
_config.open_on_setup_file = opts.open_on_setup_file _config.open_on_setup_file = opts.open_on_setup_file
_config.ignore_buffer_on_setup = opts.ignore_buffer_on_setup _config.ignore_buffer_on_setup = opts.ignore_buffer_on_setup
_config.ignore_ft_on_setup = opts.ignore_ft_on_setup _config.ignore_ft_on_setup = opts.ignore_ft_on_setup
_config.ignore_buf_on_tab_change = opts.ignore_buf_on_tab_change
_config.hijack_directories = opts.hijack_directories _config.hijack_directories = opts.hijack_directories
_config.hijack_directories.enable = _config.hijack_directories.enable and netrw_disabled _config.hijack_directories.enable = _config.hijack_directories.enable and netrw_disabled
@ -772,9 +776,9 @@ function M.setup(conf)
setup_vim_commands() setup_vim_commands()
end end
if M.setup_called and view.is_visible() then if M.setup_called then
view.close() view.close_all_tabs()
view.abandon_current_window() view.abandon_all_windows()
end end
if M.setup_called and core.get_explorer() ~= nil then if M.setup_called and core.get_explorer() ~= nil then

View File

@ -18,6 +18,8 @@ end
Api.tree.open = require("nvim-tree").open Api.tree.open = require("nvim-tree").open
Api.tree.toggle = require("nvim-tree").toggle Api.tree.toggle = require("nvim-tree").toggle
Api.tree.close = require("nvim-tree.view").close Api.tree.close = require("nvim-tree.view").close
Api.tree.close_in_this_tab = require("nvim-tree.view").close_this_tab_only
Api.tree.close_in_all_tabs = require("nvim-tree.view").close_all_tabs
Api.tree.focus = require("nvim-tree").focus Api.tree.focus = require("nvim-tree").focus
Api.tree.reload = require("nvim-tree.actions.reloaders.reloaders").reload_explorer Api.tree.reload = require("nvim-tree.actions.reloaders.reloaders").reload_explorer
Api.tree.change_root = require("nvim-tree").change_dir Api.tree.change_root = require("nvim-tree").change_dir

View File

@ -289,6 +289,11 @@ local function refactored(opts)
-- 2022/06/20 -- 2022/06/20
utils.move_missing_val(opts, "update_focused_file", "update_cwd", opts, "update_focused_file", "update_root") utils.move_missing_val(opts, "update_focused_file", "update_cwd", opts, "update_focused_file", "update_root")
utils.move_missing_val(opts, "", "update_cwd", opts, "", "sync_root_with_cwd") utils.move_missing_val(opts, "", "update_cwd", opts, "", "sync_root_with_cwd")
-- 2022/11/07
utils.move_missing_val(opts, "", "open_on_tab", opts, "tab.sync", "open", false)
utils.move_missing_val(opts, "", "open_on_tab", opts, "tab.sync", "close")
utils.move_missing_val(opts, "", "ignore_buf_on_tab_change", opts, "tab.sync", "ignore")
end end
local function removed(opts) local function removed(opts)

View File

@ -117,7 +117,7 @@ function M.open(cwd)
core.init(cwd or vim.loop.cwd()) core.init(cwd or vim.loop.cwd())
end end
if should_hijack_current_buf() then if should_hijack_current_buf() then
view.close() view.close_this_tab_only()
view.open_in_current_win() view.open_in_current_win()
renderer.draw() renderer.draw()
else else

View File

@ -1,4 +1,5 @@
local Iterator = require "nvim-tree.iterators.node-iterator" local Iterator = require "nvim-tree.iterators.node-iterator"
local notify = require "nvim-tree.notify"
local M = { local M = {
debouncers = {}, debouncers = {},
@ -266,14 +267,20 @@ function M.table_create_missing(tbl, path)
return t return t
end end
-- Move a value from src to dst if value is nil on dst --- Move a value from src to dst if value is nil on dst.
-- @param src to copy from --- Remove value from src
-- @param src_path dot separated string of sub-tables --- @param src table to copy from
-- @param src_pos value pos --- @param src_path string dot separated string of sub-tables
-- @param dst to copy to --- @param src_pos string value pos
-- @param dst_path dot separated string of sub-tables, created when missing --- @param dst table to copy to
-- @param dst_pos value pos --- @param dst_path string dot separated string of sub-tables, created when missing
function M.move_missing_val(src, src_path, src_pos, dst, dst_path, dst_pos) --- @param dst_pos string value pos
--- @param remove boolean default true
function M.move_missing_val(src, src_path, src_pos, dst, dst_path, dst_pos, remove)
if remove == nil then
remove = true
end
local ok, err = pcall(vim.validate, { local ok, err = pcall(vim.validate, {
src = { src, "table" }, src = { src, "table" },
src_path = { src_path, "string" }, src_path = { src_path, "string" },
@ -281,9 +288,11 @@ function M.move_missing_val(src, src_path, src_pos, dst, dst_path, dst_pos)
dst = { dst, "table" }, dst = { dst, "table" },
dst_path = { dst_path, "string" }, dst_path = { dst_path, "string" },
dst_pos = { dst_pos, "string" }, dst_pos = { dst_pos, "string" },
remove = { remove, "boolean" },
}) })
if not ok then if not ok then
M.notify.warn("move_missing_val: " .. (err or "invalid arguments")) notify.warn("move_missing_val: " .. (err or "invalid arguments"))
return
end end
for pos in string.gmatch(src_path, "([^%.]+)%.*") do 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)
dst[dst_pos] = src_val dst[dst_pos] = src_val
end end
src[src_pos] = nil if remove then
src[src_pos] = nil
end
end end
function M.format_bytes(bytes) function M.format_bytes(bytes)

View File

@ -179,21 +179,21 @@ local function switch_buf_if_last_buf()
end end
-- save_tab_state saves any state that should be preserved across redraws. -- save_tab_state saves any state that should be preserved across redraws.
local function save_tab_state() local function save_tab_state(tabnr)
local tabpage = vim.api.nvim_get_current_tabpage() local tabpage = tabnr or vim.api.nvim_get_current_tabpage()
M.View.cursors[tabpage] = vim.api.nvim_win_get_cursor(M.get_winnr()) M.View.cursors[tabpage] = vim.api.nvim_win_get_cursor(M.get_winnr(tabpage))
end end
function M.close() local function close(tabpage)
if not M.is_visible() then if not M.is_visible { tabpage = tabpage } then
return return
end end
save_tab_state() save_tab_state(tabpage)
switch_buf_if_last_buf() switch_buf_if_last_buf()
local tree_win = M.get_winnr() local tree_win = M.get_winnr(tabpage)
local current_win = vim.api.nvim_get_current_win() local current_win = vim.api.nvim_get_current_win()
for _, win in pairs(vim.api.nvim_list_wins()) do for _, win in pairs(vim.api.nvim_tabpage_list_wins(tabpage)) do
if tree_win ~= win and vim.api.nvim_win_get_config(win).relative == "" then if vim.api.nvim_win_get_config(win).relative == "" then
local prev_win = vim.fn.winnr "#" -- this tab only local prev_win = vim.fn.winnr "#" -- this tab only
if tree_win == current_win and prev_win > 0 then if tree_win == current_win and prev_win > 0 then
vim.api.nvim_set_current_win(vim.fn.win_getid(prev_win)) vim.api.nvim_set_current_win(vim.fn.win_getid(prev_win))
@ -207,6 +207,24 @@ function M.close()
end end
end end
function M.close_this_tab_only()
close(vim.api.nvim_get_current_tabpage())
end
function M.close_all_tabs()
for tabpage, _ in pairs(M.View.tabpages) do
close(tabpage)
end
end
function M.close()
if M.View.tab.sync.close then
M.close_all_tabs()
else
M.close_this_tab_only()
end
end
function M.open(options) function M.open(options)
if M.is_visible() then if M.is_visible() then
return return
@ -308,10 +326,29 @@ end
function M.abandon_current_window() function M.abandon_current_window()
local tab = vim.api.nvim_get_current_tabpage() local tab = vim.api.nvim_get_current_tabpage()
BUFNR_PER_TAB[tab] = nil BUFNR_PER_TAB[tab] = nil
M.View.tabpages[tab].winnr = nil if M.View.tabpages[tab] then
M.View.tabpages[tab].winnr = nil
end
end
function M.abandon_all_windows()
for tab, _ in pairs(vim.api.nvim_list_tabpages()) do
BUFNR_PER_TAB[tab] = nil
if M.View.tabpages[tab] then
M.View.tabpages[tab].winnr = nil
end
end
end end
function M.is_visible(opts) function M.is_visible(opts)
if opts and opts.tabpage then
if M.View.tabpages[opts.tabpage] == nil then
return false
end
local winnr = M.View.tabpages[opts.tabpage].winnr
return winnr and vim.api.nvim_win_is_valid(winnr)
end
if opts and opts.any_tabpage then if opts and opts.any_tabpage then
for _, v in pairs(M.View.tabpages) do for _, v in pairs(M.View.tabpages) do
if v.winnr and vim.api.nvim_win_is_valid(v.winnr) then if v.winnr and vim.api.nvim_win_is_valid(v.winnr) then
@ -450,6 +487,7 @@ function M.setup(opts)
M.View.height = options.height M.View.height = options.height
M.View.initial_width = get_size() M.View.initial_width = get_size()
M.View.hide_root_folder = options.hide_root_folder M.View.hide_root_folder = options.hide_root_folder
M.View.tab = opts.tab
M.View.preserve_window_proportions = options.preserve_window_proportions M.View.preserve_window_proportions = options.preserve_window_proportions
M.View.winopts.number = options.number M.View.winopts.number = options.number
M.View.winopts.relativenumber = options.relativenumber M.View.winopts.relativenumber = options.relativenumber