mirror of
https://github.com/neovim/neovim.git
synced 2026-08-29 10:31:48 +00:00
feat(events): add TabMoved autocommand event #24137
Problem: No way to handle a "tab moved" event. Use-case: tabline plugins may cache tab labels, and need to know when to invalidate their cache. Solution: Add a `TabMoved` event that triggers whenever tabs are reordered via `:tabmove` or via mouse click-and-drag.
This commit is contained in:
@@ -1165,6 +1165,14 @@ TabEnter Just after entering a |tabpage|.
|
||||
TabLeave Just before leaving a |tabpage|.
|
||||
After WinLeave.
|
||||
|
||||
*TabMoved*
|
||||
TabMoved After a |tabpage| is moved.
|
||||
See |:tabmove|. <amatch> expands to the
|
||||
affected |tabpage-number| before the move.
|
||||
The |event-data| has these keys (type: `vim.event.tabmoved.data`)
|
||||
- tabnr_old: |tabpage-number| before the move.
|
||||
- tabnr_new: |tabpage-number| after the move.
|
||||
|
||||
*TabNew*
|
||||
TabNew When creating a new |tabpage|.
|
||||
After WinEnter.
|
||||
|
||||
@@ -148,6 +148,7 @@ EVENTS
|
||||
• |:delmarks| now triggers the |MarkSet| autocommand with line==col==0, same
|
||||
as |nvim_buf_del_mark()|
|
||||
• |TextPutPre| and |TextPutPost| are triggered before/after putting text.
|
||||
• |TabMoved| is triggered when tabs are reordered.
|
||||
|
||||
HIGHLIGHTS
|
||||
|
||||
|
||||
@@ -2593,6 +2593,7 @@ A jump table for the options with a short description can be found at |Q_op|.
|
||||
|TabClosedPre|,
|
||||
|TabEnter|,
|
||||
|TabLeave|,
|
||||
|TabMoved|,
|
||||
|TabNew|,
|
||||
|TabNewEntered|,
|
||||
|TermClose|,
|
||||
|
||||
@@ -343,6 +343,7 @@ Events (autocommands):
|
||||
- |RecordingLeave|
|
||||
- |SearchWrapped|
|
||||
- |Signal|
|
||||
- |TabMoved|
|
||||
- |TabNewEntered|
|
||||
- |TermClose|
|
||||
- |TermOpen|
|
||||
|
||||
1
runtime/lua/vim/_meta/api_keysets.gen.lua
generated
1
runtime/lua/vim/_meta/api_keysets.gen.lua
generated
@@ -194,6 +194,7 @@ error('Cannot require a meta file')
|
||||
--- |'TabClosedPre'
|
||||
--- |'TabEnter'
|
||||
--- |'TabLeave'
|
||||
--- |'TabMoved'
|
||||
--- |'TabNew'
|
||||
--- |'TabNewEntered'
|
||||
--- |'TermChanged'
|
||||
|
||||
@@ -50,6 +50,10 @@ error('Cannot require a meta file')
|
||||
--- @field status? string
|
||||
--- @field title? string
|
||||
|
||||
--- @class vim.event.tabmoved.data
|
||||
--- @field tabnr_old integer
|
||||
--- @field tabnr_new integer
|
||||
|
||||
--- @class vim.event.termrequest.data
|
||||
--- @field sequence string
|
||||
--- @field terminator string
|
||||
|
||||
1
runtime/lua/vim/_meta/options.gen.lua
generated
1
runtime/lua/vim/_meta/options.gen.lua
generated
@@ -2240,6 +2240,7 @@ vim.go.ei = vim.go.eventignore
|
||||
--- `TabClosedPre`,
|
||||
--- `TabEnter`,
|
||||
--- `TabLeave`,
|
||||
--- `TabMoved`,
|
||||
--- `TabNew`,
|
||||
--- `TabNewEntered`,
|
||||
--- `TermClose`,
|
||||
|
||||
@@ -113,6 +113,7 @@ return {
|
||||
TabClosedPre = false, -- before closing a tab page
|
||||
TabEnter = false, -- after entering a tab page
|
||||
TabLeave = false, -- before leaving a tab page
|
||||
TabMoved = false, -- after a tab was moved
|
||||
TabNew = false, -- when creating a new tab
|
||||
TabNewEntered = false, -- after entering a new tab
|
||||
TermChanged = false, -- after changing 'term'
|
||||
@@ -171,6 +172,7 @@ return {
|
||||
RecordingEnter = true,
|
||||
RecordingLeave = true,
|
||||
Signal = true,
|
||||
TabMoved = true,
|
||||
TabNewEntered = true,
|
||||
TermClose = true,
|
||||
TermOpen = true,
|
||||
|
||||
@@ -1843,6 +1843,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
|
||||
|| event == EVENT_SPELLFILEMISSING
|
||||
|| event == EVENT_SYNTAX
|
||||
|| event == EVENT_TABCLOSED
|
||||
|| event == EVENT_TABMOVED
|
||||
|| event == EVENT_USER
|
||||
|| event == EVENT_WINCLOSED
|
||||
|| event == EVENT_WINRESIZED
|
||||
|
||||
@@ -5005,6 +5005,7 @@ void tabpage_move(int nr)
|
||||
return;
|
||||
}
|
||||
|
||||
int old_nr = tabpage_index(curtab);
|
||||
tabpage_T *tp_dst = tp;
|
||||
|
||||
// Remove the current tab page from the list of tab pages.
|
||||
@@ -5035,6 +5036,15 @@ void tabpage_move(int nr)
|
||||
|
||||
// Need to redraw the tabline. Tab page contents doesn't change.
|
||||
redraw_tabline = true;
|
||||
|
||||
if (has_event(EVENT_TABMOVED)) {
|
||||
char prev_idx[NUMBUFLEN];
|
||||
vim_snprintf(prev_idx, NUMBUFLEN, "%i", old_nr);
|
||||
MAXSIZE_TEMP_DICT(data, 2);
|
||||
PUT_C(data, "tabnr_old", INTEGER_OBJ(old_nr));
|
||||
PUT_C(data, "tabnr_new", INTEGER_OBJ(tabpage_index(curtab)));
|
||||
aucmd_defer(EVENT_TABMOVED, prev_idx, NULL, AUGROUP_ALL, curbuf, NULL, &DICT_OBJ(data));
|
||||
}
|
||||
}
|
||||
|
||||
/// Go to another window.
|
||||
|
||||
114
test/functional/autocmd/tabmove_spec.lua
Normal file
114
test/functional/autocmd/tabmove_spec.lua
Normal file
@@ -0,0 +1,114 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
local clear = n.clear
|
||||
local command = n.command
|
||||
local exec_lua = n.exec_lua
|
||||
local insert = n.insert
|
||||
local poke_eventloop = n.poke_eventloop
|
||||
local api = n.api
|
||||
local eval = n.eval
|
||||
|
||||
local eq = t.eq
|
||||
|
||||
describe('TabMoved', function()
|
||||
before_each(function()
|
||||
clear()
|
||||
end)
|
||||
|
||||
it('sets <amatch> and event-data fields', function()
|
||||
-- start on tabpage 1, create 2 more so tabpage 3 is "current tab"
|
||||
command('tabnew')
|
||||
command('tabnew')
|
||||
|
||||
exec_lua([[
|
||||
_G.tm_events = {}
|
||||
vim.api.nvim_create_autocmd('TabMoved', {
|
||||
callback = function(ev)
|
||||
table.insert(_G.tm_events, ev)
|
||||
end,
|
||||
})
|
||||
]])
|
||||
|
||||
command('tabmove 0')
|
||||
poke_eventloop()
|
||||
local events = exec_lua('return _G.tm_events')
|
||||
eq('3', events[1].match)
|
||||
eq(3, events[1].data.tabnr_old)
|
||||
eq(1, events[1].data.tabnr_new)
|
||||
|
||||
command('tabnext 2')
|
||||
command('tabmove $')
|
||||
poke_eventloop()
|
||||
events = exec_lua('return _G.tm_events')
|
||||
eq('2', events[2].match)
|
||||
eq(2, events[2].data.tabnr_old)
|
||||
eq(3, events[2].data.tabnr_new)
|
||||
|
||||
command('tabmove -1')
|
||||
poke_eventloop()
|
||||
events = exec_lua('return _G.tm_events')
|
||||
eq('3', events[3].match)
|
||||
eq(3, events[3].data.tabnr_old)
|
||||
eq(2, events[3].data.tabnr_new)
|
||||
end)
|
||||
|
||||
it('does not trigger when a tab is moved to same position', function()
|
||||
command('tabnew')
|
||||
command('tabnew')
|
||||
command('let g:test = 0')
|
||||
command('au! TabMoved * let g:test += 1')
|
||||
|
||||
command('tabmove $') -- already at last position, no-op
|
||||
poke_eventloop()
|
||||
eq(0, eval('g:test'))
|
||||
end)
|
||||
|
||||
it('is not triggered when creating or closing tabs', function()
|
||||
command('let g:test = 0')
|
||||
command('au! TabMoved * let g:test += 1')
|
||||
command('file Xtestfile1')
|
||||
command('0tabedit Xtestfile2')
|
||||
command('tabclose')
|
||||
poke_eventloop()
|
||||
eq(0, eval('g:test'))
|
||||
end)
|
||||
|
||||
it('responds correctly to mouse interactions on the tabline', function()
|
||||
local function setup()
|
||||
Screen.new(25, 5)
|
||||
command('set mouse=a')
|
||||
command('tabnew')
|
||||
command('tabnew')
|
||||
exec_lua([[
|
||||
_G.tm_events = {}
|
||||
vim.api.nvim_create_autocmd('TabMoved', {
|
||||
callback = function(ev)
|
||||
table.insert(_G.tm_events, ev)
|
||||
end,
|
||||
})
|
||||
]])
|
||||
end
|
||||
|
||||
-- clicking tab 2 shouldn't trigger the event.
|
||||
setup()
|
||||
api.nvim_input_mouse('left', 'press', '', 0, 0, 5)
|
||||
api.nvim_input_mouse('left', 'release', '', 0, 0, 5)
|
||||
poke_eventloop()
|
||||
eq(0, #exec_lua('return _G.tm_events'))
|
||||
clear()
|
||||
|
||||
-- dragging tab 1 to the right should trigger the event.
|
||||
setup()
|
||||
api.nvim_input_mouse('left', 'press', '', 0, 0, 4)
|
||||
poke_eventloop()
|
||||
eq(0, #exec_lua('return _G.tm_events'))
|
||||
api.nvim_input_mouse('left', 'drag', '', 0, 0, 14)
|
||||
poke_eventloop()
|
||||
local events = exec_lua('return _G.tm_events')
|
||||
eq(1, #events)
|
||||
eq(1, events[1].data.tabnr_old)
|
||||
eq(2, events[1].data.tabnr_new)
|
||||
end)
|
||||
end)
|
||||
Reference in New Issue
Block a user