feat(options): add 'eventignorewin' (#32152)

vim-patch:partial:9.1.1084: Unable to persistently ignore events in a window and its buffers

Problem:  Unable to persistently ignore events in a window and its buffers.
Solution: Add 'eventignorewin' option to ignore events in a window and buffer
          (Luuk van Baal)

Add the window-local 'eventignorewin' option that is analogous to
'eventignore', but applies to a certain window and its buffers. Identify
events that should be allowed in 'eventignorewin', adapt "auto_event"
and "event_tab" to encode this information. Window context is not passed
onto apply_autocmds_group(), and when to ignore an event is a bit
ambiguous when "buf" is not "curbuf", rather than a large refactor, only
ignore an event when all windows into "buf" are ignoring the event.

b7147f8236

vim-patch:9.1.1102: tests: Test_WinScrolled_Resized_eiw() uses wrong filename

Problem:  tests: Test_WinScrolled_Resized_eiw() uses wrong filename
          (Luuk van Baal, after v9.1.1084)
Solution: Rename the filename to something more unique

bfc7719e48
This commit is contained in:
luukvbaal
2025-02-12 11:01:06 +01:00
committed by GitHub
parent 6982106f8c
commit 82a215cb2d
19 changed files with 406 additions and 259 deletions

View File

@@ -1676,6 +1676,9 @@ To disable autocommands for some time use the 'eventignore' option. Note that
this may cause unexpected behavior, make sure you restore 'eventignore' this may cause unexpected behavior, make sure you restore 'eventignore'
afterwards, using a |:try| block with |:finally|. afterwards, using a |:try| block with |:finally|.
To disable autocmds indefinitely in a specific window use the 'eventignorewin'
option. This can only be used to ignore window and buffer related events.
*:noautocmd* *:noa* *:noautocmd* *:noa*
To disable autocommands for just one command use the ":noautocmd" command To disable autocommands for just one command use the ":noautocmd" command
modifier. This will set 'eventignore' to "all" for the duration of the modifier. This will set 'eventignore' to "all" for the duration of the

View File

@@ -319,6 +319,7 @@ OPTIONS
• 'completeopt' flag "preinsert" highlights text to be inserted. • 'completeopt' flag "preinsert" highlights text to be inserted.
• 'messagesopt' configures |:messages| and |hit-enter| prompt. • 'messagesopt' configures |:messages| and |hit-enter| prompt.
• 'tabclose' controls which tab page to focus when closing a tab page. • 'tabclose' controls which tab page to focus when closing a tab page.
• 'eventignorewin' to persistently ignore events in a window.
PERFORMANCE PERFORMANCE

View File

@@ -2334,6 +2334,13 @@ A jump table for the options with a short description can be found at |Q_op|.
set ei=WinEnter,WinLeave set ei=WinEnter,WinLeave
< <
*'eventignorewin'* *'eiw'*
'eventignorewin' 'eiw' string (default "")
local to window
Similar to 'eventignore' but applies to a particular window and its
buffers, for which window and buffer related autocommands can be
ignored indefinitely without affecting the global 'eventignore'.
*'expandtab'* *'et'* *'noexpandtab'* *'noet'* *'expandtab'* *'et'* *'noexpandtab'* *'noet'*
'expandtab' 'et' boolean (default off) 'expandtab' 'et' boolean (default off)
local to buffer local to buffer

View File

@@ -696,6 +696,7 @@ Short explanation of each option: *option-list*
'errorfile' 'ef' name of the errorfile for the QuickFix mode 'errorfile' 'ef' name of the errorfile for the QuickFix mode
'errorformat' 'efm' description of the lines in the error file 'errorformat' 'efm' description of the lines in the error file
'eventignore' 'ei' autocommand events that are ignored 'eventignore' 'ei' autocommand events that are ignored
'eventignorewin' 'eiw' autocommand events that are ignored in a window
'expandtab' 'et' use spaces when <Tab> is inserted 'expandtab' 'et' use spaces when <Tab> is inserted
'exrc' 'ex' read init files in the current directory 'exrc' 'ex' read init files in the current directory
'fileencoding' 'fenc' file encoding for multibyte text 'fileencoding' 'fenc' file encoding for multibyte text

View File

@@ -1989,6 +1989,16 @@ vim.o.ei = vim.o.eventignore
vim.go.eventignore = vim.o.eventignore vim.go.eventignore = vim.o.eventignore
vim.go.ei = vim.go.eventignore vim.go.ei = vim.go.eventignore
--- Similar to 'eventignore' but applies to a particular window and its
--- buffers, for which window and buffer related autocommands can be
--- ignored indefinitely without affecting the global 'eventignore'.
---
--- @type string
vim.o.eventignorewin = ""
vim.o.eiw = vim.o.eventignorewin
vim.wo.eventignorewin = vim.o.eventignorewin
vim.wo.eiw = vim.wo.eventignorewin
--- In Insert mode: Use the appropriate number of spaces to insert a --- In Insert mode: Use the appropriate number of spaces to insert a
--- <Tab>. Spaces are used in indents with the '>' and '<' commands and --- <Tab>. Spaces are used in indents with the '>' and '<' commands and
--- when 'autoindent' is on. To insert a real tab when 'expandtab' is --- when 'autoindent' is on. To insert a real tab when 'expandtab' is

View File

@@ -1,7 +1,7 @@
" These commands create the option window. " These commands create the option window.
" "
" Maintainer: The Vim Project <https://github.com/vim/vim> " Maintainer: The Vim Project <https://github.com/vim/vim>
" Last Change: 2024 Dec 07 " Last Change: 2025 Feb 08
" Former Maintainer: Bram Moolenaar <Bram@vim.org> " Former Maintainer: Bram Moolenaar <Bram@vim.org>
" If there already is an option window, jump to that one. " If there already is an option window, jump to that one.
@@ -1187,6 +1187,8 @@ call <SID>AddOption("virtualedit", gettext("when to use virtual editing: \"block
call <SID>OptionG("ve", &ve) call <SID>OptionG("ve", &ve)
call <SID>AddOption("eventignore", gettext("list of autocommand events which are to be ignored")) call <SID>AddOption("eventignore", gettext("list of autocommand events which are to be ignored"))
call <SID>OptionG("ei", &ei) call <SID>OptionG("ei", &ei)
call <SID>AddOption("eventignorewin", gettext("list of autocommand events which are to be ignored in a window"))
call <SID>OptionG("eiw", &eiw)
call <SID>AddOption("loadplugins", gettext("load plugin scripts when starting up")) call <SID>AddOption("loadplugins", gettext("load plugin scripts when starting up"))
call <SID>BinOptionG("lpl", &lpl) call <SID>BinOptionG("lpl", &lpl)
call <SID>AddOption("exrc", gettext("enable reading .vimrc/.exrc/.gvimrc in the current directory")) call <SID>AddOption("exrc", gettext("enable reading .vimrc/.exrc/.gvimrc in the current directory"))

View File

@@ -1,156 +1,139 @@
return { return {
--- @type [string, string[], boolean][] List of [eventname, aliases, window-local event] tuples.
events = { events = {
'BufAdd', -- after adding a buffer to the buffer list { 'BufAdd', { 'BufCreate' }, true }, -- after adding a buffer to the buffer list
'BufDelete', -- deleting a buffer from the buffer list { 'BufDelete', {}, true }, -- deleting a buffer from the buffer list
'BufEnter', -- after entering a buffer { 'BufEnter', {}, true }, -- after entering a buffer
'BufFilePost', -- after renaming a buffer { 'BufFilePost', {}, true }, -- after renaming a buffer
'BufFilePre', -- before renaming a buffer { 'BufFilePre', {}, true }, -- before renaming a buffer
'BufHidden', -- just after buffer becomes hidden { 'BufHidden', {}, true }, -- just after buffer becomes hidden
'BufLeave', -- before leaving a buffer { 'BufLeave', {}, true }, -- before leaving a buffer
'BufModifiedSet', -- after the 'modified' state of a buffer changes { 'BufModifiedSet', {}, true }, -- after the 'modified' state of a buffer changes
'BufNew', -- after creating any buffer { 'BufNew', {}, true }, -- after creating any buffer
'BufNewFile', -- when creating a buffer for a new file { 'BufNewFile', {}, true }, -- when creating a buffer for a new file
'BufReadCmd', -- read buffer using command { 'BufReadCmd', {}, true }, -- read buffer using command
'BufReadPost', -- after reading a buffer { 'BufReadPost', { 'BufRead' }, true }, -- after reading a buffer
'BufReadPre', -- before reading a buffer { 'BufReadPre', {}, true }, -- before reading a buffer
'BufUnload', -- just before unloading a buffer { 'BufUnload', {}, true }, -- just before unloading a buffer
'BufWinEnter', -- after showing a buffer in a window { 'BufWinEnter', {}, true }, -- after showing a buffer in a window
'BufWinLeave', -- just after buffer removed from window { 'BufWinLeave', {}, true }, -- just after buffer removed from window
'BufWipeout', -- just before really deleting a buffer { 'BufWipeout', {}, true }, -- just before really deleting a buffer
'BufWriteCmd', -- write buffer using command { 'BufWriteCmd', {}, true }, -- write buffer using command
'BufWritePost', -- after writing a buffer { 'BufWritePost', {}, true }, -- after writing a buffer
'BufWritePre', -- before writing a buffer { 'BufWritePre', { 'BufWrite' }, true }, -- before writing a buffer
'ChanInfo', -- info was received about channel { 'ChanInfo', {}, false }, -- info was received about channel
'ChanOpen', -- channel was opened { 'ChanOpen', {}, false }, -- channel was opened
'CmdUndefined', -- command undefined { 'CmdUndefined', {}, false }, -- command undefined
'CmdWinEnter', -- after entering the cmdline window { 'CmdWinEnter', {}, false }, -- after entering the cmdline window
'CmdWinLeave', -- before leaving the cmdline window { 'CmdWinLeave', {}, false }, -- before leaving the cmdline window
'CmdlineChanged', -- command line was modified { 'CmdlineChanged', {}, false }, -- command line was modified
'CmdlineEnter', -- after entering cmdline mode { 'CmdlineEnter', {}, false }, -- after entering cmdline mode
'CmdlineLeave', -- before leaving cmdline mode { 'CmdlineLeave', {}, false }, -- before leaving cmdline mode
'ColorScheme', -- after loading a colorscheme { 'ColorScheme', {}, false }, -- after loading a colorscheme
'ColorSchemePre', -- before loading a colorscheme { 'ColorSchemePre', {}, false }, -- before loading a colorscheme
'CompleteChanged', -- after popup menu changed { 'CompleteChanged', {}, false }, -- after popup menu changed
'CompleteDone', -- after finishing insert complete { 'CompleteDone', {}, false }, -- after finishing insert complete
'CompleteDonePre', -- idem, before clearing info { 'CompleteDonePre', {}, false }, -- idem, before clearing info
'CursorHold', -- cursor in same position for a while { 'CursorHold', {}, true }, -- cursor in same position for a while
'CursorHoldI', -- idem, in Insert mode { 'CursorHoldI', {}, true }, -- idem, in Insert mode
'CursorMoved', -- cursor was moved { 'CursorMoved', {}, true }, -- cursor was moved
'CursorMovedC', -- cursor was moved in Cmdline mode { 'CursorMovedC', {}, true }, -- cursor was moved in Cmdline mode
'CursorMovedI', -- cursor was moved in Insert mode { 'CursorMovedI', {}, true }, -- cursor was moved in Insert mode
'DiagnosticChanged', -- diagnostics in a buffer were modified { 'DiagnosticChanged', {}, false }, -- diagnostics in a buffer were modified
'DiffUpdated', -- diffs have been updated { 'DiffUpdated', {}, false }, -- diffs have been updated
'DirChanged', -- directory changed { 'DirChanged', {}, false }, -- directory changed
'DirChangedPre', -- directory is going to change { 'DirChangedPre', {}, false }, -- directory is going to change
'EncodingChanged', -- after changing the 'encoding' option { 'EncodingChanged', { 'FileEncoding' }, false }, -- after changing the 'encoding' option
'ExitPre', -- before exiting { 'ExitPre', {}, false }, -- before exiting
'FileAppendCmd', -- append to a file using command { 'FileAppendCmd', {}, true }, -- append to a file using command
'FileAppendPost', -- after appending to a file { 'FileAppendPost', {}, true }, -- after appending to a file
'FileAppendPre', -- before appending to a file { 'FileAppendPre', {}, true }, -- before appending to a file
'FileChangedRO', -- before first change to read-only file { 'FileChangedRO', {}, true }, -- before first change to read-only file
'FileChangedShell', -- after shell command that changed file { 'FileChangedShell', {}, true }, -- after shell command that changed file
'FileChangedShellPost', -- after (not) reloading changed file { 'FileChangedShellPost', {}, true }, -- after (not) reloading changed file
'FileReadCmd', -- read from a file using command { 'FileReadCmd', {}, true }, -- read from a file using command
'FileReadPost', -- after reading a file { 'FileReadPost', {}, true }, -- after reading a file
'FileReadPre', -- before reading a file { 'FileReadPre', {}, true }, -- before reading a file
'FileType', -- new file type detected (user defined) { 'FileType', {}, true }, -- new file type detected (user defined)
'FileWriteCmd', -- write to a file using command { 'FileWriteCmd', {}, true }, -- write to a file using command
'FileWritePost', -- after writing a file { 'FileWritePost', {}, true }, -- after writing a file
'FileWritePre', -- before writing a file { 'FileWritePre', {}, true }, -- before writing a file
'FilterReadPost', -- after reading from a filter { 'FilterReadPost', {}, true }, -- after reading from a filter
'FilterReadPre', -- before reading from a filter { 'FilterReadPre', {}, true }, -- before reading from a filter
'FilterWritePost', -- after writing to a filter { 'FilterWritePost', {}, true }, -- after writing to a filter
'FilterWritePre', -- before writing to a filter { 'FilterWritePre', {}, true }, -- before writing to a filter
'FocusGained', -- got the focus { 'FocusGained', {}, false }, -- got the focus
'FocusLost', -- lost the focus to another app { 'FocusLost', {}, false }, -- lost the focus to another app
'FuncUndefined', -- if calling a function which doesn't exist { 'FuncUndefined', {}, false }, -- if calling a function which doesn't exist
'GUIEnter', -- after starting the GUI { 'GUIEnter', {}, false }, -- after starting the GUI
'GUIFailed', -- after starting the GUI failed { 'GUIFailed', {}, false }, -- after starting the GUI failed
'InsertChange', -- when changing Insert/Replace mode { 'InsertChange', {}, true }, -- when changing Insert/Replace mode
'InsertCharPre', -- before inserting a char { 'InsertCharPre', {}, true }, -- before inserting a char
'InsertEnter', -- when entering Insert mode { 'InsertEnter', {}, true }, -- when entering Insert mode
'InsertLeave', -- just after leaving Insert mode { 'InsertLeave', {}, true }, -- just after leaving Insert mode
'InsertLeavePre', -- just before leaving Insert mode { 'InsertLeavePre', {}, true }, -- just before leaving Insert mode
'LspAttach', -- after an LSP client attaches to a buffer { 'LspAttach', {}, false }, -- after an LSP client attaches to a buffer
'LspDetach', -- after an LSP client detaches from a buffer { 'LspDetach', {}, false }, -- after an LSP client detaches from a buffer
'LspNotify', -- after an LSP notice has been sent to the server { 'LspNotify', {}, false }, -- after an LSP notice has been sent to the server
'LspProgress', -- after a LSP progress update { 'LspProgress', {}, false }, -- after a LSP progress update
'LspRequest', -- after an LSP request is started, canceled, or completed { 'LspRequest', {}, false }, -- after an LSP request is started, canceled, or completed
'LspTokenUpdate', -- after a visible LSP token is updated { 'LspTokenUpdate', {}, false }, -- after a visible LSP token is updated
'MenuPopup', -- just before popup menu is displayed { 'MenuPopup', {}, false }, -- just before popup menu is displayed
'ModeChanged', -- after changing the mode { 'ModeChanged', {}, false }, -- after changing the mode
'OptionSet', -- after setting any option { 'OptionSet', {}, false }, -- after setting any option
'QuickFixCmdPost', -- after :make, :grep etc. { 'QuickFixCmdPost', {}, false }, -- after :make, :grep etc.
'QuickFixCmdPre', -- before :make, :grep etc. { 'QuickFixCmdPre', {}, false }, -- before :make, :grep etc.
'QuitPre', -- before :quit { 'QuitPre', {}, false }, -- before :quit
'RecordingEnter', -- when starting to record a macro { 'RecordingEnter', {}, true }, -- when starting to record a macro
'RecordingLeave', -- just before a macro stops recording { 'RecordingLeave', {}, true }, -- just before a macro stops recording
'RemoteReply', -- upon string reception from a remote vim { 'RemoteReply', {}, false }, -- upon string reception from a remote vim
'SafeState', -- going to wait for a character { 'SafeState', {}, false }, -- going to wait for a character
'SearchWrapped', -- after the search wrapped around { 'SearchWrapped', {}, true }, -- after the search wrapped around
'SessionLoadPost', -- after loading a session file { 'SessionLoadPost', {}, false }, -- after loading a session file
'SessionWritePost', -- after writing a session file { 'SessionWritePost', {}, false }, -- after writing a session file
'ShellCmdPost', -- after ":!cmd" { 'ShellCmdPost', {}, false }, -- after ":!cmd"
'ShellFilterPost', -- after ":1,2!cmd", ":w !cmd", ":r !cmd". { 'ShellFilterPost', {}, true }, -- after ":1,2!cmd", ":w !cmd", ":r !cmd".
'Signal', -- after nvim process received a signal { 'Signal', {}, false }, -- after nvim process received a signal
'SourceCmd', -- sourcing a Vim script using command { 'SourceCmd', {}, false }, -- sourcing a Vim script using command
'SourcePost', -- after sourcing a Vim script { 'SourcePost', {}, false }, -- after sourcing a Vim script
'SourcePre', -- before sourcing a Vim script { 'SourcePre', {}, false }, -- before sourcing a Vim script
'SpellFileMissing', -- spell file missing { 'SpellFileMissing', {}, false }, -- spell file missing
'StdinReadPost', -- after reading from stdin { 'StdinReadPost', {}, false }, -- after reading from stdin
'StdinReadPre', -- before reading from stdin { 'StdinReadPre', {}, false }, -- before reading from stdin
'SwapExists', -- found existing swap file { 'SwapExists', {}, false }, -- found existing swap file
'Syntax', -- syntax selected { 'Syntax', {}, false }, -- syntax selected
'TabClosed', -- a tab has closed { 'TabClosed', {}, false }, -- a tab has closed
'TabEnter', -- after entering a tab page { 'TabEnter', {}, false }, -- after entering a tab page
'TabLeave', -- before leaving a tab page { 'TabLeave', {}, false }, -- before leaving a tab page
'TabNew', -- when creating a new tab { 'TabNew', {}, false }, -- when creating a new tab
'TabNewEntered', -- after entering a new tab { 'TabNewEntered', {}, false }, -- after entering a new tab
'TermChanged', -- after changing 'term' { 'TermChanged', {}, false }, -- after changing 'term'
'TermClose', -- after the process exits { 'TermClose', {}, false }, -- after the process exits
'TermEnter', -- after entering Terminal mode { 'TermEnter', {}, false }, -- after entering Terminal mode
'TermLeave', -- after leaving Terminal mode { 'TermLeave', {}, false }, -- after leaving Terminal mode
'TermOpen', -- after opening a terminal buffer { 'TermOpen', {}, false }, -- after opening a terminal buffer
'TermRequest', -- after an unhandled OSC sequence is emitted { 'TermRequest', {}, false }, -- after an unhandled OSC sequence is emitted
'TermResponse', -- after setting "v:termresponse" { 'TermResponse', {}, false }, -- after setting "v:termresponse"
'TextChanged', -- text was modified { 'TextChanged', {}, true }, -- text was modified
'TextChangedI', -- text was modified in Insert mode(no popup) { 'TextChangedI', {}, true }, -- text was modified in Insert mode(no popup)
'TextChangedP', -- text was modified in Insert mode(popup) { 'TextChangedP', {}, true }, -- text was modified in Insert mode(popup)
'TextChangedT', -- text was modified in Terminal mode { 'TextChangedT', {}, true }, -- text was modified in Terminal mode
'TextYankPost', -- after a yank or delete was done (y, d, c) { 'TextYankPost', {}, true }, -- after a yank or delete was done (y, d, c)
'UIEnter', -- after UI attaches { 'UIEnter', {}, false }, -- after UI attaches
'UILeave', -- after UI detaches { 'UILeave', {}, false }, -- after UI detaches
'User', -- user defined autocommand { 'User', {}, false }, -- user defined autocommand
'VimEnter', -- after starting Vim { 'VimEnter', {}, false }, -- after starting Vim
'VimLeave', -- before exiting Vim { 'VimLeave', {}, false }, -- before exiting Vim
'VimLeavePre', -- before exiting Vim and writing ShaDa file { 'VimLeavePre', {}, false }, -- before exiting Vim and writing ShaDa file
'VimResized', -- after Vim window was resized { 'VimResized', {}, false }, -- after Vim window was resized
'VimResume', -- after Nvim is resumed { 'VimResume', {}, false }, -- after Nvim is resumed
'VimSuspend', -- before Nvim is suspended { 'VimSuspend', {}, false }, -- before Nvim is suspended
'WinClosed', -- after closing a window { 'WinClosed', {}, true }, -- after closing a window
'WinEnter', -- after entering a window { 'WinEnter', {}, true }, -- after entering a window
'WinLeave', -- before leaving a window { 'WinLeave', {}, true }, -- before leaving a window
'WinNew', -- when entering a new window { 'WinNew', {}, false }, -- when entering a new window
'WinResized', -- after a window was resized { 'WinResized', {}, true }, -- after a window was resized
'WinScrolled', -- after a window was scrolled or resized { 'WinScrolled', {}, true }, -- after a window was scrolled or resized
},
aliases = {
{
'BufCreate',
'BufAdd',
},
{
'BufRead',
'BufReadPost',
},
{
'BufWrite',
'BufWritePre',
},
{
'FileEncoding',
'EncodingChanged',
},
}, },
-- List of nvim-specific events or aliases for the purpose of generating -- List of nvim-specific events or aliases for the purpose of generating
-- syntax file -- syntax file

View File

@@ -634,7 +634,7 @@ event_T event_name2nr(const char *start, char **end)
if (event_names[i].name == NULL) { if (event_names[i].name == NULL) {
return NUM_EVENTS; return NUM_EVENTS;
} }
return event_names[i].event; return (event_T)abs(event_names[i].event);
} }
/// Return the event number for event name "str". /// Return the event number for event name "str".
@@ -643,7 +643,7 @@ event_T event_name2nr_str(String str)
{ {
for (int i = 0; event_names[i].name != NULL; i++) { for (int i = 0; event_names[i].name != NULL; i++) {
if (str.size == event_names[i].len && STRNICMP(str.data, event_names[i].name, str.size) == 0) { if (str.size == event_names[i].len && STRNICMP(str.data, event_names[i].name, str.size) == 0) {
return event_names[i].event; return (event_T)abs(event_names[i].event);
} }
} }
return NUM_EVENTS; return NUM_EVENTS;
@@ -658,25 +658,23 @@ const char *event_nr2name(event_T event)
FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST FUNC_ATTR_NONNULL_RET FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_CONST
{ {
for (int i = 0; event_names[i].name != NULL; i++) { for (int i = 0; event_names[i].name != NULL; i++) {
if (event_names[i].event == event) { if ((event_T)abs(event_names[i].event) == event) {
return event_names[i].name; return event_names[i].name;
} }
} }
return "Unknown"; return "Unknown";
} }
/// Return true if "event" is included in 'eventignore'. /// Return true if "event" is included in 'eventignore(win)'.
/// ///
/// @param event event to check /// @param event event to check
static bool event_ignored(event_T event) bool event_ignored(event_T event, char *ei)
FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT
{ {
char *p = p_ei; while (*ei != NUL) {
if (STRNICMP(ei, "all", 3) == 0 && (ei[3] == NUL || ei[3] == ',')) {
while (*p != NUL) {
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) {
return true; return true;
} else if (event_name2nr(p, &p) == event) { } else if (event_name2nr(ei, &ei) == event) {
return true; return true;
} }
} }
@@ -684,21 +682,25 @@ static bool event_ignored(event_T event)
return false; return false;
} }
// Return OK when the contents of p_ei is valid, FAIL otherwise. /// Return OK when the contents of 'eventignore' or 'eventignorewin' is valid,
int check_ei(void) /// FAIL otherwise.
int check_ei(char *ei)
{ {
char *p = p_ei; bool win = ei != p_ei;
while (*p) { while (*ei) {
if (STRNICMP(p, "all", 3) == 0 && (p[3] == NUL || p[3] == ',')) { if (STRNICMP(ei, "all", 3) == 0 && (ei[3] == NUL || ei[3] == ',')) {
p += 3; ei += 3;
if (*p == ',') { if (*ei == ',') {
p++; ei++;
} }
} else if (event_name2nr(p, &p) == NUM_EVENTS) { } else {
event_T event = event_name2nr(ei, &ei);
if (event == NUM_EVENTS || (win && event_names[event].event > 0)) {
return FAIL; return FAIL;
} }
} }
}
return OK; return OK;
} }
@@ -1631,7 +1633,24 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
} }
// Ignore events in 'eventignore'. // Ignore events in 'eventignore'.
if (event_ignored(event)) { if (event_ignored(event, p_ei)) {
goto BYPASS_AU;
}
bool win_ignore = false;
// If event is allowed in 'eventignorewin', check if curwin or all windows
// into "buf" are ignoring the event.
if (buf == curbuf && event_names[event].event <= 0) {
win_ignore = event_ignored(event, curwin->w_p_eiw);
} else if (buf != NULL && event_names[event].event <= 0) {
for (size_t i = 0; i < kv_size(buf->b_wininfo); i++) {
WinInfo *wip = kv_A(buf->b_wininfo, i);
if (wip->wi_win != NULL && wip->wi_win->w_buffer == buf) {
win_ignore = event_ignored(event, wip->wi_win->w_p_eiw);
}
}
}
if (win_ignore) {
goto BYPASS_AU; goto BYPASS_AU;
} }
@@ -2279,11 +2298,23 @@ char *expand_get_event_name(expand_T *xp, int idx)
/// Function given to ExpandGeneric() to obtain the list of event names. Don't /// Function given to ExpandGeneric() to obtain the list of event names. Don't
/// include groups. /// include groups.
char *get_event_name_no_group(expand_T *xp FUNC_ATTR_UNUSED, int idx) char *get_event_name_no_group(expand_T *xp FUNC_ATTR_UNUSED, int idx, bool win)
{ {
if (!win) {
return event_names[idx].name; return event_names[idx].name;
} }
// Need to check subset of allowed values for 'eventignorewin'.
int j = 0;
for (int i = 0; i < NUM_EVENTS; i++) {
j += event_names[i].event <= 0;
if (j == idx + 1) {
return event_names[i].name;
}
}
return NULL;
}
/// Check whether given autocommand is supported /// Check whether given autocommand is supported
/// ///
/// @param[in] event Event to check. /// @param[in] event Event to check.

View File

@@ -96,6 +96,8 @@ typedef struct {
int wo_diff; int wo_diff;
#define w_p_diff w_onebuf_opt.wo_diff // 'diff' #define w_p_diff w_onebuf_opt.wo_diff // 'diff'
char *wo_fdc; char *wo_fdc;
#define w_p_eiw w_onebuf_opt.wo_eiw // 'eventignorewin'
char *wo_eiw;
#define w_p_fdc w_onebuf_opt.wo_fdc // 'foldcolumn' #define w_p_fdc w_onebuf_opt.wo_fdc // 'foldcolumn'
char *wo_fdc_save; char *wo_fdc_save;
#define w_p_fdc_save w_onebuf_opt.wo_fdc_save // 'fdc' saved for diff mode #define w_p_fdc_save w_onebuf_opt.wo_fdc_save // 'fdc' saved for diff mode

View File

@@ -3,7 +3,6 @@ local names_file = arg[2]
local auevents = require('auevents') local auevents = require('auevents')
local events = auevents.events local events = auevents.events
local aliases = auevents.aliases
local enum_tgt = io.open(fileio_enum_file, 'w') local enum_tgt = io.open(fileio_enum_file, 'w')
local names_tgt = io.open(names_file, 'w') local names_tgt = io.open(names_file, 'w')
@@ -16,46 +15,28 @@ names_tgt:write([[
static const struct event_name { static const struct event_name {
size_t len; size_t len;
char *name; char *name;
event_T event; int event;
} event_names[] = {]]) } event_names[] = {]])
local aliases = 0
for i, event in ipairs(events) do for i, event in ipairs(events) do
enum_tgt:write(('\n EVENT_%s = %u,'):format(event:upper(), i - 1)) enum_tgt:write(('\n EVENT_%s = %u,'):format(event[1]:upper(), i + aliases - 1))
names_tgt:write(('\n {%u, "%s", EVENT_%s},'):format(#event, event, event:upper())) -- Events with positive keys aren't allowed in 'eventignorewin'.
local event_int = ('%sEVENT_%s'):format(event[3] and '-' or '', event[1]:upper())
names_tgt:write(('\n {%u, "%s", %s},'):format(#event[1], event[1], event_int))
for _, alias in ipairs(event[2]) do
aliases = aliases + 1
names_tgt:write(('\n {%u, "%s", %s},'):format(#alias, alias, event_int))
enum_tgt:write(('\n EVENT_%s = %u,'):format(alias:upper(), i + aliases - 1))
end
if i == #events then -- Last item. if i == #events then -- Last item.
enum_tgt:write(('\n NUM_EVENTS = %u,'):format(i)) enum_tgt:write(('\n NUM_EVENTS = %u,'):format(i + aliases))
end end
end end
for _, v in ipairs(aliases) do names_tgt:write('\n {0, NULL, (event_T)0},\n};\n')
local alias = v[1] names_tgt:write('\nstatic AutoCmdVec autocmds[NUM_EVENTS] = { 0 };\n')
local event = v[2] names_tgt:close()
names_tgt:write(('\n {%u, "%s", EVENT_%s},'):format(#alias, alias, event:upper()))
end
names_tgt:write('\n {0, NULL, (event_T)0},')
enum_tgt:write('\n} event_T;\n') enum_tgt:write('\n} event_T;\n')
names_tgt:write('\n};\n')
do
names_tgt:write('\nstatic AutoCmdVec autocmds[NUM_EVENTS] = {\n ')
local line_len = 1
for _ = 1, (#events - 1) do
line_len = line_len + #' KV_INITIAL_VALUE,'
if line_len > 80 then
names_tgt:write('\n ')
line_len = 1 + #' KV_INITIAL_VALUE,'
end
names_tgt:write(' KV_INITIAL_VALUE,')
end
if line_len + #' KV_INITIAL_VALUE' > 80 then
names_tgt:write('\n KV_INITIAL_VALUE')
else
names_tgt:write(' KV_INITIAL_VALUE')
end
names_tgt:write('\n};\n')
end
enum_tgt:close() enum_tgt:close()
names_tgt:close()

View File

@@ -114,19 +114,19 @@ local vimau_start = 'syn keyword vimAutoEvent contained '
w('\n\n' .. vimau_start) w('\n\n' .. vimau_start)
for _, au in ipairs(auevents.events) do for _, au in ipairs(auevents.events) do
if not auevents.nvim_specific[au] then if not auevents.nvim_specific[au[1]] then
if lld.line_length > 850 then if lld.line_length > 850 then
w('\n' .. vimau_start) w('\n' .. vimau_start)
end end
w(' ' .. au)
end
end
for _, au in pairs(auevents.aliases) do
if lld.line_length > 850 then
w('\n' .. vimau_start)
end
-- au[1] is aliased to au[2]
w(' ' .. au[1]) w(' ' .. au[1])
for _, alias in ipairs(au[2]) do
if lld.line_length > 850 then
w('\n' .. vimau_start)
end
-- au[1] is aliased to alias
w(' ' .. alias)
end
end
end end
local nvimau_start = 'syn keyword nvimAutoEvent contained ' local nvimau_start = 'syn keyword nvimAutoEvent contained '

View File

@@ -4580,6 +4580,8 @@ void *get_varp_from(vimoption_T *p, buf_T *buf, win_T *win)
return &(win->w_p_cc); return &(win->w_p_cc);
case kOptDiff: case kOptDiff:
return &(win->w_p_diff); return &(win->w_p_diff);
case kOptEventignorewin:
return &(win->w_p_eiw);
case kOptFoldcolumn: case kOptFoldcolumn:
return &(win->w_p_fdc); return &(win->w_p_fdc);
case kOptFoldenable: case kOptFoldenable:
@@ -4875,6 +4877,7 @@ void copy_winopt(winopt_T *from, winopt_T *to)
to->wo_cc = copy_option_val(from->wo_cc); to->wo_cc = copy_option_val(from->wo_cc);
to->wo_diff = from->wo_diff; to->wo_diff = from->wo_diff;
to->wo_diff_saved = from->wo_diff_saved; to->wo_diff_saved = from->wo_diff_saved;
to->wo_eiw = copy_option_val(from->wo_eiw);
to->wo_cocu = copy_option_val(from->wo_cocu); to->wo_cocu = copy_option_val(from->wo_cocu);
to->wo_cole = from->wo_cole; to->wo_cole = from->wo_cole;
to->wo_fdc = copy_option_val(from->wo_fdc); to->wo_fdc = copy_option_val(from->wo_fdc);
@@ -4919,6 +4922,7 @@ static void check_winopt(winopt_T *wop)
check_string_option(&wop->wo_fde); check_string_option(&wop->wo_fde);
check_string_option(&wop->wo_fdt); check_string_option(&wop->wo_fdt);
check_string_option(&wop->wo_fmr); check_string_option(&wop->wo_fmr);
check_string_option(&wop->wo_eiw);
check_string_option(&wop->wo_scl); check_string_option(&wop->wo_scl);
check_string_option(&wop->wo_rlc); check_string_option(&wop->wo_rlc);
check_string_option(&wop->wo_sbr); check_string_option(&wop->wo_sbr);
@@ -4946,6 +4950,7 @@ void clear_winopt(winopt_T *wop)
clear_string_option(&wop->wo_fde); clear_string_option(&wop->wo_fde);
clear_string_option(&wop->wo_fdt); clear_string_option(&wop->wo_fdt);
clear_string_option(&wop->wo_fmr); clear_string_option(&wop->wo_fmr);
clear_string_option(&wop->wo_eiw);
clear_string_option(&wop->wo_scl); clear_string_option(&wop->wo_scl);
clear_string_option(&wop->wo_rlc); clear_string_option(&wop->wo_rlc);
clear_string_option(&wop->wo_sbr); clear_string_option(&wop->wo_sbr);

View File

@@ -2627,6 +2627,23 @@ local options = {
type = 'string', type = 'string',
varname = 'p_ei', varname = 'p_ei',
}, },
{
abbreviation = 'eiw',
cb = 'did_set_eventignore',
defaults = '',
deny_duplicates = true,
desc = [=[
Similar to 'eventignore' but applies to a particular window and its
buffers, for which window and buffer related autocommands can be
ignored indefinitely without affecting the global 'eventignore'.
]=],
expand_cb = 'expand_set_eventignore',
full_name = 'eventignorewin',
list = 'onecomma',
scope = { 'win' },
short_desc = N_('autocommand events that are ignored in a window'),
type = 'string',
},
{ {
abbreviation = 'et', abbreviation = 'et',
defaults = false, defaults = false,

View File

@@ -1082,27 +1082,32 @@ int expand_set_encoding(optexpand_T *args, int *numMatches, char ***matches)
return expand_set_opt_generic(args, get_encoding_name, numMatches, matches); return expand_set_opt_generic(args, get_encoding_name, numMatches, matches);
} }
/// The 'eventignore' option is changed. /// The 'eventignore(win)' option is changed.
const char *did_set_eventignore(optset_T *args FUNC_ATTR_UNUSED) const char *did_set_eventignore(optset_T *args)
{ {
if (check_ei() == FAIL) { char **varp = (char **)args->os_varp;
if (check_ei(*varp) == FAIL) {
return e_invarg; return e_invarg;
} }
return NULL; return NULL;
} }
static bool expand_eiw = false;
static char *get_eventignore_name(expand_T *xp, int idx) static char *get_eventignore_name(expand_T *xp, int idx)
{ {
// 'eventignore' allows special keyword "all" in addition to // 'eventignore(win)' allows special keyword "all" in addition to
// all event names. // all event names.
if (idx == 0) { if (idx == 0) {
return "all"; return "all";
} }
return get_event_name_no_group(xp, idx - 1); return get_event_name_no_group(xp, idx - 1, expand_eiw);
} }
int expand_set_eventignore(optexpand_T *args, int *numMatches, char ***matches) int expand_set_eventignore(optexpand_T *args, int *numMatches, char ***matches)
{ {
expand_eiw = args->oe_varp != (char *)&p_ei;
return expand_set_opt_generic(args, get_eventignore_name, numMatches, matches); return expand_set_opt_generic(args, get_eventignore_name, numMatches, matches);
} }

View File

@@ -5530,31 +5530,20 @@ static dict_T *make_win_info_dict(int width, int height, int topline, int topfil
return NULL; return NULL;
} }
/// Return values of check_window_scroll_resize():
enum {
CWSR_SCROLLED = 1, ///< at least one window scrolled
CWSR_RESIZED = 2, ///< at least one window size changed
};
/// This function is used for three purposes: /// This function is used for three purposes:
/// 1. Goes over all windows in the current tab page and returns: /// 1. Goes over all windows in the current tab page and sets:
/// 0 no scrolling and no size changes found /// "size_count" to the nr of windows with size changes.
/// CWSR_SCROLLED at least one window scrolled /// "first_scroll_win" to the first window with any relevant changes.
/// CWSR_RESIZED at least one window changed size /// "first_size_win" to the first window with size changes.
/// CWSR_SCROLLED + CWSR_RESIZED both
/// "size_count" is set to the nr of windows with size changes.
/// "first_scroll_win" is set to the first window with any relevant changes.
/// "first_size_win" is set to the first window with size changes.
/// ///
/// 2. When the first three arguments are NULL and "winlist" is not NULL, /// 2. When the first three arguments are NULL and "winlist" is not NULL,
/// "winlist" is set to the list of window IDs with size changes. /// "winlist" is set to the list of window IDs with size changes.
/// ///
/// 3. When the first three arguments are NULL and "v_event" is not NULL, /// 3. When the first three arguments are NULL and "v_event" is not NULL,
/// information about changed windows is added to "v_event". /// information about changed windows is added to "v_event".
static int check_window_scroll_resize(int *size_count, win_T **first_scroll_win, static void check_window_scroll_resize(int *size_count, win_T **first_scroll_win,
win_T **first_size_win, list_T *winlist, dict_T *v_event) win_T **first_size_win, list_T *winlist, dict_T *v_event)
{ {
int result = 0;
// int listidx = 0; // int listidx = 0;
int tot_width = 0; int tot_width = 0;
int tot_height = 0; int tot_height = 0;
@@ -5576,10 +5565,11 @@ static int check_window_scroll_resize(int *size_count, win_T **first_scroll_win,
continue; continue;
} }
const bool size_changed = wp->w_last_width != wp->w_width const bool ignore_scroll = event_ignored(EVENT_WINSCROLLED, wp->w_p_eiw);
|| wp->w_last_height != wp->w_height; const bool size_changed = !event_ignored(EVENT_WINRESIZED, wp->w_p_eiw)
&& (wp->w_last_width != wp->w_width
|| wp->w_last_height != wp->w_height);
if (size_changed) { if (size_changed) {
result |= CWSR_RESIZED;
if (winlist != NULL) { if (winlist != NULL) {
// Add this window to the list of changed windows. // Add this window to the list of changed windows.
typval_T tv = { typval_T tv = {
@@ -5597,22 +5587,20 @@ static int check_window_scroll_resize(int *size_count, win_T **first_scroll_win,
} }
// For WinScrolled the first window with a size change is used // For WinScrolled the first window with a size change is used
// even when it didn't scroll. // even when it didn't scroll.
if (*first_scroll_win == NULL) { if (*first_scroll_win == NULL && !ignore_scroll) {
*first_scroll_win = wp; *first_scroll_win = wp;
} }
} }
} }
const bool scroll_changed = wp->w_last_topline != wp->w_topline const bool scroll_changed = !ignore_scroll
&& (wp->w_last_topline != wp->w_topline
|| wp->w_last_topfill != wp->w_topfill || wp->w_last_topfill != wp->w_topfill
|| wp->w_last_leftcol != wp->w_leftcol || wp->w_last_leftcol != wp->w_leftcol
|| wp->w_last_skipcol != wp->w_skipcol; || wp->w_last_skipcol != wp->w_skipcol);
if (scroll_changed) { if (scroll_changed && first_scroll_win != NULL && *first_scroll_win == NULL) {
result |= CWSR_SCROLLED;
if (first_scroll_win != NULL && *first_scroll_win == NULL) {
*first_scroll_win = wp; *first_scroll_win = wp;
} }
}
if ((size_changed || scroll_changed) && v_event != NULL) { if ((size_changed || scroll_changed) && v_event != NULL) {
// Add info about this window to the v:event dictionary. // Add info about this window to the v:event dictionary.
@@ -5655,8 +5643,6 @@ static int check_window_scroll_resize(int *size_count, win_T **first_scroll_win,
} }
} }
} }
return result;
} }
/// Trigger WinScrolled and/or WinResized if any window in the current tab page /// Trigger WinScrolled and/or WinResized if any window in the current tab page
@@ -5676,11 +5662,9 @@ void may_trigger_win_scrolled_resized(void)
int size_count = 0; int size_count = 0;
win_T *first_scroll_win = NULL; win_T *first_scroll_win = NULL;
win_T *first_size_win = NULL; win_T *first_size_win = NULL;
int cwsr = check_window_scroll_resize(&size_count, check_window_scroll_resize(&size_count, &first_scroll_win, &first_size_win, NULL, NULL);
&first_scroll_win, &first_size_win,
NULL, NULL);
bool trigger_resize = do_resize && size_count > 0; bool trigger_resize = do_resize && size_count > 0;
bool trigger_scroll = do_scroll && cwsr != 0; bool trigger_scroll = do_scroll && first_scroll_win != NULL;
if (!trigger_resize && !trigger_scroll) { if (!trigger_resize && !trigger_scroll) {
return; // no relevant changes return; // no relevant changes
} }

View File

@@ -1,5 +1,6 @@
local t = require('test.testutil') local t = require('test.testutil')
local n = require('test.functional.testnvim')() local n = require('test.functional.testnvim')()
local Screen = require('test.functional.ui.screen')
local clear = n.clear local clear = n.clear
local write_file = t.write_file local write_file = t.write_file
@@ -40,3 +41,53 @@ it('no E440 in quickfix window when autocommand invalidates undo', function()
feed('G') feed('G')
eq('', api.nvim_get_vvar('errmsg')) eq('', api.nvim_get_vvar('errmsg'))
end) end)
-- oldtest: Test_WinScrolled_Resized_eiw()
it('WinScrolled and WinResized events can be ignored in a window', function()
local screen = Screen.new()
n.exec([[
call setline(1, ['foo']->repeat(32))
set eventignorewin=WinScrolled,WinResized
split
let [g:afile,g:resized,g:scrolled] = ['none',0,0]
au WinScrolled * let [g:afile,g:scrolled] = [expand('<afile>'),g:scrolled+1]
au WinResized * let [g:afile,g:resized] = [expand('<afile>'),g:resized+1]
]])
feed('<C-W>-')
screen:expect([[
^foo |
foo |*4
{3:[No Name] [+] }|
foo |*6
{2:[No Name] [+] }|
|
]])
feed(':echo g:afile g:resized g:scrolled<CR>')
screen:expect({ any = 'none 0 0.*' })
feed('G')
screen:expect([[
foo |*4
^foo |
{3:[No Name] [+] }|
foo |*6
{2:[No Name] [+] }|
none 0 0 |
]])
feed('gg')
screen:expect([[
^foo |
foo |*4
{3:[No Name] [+] }|
foo |*6
{2:[No Name] [+] }|
none 0 0 |
]])
feed(':echo g:afile g:resized g:scrolled')
screen:expect({ any = ':echo g:afile g:resized g:scrolled.*' })
feed('<CR>')
screen:expect({ any = 'none 0 0.*' })
feed(':set eventignorewin=<CR><C-W>w<C-W>+')
screen:expect({ any = ':set eventignorewin=.*' })
feed(':echo win_getid() g:afile g:resized g:scrolled<CR>')
screen:expect({ any = '1000 1001 1 1.*' })
end)

View File

@@ -220,6 +220,8 @@ let test_values = {
"\ 'encoding': [['latin1'], ['xxx', '']], "\ 'encoding': [['latin1'], ['xxx', '']],
\ 'eventignore': [['', 'WinEnter', 'WinLeave,winenter', 'all,WinEnter'], \ 'eventignore': [['', 'WinEnter', 'WinLeave,winenter', 'all,WinEnter'],
\ ['xxx']], \ ['xxx']],
\ 'eventignorewin': [['', 'WinEnter', 'WinLeave,winenter', 'all,WinEnter'],
\ ['xxx', 'WinNew']],
\ 'fileencoding': [['', 'latin1', 'xxx'], []], \ 'fileencoding': [['', 'latin1', 'xxx'], []],
\ 'fileformat': [['dos', 'unix', 'mac'], ['xxx']], \ 'fileformat': [['dos', 'unix', 'mac'], ['xxx']],
\ 'fileformats': [['', 'dos', 'dos,unix'], ['xxx']], \ 'fileformats': [['', 'dos', 'dos,unix'], ['xxx']],

View File

@@ -4205,4 +4205,64 @@ func Test_OptionSet_cmdheight()
set cmdheight& mouse& laststatus& set cmdheight& mouse& laststatus&
endfunc endfunc
func Test_eventignorewin()
defer CleanUpTestAuGroup()
augroup testing
au WinEnter * :call add(g:evs, ["WinEnter", expand("<afile>")])
au WinLeave * :call add(g:evs, ["WinLeave", expand("<afile>")])
au BufWinEnter * :call add(g:evs, ["BufWinEnter", expand("<afile>")])
augroup END
let g:evs = []
set eventignorewin=WinLeave,WinEnter
split foo
call assert_equal([['BufWinEnter', 'foo']], g:evs)
set eventignorewin=all
edit bar
call assert_equal([['BufWinEnter', 'foo']], g:evs)
set eventignorewin=
wincmd w
call assert_equal([['BufWinEnter', 'foo'], ['WinLeave', 'bar']], g:evs)
only!
%bwipe!
set eventignorewin&
unlet g:evs
endfunc
func Test_WinScrolled_Resized_eiw()
CheckRunVimInTerminal
let lines =<< trim END
call setline(1, ['foo']->repeat(32))
set eventignorewin=WinScrolled,WinResized
split
let [g:afile,g:resized,g:scrolled] = ['none',0,0]
au WinScrolled * let [g:afile,g:scrolled] = [expand('<afile>'),g:scrolled+1]
au WinResized * let [g:afile,g:resized] = [expand('<afile>'),g:resized+1]
END
call writefile(lines, 'Xtest_winscrolled_eiw', 'D')
let buf = RunVimInTerminal('-S Xtest_winscrolled_eiw', {'rows': 10})
" Both windows are ignoring resize events
call term_sendkeys(buf, "\<C-W>-")
call TermWait(buf)
call term_sendkeys(buf, ":echo g:afile g:resized g:scrolled\<CR>")
call WaitForAssert({-> assert_equal('none 0 0', term_getline(buf, 10))}, 1000)
" And scroll events
call term_sendkeys(buf, "Ggg")
call TermWait(buf)
call term_sendkeys(buf, ":echo g:afile g:resized g:scrolled\<CR>")
call WaitForAssert({-> assert_equal('none 0 0', term_getline(buf, 10))}, 1000)
" Un-ignore events in second window, make first window current and resize
call term_sendkeys(buf, ":set eventignorewin=\<CR>\<C-W>w\<C-W>+")
call TermWait(buf)
call term_sendkeys(buf, ":echo win_getid() g:afile g:resized g:scrolled\<CR>")
call WaitForAssert({-> assert_equal('1000 1001 1 1', term_getline(buf, 10))}, 1000)
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab

View File

@@ -589,6 +589,7 @@ func Test_set_completion_string_values()
" Other string options that queries the system rather than fixed enum names " Other string options that queries the system rather than fixed enum names
call assert_equal(['all', 'BufAdd'], getcompletion('set eventignore=', 'cmdline')[0:1]) call assert_equal(['all', 'BufAdd'], getcompletion('set eventignore=', 'cmdline')[0:1])
call assert_equal(['WinLeave', 'WinResized', 'WinScrolled'], getcompletion('set eiw=', 'cmdline')[-3:-1])
call assert_equal('latin1', getcompletion('set fileencodings=', 'cmdline')[1]) call assert_equal('latin1', getcompletion('set fileencodings=', 'cmdline')[1])
" call assert_equal('top', getcompletion('set printoptions=', 'cmdline')[0]) " call assert_equal('top', getcompletion('set printoptions=', 'cmdline')[0])
" call assert_equal('SpecialKey', getcompletion('set wincolor=', 'cmdline')[0]) " call assert_equal('SpecialKey', getcompletion('set wincolor=', 'cmdline')[0])
@@ -2506,6 +2507,7 @@ func Test_string_option_revert_on_failure()
\ ['eadirection', 'hor', 'a123'], \ ['eadirection', 'hor', 'a123'],
\ ['encoding', 'utf-8', 'a123'], \ ['encoding', 'utf-8', 'a123'],
\ ['eventignore', 'TextYankPost', 'a123'], \ ['eventignore', 'TextYankPost', 'a123'],
\ ['eventignorewin', 'WinScrolled', 'a123'],
\ ['fileencoding', 'utf-8', 'a123,'], \ ['fileencoding', 'utf-8', 'a123,'],
\ ['fileformat', 'mac', 'a123'], \ ['fileformat', 'mac', 'a123'],
\ ['fileformats', 'mac', 'a123'], \ ['fileformats', 'mac', 'a123'],