mirror of
https://github.com/neovim/neovim.git
synced 2025-02-25 18:55:25 -06: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:
parent
36538417f0
commit
69bd1e4e36
@ -718,7 +718,22 @@ MenuPopup Just before showing the popup menu (under the
|
||||
o Operator-pending
|
||||
i Insert
|
||||
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
|
||||
|startup|). The |autocmd-pattern| is matched
|
||||
against the long option name. |<amatch>|
|
||||
|
@ -69,6 +69,7 @@ return {
|
||||
'InsertLeave', -- just after leaving Insert mode
|
||||
'InsertLeavePre', -- just before leaving Insert mode
|
||||
'MenuPopup', -- just before popup menu is displayed
|
||||
'ModeChanged', -- after changing the mode
|
||||
'OptionSet', -- after setting any option
|
||||
'QuickFixCmdPost', -- after :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.
|
||||
if (fname_io == NULL) {
|
||||
if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
|
||||
|| event == EVENT_OPTIONSET) {
|
||||
|| event == EVENT_OPTIONSET || event == EVENT_MODECHANGED) {
|
||||
autocmd_fname = NULL;
|
||||
} else if (fname != NULL && !ends_excmd(*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_COLORSCHEME || event == EVENT_COLORSCHEMEPRE
|
||||
|| event == EVENT_DIRCHANGED || event == EVENT_FILETYPE
|
||||
|| event == EVENT_FUNCUNDEFINED || event == EVENT_OPTIONSET
|
||||
|| event == EVENT_QUICKFIXCMDPOST || event == EVENT_QUICKFIXCMDPRE
|
||||
|| event == EVENT_REMOTEREPLY || event == EVENT_SPELLFILEMISSING
|
||||
|| event == EVENT_SYNTAX || event == EVENT_SIGNAL
|
||||
|| event == EVENT_TABCLOSED || event == EVENT_WINCLOSED) {
|
||||
|| event == EVENT_FUNCUNDEFINED || event == EVENT_MODECHANGED
|
||||
|| event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST
|
||||
|| event == EVENT_QUICKFIXCMDPRE || event == EVENT_REMOTEREPLY
|
||||
|| event == EVENT_SPELLFILEMISSING || event == EVENT_SYNTAX
|
||||
|| event == EVENT_SIGNAL || event == EVENT_TABCLOSED
|
||||
|| event == EVENT_WINCLOSED) {
|
||||
fname = vim_strsave(fname);
|
||||
} else {
|
||||
fname = (char_u *)FullName_save((char *)fname, false);
|
||||
|
@ -385,6 +385,7 @@ static void insert_enter(InsertState *s)
|
||||
State = INSERT;
|
||||
}
|
||||
|
||||
trigger_modechanged();
|
||||
stop_insert_mode = false;
|
||||
|
||||
// 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;
|
||||
trigger_modechanged();
|
||||
// need to position cursor again (e.g. when on a TAB )
|
||||
changed_cline_bef_curs();
|
||||
|
||||
@ -8066,6 +8068,7 @@ static void ins_insert(int replaceState)
|
||||
} else {
|
||||
State = replaceState | (State & LANGMAP);
|
||||
}
|
||||
trigger_modechanged();
|
||||
AppendCharToRedobuff(K_INS);
|
||||
showmode();
|
||||
ui_cursor_shape(); // may show different cursor shape
|
||||
|
@ -196,6 +196,7 @@ void do_exmode(void)
|
||||
|
||||
exmode_active = true;
|
||||
State = NORMAL;
|
||||
trigger_modechanged();
|
||||
|
||||
// When using ":global /pat/ visual" and then "Q" we return to continue
|
||||
// the :global command.
|
||||
|
@ -906,6 +906,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
|
||||
}
|
||||
tl_ret = true;
|
||||
}
|
||||
trigger_modechanged();
|
||||
|
||||
state_enter(&s->state);
|
||||
|
||||
@ -6547,6 +6548,7 @@ static int open_cmdwin(void)
|
||||
cmdmsg_rl = save_cmdmsg_rl;
|
||||
|
||||
State = save_State;
|
||||
trigger_modechanged();
|
||||
setmouse();
|
||||
|
||||
return cmdwin_result;
|
||||
|
@ -727,6 +727,7 @@ EXTERN bool listcmd_busy INIT(= false); // set when :argdo, :windo or
|
||||
// :bufdo is executing
|
||||
EXTERN bool need_start_insertmode INIT(= false);
|
||||
// start insert mode soon
|
||||
EXTERN char *last_mode INIT(= NULL);
|
||||
EXTERN char_u *last_cmdline INIT(= NULL); // last 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
|
||||
|
@ -632,6 +632,7 @@ void free_all_mem(void)
|
||||
clear_sb_text(true); // free any scrollback text
|
||||
|
||||
// Free some global vars.
|
||||
xfree(last_mode);
|
||||
xfree(last_cmdline);
|
||||
xfree(new_last_cmdline);
|
||||
set_keep_msg(NULL, 0);
|
||||
|
@ -1059,3 +1059,32 @@ void add_time(char_u *buf, size_t buflen, time_t tt)
|
||||
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)
|
||||
{
|
||||
VIsual_active = false;
|
||||
trigger_modechanged();
|
||||
setmouse();
|
||||
mouse_dragging = 0;
|
||||
|
||||
@ -6680,6 +6681,7 @@ static void nv_visual(cmdarg_T *cap)
|
||||
// or char/line mode
|
||||
VIsual_mode = cap->cmdchar;
|
||||
showmode();
|
||||
trigger_modechanged();
|
||||
}
|
||||
redraw_curbuf_later(INVERTED); // update the inversion
|
||||
} else { // start Visual mode
|
||||
@ -6782,6 +6784,7 @@ static void n_start_visual_mode(int c)
|
||||
VIsual_mode = c;
|
||||
VIsual_active = true;
|
||||
VIsual_reselect = true;
|
||||
trigger_modechanged();
|
||||
// Corner case: the 0 position in a tab may change when going into
|
||||
// virtualedit. Recalculate curwin->w_cursor to avoid bad highlighting.
|
||||
//
|
||||
|
@ -136,7 +136,7 @@ int get_real_state(void)
|
||||
/// @returns[allocated] mode string
|
||||
char *get_mode(void)
|
||||
{
|
||||
char *buf = xcalloc(4, sizeof(char));
|
||||
char *buf = xcalloc(MODE_MAX_LENGTH, sizeof(char));
|
||||
|
||||
if (VIsual_active) {
|
||||
if (VIsual_select) {
|
||||
|
@ -1644,4 +1644,38 @@ func Test_read_invalid()
|
||||
set encoding=utf-8
|
||||
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
|
||||
|
@ -72,6 +72,8 @@ enum { NUMBUFLEN = 65, };
|
||||
#define TERM_FOCUS 0x2000 // Terminal focus mode
|
||||
#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
|
||||
#define MAP_ALL_MODES (0x3f | SELECTMODE | TERM_FOCUS)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user