vim-patch:8.2.3555: ModeChanged is not triggered on every mode change

Problem:    ModeChanged is not triggered on every mode change.
Solution:   Also trigger on minor mode changes. (Maguns Gross, closes vim/vim#8999)
25def2c8b8
This commit is contained in:
Magnus Groß
2021-10-22 20:36:35 +02:00
parent 60c154687a
commit 11683193f5
6 changed files with 65 additions and 22 deletions

View File

@@ -721,18 +721,23 @@ MenuPopup Just before showing the popup menu (under the
*ModeChanged* *ModeChanged*
ModeChanged After changing the mode. The pattern is ModeChanged After changing the mode. The pattern is
matched against `'old_mode:new_mode'`, for matched against `'old_mode:new_mode'`, for
example match against `i:*` to simulate example match against `*:c` to simulate
|InsertLeave|. |CmdlineEnter|.
The following values of |v:event| are set: The following values of |v:event| are set:
old_mode The mode before it changed. old_mode The mode before it changed.
new_mode The new mode as also returned new_mode The new mode as also returned
by |mode()|. by |mode()| called with a
non-zero argument.
When ModeChanged is triggered, old_mode will When ModeChanged is triggered, old_mode will
have the value of new_mode when the event was have the value of new_mode when the event was
last triggered. last triggered.
This will be triggered on every minor mode
change.
Usage example to use relative line numbers Usage example to use relative line numbers
when entering visual mode: > when entering visual mode: >
:autocmd ModeChanged *:v set rnu :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*
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

View File

@@ -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;

View File

@@ -2049,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.
@@ -2661,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();
} }
@@ -3839,6 +3842,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)) {
@@ -4587,6 +4592,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;
} }

View File

@@ -1067,12 +1067,13 @@ void trigger_modechanged(void)
return; return;
} }
dict_T *v_event = get_vim_var_dict(VV_EVENT);
char *mode = get_mode(); char *mode = get_mode();
if (last_mode == NULL) { if (STRCMP(mode, last_mode) == 0) {
last_mode = (char *)vim_strsave((char_u *)"n"); xfree(mode);
return;
} }
dict_T *v_event = get_vim_var_dict(VV_EVENT);
tv_dict_add_str(v_event, S_LEN("new_mode"), mode); tv_dict_add_str(v_event, S_LEN("new_mode"), mode);
tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode); tv_dict_add_str(v_event, S_LEN("old_mode"), last_mode);

View File

@@ -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;
} }
@@ -3050,7 +3053,6 @@ static int get_mouse_class(char_u *p)
void end_visual_mode(void) void end_visual_mode(void)
{ {
VIsual_active = false; VIsual_active = false;
trigger_modechanged();
setmouse(); setmouse();
mouse_dragging = 0; mouse_dragging = 0;
@@ -3067,6 +3069,7 @@ void end_visual_mode(void)
may_clear_cmdline(); may_clear_cmdline();
adjust_cursor_eol(); adjust_cursor_eol();
trigger_modechanged();
} }
/* /*
@@ -4852,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
@@ -4895,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 {

View File

@@ -1646,12 +1646,8 @@ endfunc
" Test for ModeChanged pattern " Test for ModeChanged pattern
func Test_mode_changes() func Test_mode_changes()
let g:count = 0
func! DoIt()
let g:count += 1
endfunc
let g:index = 0 let g:index = 0
let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'n', 'V', 'v', 'n'] let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'i', 'ix', 'i', 'ic', 'i', 'n', 'no', 'n', 'V', 'v', 's', 'n']
func! TestMode() func! TestMode()
call assert_equal(g:mode_seq[g:index], get(v:event, "old_mode")) 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(g:mode_seq[g:index + 1], get(v:event, "new_mode"))
@@ -1660,13 +1656,15 @@ func Test_mode_changes()
endfunc endfunc
au ModeChanged * :call TestMode() au ModeChanged * :call TestMode()
au ModeChanged n:* :call DoIt() let g:n_to_any = 0
call feedkeys("i\<esc>vV\<esc>", 'tnix') au ModeChanged n:* let g:n_to_any += 1
call assert_equal(2, g:count) call feedkeys("i\<esc>vVca\<CR>\<C-X>\<C-L>\<esc>ggdG", 'tnix')
au ModeChanged V:v :call DoIt() let g:V_to_v = 0
call feedkeys("Vv\<esc>", 'tnix') au ModeChanged V:v let g:V_to_v += 1
call assert_equal(4, g:count) 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) call assert_equal(len(g:mode_seq) - 1, g:index)
let g:n_to_i = 0 let g:n_to_i = 0
@@ -1695,12 +1693,32 @@ func Test_mode_changes()
call assert_equal(2, g:i_to_any) call assert_equal(2, g:i_to_any)
call assert_equal(3, g:nori_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 au! ModeChanged
delfunc TestMode delfunc TestMode
unlet! g:mode_seq unlet! g:mode_seq
unlet! g:index unlet! g:index
delfunc DoIt unlet! g:n_to_any
unlet! g:count 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 endfunc
" vim: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab