mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06:00
Merge pull request #15840 from vimpostor/vim-8.2.3430
vim-patch:8.2.{3430,3434,3462,3463,3555,3609,3610}: ModeChanged autocmd
This commit is contained in:
commit
828bf128a6
@ -718,7 +718,27 @@ MenuPopup Just before showing the popup menu (under the
|
|||||||
o Operator-pending
|
o Operator-pending
|
||||||
i Insert
|
i Insert
|
||||||
c Command line
|
c Command line
|
||||||
*OptionSet*
|
*ModeChanged*
|
||||||
|
ModeChanged After changing the mode. The pattern is
|
||||||
|
matched against `'old_mode:new_mode'`, for
|
||||||
|
example match against `*:c` to simulate
|
||||||
|
|CmdlineEnter|.
|
||||||
|
The following values of |v:event| are set:
|
||||||
|
old_mode The mode before it changed.
|
||||||
|
new_mode The new mode as also returned
|
||||||
|
by |mode()| called with a
|
||||||
|
non-zero argument.
|
||||||
|
When ModeChanged is triggered, old_mode will
|
||||||
|
have the value of new_mode when the event was
|
||||||
|
last triggered.
|
||||||
|
This will be triggered on every minor mode
|
||||||
|
change.
|
||||||
|
Usage example to use relative line numbers
|
||||||
|
when entering visual mode: >
|
||||||
|
:au ModeChanged [vV\x16]*:* let &l:rnu = mode() =~# '^[vV\x16]'
|
||||||
|
:au ModeChanged *:[vV\x16]* let &l:rnu = mode() =~# '^[vV\x16]'
|
||||||
|
:au WinEnter,WinLeave * let &l:rnu = mode() =~# '^[vV\x16]'
|
||||||
|
< *OptionSet*
|
||||||
OptionSet After setting an option (except during
|
OptionSet After setting an option (except during
|
||||||
|startup|). The |autocmd-pattern| is matched
|
|startup|). The |autocmd-pattern| is matched
|
||||||
against the long option name. |<amatch>|
|
against the long option name. |<amatch>|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
#include "nvim/ex_getln.h"
|
#include "nvim/ex_getln.h"
|
||||||
#include "nvim/fileio.h"
|
#include "nvim/fileio.h"
|
||||||
#include "nvim/main.h"
|
#include "nvim/main.h"
|
||||||
|
#include "nvim/misc1.h"
|
||||||
#include "nvim/os/os.h"
|
#include "nvim/os/os.h"
|
||||||
#include "nvim/ui.h"
|
#include "nvim/ui.h"
|
||||||
#include "nvim/vim.h"
|
#include "nvim/vim.h"
|
||||||
@ -25,13 +26,14 @@ void do_autocmd_uienter(uint64_t chanid, bool attached)
|
|||||||
}
|
}
|
||||||
recursive = true;
|
recursive = true;
|
||||||
|
|
||||||
dict_T *dict = get_vim_var_dict(VV_EVENT);
|
save_v_event_T save_v_event;
|
||||||
|
dict_T *dict = get_v_event(&save_v_event);
|
||||||
assert(chanid < VARNUMBER_MAX);
|
assert(chanid < VARNUMBER_MAX);
|
||||||
tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid);
|
tv_dict_add_nr(dict, S_LEN("chan"), (varnumber_T)chanid);
|
||||||
tv_dict_set_keys_readonly(dict);
|
tv_dict_set_keys_readonly(dict);
|
||||||
apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE,
|
apply_autocmds(attached ? EVENT_UIENTER : EVENT_UILEAVE,
|
||||||
NULL, NULL, false, curbuf);
|
NULL, NULL, false, curbuf);
|
||||||
tv_dict_clear(dict);
|
restore_v_event(dict, &save_v_event);
|
||||||
|
|
||||||
recursive = false;
|
recursive = false;
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,7 @@ return {
|
|||||||
'InsertLeave', -- just after leaving Insert mode
|
'InsertLeave', -- just after leaving Insert mode
|
||||||
'InsertLeavePre', -- just before leaving Insert mode
|
'InsertLeavePre', -- just before leaving Insert mode
|
||||||
'MenuPopup', -- just before popup menu is displayed
|
'MenuPopup', -- just before popup menu is displayed
|
||||||
|
'ModeChanged', -- after changing the mode
|
||||||
'OptionSet', -- after setting any option
|
'OptionSet', -- after setting any option
|
||||||
'QuickFixCmdPost', -- after :make, :grep etc.
|
'QuickFixCmdPost', -- after :make, :grep etc.
|
||||||
'QuickFixCmdPre', -- before :make, :grep etc.
|
'QuickFixCmdPre', -- before :make, :grep etc.
|
||||||
|
@ -925,6 +925,13 @@ static int do_autocmd_event(event_T event, char_u *pat, bool once, int nested, c
|
|||||||
return FAIL;
|
return FAIL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// need to initialize last_mode for the first ModeChanged autocmd
|
||||||
|
if (event == EVENT_MODECHANGED && !has_event(EVENT_MODECHANGED)) {
|
||||||
|
xfree(last_mode);
|
||||||
|
last_mode = get_mode();
|
||||||
|
}
|
||||||
|
|
||||||
ap->cmds = NULL;
|
ap->cmds = NULL;
|
||||||
*prev_ap = ap;
|
*prev_ap = ap;
|
||||||
last_autopat[(int)event] = ap;
|
last_autopat[(int)event] = ap;
|
||||||
@ -1440,7 +1447,7 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
|
|||||||
// invalid.
|
// invalid.
|
||||||
if (fname_io == NULL) {
|
if (fname_io == NULL) {
|
||||||
if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
|
if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
|
||||||
|| event == EVENT_OPTIONSET) {
|
|| event == EVENT_OPTIONSET || event == EVENT_MODECHANGED) {
|
||||||
autocmd_fname = NULL;
|
autocmd_fname = NULL;
|
||||||
} else if (fname != NULL && !ends_excmd(*fname)) {
|
} else if (fname != NULL && !ends_excmd(*fname)) {
|
||||||
autocmd_fname = fname;
|
autocmd_fname = fname;
|
||||||
@ -1494,11 +1501,12 @@ static bool apply_autocmds_group(event_T event, char_u *fname, char_u *fname_io,
|
|||||||
|| event == EVENT_CMDWINLEAVE || event == EVENT_CMDUNDEFINED
|
|| event == EVENT_CMDWINLEAVE || event == EVENT_CMDUNDEFINED
|
||||||
|| event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
|
|| event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
|
||||||
|| event == EVENT_DIRCHANGED || event == EVENT_FILETYPE
|
|| event == EVENT_DIRCHANGED || event == EVENT_FILETYPE
|
||||||
|| event == EVENT_FUNCUNDEFINED || event == EVENT_OPTIONSET
|
|| event == EVENT_FUNCUNDEFINED || event == EVENT_MODECHANGED
|
||||||
|| event == EVENT_QUICKFIXCMDPOST || event == EVENT_QUICKFIXCMDPRE
|
|| event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST
|
||||||
|| event == EVENT_REMOTEREPLY || event == EVENT_SPELLFILEMISSING
|
|| event == EVENT_QUICKFIXCMDPRE || event == EVENT_REMOTEREPLY
|
||||||
|| event == EVENT_SYNTAX || event == EVENT_SIGNAL
|
|| event == EVENT_SPELLFILEMISSING || event == EVENT_SYNTAX
|
||||||
|| event == EVENT_TABCLOSED || event == EVENT_WINCLOSED) {
|
|| event == EVENT_SIGNAL || event == EVENT_TABCLOSED
|
||||||
|
|| event == EVENT_WINCLOSED) {
|
||||||
fname = vim_strsave(fname);
|
fname = vim_strsave(fname);
|
||||||
} else {
|
} else {
|
||||||
fname = (char_u *)FullName_save((char *)fname, false);
|
fname = (char_u *)FullName_save((char *)fname, false);
|
||||||
|
@ -1116,6 +1116,12 @@ typedef struct {
|
|||||||
pos_T w_cursor_corr; // corrected cursor position
|
pos_T w_cursor_corr; // corrected cursor position
|
||||||
} pos_save_T;
|
} pos_save_T;
|
||||||
|
|
||||||
|
// Struct passed to get_v_event() and restore_v_event().
|
||||||
|
typedef struct {
|
||||||
|
bool sve_did_save;
|
||||||
|
hashtab_T sve_hashtab;
|
||||||
|
} save_v_event_T;
|
||||||
|
|
||||||
/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
|
/// Indices into vimmenu_T->strings[] and vimmenu_T->noremap[] for each mode
|
||||||
/// \addtogroup MENU_INDEX
|
/// \addtogroup MENU_INDEX
|
||||||
/// @{
|
/// @{
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
#include "nvim/event/socket.h"
|
#include "nvim/event/socket.h"
|
||||||
#include "nvim/fileio.h"
|
#include "nvim/fileio.h"
|
||||||
#include "nvim/lua/executor.h"
|
#include "nvim/lua/executor.h"
|
||||||
|
#include "nvim/misc1.h"
|
||||||
#include "nvim/msgpack_rpc/channel.h"
|
#include "nvim/msgpack_rpc/channel.h"
|
||||||
#include "nvim/msgpack_rpc/server.h"
|
#include "nvim/msgpack_rpc/server.h"
|
||||||
#include "nvim/os/shell.h"
|
#include "nvim/os/shell.h"
|
||||||
@ -821,7 +822,8 @@ static void set_info_event(void **argv)
|
|||||||
Channel *chan = argv[0];
|
Channel *chan = argv[0];
|
||||||
event_T event = (event_T)(ptrdiff_t)argv[1];
|
event_T event = (event_T)(ptrdiff_t)argv[1];
|
||||||
|
|
||||||
dict_T *dict = get_vim_var_dict(VV_EVENT);
|
save_v_event_T save_v_event;
|
||||||
|
dict_T *dict = get_v_event(&save_v_event);
|
||||||
Dictionary info = channel_info(chan->id);
|
Dictionary info = channel_info(chan->id);
|
||||||
typval_T retval;
|
typval_T retval;
|
||||||
(void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL);
|
(void)object_to_vim(DICTIONARY_OBJ(info), &retval, NULL);
|
||||||
@ -829,7 +831,7 @@ static void set_info_event(void **argv)
|
|||||||
|
|
||||||
apply_autocmds(event, NULL, NULL, false, curbuf);
|
apply_autocmds(event, NULL, NULL, false, curbuf);
|
||||||
|
|
||||||
tv_dict_clear(dict);
|
restore_v_event(dict, &save_v_event);
|
||||||
api_free_dictionary(info);
|
api_free_dictionary(info);
|
||||||
channel_decref(chan);
|
channel_decref(chan);
|
||||||
}
|
}
|
||||||
|
@ -385,6 +385,7 @@ static void insert_enter(InsertState *s)
|
|||||||
State = INSERT;
|
State = INSERT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trigger_modechanged();
|
||||||
stop_insert_mode = false;
|
stop_insert_mode = false;
|
||||||
|
|
||||||
// Need to recompute the cursor position, it might move when the cursor is
|
// Need to recompute the cursor position, it might move when the cursor is
|
||||||
@ -2048,6 +2049,8 @@ static void ins_ctrl_x(void)
|
|||||||
// CTRL-V look like CTRL-N
|
// CTRL-V look like CTRL-N
|
||||||
ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X;
|
ctrl_x_mode = CTRL_X_CMDLINE_CTRL_X;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trigger_modechanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Whether other than default completion has been selected.
|
// Whether other than default completion has been selected.
|
||||||
@ -2660,6 +2663,7 @@ void set_completion(colnr_T startcol, list_T *list)
|
|||||||
show_pum(save_w_wrow, save_w_leftcol);
|
show_pum(save_w_wrow, save_w_leftcol);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trigger_modechanged();
|
||||||
ui_flush();
|
ui_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2715,12 +2719,13 @@ static bool pum_enough_matches(void)
|
|||||||
static void trigger_complete_changed_event(int cur)
|
static void trigger_complete_changed_event(int cur)
|
||||||
{
|
{
|
||||||
static bool recursive = false;
|
static bool recursive = false;
|
||||||
|
save_v_event_T save_v_event;
|
||||||
|
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
dict_T *v_event = get_vim_var_dict(VV_EVENT);
|
dict_T *v_event = get_v_event(&save_v_event);
|
||||||
if (cur < 0) {
|
if (cur < 0) {
|
||||||
tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc());
|
tv_dict_add_dict(v_event, S_LEN("completed_item"), tv_dict_alloc());
|
||||||
} else {
|
} else {
|
||||||
@ -2736,7 +2741,7 @@ static void trigger_complete_changed_event(int cur)
|
|||||||
textlock--;
|
textlock--;
|
||||||
recursive = false;
|
recursive = false;
|
||||||
|
|
||||||
tv_dict_clear(v_event);
|
restore_v_event(v_event, &save_v_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show the popup menu for the list of matches.
|
/// Show the popup menu for the list of matches.
|
||||||
@ -3838,6 +3843,8 @@ static bool ins_compl_prep(int c)
|
|||||||
ins_apply_autocmds(EVENT_COMPLETEDONE);
|
ins_apply_autocmds(EVENT_COMPLETEDONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trigger_modechanged();
|
||||||
|
|
||||||
/* reset continue_* if we left expansion-mode, if we stay they'll be
|
/* reset continue_* if we left expansion-mode, if we stay they'll be
|
||||||
* (re)set properly in ins_complete() */
|
* (re)set properly in ins_complete() */
|
||||||
if (!vim_is_ctrl_x_key(c)) {
|
if (!vim_is_ctrl_x_key(c)) {
|
||||||
@ -4586,6 +4593,8 @@ static int ins_compl_get_exp(pos_T *ini)
|
|||||||
compl_curr_match = compl_old_match;
|
compl_curr_match = compl_old_match;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
trigger_modechanged();
|
||||||
|
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7965,6 +7974,7 @@ static bool ins_esc(long *count, int cmdchar, bool nomove)
|
|||||||
|
|
||||||
|
|
||||||
State = NORMAL;
|
State = NORMAL;
|
||||||
|
trigger_modechanged();
|
||||||
// need to position cursor again (e.g. when on a TAB )
|
// need to position cursor again (e.g. when on a TAB )
|
||||||
changed_cline_bef_curs();
|
changed_cline_bef_curs();
|
||||||
|
|
||||||
@ -8066,6 +8076,7 @@ static void ins_insert(int replaceState)
|
|||||||
} else {
|
} else {
|
||||||
State = replaceState | (State & LANGMAP);
|
State = replaceState | (State & LANGMAP);
|
||||||
}
|
}
|
||||||
|
trigger_modechanged();
|
||||||
AppendCharToRedobuff(K_INS);
|
AppendCharToRedobuff(K_INS);
|
||||||
showmode();
|
showmode();
|
||||||
ui_cursor_shape(); // may show different cursor shape
|
ui_cursor_shape(); // may show different cursor shape
|
||||||
|
@ -196,6 +196,7 @@ void do_exmode(void)
|
|||||||
|
|
||||||
exmode_active = true;
|
exmode_active = true;
|
||||||
State = NORMAL;
|
State = NORMAL;
|
||||||
|
trigger_modechanged();
|
||||||
|
|
||||||
// When using ":global /pat/ visual" and then "Q" we return to continue
|
// When using ":global /pat/ visual" and then "Q" we return to continue
|
||||||
// the :global command.
|
// the :global command.
|
||||||
|
@ -880,7 +880,8 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
|||||||
TryState tstate;
|
TryState tstate;
|
||||||
Error err = ERROR_INIT;
|
Error err = ERROR_INIT;
|
||||||
bool tl_ret = true;
|
bool tl_ret = true;
|
||||||
dict_T *dict = get_vim_var_dict(VV_EVENT);
|
save_v_event_T save_v_event;
|
||||||
|
dict_T *dict = get_v_event(&save_v_event);
|
||||||
char firstcbuf[2];
|
char firstcbuf[2];
|
||||||
firstcbuf[0] = (char)(firstc > 0 ? firstc : '-');
|
firstcbuf[0] = (char)(firstc > 0 ? firstc : '-');
|
||||||
firstcbuf[1] = 0;
|
firstcbuf[1] = 0;
|
||||||
@ -894,7 +895,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
|||||||
|
|
||||||
apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf,
|
apply_autocmds(EVENT_CMDLINEENTER, (char_u *)firstcbuf, (char_u *)firstcbuf,
|
||||||
false, curbuf);
|
false, curbuf);
|
||||||
tv_dict_clear(dict);
|
restore_v_event(dict, &save_v_event);
|
||||||
|
|
||||||
|
|
||||||
tl_ret = try_leave(&tstate, &err);
|
tl_ret = try_leave(&tstate, &err);
|
||||||
@ -906,6 +907,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
|||||||
}
|
}
|
||||||
tl_ret = true;
|
tl_ret = true;
|
||||||
}
|
}
|
||||||
|
trigger_modechanged();
|
||||||
|
|
||||||
state_enter(&s->state);
|
state_enter(&s->state);
|
||||||
|
|
||||||
@ -924,7 +926,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
|||||||
if (tv_dict_get_number(dict, "abort") != 0) {
|
if (tv_dict_get_number(dict, "abort") != 0) {
|
||||||
s->gotesc = 1;
|
s->gotesc = 1;
|
||||||
}
|
}
|
||||||
tv_dict_clear(dict);
|
restore_v_event(dict, &save_v_event);
|
||||||
}
|
}
|
||||||
|
|
||||||
cmdmsg_rl = false;
|
cmdmsg_rl = false;
|
||||||
@ -2289,7 +2291,8 @@ static int command_line_changed(CommandLineState *s)
|
|||||||
if (has_event(EVENT_CMDLINECHANGED)) {
|
if (has_event(EVENT_CMDLINECHANGED)) {
|
||||||
TryState tstate;
|
TryState tstate;
|
||||||
Error err = ERROR_INIT;
|
Error err = ERROR_INIT;
|
||||||
dict_T *dict = get_vim_var_dict(VV_EVENT);
|
save_v_event_T save_v_event;
|
||||||
|
dict_T *dict = get_v_event(&save_v_event);
|
||||||
|
|
||||||
char firstcbuf[2];
|
char firstcbuf[2];
|
||||||
firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-');
|
firstcbuf[0] = (char)(s->firstc > 0 ? s->firstc : '-');
|
||||||
@ -2303,7 +2306,7 @@ static int command_line_changed(CommandLineState *s)
|
|||||||
|
|
||||||
apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf,
|
apply_autocmds(EVENT_CMDLINECHANGED, (char_u *)firstcbuf,
|
||||||
(char_u *)firstcbuf, false, curbuf);
|
(char_u *)firstcbuf, false, curbuf);
|
||||||
tv_dict_clear(dict);
|
restore_v_event(dict, &save_v_event);
|
||||||
|
|
||||||
bool tl_ret = try_leave(&tstate, &err);
|
bool tl_ret = try_leave(&tstate, &err);
|
||||||
if (!tl_ret && ERROR_SET(&err)) {
|
if (!tl_ret && ERROR_SET(&err)) {
|
||||||
@ -6547,6 +6550,7 @@ static int open_cmdwin(void)
|
|||||||
cmdmsg_rl = save_cmdmsg_rl;
|
cmdmsg_rl = save_cmdmsg_rl;
|
||||||
|
|
||||||
State = save_State;
|
State = save_State;
|
||||||
|
trigger_modechanged();
|
||||||
setmouse();
|
setmouse();
|
||||||
|
|
||||||
return cmdwin_result;
|
return cmdwin_result;
|
||||||
|
@ -1603,7 +1603,8 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
|
|||||||
|
|
||||||
recursive = true;
|
recursive = true;
|
||||||
|
|
||||||
dict_T *dict = get_vim_var_dict(VV_EVENT);
|
save_v_event_T save_v_event;
|
||||||
|
dict_T *dict = get_v_event(&save_v_event);
|
||||||
char buf[8];
|
char buf[8];
|
||||||
|
|
||||||
switch (scope) {
|
switch (scope) {
|
||||||
@ -1648,7 +1649,7 @@ void do_autocmd_dirchanged(char *new_dir, CdScope scope, CdCause cause)
|
|||||||
apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false,
|
apply_autocmds(EVENT_DIRCHANGED, (char_u *)buf, (char_u *)new_dir, false,
|
||||||
curbuf);
|
curbuf);
|
||||||
|
|
||||||
tv_dict_clear(dict);
|
restore_v_event(dict, &save_v_event);
|
||||||
|
|
||||||
recursive = false;
|
recursive = false;
|
||||||
}
|
}
|
||||||
|
@ -727,6 +727,7 @@ EXTERN bool listcmd_busy INIT(= false); // set when :argdo, :windo or
|
|||||||
// :bufdo is executing
|
// :bufdo is executing
|
||||||
EXTERN bool need_start_insertmode INIT(= false);
|
EXTERN bool need_start_insertmode INIT(= false);
|
||||||
// start insert mode soon
|
// start insert mode soon
|
||||||
|
EXTERN char *last_mode INIT(= NULL);
|
||||||
EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":)
|
EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":)
|
||||||
EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "."
|
EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "."
|
||||||
EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline
|
EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline
|
||||||
|
@ -631,6 +631,7 @@ void free_all_mem(void)
|
|||||||
clear_sb_text(true); // free any scrollback text
|
clear_sb_text(true); // free any scrollback text
|
||||||
|
|
||||||
// Free some global vars.
|
// Free some global vars.
|
||||||
|
xfree(last_mode);
|
||||||
xfree(last_cmdline);
|
xfree(last_cmdline);
|
||||||
xfree(new_last_cmdline);
|
xfree(new_last_cmdline);
|
||||||
set_keep_msg(NULL, 0);
|
set_keep_msg(NULL, 0);
|
||||||
|
@ -1059,3 +1059,58 @@ void add_time(char_u *buf, size_t buflen, time_t tt)
|
|||||||
seconds);
|
seconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dict_T *get_v_event(save_v_event_T *sve)
|
||||||
|
{
|
||||||
|
dict_T *v_event = get_vim_var_dict(VV_EVENT);
|
||||||
|
|
||||||
|
if (v_event->dv_hashtab.ht_used > 0) {
|
||||||
|
// recursive use of v:event, save, make empty and restore later
|
||||||
|
sve->sve_did_save = true;
|
||||||
|
sve->sve_hashtab = v_event->dv_hashtab;
|
||||||
|
hash_init(&v_event->dv_hashtab);
|
||||||
|
} else {
|
||||||
|
sve->sve_did_save = false;
|
||||||
|
}
|
||||||
|
return v_event;
|
||||||
|
}
|
||||||
|
|
||||||
|
void restore_v_event(dict_T *v_event, save_v_event_T *sve)
|
||||||
|
{
|
||||||
|
tv_dict_free_contents(v_event);
|
||||||
|
if (sve->sve_did_save) {
|
||||||
|
v_event->dv_hashtab = sve->sve_hashtab;
|
||||||
|
} else {
|
||||||
|
hash_init(&v_event->dv_hashtab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fires a ModeChanged autocmd.
|
||||||
|
void trigger_modechanged(void)
|
||||||
|
{
|
||||||
|
if (!has_event(EVENT_MODECHANGED)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *mode = get_mode();
|
||||||
|
if (STRCMP(mode, last_mode) == 0) {
|
||||||
|
xfree(mode);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
save_v_event_T save_v_event;
|
||||||
|
dict_T *v_event = get_v_event(&save_v_event);
|
||||||
|
tv_dict_add_str(v_event, S_LEN("new_mode"), mode);
|
||||||
|
tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode);
|
||||||
|
|
||||||
|
char_u *pat_pre = concat_str((char_u *)last_mode, (char_u *)":");
|
||||||
|
char_u *pat = concat_str(pat_pre, (char_u *)mode);
|
||||||
|
xfree(pat_pre);
|
||||||
|
|
||||||
|
apply_autocmds(EVENT_MODECHANGED, pat, NULL, false, curbuf);
|
||||||
|
xfree(last_mode);
|
||||||
|
last_mode = mode;
|
||||||
|
|
||||||
|
xfree(pat);
|
||||||
|
restore_v_event(v_event, &save_v_event);
|
||||||
|
}
|
||||||
|
@ -487,6 +487,7 @@ static void normal_prepare(NormalState *s)
|
|||||||
if (finish_op != c) {
|
if (finish_op != c) {
|
||||||
ui_cursor_shape(); // may show different cursor shape
|
ui_cursor_shape(); // may show different cursor shape
|
||||||
}
|
}
|
||||||
|
trigger_modechanged();
|
||||||
|
|
||||||
// When not finishing an operator and no register name typed, reset the count.
|
// When not finishing an operator and no register name typed, reset the count.
|
||||||
if (!finish_op && !s->oa.regname) {
|
if (!finish_op && !s->oa.regname) {
|
||||||
@ -928,6 +929,7 @@ normal_end:
|
|||||||
// Reset finish_op, in case it was set
|
// Reset finish_op, in case it was set
|
||||||
s->c = finish_op;
|
s->c = finish_op;
|
||||||
finish_op = false;
|
finish_op = false;
|
||||||
|
trigger_modechanged();
|
||||||
// Redraw the cursor with another shape, if we were in Operator-pending
|
// Redraw the cursor with another shape, if we were in Operator-pending
|
||||||
// mode or did a replace command.
|
// mode or did a replace command.
|
||||||
if (s->c || s->ca.cmdchar == 'r') {
|
if (s->c || s->ca.cmdchar == 'r') {
|
||||||
@ -965,6 +967,7 @@ normal_end:
|
|||||||
&& s->oa.regname == 0) {
|
&& s->oa.regname == 0) {
|
||||||
if (restart_VIsual_select == 1) {
|
if (restart_VIsual_select == 1) {
|
||||||
VIsual_select = true;
|
VIsual_select = true;
|
||||||
|
trigger_modechanged();
|
||||||
showmode();
|
showmode();
|
||||||
restart_VIsual_select = 0;
|
restart_VIsual_select = 0;
|
||||||
}
|
}
|
||||||
@ -3066,6 +3069,7 @@ void end_visual_mode(void)
|
|||||||
may_clear_cmdline();
|
may_clear_cmdline();
|
||||||
|
|
||||||
adjust_cursor_eol();
|
adjust_cursor_eol();
|
||||||
|
trigger_modechanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4851,6 +4855,7 @@ static void nv_ctrlg(cmdarg_T *cap)
|
|||||||
{
|
{
|
||||||
if (VIsual_active) { // toggle Selection/Visual mode
|
if (VIsual_active) { // toggle Selection/Visual mode
|
||||||
VIsual_select = !VIsual_select;
|
VIsual_select = !VIsual_select;
|
||||||
|
trigger_modechanged();
|
||||||
showmode();
|
showmode();
|
||||||
} else if (!checkclearop(cap->oap)) {
|
} else if (!checkclearop(cap->oap)) {
|
||||||
// print full name if count given or :cd used
|
// print full name if count given or :cd used
|
||||||
@ -4894,6 +4899,7 @@ static void nv_ctrlo(cmdarg_T *cap)
|
|||||||
{
|
{
|
||||||
if (VIsual_active && VIsual_select) {
|
if (VIsual_active && VIsual_select) {
|
||||||
VIsual_select = false;
|
VIsual_select = false;
|
||||||
|
trigger_modechanged();
|
||||||
showmode();
|
showmode();
|
||||||
restart_VIsual_select = 2; // restart Select mode later
|
restart_VIsual_select = 2; // restart Select mode later
|
||||||
} else {
|
} else {
|
||||||
@ -6680,6 +6686,7 @@ static void nv_visual(cmdarg_T *cap)
|
|||||||
// or char/line mode
|
// or char/line mode
|
||||||
VIsual_mode = cap->cmdchar;
|
VIsual_mode = cap->cmdchar;
|
||||||
showmode();
|
showmode();
|
||||||
|
trigger_modechanged();
|
||||||
}
|
}
|
||||||
redraw_curbuf_later(INVERTED); // update the inversion
|
redraw_curbuf_later(INVERTED); // update the inversion
|
||||||
} else { // start Visual mode
|
} else { // start Visual mode
|
||||||
@ -6793,6 +6800,7 @@ static void n_start_visual_mode(int c)
|
|||||||
|
|
||||||
foldAdjustVisual();
|
foldAdjustVisual();
|
||||||
|
|
||||||
|
trigger_modechanged();
|
||||||
setmouse();
|
setmouse();
|
||||||
// Check for redraw after changing the state.
|
// Check for redraw after changing the state.
|
||||||
conceal_check_cursor_line();
|
conceal_check_cursor_line();
|
||||||
|
@ -2809,8 +2809,9 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
|
|||||||
|
|
||||||
recursive = true;
|
recursive = true;
|
||||||
|
|
||||||
|
save_v_event_T save_v_event;
|
||||||
// Set the v:event dictionary with information about the yank.
|
// Set the v:event dictionary with information about the yank.
|
||||||
dict_T *dict = get_vim_var_dict(VV_EVENT);
|
dict_T *dict = get_v_event(&save_v_event);
|
||||||
|
|
||||||
// The yanked text contents.
|
// The yanked text contents.
|
||||||
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
|
list_T *const list = tv_list_alloc((ptrdiff_t)reg->y_size);
|
||||||
@ -2847,7 +2848,7 @@ static void do_autocmd_textyankpost(oparg_T *oap, yankreg_T *reg)
|
|||||||
textlock++;
|
textlock++;
|
||||||
apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
|
apply_autocmds(EVENT_TEXTYANKPOST, NULL, NULL, false, curbuf);
|
||||||
textlock--;
|
textlock--;
|
||||||
tv_dict_clear(dict);
|
restore_v_event(dict, &save_v_event);
|
||||||
|
|
||||||
recursive = false;
|
recursive = false;
|
||||||
}
|
}
|
||||||
|
@ -136,7 +136,7 @@ int get_real_state(void)
|
|||||||
/// @returns[allocated] mode string
|
/// @returns[allocated] mode string
|
||||||
char *get_mode(void)
|
char *get_mode(void)
|
||||||
{
|
{
|
||||||
char *buf = xcalloc(4, sizeof(char));
|
char *buf = xcalloc(MODE_MAX_LENGTH, sizeof(char));
|
||||||
|
|
||||||
if (VIsual_active) {
|
if (VIsual_active) {
|
||||||
if (VIsual_select) {
|
if (VIsual_select) {
|
||||||
|
@ -324,10 +324,11 @@ void terminal_close(Terminal *term, int status)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (buf && !is_autocmd_blocked()) {
|
if (buf && !is_autocmd_blocked()) {
|
||||||
dict_T *dict = get_vim_var_dict(VV_EVENT);
|
save_v_event_T save_v_event;
|
||||||
|
dict_T *dict = get_v_event(&save_v_event);
|
||||||
tv_dict_add_nr(dict, S_LEN("status"), status);
|
tv_dict_add_nr(dict, S_LEN("status"), status);
|
||||||
apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf);
|
apply_autocmds(EVENT_TERMCLOSE, NULL, NULL, false, buf);
|
||||||
tv_dict_clear(dict);
|
restore_v_event(dict, &save_v_event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -412,6 +413,7 @@ void terminal_enter(void)
|
|||||||
curwin->w_redr_status = true; // For mode() in statusline. #8323
|
curwin->w_redr_status = true; // For mode() in statusline. #8323
|
||||||
ui_busy_start();
|
ui_busy_start();
|
||||||
apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf);
|
apply_autocmds(EVENT_TERMENTER, NULL, NULL, false, curbuf);
|
||||||
|
trigger_modechanged();
|
||||||
|
|
||||||
s->state.execute = terminal_execute;
|
s->state.execute = terminal_execute;
|
||||||
s->state.check = terminal_check;
|
s->state.check = terminal_check;
|
||||||
|
@ -1644,4 +1644,95 @@ func Test_read_invalid()
|
|||||||
set encoding=utf-8
|
set encoding=utf-8
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Test for ModeChanged pattern
|
||||||
|
func Test_mode_changes()
|
||||||
|
let g:index = 0
|
||||||
|
let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n']
|
||||||
|
func! TestMode()
|
||||||
|
call assert_equal(g:mode_seq[g:index], get(v:event, "old_mode"))
|
||||||
|
call assert_equal(g:mode_seq[g:index + 1], get(v:event, "new_mode"))
|
||||||
|
call assert_equal(mode(1), get(v:event, "new_mode"))
|
||||||
|
let g:index += 1
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
au ModeChanged * :call TestMode()
|
||||||
|
let g:n_to_any = 0
|
||||||
|
au ModeChanged n:* let g:n_to_any += 1
|
||||||
|
call feedkeys("i\<esc>vVca\<CR>\<C-X>\<C-L>\<esc>ggdG", 'tnix')
|
||||||
|
|
||||||
|
let g:V_to_v = 0
|
||||||
|
au ModeChanged V:v let g:V_to_v += 1
|
||||||
|
call feedkeys("Vv\<C-G>\<esc>", 'tnix')
|
||||||
|
call assert_equal(len(filter(g:mode_seq[1:], {idx, val -> val == 'n'})), g:n_to_any)
|
||||||
|
call assert_equal(1, g:V_to_v)
|
||||||
|
call assert_equal(len(g:mode_seq) - 1, g:index)
|
||||||
|
|
||||||
|
let g:n_to_i = 0
|
||||||
|
au ModeChanged n:i let g:n_to_i += 1
|
||||||
|
let g:n_to_niI = 0
|
||||||
|
au ModeChanged i:niI let g:n_to_niI += 1
|
||||||
|
let g:niI_to_i = 0
|
||||||
|
au ModeChanged niI:i let g:niI_to_i += 1
|
||||||
|
let g:nany_to_i = 0
|
||||||
|
au ModeChanged n*:i let g:nany_to_i += 1
|
||||||
|
let g:i_to_n = 0
|
||||||
|
au ModeChanged i:n let g:i_to_n += 1
|
||||||
|
let g:nori_to_any = 0
|
||||||
|
au ModeChanged [ni]:* let g:nori_to_any += 1
|
||||||
|
let g:i_to_any = 0
|
||||||
|
au ModeChanged i:* let g:i_to_any += 1
|
||||||
|
let g:index = 0
|
||||||
|
let g:mode_seq = ['n', 'i', 'niI', 'i', 'n']
|
||||||
|
call feedkeys("a\<C-O>l\<esc>", 'tnix')
|
||||||
|
call assert_equal(len(g:mode_seq) - 1, g:index)
|
||||||
|
call assert_equal(1, g:n_to_i)
|
||||||
|
call assert_equal(1, g:n_to_niI)
|
||||||
|
call assert_equal(1, g:niI_to_i)
|
||||||
|
call assert_equal(2, g:nany_to_i)
|
||||||
|
call assert_equal(1, g:i_to_n)
|
||||||
|
call assert_equal(2, g:i_to_any)
|
||||||
|
call assert_equal(3, g:nori_to_any)
|
||||||
|
|
||||||
|
if has('terminal')
|
||||||
|
let g:mode_seq += ['c', 'n', 't', 'nt', 'c', 'nt', 'n']
|
||||||
|
call feedkeys(":term\<CR>\<C-W>N:bd!\<CR>", 'tnix')
|
||||||
|
call assert_equal(len(g:mode_seq) - 1, g:index)
|
||||||
|
call assert_equal(1, g:n_to_i)
|
||||||
|
call assert_equal(1, g:n_to_niI)
|
||||||
|
call assert_equal(1, g:niI_to_i)
|
||||||
|
call assert_equal(2, g:nany_to_i)
|
||||||
|
call assert_equal(1, g:i_to_n)
|
||||||
|
call assert_equal(2, g:i_to_any)
|
||||||
|
call assert_equal(5, g:nori_to_any)
|
||||||
|
endif
|
||||||
|
|
||||||
|
au! ModeChanged
|
||||||
|
delfunc TestMode
|
||||||
|
unlet! g:mode_seq
|
||||||
|
unlet! g:index
|
||||||
|
unlet! g:n_to_any
|
||||||
|
unlet! g:V_to_v
|
||||||
|
unlet! g:n_to_i
|
||||||
|
unlet! g:n_to_niI
|
||||||
|
unlet! g:niI_to_i
|
||||||
|
unlet! g:nany_to_i
|
||||||
|
unlet! g:i_to_n
|
||||||
|
unlet! g:nori_to_any
|
||||||
|
unlet! g:i_to_any
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_recursive_ModeChanged()
|
||||||
|
au! ModeChanged * norm 0u
|
||||||
|
sil! norm
|
||||||
|
au!
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
func Test_ModeChanged_starts_visual()
|
||||||
|
" This was triggering ModeChanged before setting VIsual, causing a crash.
|
||||||
|
au! ModeChanged * norm 0u
|
||||||
|
sil! norm
|
||||||
|
|
||||||
|
au! ModeChanged
|
||||||
|
endfunc
|
||||||
|
|
||||||
" vim: shiftwidth=2 sts=2 expandtab
|
" vim: shiftwidth=2 sts=2 expandtab
|
||||||
|
@ -72,6 +72,8 @@ enum { NUMBUFLEN = 65, };
|
|||||||
#define TERM_FOCUS 0x2000 // Terminal focus mode
|
#define TERM_FOCUS 0x2000 // Terminal focus mode
|
||||||
#define CMDPREVIEW 0x4000 // Showing 'inccommand' command "live" preview.
|
#define CMDPREVIEW 0x4000 // Showing 'inccommand' command "live" preview.
|
||||||
|
|
||||||
|
#define MODE_MAX_LENGTH 4 // max mode length returned in mode()
|
||||||
|
|
||||||
// all mode bits used for mapping
|
// all mode bits used for mapping
|
||||||
#define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS)
|
#define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS)
|
||||||
|
|
||||||
|
31
test/functional/autocmd/modechanged_spec.lua
Normal file
31
test/functional/autocmd/modechanged_spec.lua
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
local helpers = require('test.functional.helpers')(after_each)
|
||||||
|
local clear, eval, eq = helpers.clear, helpers.eval, helpers.eq
|
||||||
|
local feed, command = helpers.feed, helpers.command
|
||||||
|
|
||||||
|
describe('ModeChanged', function()
|
||||||
|
before_each(function()
|
||||||
|
clear()
|
||||||
|
command('let g:count = 0')
|
||||||
|
command('au ModeChanged * let g:event = copy(v:event)')
|
||||||
|
command('au ModeChanged * let g:count += 1')
|
||||||
|
end)
|
||||||
|
|
||||||
|
it('picks up terminal mode changes', function()
|
||||||
|
command("term")
|
||||||
|
feed('i')
|
||||||
|
eq({
|
||||||
|
old_mode = 'nt',
|
||||||
|
new_mode = 't'
|
||||||
|
}, eval('g:event'))
|
||||||
|
feed('<c-\\><c-n>')
|
||||||
|
eq({
|
||||||
|
old_mode = 't',
|
||||||
|
new_mode = 'nt'
|
||||||
|
}, eval('g:event'))
|
||||||
|
eq(3, eval('g:count'))
|
||||||
|
command("bd!")
|
||||||
|
|
||||||
|
-- v:event is cleared after the autocommand is done
|
||||||
|
eq({}, eval('v:event'))
|
||||||
|
end)
|
||||||
|
end)
|
Loading…
Reference in New Issue
Block a user