To: vim_dev@googlegroups.com Subject: Patch 8.2.4713 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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 and 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('')) + au WinScrolled * let g:afile = str2nr(expand('')) + END + call writefile(lines, 'Xtest_winscrolled') + let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6}) + + call term_sendkeys(buf, ":echo g:scrolled\") + call WaitForAssert({-> assert_match('^0 ', term_getline(buf, 6))}, 1000) + + " Scroll left/right in Normal mode. + call term_sendkeys(buf, "zlzh:echo g:scrolled\") + call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000) + + " Scroll up/down in Normal mode. + call term_sendkeys(buf, "\\:echo g:scrolled\") + call WaitForAssert({-> assert_match('^4 ', term_getline(buf, 6))}, 1000) + + " Scroll up/down in Insert mode. + call term_sendkeys(buf, "Mi\\\i\\\") + call term_sendkeys(buf, ":echo g:scrolled\") + 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\") + 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\") + call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000) + + " Ensure the expansion of and matches the window ID. + call term_sendkeys(buf, ":echo g:amatch == win_id && g:afile == win_id\") + 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 " and 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 -- 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 ///