patch 9.2.0470: No way to hook into put commands

Commit: 
https://github.com/vim/vim/commit/e0781bd5bfd1acc2c4d5d01ec60fecafc99ee3e2
Author: Foxe Chen <[email protected]>
Date:   Sun May 10 19:12:22 2026 +0000

    patch 9.2.0470: No way to hook into put commands
    
    Problem:  No way to hook into put commands
              (yochem)
    Solution: Introduce TextPutPre and TextPutPost autocommands
              (Foxe Chen).
    
    fixes:  #18701
    closes: #20144
    
    Signed-off-by: Foxe Chen <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/doc/autocmd.txt b/runtime/doc/autocmd.txt
index eba6e4354..62cd761e1 100644
--- a/runtime/doc/autocmd.txt
+++ b/runtime/doc/autocmd.txt
@@ -1,4 +1,4 @@
-*autocmd.txt*  For Vim version 9.2.  Last change: 2026 Feb 25
+*autocmd.txt*  For Vim version 9.2.  Last change: 2026 May 10
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -421,6 +421,8 @@ Name                        triggered by ~
 |TextChangedP|         after a change was made to the text in Insert mode
                        when popup menu visible
 |TextChangedT|         after a change was made to the text in Terminal mode
+|TextPutPost|          after text has been put
+|TextPutPre|           before text is put
 |TextYankPost|         after text has been yanked or deleted
 
 |SafeState|            nothing pending, going to wait for the user to type a
@@ -1359,6 +1361,45 @@ TextChangedP                     After a change was made 
to the text in the
 TextChangedT                   After a change was made to the text in the
                                current buffer in Terminal mode.
                                Otherwise the same as TextChanged.
+                                                       *TextPutPost*
+TextPutPost                    After text has been put in the current buffer.
+                               The following values in |v:event| are mostly
+                               the same as |TextYankPost|:
+                                  operator     The operation performed,
+                                               either 'p' or 'P'.
+                                  regcontents  Text that was put. For
+                                               |quote_=|, this is the result
+                                               of the expression.
+                                  regname      Name of the register or empty
+                                               string for the unnamed
+                                               register.
+                                  regtype      Type of the register, see
+                                               |getregtype()|.
+                                  visual       True if the operation is
+                                               performed in |Visual| mode.
+                               Not triggered when |quote_| is used nor when
+                               called recursively.
+                               It is not allowed to change the buffer text,
+                               see |textlock|.
+                               Note that for the |quote_.| register, since
+                               the last inserted text is buffered into the
+                               input buffer (buffer isn't modified directly),
+                               this autocommand is called directly after
+                               |TextPutPre|.
+                               {only when compiled with the +eval feature}
+                                                       *TextPutPre*
+TextPutPre                     Before text has been put in the current buffer.
+                               Same values as |TextPutPost| in |v:event|.  It
+                               is valid to call |setreg()| in this
+                               autocommand, allowing you to process and
+                               modify the text in "regcontents" before it is
+                               put.  However this does not apply to |quote_#|,
+                               |quote_=|, |quote_%|, |quote_:|, |quote_/| or 
|quote_.|.
+                               Not triggered when |quote_| is used nor when
+                               called recursively.
+                               It is not allowed to change the buffer text,
+                               see |textlock|.
+                               {only when compiled with the +eval feature}
                                                        *TextYankPost*
 TextYankPost                   After text has been yanked or deleted in the
                                current buffer.  The following values of
diff --git a/runtime/doc/tags b/runtime/doc/tags
index 8eef8cf28..d5a8a1b30 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -5969,6 +5969,8 @@ TextChanged       autocmd.txt     /*TextChanged*
 TextChangedI   autocmd.txt     /*TextChangedI*
 TextChangedP   autocmd.txt     /*TextChangedP*
 TextChangedT   autocmd.txt     /*TextChangedT*
+TextPutPost    autocmd.txt     /*TextPutPost*
+TextPutPre     autocmd.txt     /*TextPutPre*
 TextYankPost   autocmd.txt     /*TextYankPost*
 Transact-SQL   ft_sql.txt      /*Transact-SQL*
 Tuple  eval.txt        /*Tuple*
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index f4cdc702b..45a25b08b 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -52669,6 +52669,8 @@ Functions: ~
 Autocommands: ~
 
 |SessionLoadPre|       before loading a |Session| file
+|TextPutPost|          after putting text
+|TextPutPre|           before putting text
 
 Options: ~
 
diff --git a/runtime/syntax/vim.vim b/runtime/syntax/vim.vim
index 698e551a7..1cf3b9940 100644
--- a/runtime/syntax/vim.vim
+++ b/runtime/syntax/vim.vim
@@ -2,7 +2,7 @@
 " Language:       Vim script
 " Maintainer:     Hirohito Higashi <h.east.727 ATMARK gmail.com>
 "         Doug Kearns <[email protected]>
-" Last Change:    2026 May 08
+" Last Change:    2026 May 10
 " Former Maintainer: Charles E. Campbell
 
 " DO NOT CHANGE DIRECTLY.
@@ -134,8 +134,8 @@ syn keyword vimErrSetting contained invakm invaltkeymap 
invanti invantialias inv
 syn case ignore
 " GEN_SYN_VIM: vimAutoEvent, START_STR='syn keyword vimAutoEvent contained', 
END_STR='skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern'
 syn keyword vimAutoEvent contained BufAdd BufCreate BufDelete BufEnter 
BufFilePost BufFilePre BufHidden BufLeave BufNew BufNewFile BufRead BufReadCmd 
BufReadPost BufReadPre BufUnload BufWinEnter BufWinLeave BufWipeout BufWrite 
BufWriteCmd BufWritePost BufWritePre CmdlineChanged CmdlineEnter CmdlineLeave 
CmdlineLeavePre CmdUndefined CmdwinEnter CmdwinLeave ColorScheme ColorSchemePre 
CompleteChanged CompleteDone CompleteDonePre CursorHold CursorHoldI CursorMoved 
CursorMovedC CursorMovedI DiffUpdated DirChanged DirChangedPre EncodingChanged 
ExitPre FileAppendCmd FileAppendPost FileAppendPre FileChangedRO 
FileChangedShell FileChangedShellPost FileEncoding FileReadCmd FileReadPost 
FileReadPre FileType FileWriteCmd FileWritePost FileWritePre FilterReadPost 
FilterReadPre skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern
-syn keyword vimAutoEvent contained FilterWritePost FilterWritePre FocusGained 
FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre 
InsertEnter InsertLeave InsertLeavePre KeyInputPre MenuPopup ModeChanged 
OptionSet QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SafeState 
SafeStateAgain SessionLoadPost SessionLoadPre SessionWritePost ShellCmdPost 
ShellFilterPost SigUSR1 SourceCmd SourcePost SourcePre SpellFileMissing 
StdinReadPost StdinReadPre SwapExists Syntax TabClosed TabClosedPre TabEnter 
TabLeave TabNew TermChanged TerminalOpen TerminalWinOpen TermResponse 
TermResponseAll TextChanged TextChangedI TextChangedP TextChangedT TextYankPost 
VimEnter VimLeave VimLeavePre VimResized VimResume VimSuspend WinClosed 
WinEnter WinLeave WinNew WinNewPre skipwhite 
nextgroup=vimAutoEventSep,@vimAutocmdPattern
-syn keyword vimAutoEvent contained WinResized WinScrolled skipwhite 
nextgroup=vimAutoEventSep,@vimAutocmdPattern
+syn keyword vimAutoEvent contained FilterWritePost FilterWritePre FocusGained 
FocusLost FuncUndefined GUIEnter GUIFailed InsertChange InsertCharPre 
InsertEnter InsertLeave InsertLeavePre KeyInputPre MenuPopup ModeChanged 
OptionSet QuickFixCmdPost QuickFixCmdPre QuitPre RemoteReply SafeState 
SafeStateAgain SessionLoadPost SessionLoadPre SessionWritePost ShellCmdPost 
ShellFilterPost SigUSR1 SourceCmd SourcePost SourcePre SpellFileMissing 
StdinReadPost StdinReadPre SwapExists Syntax TabClosed TabClosedPre TabEnter 
TabLeave TabNew TermChanged TerminalOpen TerminalWinOpen TermResponse 
TermResponseAll TextChanged TextChangedI TextChangedP TextChangedT TextPutPost 
TextPutPre TextYankPost VimEnter VimLeave VimLeavePre VimResized VimResume 
VimSuspend WinClosed WinEnter skipwhite 
nextgroup=vimAutoEventSep,@vimAutocmdPattern
+syn keyword vimAutoEvent contained WinLeave WinNew WinNewPre WinResized 
WinScrolled skipwhite nextgroup=vimAutoEventSep,@vimAutocmdPattern
 
 syn keyword    vimAutoEvent    contained       User    skipwhite 
nextgroup=vimUserAutoEvent
 syn match      vimUserAutoEvent        contained       "\<\h\w*\>"     
skipwhite nextgroup=vimUserAutoEventSep,vimAutocmdMod,vimAutocmdBlock
diff --git a/src/autocmd.c b/src/autocmd.c
index 78965529d..3b16a6b5b 100644
--- a/src/autocmd.c
+++ b/src/autocmd.c
@@ -195,6 +195,8 @@ static keyvalue_T event_tab[NUM_EVENTS] = {
     KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDI, "TextChangedI"),
     KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDP, "TextChangedP"),
     KEYVALUE_ENTRY(-EVENT_TEXTCHANGEDT, "TextChangedT"),
+    KEYVALUE_ENTRY(-EVENT_TEXTPUTPOST, "TextPutPost"),
+    KEYVALUE_ENTRY(-EVENT_TEXTPUTPRE, "TextPutPre"),
     KEYVALUE_ENTRY(-EVENT_TEXTYANKPOST, "TextYankPost"),
     KEYVALUE_ENTRY(EVENT_USER, "User"),
     KEYVALUE_ENTRY(EVENT_VIMENTER, "VimEnter"),
@@ -2023,6 +2025,25 @@ has_cmdundefined(void)
 }
 
 #if defined(FEAT_EVAL)
+
+/*
+ * Return TRUE when there is a TextPutPost autocommand defined.
+ */
+    int
+has_textputpost(void)
+{
+    return (first_autopat[(int)EVENT_TEXTPUTPOST] != NULL);
+}
+
+/*
+ * Return TRUE when there is a TextPutPre autocommand defined.
+ */
+    int
+has_textputpre(void)
+{
+    return (first_autopat[(int)EVENT_TEXTPUTPRE] != NULL);
+}
+
 /*
  * Return TRUE when there is a TextYankPost autocommand defined.
  */
diff --git a/src/getchar.c b/src/getchar.c
index 1fe155dcd..2e3079a1e 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -714,6 +714,11 @@ stuffRedoReadbuff(char_u *s)
     void
 stuffReadbuffLen(char_u *s, long len)
 {
+#ifdef FEAT_EVAL
+    if (add_last_insert == 1) // Only add if this is the first call, for
+                             // recursive calls, ignore.
+       ga_concat_len(&last_insert_ga, s, (size_t)len);
+#endif
     add_buff(&readbuf1, s, len);
 }
 
diff --git a/src/globals.h b/src/globals.h
index 9a6c28593..2fdc94464 100644
--- a/src/globals.h
+++ b/src/globals.h
@@ -2165,3 +2165,11 @@ EXTERN bool inside_redraw_on_start_cb INIT(= false);
 
 // If greater than zero, then silence the W23/W24 warning.
 EXTERN int silence_w23_w24_msg INIT( = 0);
+
+#ifdef FEAT_EVAL
+// Used by TextPutPost/TextPutPre autocommands for the '.' register. If
+// "add_last_insert" is == 1, then "stuff_inserted" will add the last inserted
+// text to "last_insert_ga".
+EXTERN garray_T last_insert_ga INIT5(0, 0, 1, 64, NULL);
+EXTERN int     add_last_insert INIT(= 0);
+#endif
diff --git a/src/proto/autocmd.pro b/src/proto/autocmd.pro
index ce1f75910..abff7fd41 100644
--- a/src/proto/autocmd.pro
+++ b/src/proto/autocmd.pro
@@ -29,6 +29,8 @@ int has_textchangedP(void);
 int has_insertcharpre(void);
 int has_keyinputpre(void);
 int has_cmdundefined(void);
+int has_textputpost(void);
+int has_textputpre(void);
 int has_textyankpost(void);
 int has_completechanged(void);
 int has_modechanged(void);
diff --git a/src/register.c b/src/register.c
index 9f0e06b54..44e6aba6f 100644
--- a/src/register.c
+++ b/src/register.c
@@ -1081,6 +1081,36 @@ shift_delete_registers(void)
 }
 
 #if defined(FEAT_EVAL)
+    static void
+add_regtype_to_dict(int regname, dict_T *dict, char_u *buf, int bufsize)
+{
+    size_t  buflen;
+    long    reglen;
+    // Register type
+    switch (get_reg_type(regname, &reglen))
+    {
+       case MLINE:
+           buf[0] = 'V';
+           buf[1] = NUL;
+           buflen = 1;
+           break;
+       case MCHAR:
+           buf[0] = 'v';
+           buf[1] = NUL;
+           buflen = 1;
+           break;
+       case MBLOCK:
+           buflen = vim_snprintf_safelen((char *)buf, bufsize,
+               "%c%ld", Ctrl_V, reglen + 1);
+           break;
+       default:
+           buf[0] = NUL;
+           buflen = 0;
+           break;
+    }
+    (void)dict_add_string_len(dict, "regtype", buf, (int)buflen);
+}
+
     void
 yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
 {
@@ -1090,7 +1120,6 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
     int                    n;
     char_u         buf[NUMBUFLEN + 2];
     size_t         buflen;
-    long           reglen = 0;
     save_v_event_T  save_v_event;
 
     if (recursive)
@@ -1124,29 +1153,7 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
     buflen = (buf[0] == NUL) ? 0 : (buf[1] == NUL) ? 1 : 2;
     (void)dict_add_string_len(v_event, "operator", buf, (int)buflen);
 
-    // register type
-    switch (get_reg_type(oap->regname, &reglen))
-    {
-       case MLINE:
-           buf[0] = 'V';
-           buf[1] = NUL;
-           buflen = 1;
-           break;
-       case MCHAR:
-           buf[0] = 'v';
-           buf[1] = NUL;
-           buflen = 1;
-           break;
-       case MBLOCK:
-           buflen = vim_snprintf_safelen((char *)buf, sizeof(buf),
-               "%c%ld", Ctrl_V, reglen + 1);
-           break;
-       default:
-           buf[0] = NUL;
-           buflen = 0;
-           break;
-    }
-    (void)dict_add_string_len(v_event, "regtype", buf, (int)buflen);
+    add_regtype_to_dict(oap->regname, v_event, buf, sizeof(buf));
 
     // selection type - visual or not
     (void)dict_add_bool(v_event, "visual", oap->is_VIsual);
@@ -1163,6 +1170,85 @@ yank_do_autocmd(oparg_T *oap, yankreg_T *reg)
     // Empty the dictionary, v:event is still valid
     restore_v_event(v_event, &save_v_event);
 }
+
+    static void
+put_do_autocmd(
+       int         regname,
+       yankreg_T   *reg,       // May be NULL, if special register
+       string_T    *insert,    // Not NULL if special register, except '.'
+       bool        post,       // If Post or Pre
+       int         dir)        // BACKWARD for 'P', FORWARD for 'p'
+{
+    static bool            recursive = false;
+    dict_T         *v_event;
+    list_T         *list;
+    int                    n;
+    char_u         buf[NUMBUFLEN + 2];
+    size_t         buflen;
+    save_v_event_T  save_v_event;
+
+    if (recursive || regname == '_')
+       return;
+
+    v_event = get_v_event(&save_v_event);
+
+    list = list_alloc();
+    if (list == NULL)
+       return;
+
+    if (regname == '.')
+    {
+       if (last_insert_ga.ga_data != NULL)
+           // Get the last inserted text to place in "regcontents"
+           list_append_string(list, last_insert_ga.ga_data,
+                   (int)last_insert_ga.ga_len);
+    }
+    else if (insert != NULL)
+    {
+       list_append_string(list, insert->string, (int)insert->length);
+    }
+    else
+    {
+       assert(reg != NULL);
+       for (n = 0; n < reg->y_size; n++)
+           list_append_string(list, reg->y_array[n].string,
+                   (int)reg->y_array[n].length);
+    }
+
+    list->lv_lock = VAR_FIXED;
+    (void)dict_add_list(v_event, "regcontents", list);
+
+    // register name or empty string for unnamed operation
+    buf[0] = (char_u)regname;
+    buf[1] = NUL;
+    buflen = (buf[0] == NUL) ? 0 : 1;
+    (void)dict_add_string_len(v_event, "regname", buf, (int)buflen);
+
+    // kind of operation (P, p)
+    buf[0] = dir == BACKWARD ? 'P' : 'p';
+    buf[1] = NUL;
+    buflen = 1;
+    (void)dict_add_string_len(v_event, "operator", buf, (int)buflen);
+
+    add_regtype_to_dict(regname, v_event, buf, sizeof(buf));
+
+    (void)dict_add_bool(v_event, "visual", VIsual_active);
+
+    // Lock the dictionary and its keys
+    dict_set_items_ro(v_event);
+
+    recursive = true;
+    textlock++;
+    if (post)
+       apply_autocmds(EVENT_TEXTPUTPOST, NULL, NULL, FALSE, curbuf);
+    else
+       apply_autocmds(EVENT_TEXTPUTPRE, NULL, NULL, FALSE, curbuf);
+    textlock--;
+    recursive = false;
+
+    // Empty the dictionary, v:event is still valid
+    restore_v_event(v_event, &save_v_event);
+}
 #endif
 
 /*
@@ -1648,8 +1734,28 @@ do_put(
     {
        if (VIsual_active)
            stuffcharReadbuff(VIsual_mode);
+
+#ifdef FEAT_EVAL
+       if (has_textputpre() || has_textputpost())
+           add_last_insert++;
+#endif
+
        (void)stuff_inserted((dir == FORWARD ? (count == -1 ? 'o' : 'a') :
                                    (count == -1 ? 'O' : 'i')), count, FALSE);
+
+#ifdef FEAT_EVAL
+       // Since the text is not inserted into the buffer immediately, just call
+       // TextPutPost after TextPutPre.
+       if (has_textputpre())
+           put_do_autocmd('.', NULL, NULL, false, dir);
+
+       if (has_textputpost())
+           put_do_autocmd('.', NULL, NULL, true, dir);
+
+       if (--add_last_insert == 0)
+           ga_clear(&last_insert_ga);
+#endif
+
        // Putting the text is done later, so can't really move the cursor to
        // the next character.  Use "l" to simulate it.
        if ((flags & PUT_CURSEND) && gchar_cursor() != NUL)
@@ -1733,9 +1839,22 @@ do_put(
            y_size = 1;         // use fake one-line yank register
            y_array = &insert_string;
        }
+#ifdef FEAT_EVAL
+       if (has_textputpre())
+           put_do_autocmd(regname, NULL, &insert_string, false, dir);
+#endif
     }
     else
     {
+#ifdef FEAT_EVAL
+       if (has_textputpre())
+       {
+           // Make sure to call this before we set the variables, as setreg()
+           // may be called and invalidate them.
+           get_yank_register(regname, FALSE);
+           put_do_autocmd(regname, y_current, NULL, false, dir);
+       }
+#endif
        get_yank_register(regname, FALSE);
 
        y_type = y_current->y_type;
@@ -2401,6 +2520,17 @@ end:
        curbuf->b_op_start = orig_start;
        curbuf->b_op_end = orig_end;
     }
+
+#ifdef FEAT_EVAL
+    if (has_textputpost())
+    {
+       if (insert_string.string == NULL)
+           put_do_autocmd(regname, y_current, NULL, true, dir);
+       else
+           put_do_autocmd(regname, NULL, &insert_string, true, dir);
+    }
+#endif
+
     if (allocated)
        vim_free(insert_string.string);
     if (regname == '=')
diff --git a/src/testdir/test_autocmd.vim b/src/testdir/test_autocmd.vim
index 592dd4715..2a3616893 100644
--- a/src/testdir/test_autocmd.vim
+++ b/src/testdir/test_autocmd.vim
@@ -5967,4 +5967,129 @@ func Test_autocmd_add_secure()
   call assert_fails('sandbox call autocmd_delete([{"event": "BufRead"}])', 
'E48:')
 endfunc
 
+func Test_TextPutX()
+  enew!
+
+  let g:pre_event = []
+  let g:post_event = []
+  au TextPutPre * let g:pre_event = copy(v:event)
+  au TextPutPost * let g:post_event = copy(v:event)
+
+  call setreg('a', ['foo'], 'v')
+  norm "ap
+  call assert_equal(
+        \ #{regcontents: ['foo'], regname: 'a', operator: 'p',
+        \ visual: v:false, regtype: 'v'},
+        \ g:pre_event)
+  call assert_equal(g:pre_event, g:post_event)
+
+  call setreg('', ['hello'], 'V')
+  norm P
+  call assert_equal(
+        \ #{regcontents: ['hello'], regname: '',  operator: 'P',
+        \ visual: v:false, regtype: 'V'},
+        \ g:pre_event)
+  call assert_equal(g:pre_event, g:post_event)
+
+  call setreg('', ['maybe', 'true'], 'V')
+  norm Vp
+  call assert_equal(
+        \ #{regcontents: ['maybe', 'true'], regname: '',  operator: 'P',
+        \ regtype: 'V', visual: v:true},
+        \ g:pre_event)
+  call assert_equal(g:pre_event, g:post_event)
+  call assert_equal({}, v:event)
+
+  call feedkeys("iinserted text\<CR>below\<Esc>", 'x')
+  norm ".p
+  call assert_equal(
+        \ #{regcontents: ["inserted text
below"], regname: '.',
+        \ operator: 'p', regtype: 'v', visual: v:false},
+        \ g:pre_event)
+  call assert_equal(g:pre_event, g:post_event)
+
+  call feedkeys("\"=201\<CR>p", 'x')
+  call assert_equal(
+        \ #{regcontents: ["201"], regname: '=',
+        \ operator: 'p', regtype: 'v', visual: v:false},
+        \ g:pre_event)
+  call assert_equal(g:pre_event, g:post_event)
+
+  vsplit some.txt
+  wincmd l
+  norm "#p
+  call assert_equal(
+        \ #{regcontents: ["some.txt"], regname: '#',
+        \ operator: 'p', regtype: 'v', visual: v:false},
+        \ g:pre_event)
+  call assert_equal(g:pre_event, g:post_event)
+  wincmd h
+  bw!
+
+  if has('clipboard_working')
+    let @+ = 'clipboard'
+
+    norm "+p
+    call assert_equal(
+          \ #{regcontents: ["clipboard"], regname: '+',
+          \ operator: 'p', regtype: 'v', visual: v:false},
+          \ g:pre_event)
+    call assert_equal(g:pre_event, g:post_event)
+  endif
+
+  %delete " Clear buffer
+
+  au! TextPutPre
+  au! TextPutPost
+  let g:pre_event = []
+  let g:post_event = []
+
+  au TextPutPre * call setreg(v:event['regname'],
+        \ getreg('', 0, v:true) + ['!']) " Unnamed register should be same as 
regname
+
+  call setreg('', ['hello', 'world'])
+  norm p
+  call assert_equal(['', 'hello', 'world', '!'], getline(1, '$'))
+
+  au! TextPutPre
+
+  " Test that special registers cannot be modified
+  %delete
+  au TextPutPre * call setreg('=', '"modified"') | let g:pre_event = 
copy(v:event)
+
+  " Set up the expression register to evaluate to a known value.
+  call feedkeys("\"=\"original\"\<CR>p", 'x')
+
+  " The original value is what got put, not the modified one.
+  call assert_equal(['original'], getline(1, '$'))
+
+  " v:event still reports the original value.
+  call assert_equal(['original'], g:pre_event['regcontents'])
+
+  au! TextPutPre
+  let g:pre_event = []
+
+  " Test that recursive ". register calls have the same contents for post and
+  " pre
+  au TextPutPre * put . | let g:pre_event = copy(v:event)
+  au TextPutPost * let g:post_event = copy(v:event)
+
+  call feedkeys("iinserted\<Esc>", 'x')
+  norm! ".p
+
+  call assert_equal(
+        \ #{regcontents: ["inserted"], regname: '.',
+        \ operator: 'p', regtype: 'v', visual: v:false},
+        \ g:pre_event)
+  call assert_equal(g:pre_event, g:post_event)
+
+  au! TextPutPre
+  au! TextPutPost
+
+  unlet g:post_event
+  unlet g:pre_event
+  bwipe!
+
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 2882be579..5e5f17f85 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    470,
 /**/
     469,
 /**/
diff --git a/src/vim.h b/src/vim.h
index d9a22ef01..25c81f2f8 100644
--- a/src/vim.h
+++ b/src/vim.h
@@ -1484,6 +1484,8 @@ enum auto_event
     EVENT_TEXTCHANGEDI,                // text was modified in Insert mode
     EVENT_TEXTCHANGEDP,                // TextChangedI with popup menu visible
     EVENT_TEXTCHANGEDT,                // text was modified in Terminal mode
+    EVENT_TEXTPUTPOST,         // after some text was put
+    EVENT_TEXTPUTPRE,          // before some text was put
     EVENT_TEXTYANKPOST,                // after some text was yanked
     EVENT_USER,                        // user defined autocommand
     EVENT_VIMENTER,            // after starting Vim

-- 
-- 
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 visit 
https://groups.google.com/d/msgid/vim_dev/E1wM9qp-00Fpdw-E6%40256bit.org.

Raspunde prin e-mail lui