Skip to content

Commit f1e8876

Browse files
vimpostorbrammool
authored andcommitted
patch 8.2.3430: no generic way to trigger an autocommand on mode change
Problem: No generic way to trigger an autocommand on mode change. Solution: Add the ModeChanged autocommand event. (Magnus Gross, closes #8856)
1 parent 464393a commit f1e8876

File tree

13 files changed

+135
-4
lines changed

13 files changed

+135
-4
lines changed

runtime/doc/autocmd.txt

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,6 +366,8 @@ Name triggered by ~
366366
|InsertCharPre| when a character was typed in Insert mode, before
367367
inserting it
368368

369+
|ModeChanged| after changing the mode
370+
369371
|TextChanged| after a change was made to the text in Normal mode
370372
|TextChangedI| after a change was made to the text in Insert mode
371373
when popup menu is not visible
@@ -925,7 +927,22 @@ MenuPopup Just before showing the popup menu (under the
925927
i Insert
926928
c Command line
927929
tl Terminal
928-
*OptionSet*
930+
*ModeChanged*
931+
ModeChanged After changing the mode. The pattern is
932+
matched against `'old_mode:new_mode'`, for
933+
example match against `i:*` to simulate
934+
|InsertLeave|.
935+
The following values of |v:event| are set:
936+
old_mode The mode before it changed.
937+
new_mode The new mode as also returned
938+
by |mode()|.
939+
When ModeChanged is triggered, old_mode will
940+
have the value of new_mode when the event was
941+
last triggered.
942+
Usage example to use relative line numbers
943+
when entering visual mode: >
944+
:autocmd ModeChanged *:v set relativenumber
945+
< *OptionSet*
929946
OptionSet After setting an option. The pattern is
930947
matched against the long option name.
931948
|<amatch>| indicates what option has been set.

src/autocmd.c

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ static struct event_name
150150
{"InsertLeavePre", EVENT_INSERTLEAVEPRE},
151151
{"InsertCharPre", EVENT_INSERTCHARPRE},
152152
{"MenuPopup", EVENT_MENUPOPUP},
153+
{"ModeChanged", EVENT_MODECHANGED},
153154
{"OptionSet", EVENT_OPTIONSET},
154155
{"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST},
155156
{"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE},
@@ -1817,6 +1818,17 @@ has_completechanged(void)
18171818
}
18181819
#endif
18191820

1821+
#if defined(FEAT_EVAL) || defined(PROTO)
1822+
/*
1823+
* Return TRUE when there is a ModeChanged autocommand defined.
1824+
*/
1825+
int
1826+
has_modechanged(void)
1827+
{
1828+
return (first_autopat[(int)EVENT_MODECHANGED] != NULL);
1829+
}
1830+
#endif
1831+
18201832
/*
18211833
* Execute autocommands for "event" and file name "fname".
18221834
* Return TRUE if some commands were executed.
@@ -1938,7 +1950,8 @@ apply_autocmds_group(
19381950
if (fname_io == NULL)
19391951
{
19401952
if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
1941-
|| event == EVENT_OPTIONSET)
1953+
|| event == EVENT_OPTIONSET
1954+
|| event == EVENT_MODECHANGED)
19421955
autocmd_fname = NULL;
19431956
else if (fname != NULL && !ends_excmd(*fname))
19441957
autocmd_fname = fname;
@@ -2011,7 +2024,8 @@ apply_autocmds_group(
20112024
|| event == EVENT_COLORSCHEMEPRE
20122025
|| event == EVENT_OPTIONSET
20132026
|| event == EVENT_QUICKFIXCMDPOST
2014-
|| event == EVENT_DIRCHANGED)
2027+
|| event == EVENT_DIRCHANGED
2028+
|| event == EVENT_MODECHANGED)
20152029
{
20162030
fname = vim_strsave(fname);
20172031
autocmd_fname_full = TRUE; // don't expand it later

src/edit.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,7 @@ edit(
284284
else
285285
State = INSERT;
286286

287+
trigger_modechanged();
287288
stop_insert_mode = FALSE;
288289

289290
#ifdef FEAT_CONCEAL
@@ -3681,6 +3682,7 @@ ins_esc(
36813682
#endif
36823683

36833684
State = NORMAL;
3685+
trigger_modechanged();
36843686
// need to position cursor again (e.g. when on a TAB )
36853687
changed_cline_bef_curs();
36863688

@@ -3811,6 +3813,7 @@ ins_insert(int replaceState)
38113813
State = INSERT | (State & LANGMAP);
38123814
else
38133815
State = replaceState | (State & LANGMAP);
3816+
trigger_modechanged();
38143817
AppendCharToRedobuff(K_INS);
38153818
showmode();
38163819
#ifdef CURSOR_SHAPE

src/ex_docmd.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -485,6 +485,7 @@ do_exmode(
485485
else
486486
exmode_active = EXMODE_NORMAL;
487487
State = NORMAL;
488+
trigger_modechanged();
488489

489490
// When using ":global /pat/ visual" and then "Q" we return to continue
490491
// the :global command.

src/ex_getln.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,6 +1696,10 @@ getcmdline_int(
16961696
// Trigger CmdlineEnter autocommands.
16971697
cmdline_type = firstc == NUL ? '-' : firstc;
16981698
trigger_cmd_autocmd(cmdline_type, EVENT_CMDLINEENTER);
1699+
#ifdef FEAT_EVAL
1700+
if (!debug_mode)
1701+
trigger_modechanged();
1702+
#endif
16991703

17001704
init_history();
17011705
hiscnt = get_hislen(); // set hiscnt to impossible history value
@@ -2461,6 +2465,12 @@ getcmdline_int(
24612465
trigger_cmd_autocmd(cmdline_type, EVENT_CMDLINELEAVE);
24622466

24632467
State = save_State;
2468+
2469+
#ifdef FEAT_EVAL
2470+
if (!debug_mode)
2471+
trigger_modechanged();
2472+
#endif
2473+
24642474
#ifdef HAVE_INPUT_METHOD
24652475
if (b_im_ptr != NULL && *b_im_ptr != B_IMODE_LMAP)
24662476
im_save_status(b_im_ptr);

src/globals.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,6 +1256,9 @@ EXTERN int listcmd_busy INIT(= FALSE); // set when :argdo, :windo or
12561256
// :bufdo is executing
12571257
EXTERN int need_start_insertmode INIT(= FALSE);
12581258
// start insert mode soon
1259+
#if defined(FEAT_EVAL) || defined(PROTO)
1260+
EXTERN char_u last_mode[MODE_MAX_LENGTH] INIT(= "n"); // for ModeChanged event
1261+
#endif
12591262
EXTERN char_u *last_cmdline INIT(= NULL); // last command line (for ":)
12601263
EXTERN char_u *repeat_cmdline INIT(= NULL); // command line for "."
12611264
EXTERN char_u *new_last_cmdline INIT(= NULL); // new value for last_cmdline

src/misc1.c

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -630,7 +630,7 @@ ask_yesno(char_u *str, int direct)
630630
void
631631
f_mode(typval_T *argvars, typval_T *rettv)
632632
{
633-
char_u buf[4];
633+
char_u buf[MODE_MAX_LENGTH];
634634

635635
if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL)
636636
return;
@@ -2643,3 +2643,42 @@ path_with_url(char_u *fname)
26432643
// "://" or ":\\" must follow
26442644
return path_is_url(p);
26452645
}
2646+
2647+
/*
2648+
* Fires a ModeChanged autocmd
2649+
*/
2650+
void
2651+
trigger_modechanged()
2652+
{
2653+
#if defined(FEAT_EVAL) || defined(PROTO)
2654+
dict_T *v_event;
2655+
typval_T rettv;
2656+
typval_T tv;
2657+
char_u *pat_pre;
2658+
char_u *pat;
2659+
2660+
if (!has_modechanged())
2661+
return;
2662+
2663+
v_event = get_vim_var_dict(VV_EVENT);
2664+
2665+
tv.v_type = VAR_UNKNOWN;
2666+
f_mode(&tv, &rettv);
2667+
(void)dict_add_string(v_event, "new_mode", rettv.vval.v_string);
2668+
(void)dict_add_string(v_event, "old_mode", last_mode);
2669+
dict_set_items_ro(v_event);
2670+
2671+
// concatenate modes in format "old_mode:new_mode"
2672+
pat_pre = concat_str(last_mode, (char_u*)":");
2673+
pat = concat_str(pat_pre, rettv.vval.v_string);
2674+
vim_free(pat_pre);
2675+
2676+
apply_autocmds(EVENT_MODECHANGED, pat, NULL, FALSE, curbuf);
2677+
STRCPY(last_mode, rettv.vval.v_string);
2678+
2679+
vim_free(rettv.vval.v_string);
2680+
vim_free(pat);
2681+
dict_free_contents(v_event);
2682+
hash_init(&v_event->dv_hashtab);
2683+
#endif
2684+
}

src/normal.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,7 @@ end_visual_mode_keep_button()
13861386
#endif
13871387

13881388
VIsual_active = FALSE;
1389+
trigger_modechanged();
13891390
setmouse();
13901391
mouse_dragging = 0;
13911392

@@ -5642,6 +5643,7 @@ nv_visual(cmdarg_T *cap)
56425643
{ // or char/line mode
56435644
VIsual_mode = cap->cmdchar;
56445645
showmode();
5646+
trigger_modechanged();
56455647
}
56465648
redraw_curbuf_later(INVERTED); // update the inversion
56475649
}
@@ -5757,6 +5759,7 @@ n_start_visual_mode(int c)
57575759
VIsual_mode = c;
57585760
VIsual_active = TRUE;
57595761
VIsual_reselect = TRUE;
5762+
trigger_modechanged();
57605763

57615764
// Corner case: the 0 position in a tab may change when going into
57625765
// virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.

src/proto/autocmd.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ int has_insertcharpre(void);
2525
int has_cmdundefined(void);
2626
int has_textyankpost(void);
2727
int has_completechanged(void);
28+
int has_modechanged(void);
2829
void block_autocmds(void);
2930
void unblock_autocmds(void);
3031
int is_autocmd_blocked(void);

src/proto/misc1.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,5 @@ int goto_im(void);
4747
char_u *get_isolated_shell_name(void);
4848
int path_is_url(char_u *p);
4949
int path_with_url(char_u *fname);
50+
void trigger_modechanged();
5051
/* vim: set ft=c : */

0 commit comments

Comments
 (0)