mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
cmdline: CmdlineEnter and CmdlineLeave autocommands (#7422)
vim-patch:fafcf0dd59fd
patch 8.0.1206: no autocmd for entering or leaving the command line
Problem: No autocmd for entering or leaving the command line.
Solution: Add CmdlineEnter and CmdlineLeave.
fafcf0dd59
This commit is contained in:
parent
9393be477a
commit
a4f6cec7a3
@ -310,6 +310,8 @@ Name triggered by ~
|
|||||||
|TabNew| when creating a new tab page
|
|TabNew| when creating a new tab page
|
||||||
|TabNewEntered| after entering a new tab page
|
|TabNewEntered| after entering a new tab page
|
||||||
|TabClosed| after closing a tab page
|
|TabClosed| after closing a tab page
|
||||||
|
|CmdlineEnter| after entering cmdline mode
|
||||||
|
|CmdlineLeave| before leaving cmdline mode
|
||||||
|CmdwinEnter| after entering the command-line window
|
|CmdwinEnter| after entering the command-line window
|
||||||
|CmdwinLeave| before leaving the command-line window
|
|CmdwinLeave| before leaving the command-line window
|
||||||
|
|
||||||
@ -492,6 +494,28 @@ CmdUndefined When a user command is used but it isn't
|
|||||||
command is defined. An alternative is to
|
command is defined. An alternative is to
|
||||||
always define the user command and have it
|
always define the user command and have it
|
||||||
invoke an autoloaded function. See |autoload|.
|
invoke an autoloaded function. See |autoload|.
|
||||||
|
*CmdlineEnter*
|
||||||
|
CmdlineEnter After moving the cursor to the command line,
|
||||||
|
where the user can type a command or search
|
||||||
|
string.
|
||||||
|
<afile> is set to a single character,
|
||||||
|
indicating the type of command-line.
|
||||||
|
|cmdline-char|
|
||||||
|
Sets these |v:event| keys:
|
||||||
|
cmdlevel
|
||||||
|
cmdtype
|
||||||
|
*CmdlineLeave*
|
||||||
|
CmdlineLeave Before leaving the command line.
|
||||||
|
<afile> is set to a single character,
|
||||||
|
indicating the type of command-line.
|
||||||
|
|cmdline-char|
|
||||||
|
Sets these |v:event| keys:
|
||||||
|
abort (mutable)
|
||||||
|
cmdlevel
|
||||||
|
cmdtype
|
||||||
|
Note: `abort` can only be changed from false
|
||||||
|
to true. An autocmd cannot execute an already
|
||||||
|
aborted cmdline by changing it to false.
|
||||||
*CmdwinEnter*
|
*CmdwinEnter*
|
||||||
CmdwinEnter After entering the command-line window.
|
CmdwinEnter After entering the command-line window.
|
||||||
Useful for setting options specifically for
|
Useful for setting options specifically for
|
||||||
|
@ -1088,7 +1088,7 @@ Another example: >
|
|||||||
:au CmdwinEnter [/?] startinsert
|
:au CmdwinEnter [/?] startinsert
|
||||||
This will make Vim start in Insert mode in the command-line window.
|
This will make Vim start in Insert mode in the command-line window.
|
||||||
|
|
||||||
*cmdwin-char*
|
*cmdline-char* *cmdwin-char*
|
||||||
The character used for the pattern indicates the type of command-line:
|
The character used for the pattern indicates the type of command-line:
|
||||||
: normal Ex command
|
: normal Ex command
|
||||||
> debug mode command |debug-mode|
|
> debug mode command |debug-mode|
|
||||||
|
@ -1527,16 +1527,21 @@ v:event Dictionary of event data for the current |autocommand|. Valid
|
|||||||
< Keys vary by event; see the documentation for the specific
|
< Keys vary by event; see the documentation for the specific
|
||||||
event, e.g. |DirChanged| or |TextYankPost|.
|
event, e.g. |DirChanged| or |TextYankPost|.
|
||||||
KEY DESCRIPTION ~
|
KEY DESCRIPTION ~
|
||||||
cwd Current working directory
|
abort Whether the event triggered during
|
||||||
|
an aborting condition, i e |c_Esc| or
|
||||||
|
|c_CTRL-c|for |CmdlineLeave|.
|
||||||
|
cmdlevel Level of cmdline.
|
||||||
|
cmdtype Type of cmdline, |cmdline-char|.
|
||||||
|
cwd Current working directory.
|
||||||
scope Event-specific scope name.
|
scope Event-specific scope name.
|
||||||
operator Current |operator|. Also set for Ex
|
operator Current |operator|. Also set for Ex
|
||||||
commands (unlike |v:operator|). For
|
commands (unlike |v:operator|). For
|
||||||
example if |TextYankPost| is triggered
|
example if |TextYankPost| is triggered
|
||||||
by the |:yank| Ex command then
|
by the |:yank| Ex command then
|
||||||
`v:event['operator']` is "y".
|
`v:event.operator` is "y".
|
||||||
regcontents Text stored in the register as a
|
regcontents Text stored in the register as a
|
||||||
|readfile()|-style list of lines.
|
|readfile()|-style list of lines.
|
||||||
regname Requested register (e.g "x" for "xyy)
|
regname Requested register (e.g "x" for "xyy)
|
||||||
or the empty string for an unnamed
|
or the empty string for an unnamed
|
||||||
operation.
|
operation.
|
||||||
regtype Type of register as returned by
|
regtype Type of register as returned by
|
||||||
|
@ -19,6 +19,8 @@ return {
|
|||||||
'BufWriteCmd', -- write buffer using command
|
'BufWriteCmd', -- write buffer using command
|
||||||
'BufWritePost', -- after writing a buffer
|
'BufWritePost', -- after writing a buffer
|
||||||
'BufWritePre', -- before writing a buffer
|
'BufWritePre', -- before writing a buffer
|
||||||
|
'CmdLineEnter', -- after entering cmdline mode
|
||||||
|
'CmdLineLeave', -- before leaving cmdline mode
|
||||||
'CmdUndefined', -- command undefined
|
'CmdUndefined', -- command undefined
|
||||||
'CmdWinEnter', -- after entering the cmdline window
|
'CmdWinEnter', -- after entering the cmdline window
|
||||||
'CmdWinLeave', -- before leaving the cmdline window
|
'CmdWinLeave', -- before leaving the cmdline window
|
||||||
|
@ -1374,6 +1374,29 @@ int tv_dict_add_nr(dict_T *const d, const char *const key,
|
|||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Add a special entry to dictionary
|
||||||
|
///
|
||||||
|
/// @param[out] d Dictionary to add entry to.
|
||||||
|
/// @param[in] key Key to add.
|
||||||
|
/// @param[in] key_len Key length.
|
||||||
|
/// @param[in] val SpecialVarValue to add.
|
||||||
|
///
|
||||||
|
/// @return OK in case of success, FAIL when key already exists.
|
||||||
|
int tv_dict_add_special(dict_T *const d, const char *const key,
|
||||||
|
const size_t key_len, SpecialVarValue val)
|
||||||
|
{
|
||||||
|
dictitem_T *const item = tv_dict_item_alloc_len(key, key_len);
|
||||||
|
|
||||||
|
item->di_tv.v_lock = VAR_UNLOCKED;
|
||||||
|
item->di_tv.v_type = VAR_SPECIAL;
|
||||||
|
item->di_tv.vval.v_special = val;
|
||||||
|
if (tv_dict_add(d, item) == FAIL) {
|
||||||
|
tv_dict_item_free(item);
|
||||||
|
return FAIL;
|
||||||
|
}
|
||||||
|
return OK;
|
||||||
|
}
|
||||||
|
|
||||||
/// Add a string entry to dictionary
|
/// Add a string entry to dictionary
|
||||||
///
|
///
|
||||||
/// @param[out] d Dictionary to add entry to.
|
/// @param[out] d Dictionary to add entry to.
|
||||||
|
@ -348,8 +348,57 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
|||||||
got_int = false;
|
got_int = false;
|
||||||
s->state.check = command_line_check;
|
s->state.check = command_line_check;
|
||||||
s->state.execute = command_line_execute;
|
s->state.execute = command_line_execute;
|
||||||
|
|
||||||
|
TryState tstate;
|
||||||
|
Error err = ERROR_INIT;
|
||||||
|
bool tl_ret = true;
|
||||||
|
dict_T *dict = get_vim_var_dict(VV_EVENT);
|
||||||
|
char firstcbuf[2];
|
||||||
|
firstcbuf[0] = firstc > 0 ? firstc : '-';
|
||||||
|
firstcbuf[1] = 0;
|
||||||
|
|
||||||
|
if (has_event(EVENT_CMDLINEENTER)) {
|
||||||
|
// set v:event to a dictionary with information about the commandline
|
||||||
|
tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
|
||||||
|
tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
|
||||||
|
tv_dict_set_keys_readonly(dict);
|
||||||
|
try_enter(&tstate);
|
||||||
|
|
||||||
|
apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf,
|
||||||
|
false, curbuf);
|
||||||
|
tv_dict_clear(dict);
|
||||||
|
|
||||||
|
|
||||||
|
tl_ret = try_leave(&tstate, &err);
|
||||||
|
if (!tl_ret && ERROR_SET(&err)) {
|
||||||
|
msg_putchar('\n');
|
||||||
|
msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg);
|
||||||
|
api_clear_error(&err);
|
||||||
|
redrawcmd();
|
||||||
|
}
|
||||||
|
tl_ret = true;
|
||||||
|
}
|
||||||
|
|
||||||
state_enter(&s->state);
|
state_enter(&s->state);
|
||||||
|
|
||||||
|
if (has_event(EVENT_CMDLINELEAVE)) {
|
||||||
|
tv_dict_add_str(dict, S_LEN("cmdtype"), firstcbuf);
|
||||||
|
tv_dict_add_nr(dict, S_LEN("cmdlevel"), ccline.level);
|
||||||
|
tv_dict_set_keys_readonly(dict);
|
||||||
|
// not readonly:
|
||||||
|
tv_dict_add_special(dict, S_LEN("abort"),
|
||||||
|
s->gotesc ? kSpecialVarTrue : kSpecialVarFalse);
|
||||||
|
try_enter(&tstate);
|
||||||
|
apply_autocmds(EVENT_CMDLINELEAVE, (char_u *)firstcbuf, (char_u *)firstcbuf,
|
||||||
|
false, curbuf);
|
||||||
|
// error printed below, to avoid redraw issues
|
||||||
|
tl_ret = try_leave(&tstate, &err);
|
||||||
|
if (tv_dict_get_number(dict, "abort") != 0) {
|
||||||
|
s->gotesc = 1;
|
||||||
|
}
|
||||||
|
tv_dict_clear(dict);
|
||||||
|
}
|
||||||
|
|
||||||
cmdmsg_rl = false;
|
cmdmsg_rl = false;
|
||||||
|
|
||||||
cmd_fkmap = 0;
|
cmd_fkmap = 0;
|
||||||
@ -410,8 +459,14 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
|||||||
msg_scroll = s->save_msg_scroll;
|
msg_scroll = s->save_msg_scroll;
|
||||||
redir_off = false;
|
redir_off = false;
|
||||||
|
|
||||||
|
if (!tl_ret && ERROR_SET(&err)) {
|
||||||
|
msg_putchar('\n');
|
||||||
|
msg_printf_attr(hl_attr(HLF_E)|MSG_HIST, (char *)e_autocmd_err, err.msg);
|
||||||
|
api_clear_error(&err);
|
||||||
|
}
|
||||||
|
|
||||||
// When the command line was typed, no need for a wait-return prompt.
|
// When the command line was typed, no need for a wait-return prompt.
|
||||||
if (s->some_key_typed) {
|
if (s->some_key_typed && tl_ret) {
|
||||||
need_wait_return = false;
|
need_wait_return = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1167,6 +1167,8 @@ EXTERN char_u e_dirnotf[] INIT(= N_(
|
|||||||
EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
|
EXTERN char_u e_unsupportedoption[] INIT(= N_("E519: Option not supported"));
|
||||||
EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long"));
|
EXTERN char_u e_fnametoolong[] INIT(= N_("E856: Filename too long"));
|
||||||
EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String"));
|
EXTERN char_u e_float_as_string[] INIT(= N_("E806: using Float as a String"));
|
||||||
|
EXTERN char_u e_autocmd_err[] INIT(=N_(
|
||||||
|
"E920: autocmd has thrown an exception: %s"));
|
||||||
|
|
||||||
|
|
||||||
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
|
EXTERN char top_bot_msg[] INIT(= N_("search hit TOP, continuing at BOTTOM"));
|
||||||
|
@ -420,3 +420,25 @@ function Test_autocmd_bufwipe_in_SessLoadPost2()
|
|||||||
call delete(file)
|
call delete(file)
|
||||||
endfor
|
endfor
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
func Test_Cmdline()
|
||||||
|
au! CmdlineEnter : let g:entered = expand('<afile>')
|
||||||
|
au! CmdlineLeave : let g:left = expand('<afile>')
|
||||||
|
let g:entered = 0
|
||||||
|
let g:left = 0
|
||||||
|
call feedkeys(":echo 'hello'\<CR>", 'xt')
|
||||||
|
call assert_equal(':', g:entered)
|
||||||
|
call assert_equal(':', g:left)
|
||||||
|
au! CmdlineEnter
|
||||||
|
au! CmdlineLeave
|
||||||
|
|
||||||
|
au! CmdlineEnter / let g:entered = expand('<afile>')
|
||||||
|
au! CmdlineLeave / let g:left = expand('<afile>')
|
||||||
|
let g:entered = 0
|
||||||
|
let g:left = 0
|
||||||
|
call feedkeys("/hello<CR>", 'xt')
|
||||||
|
call assert_equal('/', g:entered)
|
||||||
|
call assert_equal('/', g:left)
|
||||||
|
au! CmdlineEnter
|
||||||
|
au! CmdlineLeave
|
||||||
|
endfunc
|
||||||
|
@ -78,6 +78,7 @@ static char *features[] = {
|
|||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
static const int included_patches[] = {
|
static const int included_patches[] = {
|
||||||
|
1206,
|
||||||
// 1026,
|
// 1026,
|
||||||
1025,
|
1025,
|
||||||
1024,
|
1024,
|
||||||
|
117
test/functional/autocmd/cmdline_spec.lua
Normal file
117
test/functional/autocmd/cmdline_spec.lua
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
local Screen = require('test.functional.ui.screen')
|
||||||
|
|
||||||
|
local clear = helpers.clear
|
||||||
|
local command = helpers.command
|
||||||
|
local eq = helpers.eq
|
||||||
|
local expect = helpers.expect
|
||||||
|
local next_msg = helpers.next_message
|
||||||
|
local feed = helpers.feed
|
||||||
|
local meths = helpers.meths
|
||||||
|
|
||||||
|
describe('cmdline autocommands', function()
|
||||||
|
local channel
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
channel = meths.get_api_info()[1]
|
||||||
|
meths.set_var("channel",channel)
|
||||||
|
command("autocmd CmdlineEnter * call rpcnotify(g:channel, 'CmdlineEnter', v:event)")
|
||||||
|
command("autocmd CmdlineLeave * call rpcnotify(g:channel, 'CmdlineLeave', v:event)")
|
||||||
|
command("autocmd CmdWinEnter * call rpcnotify(g:channel, 'CmdWinEnter', v:event)")
|
||||||
|
command("autocmd CmdWinLeave * call rpcnotify(g:channel, 'CmdWinLeave', v:event)")
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works', function()
|
||||||
|
feed(':')
|
||||||
|
eq({'notification', 'CmdlineEnter', {{cmdtype=':', cmdlevel=1}}}, next_msg())
|
||||||
|
feed('redraw<cr>')
|
||||||
|
eq({'notification', 'CmdlineLeave',
|
||||||
|
{{cmdtype=':', cmdlevel=1, abort=false}}}, next_msg())
|
||||||
|
|
||||||
|
feed(':')
|
||||||
|
eq({'notification', 'CmdlineEnter', {{cmdtype=':', cmdlevel=1}}}, next_msg())
|
||||||
|
|
||||||
|
-- note: feed('bork<c-c>') might not consume 'bork'
|
||||||
|
-- due to out-of-band interupt handling
|
||||||
|
feed('bork<esc>')
|
||||||
|
eq({'notification', 'CmdlineLeave',
|
||||||
|
{{cmdtype=':', cmdlevel=1, abort=true}}}, next_msg())
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('can abort cmdline', function()
|
||||||
|
command("autocmd CmdlineLeave * let v:event.abort= len(getcmdline())>15")
|
||||||
|
feed(":put! ='ok'<cr>")
|
||||||
|
expect([[
|
||||||
|
ok
|
||||||
|
]])
|
||||||
|
|
||||||
|
feed(":put! ='blah blah'<cr>")
|
||||||
|
expect([[
|
||||||
|
ok
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('handles errors correctly', function()
|
||||||
|
clear()
|
||||||
|
local screen = Screen.new(72, 8)
|
||||||
|
screen:attach()
|
||||||
|
screen:set_default_attr_ids({
|
||||||
|
[1] = {bold = true, foreground = Screen.colors.Blue1},
|
||||||
|
[2] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
|
||||||
|
[3] = {bold = true, foreground = Screen.colors.SeaGreen4},
|
||||||
|
})
|
||||||
|
command("autocmd CmdlineEnter * echoerr 'FAIL'")
|
||||||
|
command("autocmd CmdlineLeave * echoerr 'very error'")
|
||||||
|
feed(':')
|
||||||
|
screen:expect([[
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
: |
|
||||||
|
{2:E920: autocmd has thrown an exception: Vim(echoerr):FAIL} |
|
||||||
|
:^ |
|
||||||
|
]])
|
||||||
|
feed("put ='lorem ipsum'<cr>")
|
||||||
|
screen:expect([[
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
: |
|
||||||
|
{2:E920: autocmd has thrown an exception: Vim(echoerr):FAIL} |
|
||||||
|
:put ='lorem ipsum' |
|
||||||
|
{2:E920: autocmd has thrown an exception: Vim(echoerr):very error} |
|
||||||
|
|
|
||||||
|
{3:Press ENTER or type command to continue}^ |
|
||||||
|
]])
|
||||||
|
|
||||||
|
feed('<cr>')
|
||||||
|
screen:expect([[
|
||||||
|
|
|
||||||
|
^lorem ipsum |
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
{1:~ }|
|
||||||
|
|
|
||||||
|
]])
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('works with nested cmdline', function()
|
||||||
|
feed(':')
|
||||||
|
eq({'notification', 'CmdlineEnter', {{cmdtype=':', cmdlevel=1}}}, next_msg())
|
||||||
|
feed('<c-r>=')
|
||||||
|
eq({'notification', 'CmdlineEnter', {{cmdtype='=', cmdlevel=2}}}, next_msg())
|
||||||
|
feed('<c-f>')
|
||||||
|
eq({'notification', 'CmdWinEnter', {{}}}, next_msg())
|
||||||
|
feed(':')
|
||||||
|
eq({'notification', 'CmdlineEnter', {{cmdtype=':', cmdlevel=3}}}, next_msg())
|
||||||
|
feed('<c-c>')
|
||||||
|
eq({'notification', 'CmdlineLeave', {{cmdtype=':', cmdlevel=3, abort=true}}}, next_msg())
|
||||||
|
feed('<c-c>')
|
||||||
|
eq({'notification', 'CmdWinLeave', {{}}}, next_msg())
|
||||||
|
feed('1+2<cr>')
|
||||||
|
eq({'notification', 'CmdlineLeave', {{cmdtype='=', cmdlevel=2, abort=false}}}, next_msg())
|
||||||
|
end)
|
||||||
|
end)
|
Loading…
Reference in New Issue
Block a user