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+=<:>
                  /<start
--- 76,82 ----
  script.  Thus this depends on where the autocmd is defined, not where it is
  triggered.
  
! {cmd} can be a block, like with `:command`, see |:command-repl|.  Example: >
        au BufReadPost *.xml {
                  setlocal matchpairs+=<:>
                  /<start
***************
*** 366,371 ****
--- 366,373 ----
  |InsertCharPre|               when a character was typed in Insert mode, 
before
                        inserting it
  
+ |ModeChanged|         after changing the mode
+ 
  |TextChanged|         after a change was made to the text in Normal mode
  |TextChangedI|                after a change was made to the text in Insert 
mode
                        when popup menu is not visible
***************
*** 922,928 ****
                                        i       Insert
                                        c       Command line
                                        tl      Terminal
!                                                       *OptionSet*
  OptionSet                     After setting an option.  The pattern is
                                matched against the long option name.
                                |<amatch>| 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.
                                |<amatch>| 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\<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-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 -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/202109121140.18CBeTUn847284%40masaka.moolenaar.net.

Raspunde prin e-mail lui