To: vim_dev@googlegroups.com Subject: Patch 8.2.3430 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.3430 Problem: No generic way to trigger an autocommand on mode change. Solution: Add the ModeChanged autocommand event. (Magnus Gross, closes #8856) Files: runtime/doc/autocmd.txt, src/autocmd.c, src/edit.c, src/ex_docmd.c, src/ex_getln.c, src/globals.h, src/misc1.c, src/normal.c, src/proto/autocmd.pro, src/proto/misc1.pro, src/testdir/test_edit.vim, src/vim.h *** ../vim-8.2.3429/runtime/doc/autocmd.txt 2021-08-01 14:52:05.554645412 +0200 --- runtime/doc/autocmd.txt 2021-09-12 13:30:35.724067946 +0200 *************** *** 54,60 **** :au[tocmd] [group] {event} {pat} [++once] [++nested] {cmd} Add {cmd} to the list of commands that Vim will execute automatically on {event} for a file matching ! {pat} |autocmd-patterns|. Here {event} cannot be "*". *E1155* Note: A quote character is seen as argument to the :autocmd and won't start a comment. --- 54,60 ---- :au[tocmd] [group] {event} {pat} [++once] [++nested] {cmd} Add {cmd} to the list of commands that Vim will execute automatically on {event} for a file matching ! {pat} |autocmd-patterns|. Here {event} cannot be "*". *E1155* Note: A quote character is seen as argument to the :autocmd and won't start a comment. *************** *** 76,82 **** script. Thus this depends on where the autocmd is defined, not where it is triggered. ! {cmd} can use a block, like with `:command`, see |:command-repl|. Example: > au BufReadPost *.xml { setlocal matchpairs+=<:> / au BufReadPost *.xml { setlocal matchpairs+=<:> /| indicates what option has been set. --- 927,948 ---- i Insert c Command line tl Terminal ! *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 relativenumber ! < *OptionSet* OptionSet After setting an option. The pattern is matched against the long option name. || indicates what option has been set. *** ../vim-8.2.3429/src/autocmd.c 2021-08-07 13:59:38.298919436 +0200 --- src/autocmd.c 2021-09-12 13:30:35.728067940 +0200 *************** *** 150,155 **** --- 150,156 ---- {"InsertLeavePre", EVENT_INSERTLEAVEPRE}, {"InsertCharPre", EVENT_INSERTCHARPRE}, {"MenuPopup", EVENT_MENUPOPUP}, + {"ModeChanged", EVENT_MODECHANGED}, {"OptionSet", EVENT_OPTIONSET}, {"QuickFixCmdPost", EVENT_QUICKFIXCMDPOST}, {"QuickFixCmdPre", EVENT_QUICKFIXCMDPRE}, *************** *** 1817,1822 **** --- 1818,1834 ---- } #endif + #if defined(FEAT_EVAL) || defined(PROTO) + /* + * Return TRUE when there is a ModeChanged autocommand defined. + */ + int + has_modechanged(void) + { + return (first_autopat[(int)EVENT_MODECHANGED] != NULL); + } + #endif + /* * Execute autocommands for "event" and file name "fname". * Return TRUE if some commands were executed. *************** *** 1938,1944 **** if (fname_io == NULL) { if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE ! || event == EVENT_OPTIONSET) autocmd_fname = NULL; else if (fname != NULL && !ends_excmd(*fname)) autocmd_fname = fname; --- 1950,1957 ---- if (fname_io == NULL) { if (event == EVENT_COLORSCHEME || event == EVENT_COLORSCHEMEPRE ! || event == EVENT_OPTIONSET ! || event == EVENT_MODECHANGED) autocmd_fname = NULL; else if (fname != NULL && !ends_excmd(*fname)) autocmd_fname = fname; *************** *** 2011,2017 **** || event == EVENT_COLORSCHEMEPRE || event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST ! || event == EVENT_DIRCHANGED) { fname = vim_strsave(fname); autocmd_fname_full = TRUE; // don't expand it later --- 2024,2031 ---- || event == EVENT_COLORSCHEMEPRE || event == EVENT_OPTIONSET || event == EVENT_QUICKFIXCMDPOST ! || event == EVENT_DIRCHANGED ! || event == EVENT_MODECHANGED) { fname = vim_strsave(fname); autocmd_fname_full = TRUE; // don't expand it later *** ../vim-8.2.3429/src/edit.c 2021-08-09 19:59:01.438811254 +0200 --- src/edit.c 2021-09-12 13:30:35.728067940 +0200 *************** *** 284,289 **** --- 284,290 ---- else State = INSERT; + trigger_modechanged(); stop_insert_mode = FALSE; #ifdef FEAT_CONCEAL *************** *** 3681,3686 **** --- 3682,3688 ---- #endif State = NORMAL; + trigger_modechanged(); // need to position cursor again (e.g. when on a TAB ) changed_cline_bef_curs(); *************** *** 3811,3816 **** --- 3813,3819 ---- State = INSERT | (State & LANGMAP); else State = replaceState | (State & LANGMAP); + trigger_modechanged(); AppendCharToRedobuff(K_INS); showmode(); #ifdef CURSOR_SHAPE *** ../vim-8.2.3429/src/ex_docmd.c 2021-09-08 14:29:43.117509762 +0200 --- src/ex_docmd.c 2021-09-12 13:30:35.728067940 +0200 *************** *** 485,490 **** --- 485,491 ---- else exmode_active = EXMODE_NORMAL; State = NORMAL; + trigger_modechanged(); // When using ":global /pat/ visual" and then "Q" we return to continue // the :global command. *** ../vim-8.2.3429/src/ex_getln.c 2021-07-27 22:00:39.745712396 +0200 --- src/ex_getln.c 2021-09-12 13:30:35.728067940 +0200 *************** *** 1696,1701 **** --- 1696,1705 ---- // Trigger CmdlineEnter autocommands. cmdline_type = firstc == NUL ? '-' : firstc; trigger_cmd_autocmd(cmdline_type, EVENT_CMDLINEENTER); + #ifdef FEAT_EVAL + if (!debug_mode) + trigger_modechanged(); + #endif init_history(); hiscnt = get_hislen(); // set hiscnt to impossible history value *************** *** 2461,2466 **** --- 2465,2476 ---- trigger_cmd_autocmd(cmdline_type, EVENT_CMDLINELEAVE); State = save_State; + + #ifdef FEAT_EVAL + if (!debug_mode) + trigger_modechanged(); + #endif + #ifdef HAVE_INPUT_METHOD if (b_im_ptr != NULL && *b_im_ptr != B_IMODE_LMAP) im_save_status(b_im_ptr); *** ../vim-8.2.3429/src/globals.h 2021-08-28 14:42:20.044971678 +0200 --- src/globals.h 2021-09-12 13:30:35.728067940 +0200 *************** *** 1256,1261 **** --- 1256,1264 ---- // :bufdo is executing EXTERN int need_start_insertmode INIT(= FALSE); // start insert mode soon + #if defined(FEAT_EVAL) || defined(PROTO) + EXTERN char_u last_mode[MODE_MAX_LENGTH] INIT(= "n"); // for ModeChanged event + #endif 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 *** ../vim-8.2.3429/src/misc1.c 2021-07-28 16:51:49.857364325 +0200 --- src/misc1.c 2021-09-12 13:32:56.219902210 +0200 *************** *** 630,636 **** void f_mode(typval_T *argvars, typval_T *rettv) { ! char_u buf[4]; if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL) return; --- 630,636 ---- void f_mode(typval_T *argvars, typval_T *rettv) { ! char_u buf[MODE_MAX_LENGTH]; if (in_vim9script() && check_for_opt_bool_arg(argvars, 0) == FAIL) return; *************** *** 2643,2645 **** --- 2643,2684 ---- // "://" or ":\\" must follow return path_is_url(p); } + + /* + * Fires a ModeChanged autocmd + */ + void + trigger_modechanged() + { + #if defined(FEAT_EVAL) || defined(PROTO) + dict_T *v_event; + typval_T rettv; + typval_T tv; + char_u *pat_pre; + char_u *pat; + + if (!has_modechanged()) + return; + + v_event = get_vim_var_dict(VV_EVENT); + + tv.v_type = VAR_UNKNOWN; + f_mode(&tv, &rettv); + (void)dict_add_string(v_event, "new_mode", rettv.vval.v_string); + (void)dict_add_string(v_event, "old_mode", last_mode); + dict_set_items_ro(v_event); + + // concatenate modes in format "old_mode:new_mode" + pat_pre = concat_str(last_mode, (char_u*)":"); + pat = concat_str(pat_pre, rettv.vval.v_string); + vim_free(pat_pre); + + apply_autocmds(EVENT_MODECHANGED, pat, NULL, FALSE, curbuf); + STRCPY(last_mode, rettv.vval.v_string); + + vim_free(rettv.vval.v_string); + vim_free(pat); + dict_free_contents(v_event); + hash_init(&v_event->dv_hashtab); + #endif + } *** ../vim-8.2.3429/src/normal.c 2021-09-11 21:14:16.830577302 +0200 --- src/normal.c 2021-09-12 13:30:35.732067934 +0200 *************** *** 1386,1391 **** --- 1386,1392 ---- #endif VIsual_active = FALSE; + trigger_modechanged(); setmouse(); mouse_dragging = 0; *************** *** 5642,5647 **** --- 5643,5649 ---- { // or char/line mode VIsual_mode = cap->cmdchar; showmode(); + trigger_modechanged(); } redraw_curbuf_later(INVERTED); // update the inversion } *************** *** 5757,5762 **** --- 5759,5765 ---- 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. *** ../vim-8.2.3429/src/proto/autocmd.pro 2021-08-01 14:52:05.554645412 +0200 --- src/proto/autocmd.pro 2021-09-12 13:30:35.732067934 +0200 *************** *** 25,30 **** --- 25,31 ---- int has_cmdundefined(void); int has_textyankpost(void); int has_completechanged(void); + int has_modechanged(void); void block_autocmds(void); void unblock_autocmds(void); int is_autocmd_blocked(void); *** ../vim-8.2.3429/src/proto/misc1.pro 2021-07-10 21:28:55.327050110 +0200 --- src/proto/misc1.pro 2021-09-12 13:30:35.732067934 +0200 *************** *** 47,50 **** --- 47,51 ---- char_u *get_isolated_shell_name(void); int path_is_url(char_u *p); int path_with_url(char_u *fname); + void trigger_modechanged(); /* vim: set ft=c : */ *** ../vim-8.2.3429/src/testdir/test_edit.vim 2021-09-11 21:14:16.830577302 +0200 --- src/testdir/test_edit.vim 2021-09-12 13:36:30.671648994 +0200 *************** *** 1907,1910 **** --- 1907,1944 ---- 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\vV\", 'tnix') + call assert_equal(2, g:count) + + au ModeChanged V:v :call DoIt() + call feedkeys("Vv\", '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-8.2.3429/src/vim.h 2021-09-02 18:49:02.748932320 +0200 --- src/vim.h 2021-09-12 13:30:35.732067934 +0200 *************** *** 688,693 **** --- 688,695 ---- #define TERMINAL 0x2000 // Terminal mode #define MODE_ALL 0xffff + #define MODE_MAX_LENGTH 4 // max mode length returned in mode() + // all mode bits used for mapping #define MAP_ALL_MODES (0x3f | SELECTMODE | TERMINAL) *************** *** 1317,1322 **** --- 1319,1325 ---- EVENT_INSERTLEAVEPRE, // just before leaving Insert mode EVENT_INSERTLEAVE, // just after leaving Insert mode EVENT_MENUPOPUP, // just before popup menu is displayed + EVENT_MODECHANGED, // after changing the mode EVENT_OPTIONSET, // option was set EVENT_QUICKFIXCMDPOST, // after :make, :grep etc. EVENT_QUICKFIXCMDPRE, // before :make, :grep etc. *** ../vim-8.2.3429/src/version.c 2021-09-11 23:07:39.796286455 +0200 --- src/version.c 2021-09-12 13:32:33.235929337 +0200 *************** *** 757,758 **** --- 757,760 ---- { /* Add new patch number below this line */ + /**/ + 3430, /**/ -- Far back in the mists of ancient time, in the great and glorious days of the former Galactic Empire, life was wild, rich and largely tax free. Mighty starships plied their way between exotic suns, seeking adventure and reward among the furthest reaches of Galactic space. In those days, spirits were brave, the stakes were high, men were real men, women were real women and small furry creatures from Alpha Centauri were real small furry creatures from Alpha Centauri. And all dared to brave unknown terrors, to do mighty deeds, to boldly split infinitives that no man had split before -- and thus was the Empire forged. -- Douglas Adams, "The Hitchhiker's Guide to the Galaxy" /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///