Patch 8.2.4713
Problem:    Plugins cannot track text scrolling.
Solution:   Add the WinScrolled event. (closes #10102)
Files:      runtime/doc/autocmd.txt, src/autocmd.c, src/proto/autocmd.pro,
            src/edit.c, src/gui.c, src/main.c, src/structs.h, src/vim.h,
            src/window.c, src/proto/window.pro, src/testdir/test_autocmd.vim


*** ../vim-8.2.4712/runtime/doc/autocmd.txt     2022-02-09 12:58:16.498258787 
+0000
--- runtime/doc/autocmd.txt     2022-04-08 15:06:41.462100751 +0100
***************
*** 401,406 ****
--- 402,409 ----
  |User|                        to be used in combination with ":doautocmd"
  |SigUSR1|             after the SIGUSR1 signal has been detected
  
+ |WinScrolled|         after scrolling or resizing a window
+ 
  
  The alphabetical list of autocommand events:          *autocmd-events-abc*
  
***************
*** 689,696 ****
  CursorMoved                   After the cursor was moved in Normal or Visual
                                mode.  Also when the text of the cursor line
                                has been changed, e.g., with "x", "rx" or "p".
!                               Not triggered when there is typeahead or when
!                               an operator is pending.
                                For an example see |match-parens|.
                                Note: This can not be skipped with
                                `:noautocmd`.
--- 692,702 ----
  CursorMoved                   After the cursor was moved in Normal or Visual
                                mode.  Also when the text of the cursor line
                                has been changed, e.g., with "x", "rx" or "p".
!                               Not always triggered when there is typeahead,
!                               while executing commands in a script file,
!                               when an operator is pending or when moving to
!                               another window while remaining at the same
!                               cursor position.
                                For an example see |match-parens|.
                                Note: This can not be skipped with
                                `:noautocmd`.
***************
*** 1312,1328 ****
                                the first window, when Vim has just started.
                                Before a WinEnter event.
  
  ==============================================================================
! 6. Patterns                                   *autocmd-patterns* *{pat}*
  
! The {pat} argument can be a comma separated list.  This works as if the
! command was given with each pattern separately.  Thus this command: >
        :autocmd BufRead *.txt,*.info set et
  Is equivalent to: >
        :autocmd BufRead *.txt set et
        :autocmd BufRead *.info set et
  
! The file pattern {pat} is tested for a match against the file name in one of
  two ways:
  1. When there is no '/' in the pattern, Vim checks for a match against only
     the tail part of the file name (without its leading directory path).
--- 1325,1354 ----
                                the first window, when Vim has just started.
                                Before a WinEnter event.
  
+                                                       *WinScrolled*
+ WinScrolled                   After scrolling the content of a window or
+                               resizing a window.
+                               The pattern is matched against the
+                               |window-ID|.  Both <amatch> and <afile> are
+                               set to the |window-ID|.
+                               Non-recursive (the event cannot trigger
+                               itself).  However, if the command causes the
+                               window to scroll or change size another
+                               WinScrolled event will be triggered later.
+                               Does not trigger when the command is added,
+                               only after the first scroll or resize.
+ 
  ==============================================================================
! 6. Patterns                                   *autocmd-patterns* *{aupat}*
  
! The {aupat} argument of `:autocmd` can be a comma-separated list.  This works 
as
! if the command was given with each pattern separately.  Thus this command: >
        :autocmd BufRead *.txt,*.info set et
  Is equivalent to: >
        :autocmd BufRead *.txt set et
        :autocmd BufRead *.info set et
  
! The file pattern {aupat} is tested for a match against the file name in one of
  two ways:
  1. When there is no '/' in the pattern, Vim checks for a match against only
     the tail part of the file name (without its leading directory path).
*** ../vim-8.2.4712/src/autocmd.c       2022-03-04 20:10:33.400917886 +0000
--- src/autocmd.c       2022-04-08 14:56:27.497386619 +0100
***************
*** 190,195 ****
--- 190,196 ----
      {"WinClosed",     EVENT_WINCLOSED},
      {"WinEnter",      EVENT_WINENTER},
      {"WinLeave",      EVENT_WINLEAVE},
+     {"WinScrolled",   EVENT_WINSCROLLED},
      {"VimResized",    EVENT_VIMRESIZED},
      {"TextYankPost",  EVENT_TEXTYANKPOST},
      {"VimSuspend",    EVENT_VIMSUSPEND},
***************
*** 1251,1256 ****
--- 1252,1266 ----
                    vim_free(rettv.vval.v_string);
                }
  #endif
+               // Initialize the fields checked by the WinScrolled trigger to
+               // stop it from firing right after the first autocmd is defined.
+               if (event == EVENT_WINSCROLLED && !has_winscrolled())
+               {
+                   curwin->w_last_topline = curwin->w_topline;
+                   curwin->w_last_leftcol = curwin->w_leftcol;
+                   curwin->w_last_width = curwin->w_width;
+                   curwin->w_last_height = curwin->w_height;
+               }
  
                if (is_buflocal)
                {
***************
*** 1783,1788 ****
--- 1793,1807 ----
  }
  
  /*
+  * Return TRUE when there is a WinScrolled autocommand defined.
+  */
+     int
+ has_winscrolled(void)
+ {
+     return (first_autopat[(int)EVENT_WINSCROLLED] != NULL);
+ }
+ 
+ /*
   * Return TRUE when there is a CursorMoved autocommand defined.
   */
      int
***************
*** 2078,2084 ****
                || event == EVENT_DIRCHANGEDPRE
                || event == EVENT_MODECHANGED
                || event == EVENT_USER
!               || event == EVENT_WINCLOSED)
        {
            fname = vim_strsave(fname);
            autocmd_fname_full = TRUE; // don't expand it later
--- 2097,2104 ----
                || event == EVENT_DIRCHANGEDPRE
                || event == EVENT_MODECHANGED
                || event == EVENT_USER
!               || event == EVENT_WINCLOSED
!               || event == EVENT_WINSCROLLED)
        {
            fname = vim_strsave(fname);
            autocmd_fname_full = TRUE; // don't expand it later
*** ../vim-8.2.4712/src/proto/autocmd.pro       2021-09-12 12:39:04.323467415 
+0100
--- src/proto/autocmd.pro       2022-04-08 14:56:27.497386619 +0100
***************
*** 26,31 ****
--- 26,32 ----
  int has_textyankpost(void);
  int has_completechanged(void);
  int has_modechanged(void);
+ int has_winscrolled(void);
  void block_autocmds(void);
  void unblock_autocmds(void);
  int is_autocmd_blocked(void);
*** ../vim-8.2.4712/src/edit.c  2022-04-07 21:00:49.419544901 +0100
--- src/edit.c  2022-04-08 15:07:24.021937827 +0100
***************
*** 1527,1532 ****
--- 1527,1535 ----
                                        (linenr_T)(curwin->w_cursor.lnum + 1));
      }
  
+     if (ready)
+       may_trigger_winscrolled(curwin);
+ 
      // Trigger SafeState if nothing is pending.
      may_trigger_safestate(ready
            && !ins_compl_active()
*** ../vim-8.2.4712/src/gui.c   2022-04-03 18:01:39.655574461 +0100
--- src/gui.c   2022-04-08 15:07:33.217903315 +0100
***************
*** 5237,5242 ****
--- 5237,5245 ----
        last_cursormoved = curwin->w_cursor;
      }
  
+     if (!finish_op)
+       may_trigger_winscrolled(curwin);
+ 
  # ifdef FEAT_CONCEAL
      if (conceal_update_lines
            && (conceal_old_cursor_line != conceal_new_cursor_line
*** ../vim-8.2.4712/src/main.c  2022-04-03 18:01:39.655574461 +0100
--- src/main.c  2022-04-08 15:07:42.445868901 +0100
***************
*** 1336,1341 ****
--- 1336,1349 ----
                curbuf->b_last_changedtick = CHANGEDTICK(curbuf);
            }
  
+           // Ensure curwin->w_topline and curwin->w_leftcol are up to date
+           // before triggering a WinScrolled autocommand.
+           update_topline();
+           validate_cursor();
+ 
+           if (!finish_op)
+               may_trigger_winscrolled(curwin);
+ 
            // If nothing is pending and we are going to wait for the user to
            // type a character, trigger SafeState.
            may_trigger_safestate(!op_pending() && restart_edit == 0);
*** ../vim-8.2.4712/src/structs.h       2022-04-07 13:58:00.923085110 +0100
--- src/structs.h       2022-04-08 15:00:34.067796571 +0100
***************
*** 3510,3515 ****
--- 3510,3521 ----
                                    // window
  #endif
  
+     // four fields that are only used when there is a WinScrolled autocommand
+     linenr_T  w_last_topline;     // last known value for w_topline
+     colnr_T   w_last_leftcol;     // last known value for w_leftcol
+     int               w_last_width;       // last known value for w_width
+     int               w_last_height;      // last known value for w_height
+ 
      /*
       * Layout of the window in the screen.
       * May need to add "msg_scrolled" to "w_winrow" in rare situations.
*** ../vim-8.2.4712/src/vim.h   2022-04-04 16:56:50.772629666 +0100
--- src/vim.h   2022-04-08 14:56:27.497386619 +0100
***************
*** 1386,1391 ****
--- 1386,1392 ----
      EVENT_WINCLOSED,          // after closing a window
      EVENT_VIMSUSPEND,         // before Vim is suspended
      EVENT_VIMRESUME,          // after Vim is resumed
+     EVENT_WINSCROLLED,                // after Vim window was scrolled
  
      NUM_EVENTS                        // MUST be the last one
  };
*** ../vim-8.2.4712/src/window.c        2022-04-07 14:08:26.405867546 +0100
--- src/window.c        2022-04-08 15:10:02.129374032 +0100
***************
*** 2779,2789 ****
      if (recursive)
        return;
      recursive = TRUE;
!     vim_snprintf((char *)winid, sizeof(winid), "%i", win->w_id);
      apply_autocmds(EVENT_WINCLOSED, winid, winid, FALSE, win->w_buffer);
      recursive = FALSE;
  }
  
  /*
   * Close window "win" in tab page "tp", which is not the current tab page.
   * This may be the last window in that tab page and result in closing the tab,
--- 2779,2816 ----
      if (recursive)
        return;
      recursive = TRUE;
!     vim_snprintf((char *)winid, sizeof(winid), "%d", win->w_id);
      apply_autocmds(EVENT_WINCLOSED, winid, winid, FALSE, win->w_buffer);
      recursive = FALSE;
  }
  
+     void
+ may_trigger_winscrolled(win_T *wp)
+ {
+     static int            recursive = FALSE;
+     char_u        winid[NUMBUFLEN];
+ 
+     if (recursive || !has_winscrolled())
+       return;
+ 
+     if (wp->w_last_topline != wp->w_topline
+           || wp->w_last_leftcol != wp->w_leftcol
+           || wp->w_last_width != wp->w_width
+           || wp->w_last_height != wp->w_height)
+     {
+       vim_snprintf((char *)winid, sizeof(winid), "%d", wp->w_id);
+ 
+       recursive = TRUE;
+       apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE, wp->w_buffer);
+       recursive = FALSE;
+ 
+       wp->w_last_topline = wp->w_topline;
+       wp->w_last_leftcol = wp->w_leftcol;
+       wp->w_last_width = wp->w_width;
+       wp->w_last_height = wp->w_height;
+     }
+ }
+ 
  /*
   * Close window "win" in tab page "tp", which is not the current tab page.
   * This may be the last window in that tab page and result in closing the tab,
*** ../vim-8.2.4712/src/proto/window.pro        2022-03-22 18:12:57.529367476 
+0000
--- src/proto/window.pro        2022-04-08 15:08:47.101634062 +0100
***************
*** 13,26 ****
  void win_move_after(win_T *win1, win_T *win2);
  void win_equal(win_T *next_curwin, int current, int dir);
  void entering_window(win_T *win);
  void close_windows(buf_T *buf, int keep_curwin);
  int one_window(void);
  int win_close(win_T *win, int free_buf);
  void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp);
  void win_free_all(void);
  win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp);
  void close_others(int message, int forceit);
- void curwin_init(void);
  int win_alloc_first(void);
  win_T *win_alloc_popup_win(void);
  void win_init_popup_win(win_T *wp, buf_T *buf);
--- 13,27 ----
  void win_move_after(win_T *win1, win_T *win2);
  void win_equal(win_T *next_curwin, int current, int dir);
  void entering_window(win_T *win);
+ void curwin_init(void);
  void close_windows(buf_T *buf, int keep_curwin);
  int one_window(void);
  int win_close(win_T *win, int free_buf);
+ void may_trigger_winscrolled(win_T *wp);
  void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp);
  void win_free_all(void);
  win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp);
  void close_others(int message, int forceit);
  int win_alloc_first(void);
  win_T *win_alloc_popup_win(void);
  void win_init_popup_win(win_T *wp, buf_T *buf);
*** ../vim-8.2.4712/src/testdir/test_autocmd.vim        2022-04-07 
14:08:26.405867546 +0100
--- src/testdir/test_autocmd.vim        2022-04-08 14:56:27.497386619 +0100
***************
*** 3,8 ****
--- 3,9 ----
  source shared.vim
  source check.vim
  source term_util.vim
+ source screendump.vim
  import './vim9.vim' as v9
  
  func s:cleanup_buffers() abort
***************
*** 309,314 ****
--- 310,369 ----
    unlet g:record
  endfunc
  
+ func Test_WinScrolled()
+   CheckRunVimInTerminal
+ 
+   let lines =<< trim END
+       set nowrap scrolloff=0
+         for ii in range(1, 18)
+           call setline(ii, repeat(nr2char(96 + ii), ii * 2))
+         endfor
+         let win_id = win_getid()
+         let g:matched = v:false
+         execute 'au WinScrolled' win_id 'let g:matched = v:true'
+         let g:scrolled = 0
+         au WinScrolled * let g:scrolled += 1
+         au WinScrolled * let g:amatch = str2nr(expand('<amatch>'))
+         au WinScrolled * let g:afile = str2nr(expand('<afile>'))
+   END
+   call writefile(lines, 'Xtest_winscrolled')
+   let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6})
+ 
+   call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+   call WaitForAssert({-> assert_match('^0 ', term_getline(buf, 6))}, 1000)
+ 
+   " Scroll left/right in Normal mode.
+   call term_sendkeys(buf, "zlzh:echo g:scrolled\<CR>")
+   call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000)
+ 
+   " Scroll up/down in Normal mode.
+   call term_sendkeys(buf, "\<c-e>\<c-y>:echo g:scrolled\<CR>")
+   call WaitForAssert({-> assert_match('^4 ', term_getline(buf, 6))}, 1000)
+ 
+   " Scroll up/down in Insert mode.
+   call term_sendkeys(buf, "Mi\<c-x>\<c-e>\<Esc>i\<c-x>\<c-y>\<Esc>")
+   call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+   call WaitForAssert({-> assert_match('^6 ', term_getline(buf, 6))}, 1000)
+ 
+   " Scroll the window horizontally to focus the last letter of the third line
+   " containing only six characters. Moving to the previous and shorter lines
+   " should trigger another autocommand as Vim has to make them visible.
+   call term_sendkeys(buf, "5zl2k")
+   call term_sendkeys(buf, ":echo g:scrolled\<CR>")
+   call WaitForAssert({-> assert_match('^8 ', term_getline(buf, 6))}, 1000)
+ 
+   " Ensure the command was triggered for the specified window ID.
+   call term_sendkeys(buf, ":echo g:matched\<CR>")
+   call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 
1000)
+ 
+   " Ensure the expansion of <amatch> and <afile> matches the window ID.
+   call term_sendkeys(buf, ":echo g:amatch == win_id && g:afile == 
win_id\<CR>")
+   call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 
1000)
+ 
+   call StopVimInTerminal(buf)
+   call delete('Xtest_winscrolled')
+ endfunc
+ 
  func Test_WinClosed()
    " Test that the pattern is matched against the closed window's ID, and both
    " <amatch> and <afile> are set to it.
*** ../vim-8.2.4712/src/version.c       2022-04-08 13:23:13.758731453 +0100
--- src/version.c       2022-04-08 14:58:15.416630122 +0100
***************
*** 748,749 ****
--- 748,751 ----
  {   /* Add new patch number below this line */
+ /**/
+     4713,
  /**/

-- 
(letter from Mark to Mike, about the film's probable certificate)
      I would like to get back to the Censor and agree to lose the shits, take
      the odd Jesus Christ out and lose Oh fuck off, but to retain 'fart in
      your general direction', 'castanets of your testicles' and 'oral sex'
      and ask him for an 'A' rating on that basis.
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// 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/20220408141952.D0B491C04C0%40moolenaar.net.

Raspunde prin e-mail lui