mirror of
https://github.com/neovim/neovim.git
synced 2025-09-23 11:38:31 +00:00
vim-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 vim/vim#8856)f1e8876fa2
N/A patches for version.c: vim-patch:8.2.3434: function prototype for trigger_modechanged() is incomplete Problem: Function prototype for trigger_modechanged() is incomplete. Solution: Add "void".28e591dd50
Fixes #4399. Fixes #7416.
This commit is contained in:
@@ -718,7 +718,22 @@ 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 `i:*` to simulate
|
||||||
|
|InsertLeave|.
|
||||||
|
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()|.
|
||||||
|
When ModeChanged is triggered, old_mode will
|
||||||
|
have the value of new_mode when the event was
|
||||||
|
last triggered.
|
||||||
|
Usage example to use relative line numbers
|
||||||
|
when entering visual mode: >
|
||||||
|
:autocmd ModeChanged *:v set rnu
|
||||||
|
< *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>|
|
||||||
|
@@ -69,6 +69,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.
|
||||||
|
@@ -1440,7 +1440,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 +1494,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);
|
||||||
|
@@ -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
|
||||||
@@ -7965,6 +7966,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 +8068,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.
|
||||||
|
@@ -906,6 +906,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);
|
||||||
|
|
||||||
@@ -6547,6 +6548,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;
|
||||||
|
@@ -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
|
||||||
|
@@ -632,6 +632,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,32 @@ void add_time(char_u *buf, size_t buflen, time_t tt)
|
|||||||
seconds);
|
seconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fires a ModeChanged autocmd.
|
||||||
|
void trigger_modechanged(void)
|
||||||
|
{
|
||||||
|
if (!has_event(EVENT_MODECHANGED)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dict_T *v_event = get_vim_var_dict(VV_EVENT);
|
||||||
|
|
||||||
|
char *mode = get_mode();
|
||||||
|
if (last_mode == NULL) {
|
||||||
|
last_mode = (char *)vim_strsave((char_u *)"n");
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
tv_dict_free_contents(v_event);
|
||||||
|
hash_init(&v_event->dv_hashtab);
|
||||||
|
}
|
||||||
|
@@ -3050,6 +3050,7 @@ 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;
|
||||||
|
|
||||||
@@ -6680,6 +6681,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
|
||||||
@@ -6782,6 +6784,7 @@ static void n_start_visual_mode(int c)
|
|||||||
VIsual_mode = c;
|
VIsual_mode = c;
|
||||||
VIsual_active = true;
|
VIsual_active = true;
|
||||||
VIsual_reselect = true;
|
VIsual_reselect = true;
|
||||||
|
trigger_modechanged();
|
||||||
// Corner case: the 0 position in a tab may change when going into
|
// Corner case: the 0 position in a tab may change when going into
|
||||||
// virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.
|
// virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.
|
||||||
//
|
//
|
||||||
|
@@ -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) {
|
||||||
|
@@ -1644,4 +1644,38 @@ func Test_read_invalid()
|
|||||||
set encoding=utf-8
|
set encoding=utf-8
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
" Test for ModeChanged pattern
|
||||||
|
func Test_mode_changes()
|
||||||
|
let g:count = 0
|
||||||
|
func! DoIt()
|
||||||
|
let g:count += 1
|
||||||
|
endfunc
|
||||||
|
let g:index = 0
|
||||||
|
let g:mode_seq = ['n', 'i', 'n', 'v', 'V', 'n', 'V', 'v', '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(), get(v:event, "new_mode"))
|
||||||
|
let g:index += 1
|
||||||
|
endfunc
|
||||||
|
|
||||||
|
au ModeChanged * :call TestMode()
|
||||||
|
au ModeChanged n:* :call DoIt()
|
||||||
|
call feedkeys("i\<esc>vV\<esc>", 'tnix')
|
||||||
|
call assert_equal(2, g:count)
|
||||||
|
|
||||||
|
au ModeChanged V:v :call DoIt()
|
||||||
|
call feedkeys("Vv\<esc>", 'tnix')
|
||||||
|
call assert_equal(4, g:count)
|
||||||
|
|
||||||
|
call assert_equal(len(g:mode_seq) - 1, g:index)
|
||||||
|
|
||||||
|
au! ModeChanged
|
||||||
|
delfunc TestMode
|
||||||
|
unlet! g:mode_seq
|
||||||
|
unlet! g:index
|
||||||
|
delfunc DoIt
|
||||||
|
unlet! g:count
|
||||||
|
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)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user