aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/doc/news.txt1
-rw-r--r--runtime/doc/options.txt13
-rw-r--r--runtime/doc/quickref.txt1
-rw-r--r--runtime/doc/tabpage.txt3
-rw-r--r--runtime/lua/vim/_meta/options.lua16
-rw-r--r--runtime/optwin.vim4
-rw-r--r--src/nvim/option_vars.h6
-rw-r--r--src/nvim/options.lua24
-rw-r--r--src/nvim/optionstr.c20
-rw-r--r--src/nvim/window.c18
-rw-r--r--test/old/testdir/test_options.vim8
-rw-r--r--test/old/testdir/test_tabpage.vim58
12 files changed, 161 insertions, 11 deletions
diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt
index 604515a808..39b3a506ca 100644
--- a/runtime/doc/news.txt
+++ b/runtime/doc/news.txt
@@ -141,6 +141,7 @@ LUA
OPTIONS
• 'completeopt' flag "fuzzy" enables |fuzzy-matching| during |ins-completion|.
+• 'tabclose' controls which tab page to focus when closing a tab page.
PERFORMANCE
diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt
index 4bb589fb2c..caa6649b17 100644
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -6343,6 +6343,19 @@ A jump table for the options with a short description can be found at |Q_op|.
'S' flag in 'cpoptions'.
Only normal file name characters can be used, `/\*?[|<>` are illegal.
+ *'tabclose'* *'tcl'*
+'tabclose' 'tcl' string (default "")
+ global
+ This option controls the behavior when closing tab pages (e.g., using
+ |:tabclose|). When empty Vim goes to the next (right) tab page.
+
+ Possible values (comma-separated list):
+ left If included, go to the previous tab page instead of
+ the next one.
+ uselast If included, go to the previously used tab page if
+ possible. This option takes precedence over the
+ others.
+
*'tabline'* *'tal'*
'tabline' 'tal' string (default "")
global
diff --git a/runtime/doc/quickref.txt b/runtime/doc/quickref.txt
index 5eea9baa20..d77750b485 100644
--- a/runtime/doc/quickref.txt
+++ b/runtime/doc/quickref.txt
@@ -886,6 +886,7 @@ Short explanation of each option: *option-list*
'switchbuf' 'swb' sets behavior when switching to another buffer
'synmaxcol' 'smc' maximum column to find syntax items
'syntax' 'syn' syntax to be loaded for current buffer
+'tabclose' 'tcl' which tab page to focus when closing a tab
'tabline' 'tal' custom format for the console tab pages line
'tabpagemax' 'tpm' maximum number of tab pages for |-p| and "tab all"
'tabstop' 'ts' number of spaces that <Tab> in file uses
diff --git a/runtime/doc/tabpage.txt b/runtime/doc/tabpage.txt
index 2f50e31ee5..7bfa36e8ab 100644
--- a/runtime/doc/tabpage.txt
+++ b/runtime/doc/tabpage.txt
@@ -135,7 +135,8 @@ something else.
:tabclose $ " close the last tab page
:tabclose # " close the last accessed tab page
-When a tab is closed the next tab page will become the current one.
+When a tab is closed the next tab page will become the current one. This
+behaviour can be customized using the 'tabclose' option.
*:tabo* *:tabonly*
:tabo[nly][!] Close all other tab pages.
diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua
index 40c12d94fd..332dbda6e9 100644
--- a/runtime/lua/vim/_meta/options.lua
+++ b/runtime/lua/vim/_meta/options.lua
@@ -6837,6 +6837,22 @@ vim.o.syn = vim.o.syntax
vim.bo.syntax = vim.o.syntax
vim.bo.syn = vim.bo.syntax
+--- This option controls the behavior when closing tab pages (e.g., using
+--- `:tabclose`). When empty Vim goes to the next (right) tab page.
+---
+--- Possible values (comma-separated list):
+--- left If included, go to the previous tab page instead of
+--- the next one.
+--- uselast If included, go to the previously used tab page if
+--- possible. This option takes precedence over the
+--- others.
+---
+--- @type string
+vim.o.tabclose = ""
+vim.o.tcl = vim.o.tabclose
+vim.go.tabclose = vim.o.tabclose
+vim.go.tcl = vim.go.tabclose
+
--- When non-empty, this option determines the content of the tab pages
--- line at the top of the Vim window. When empty Vim will use a default
--- tab pages line. See `setting-tabline` for more info.
diff --git a/runtime/optwin.vim b/runtime/optwin.vim
index 3b874f4cda..f168218c91 100644
--- a/runtime/optwin.vim
+++ b/runtime/optwin.vim
@@ -1,7 +1,7 @@
" These commands create the option window.
"
" Maintainer: The Vim Project <https://github.com/vim/vim>
-" Last Change: 2024 Jun 05
+" Last Change: 2024 Jul 12
" Former Maintainer: Bram Moolenaar <Bram@vim.org>
" If there already is an option window, jump to that one.
@@ -507,6 +507,8 @@ endif
call <SID>Header(gettext("multiple tab pages"))
call <SID>AddOption("showtabline", gettext("0, 1 or 2; when to use a tab pages line"))
call append("$", " \tset stal=" . &stal)
+call <SID>AddOption("tabclose", gettext("behaviour when closing tab pages: left, uselast or empty"))
+call append("$", " \tset tcl=" . &tcl)
call <SID>AddOption("tabpagemax", gettext("maximum number of tab pages to open for -p and \"tab all\""))
call append("$", " \tset tpm=" . &tpm)
call <SID>AddOption("tabline", gettext("custom tab pages line"))
diff --git a/src/nvim/option_vars.h b/src/nvim/option_vars.h
index 3326c0879a..b6b307befb 100644
--- a/src/nvim/option_vars.h
+++ b/src/nvim/option_vars.h
@@ -694,7 +694,6 @@ EXTERN unsigned tpf_flags; ///< flags from 'termpastefilter'
EXTERN char *p_tfu; ///< 'tagfunc'
EXTERN char *p_spc; ///< 'spellcapcheck'
EXTERN char *p_spf; ///< 'spellfile'
-EXTERN char *p_spk; ///< 'splitkeep'
EXTERN char *p_spl; ///< 'spelllang'
EXTERN char *p_spo; ///< 'spelloptions'
EXTERN unsigned spo_flags;
@@ -711,7 +710,12 @@ EXTERN unsigned swb_flags;
#define SWB_NEWTAB 0x008
#define SWB_VSPLIT 0x010
#define SWB_USELAST 0x020
+EXTERN char *p_spk; ///< 'splitkeep'
EXTERN char *p_syn; ///< 'syntax'
+EXTERN char *p_tcl; ///< 'tabclose'
+EXTERN unsigned tcl_flags; ///< flags from 'tabclose'
+#define TCL_LEFT 0x001
+#define TCL_USELAST 0x002
EXTERN OptInt p_ts; ///< 'tabstop'
EXTERN int p_tbs; ///< 'tagbsearch'
EXTERN char *p_tc; ///< 'tagcase'
diff --git a/src/nvim/options.lua b/src/nvim/options.lua
index 6e317a426c..6345ef5ada 100644
--- a/src/nvim/options.lua
+++ b/src/nvim/options.lua
@@ -8507,6 +8507,30 @@ return {
varname = 'p_syn',
},
{
+ abbreviation = 'tcl',
+ cb = 'did_set_tabclose',
+ defaults = { if_true = '' },
+ deny_duplicates = true,
+ desc = [=[
+ This option controls the behavior when closing tab pages (e.g., using
+ |:tabclose|). When empty Vim goes to the next (right) tab page.
+
+ Possible values (comma-separated list):
+ left If included, go to the previous tab page instead of
+ the next one.
+ uselast If included, go to the previously used tab page if
+ possible. This option takes precedence over the
+ others.
+ ]=],
+ expand_cb = 'expand_set_tabclose',
+ full_name = 'tabclose',
+ list = 'onecomma',
+ scope = { 'global' },
+ short_desc = N_('which tab page to focus when closing a tab'),
+ type = 'string',
+ varname = 'p_tcl',
+ },
+ {
abbreviation = 'tal',
cb = 'did_set_tabline',
defaults = { if_true = '' },
diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c
index 1a4c142fdd..8e853b6ee0 100644
--- a/src/nvim/optionstr.c
+++ b/src/nvim/optionstr.c
@@ -98,11 +98,13 @@ static char *(p_ssop_values[]) = { "buffers", "winpos", "resize", "winsize", "lo
"options", "help", "blank", "globals", "slash", "unix", "sesdir",
"curdir", "folds", "cursor", "tabpages", "terminal", "skiprtp",
NULL };
-// Keep in sync with SWB_ flags in option_defs.h
+// Keep in sync with SWB_ flags in option_vars.h
static char *(p_swb_values[]) = { "useopen", "usetab", "split", "newtab", "vsplit", "uselast",
NULL };
static char *(p_spk_values[]) = { "cursor", "screen", "topline", NULL };
static char *(p_tc_values[]) = { "followic", "ignore", "match", "followscs", "smart", NULL };
+// Keep in sync with TCL_ flags in option_vars.h
+static char *(p_tcl_values[]) = { "left", "uselast", NULL };
static char *(p_ve_values[]) = { "block", "insert", "all", "onemore", "none", "NONE", NULL };
// Note: Keep this in sync with check_opt_wim()
static char *(p_wim_values[]) = { "full", "longest", "list", "lastused", NULL };
@@ -169,6 +171,7 @@ void didset_string_options(void)
opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true);
opt_strings_flags(p_ve, p_ve_values, &ve_flags, true);
opt_strings_flags(p_swb, p_swb_values, &swb_flags, true);
+ opt_strings_flags(p_tcl, p_tcl_values, &tcl_flags, true);
opt_strings_flags(p_wop, p_wop_values, &wop_flags, true);
opt_strings_flags(p_cb, p_cb_values, &cb_flags, true);
}
@@ -2207,6 +2210,21 @@ int expand_set_switchbuf(optexpand_T *args, int *numMatches, char ***matches)
matches);
}
+/// The 'tabclose' option is changed.
+const char *did_set_tabclose(optset_T *args FUNC_ATTR_UNUSED)
+{
+ return did_set_opt_flags(p_tcl, p_tcl_values, &tcl_flags, true);
+}
+
+int expand_set_tabclose(optexpand_T *args, int *numMatches, char ***matches)
+{
+ return expand_set_opt_string(args,
+ p_tcl_values,
+ ARRAY_SIZE(p_tcl_values) - 1,
+ numMatches,
+ matches);
+}
+
/// The 'tabline' option is changed.
const char *did_set_tabline(optset_T *args)
{
diff --git a/src/nvim/window.c b/src/nvim/window.c
index 9203dd1bdb..132bf9ee60 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -3456,14 +3456,22 @@ static frame_T *win_altframe(win_T *win, tabpage_T *tp)
// Return the tabpage that will be used if the current one is closed.
static tabpage_T *alt_tabpage(void)
{
- // Use the next tab page if possible.
- if (curtab->tp_next != NULL) {
- return curtab->tp_next;
+ // Use the last accessed tab page, if possible.
+ if ((tcl_flags & TCL_USELAST) && valid_tabpage(lastused_tabpage)) {
+ return lastused_tabpage;
}
- // Find the last but one tab page.
+ // Use the previous tab page, if possible.
+ bool forward = curtab->tp_next != NULL
+ && ((tcl_flags & TCL_LEFT) == 0 || curtab == first_tabpage);
+
tabpage_T *tp;
- for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) {}
+ if (forward) {
+ tp = curtab->tp_next;
+ } else {
+ for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) {}
+ }
+
return tp;
}
diff --git a/test/old/testdir/test_options.vim b/test/old/testdir/test_options.vim
index f7eace59c2..26b4f64487 100644
--- a/test/old/testdir/test_options.vim
+++ b/test/old/testdir/test_options.vim
@@ -558,6 +558,9 @@ func Test_set_completion_string_values()
" call assert_equal('sync', getcompletion('set swapsync=', 'cmdline')[1])
call assert_equal('usetab', getcompletion('set switchbuf=', 'cmdline')[1])
call assert_equal('ignore', getcompletion('set tagcase=', 'cmdline')[1])
+ if exists('+tabclose')
+ call assert_equal('left uselast', join(sort(getcompletion('set tabclose=', 'cmdline'))), ' ')
+ endif
if exists('+termwintype')
call assert_equal('conpty', getcompletion('set termwintype=', 'cmdline')[1])
endif
@@ -1377,9 +1380,10 @@ func Test_write()
new
call setline(1, ['L1'])
set nowrite
- call assert_fails('write Xfile', 'E142:')
+ call assert_fails('write Xwrfile', 'E142:')
set write
- close!
+ " close swapfile
+ bw!
endfunc
" Test for 'buftype' option
diff --git a/test/old/testdir/test_tabpage.vim b/test/old/testdir/test_tabpage.vim
index 2bd2907a55..482da2de7f 100644
--- a/test/old/testdir/test_tabpage.vim
+++ b/test/old/testdir/test_tabpage.vim
@@ -967,6 +967,64 @@ func Test_tabpage_alloc_failure()
call assert_equal(1, tabpagenr('$'))
endfunc
+func Test_tabpage_tabclose()
+ " Default behaviour, move to the right.
+ call s:reconstruct_tabpage_for_test(6)
+ norm! 4gt
+ setl tcl=
+ tabclose
+ call assert_equal("n3", bufname())
+
+ " Move to the left.
+ call s:reconstruct_tabpage_for_test(6)
+ norm! 4gt
+ setl tcl=left
+ tabclose
+ call assert_equal("n1", bufname())
+
+ " Move to the last used tab page.
+ call s:reconstruct_tabpage_for_test(6)
+ norm! 5gt
+ norm! 2gt
+ setl tcl=uselast
+ tabclose
+ call assert_equal("n3", bufname())
+
+ " Same, but the last used tab page is invalid. Move to the right.
+ call s:reconstruct_tabpage_for_test(6)
+ norm! 5gt
+ norm! 3gt
+ setl tcl=uselast
+ tabclose 5
+ tabclose!
+ call assert_equal("n2", bufname())
+
+ " Same, but the last used tab page is invalid. Move to the left.
+ call s:reconstruct_tabpage_for_test(6)
+ norm! 5gt
+ norm! 3gt
+ setl tcl=uselast,left
+ tabclose 5
+ tabclose!
+ call assert_equal("n0", bufname())
+
+ " Move left when moving right is not possible.
+ call s:reconstruct_tabpage_for_test(6)
+ setl tcl=
+ norm! 6gt
+ tabclose
+ call assert_equal("n3", bufname())
+
+ " Move right when moving left is not possible.
+ call s:reconstruct_tabpage_for_test(6)
+ setl tcl=left
+ norm! 1gt
+ tabclose
+ call assert_equal("n0", bufname())
+
+ setl tcl&
+endfunc
+
" this was giving ml_get errors
func Test_tabpage_last_line()
enew