To: vim_dev@googlegroups.com Subject: Patch 9.0.0917 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 9.0.0917 Problem: The WinScrolled autocommand event is not enough. Solution: Add WinResized and provide information about what changed. (closes #11576) Files: runtime/doc/autocmd.txt, runtime/doc/windows.txt, src/autocmd.c, src/proto/autocmd.pro, src/window.c, src/proto/window.pro, src/vim.h, src/edit.c, src/gui.c, src/main.c, src/mouse.c, src/dict.c, src/testdir/test_autocmd.vim *** ../vim-9.0.0916/runtime/doc/autocmd.txt 2022-11-19 21:17:48.841226535 +0000 --- runtime/doc/autocmd.txt 2022-11-22 12:33:47.490253876 +0000 *************** *** 1369,1389 **** Before a WinEnter event. *WinScrolled* ! WinScrolled After scrolling the content of a window or ! resizing a window in the current tab page. ! ! When more than one window scrolled or resized ! only one WinScrolled event is triggered. You ! can use the `winlayout()` and `getwininfo()` ! functions to see what changed. The pattern is matched against the |window-ID| of the first window that scrolled or resized. Both and are set to the |window-ID|. Only starts triggering after startup finished and the first screen redraw was done. Non-recursive: the event will not trigger while executing commands for the WinScrolled --- 1372,1395 ---- Before a WinEnter event. *WinScrolled* ! WinScrolled After any window in the current tab page ! scrolled the text (horizontally or vertically) ! or changed width or height. See ! |win-scrolled-resized|. The pattern is matched against the |window-ID| of the first window that scrolled or resized. Both and are set to the |window-ID|. + |v:event| is set with information about size + and scroll changes. |WinScrolled-event| + Only starts triggering after startup finished and the first screen redraw was done. + Does not trigger when defining the first + WinScrolled or WinResized event, but may + trigger when adding more. Non-recursive: the event will not trigger while executing commands for the WinScrolled *************** *** 1391,1401 **** window to scroll or change size, then another WinScrolled event will be triggered later. ! Does not trigger when the command is added, ! only after the first scroll or resize. ! *E1312* ! It is not allowed to change the window layout ! here (split, close or move windows). ============================================================================== 6. Patterns *autocmd-patterns* *{aupat}* --- 1397,1413 ---- window to scroll or change size, then another WinScrolled event will be triggered later. ! ! *WinResized* ! WinResized After a window in the current tab page changed ! width or height. ! See |win-scrolled-resized|. ! ! |v:event| is set with information about size ! changes. |WinResized-event| ! ! Same behavior as |WinScrolled| for the ! pattern, triggering and recursiveness. ============================================================================== 6. Patterns *autocmd-patterns* *{aupat}* *** ../vim-9.0.0916/runtime/doc/windows.txt 2022-08-31 17:48:05.711547579 +0100 --- runtime/doc/windows.txt 2022-11-22 12:38:40.458362177 +0000 *************** *** 631,636 **** --- 631,684 ---- The minimal height and width of a window is set with 'winminheight' and 'winminwidth'. These are hard values, a window will never become smaller. + + WinScrolled and WinResized autocommands ~ + *win-scrolled-resized* + If you want to get notified of changes in window sizes, the |WinResized| + autocommand event can be used. + If you want to get notified of text in windows scrolling vertically or + horizontally, the |WinScrolled| autocommand event can be used. This will also + trigger in window size changes. + *WinResized-event* + The |WinResized| event is triggered after updating the display, several + windows may have changed size then. A list of the IDs of windows that changed + since last time is provided in the v:event.windows variable, for example: + [1003, 1006] + *WinScrolled-event* + The |WinScrolled| event is triggered after |WinResized|, and also if a window + was scrolled. That can be vertically (the text at the top of the window + changed) or horizontally (when 'wrap' is off or when the first displayed part + of the first line changes). Note that |WinScrolled| will trigger many more + times than |WinResized|, it may slow down editing a bit. + + The information provided by |WinScrolled| is a dictionary for each window that + has changes, using the window ID as the key, and a total count of the changes + with the key "all". Example value for |v:event| (|Vim9| syntax): + { + all: {width: 0, height: 2, leftcol: 0, topline: 1, skipcol: 0}, + 1003: {width: 0, height: -1, leftcol: 0, topline: 0, skipcol: 0}, + 1006: {width: 0, height: 1, leftcol: 0, topline: 1, skipcol: 0}, + } + + Note that the "all" entry has the absolute values of the individual windows + accumulated. + + If you need more information about what changed, or you want to "debounce" the + events (not handle every event to avoid doing too much work), you may want to + use the `winlayout()` and `getwininfo()` functions. + + |WinScrolled| and |WinResized| do not trigger when the first autocommand is + added, only after the first scroll or resize. They may trigger when switching + to another tab page. + + The commands executed are expected to not cause window size or scroll changes. + If this happens anyway, the event will trigger again very soon. In other + words: Just before triggering the event, the current sizes and scroll + positions are stored and used to decide whether there was a change. + *E1312* + It is not allowed to change the window layout here (split, close or move + windows). + ============================================================================== 7. Argument and buffer list commands *buffer-list* *** ../vim-9.0.0916/src/autocmd.c 2022-11-20 12:11:22.920741574 +0000 --- src/autocmd.c 2022-11-21 12:50:12.211329954 +0000 *************** *** 191,196 **** --- 191,197 ---- {"WinClosed", EVENT_WINCLOSED}, {"WinEnter", EVENT_WINENTER}, {"WinLeave", EVENT_WINLEAVE}, + {"WinResized", EVENT_WINRESIZED}, {"WinScrolled", EVENT_WINSCROLLED}, {"VimResized", EVENT_VIMRESIZED}, {"TextYankPost", EVENT_TEXTYANKPOST}, *************** *** 1263,1272 **** if (event == EVENT_MODECHANGED && !has_modechanged()) get_mode(last_mode); #endif ! // Initialize the fields checked by the WinScrolled trigger to ! // prevent it from firing right after the first autocmd is ! // defined. ! if (event == EVENT_WINSCROLLED && !has_winscrolled()) { tabpage_T *save_curtab = curtab; tabpage_T *tp; --- 1264,1274 ---- if (event == EVENT_MODECHANGED && !has_modechanged()) get_mode(last_mode); #endif ! // Initialize the fields checked by the WinScrolled and ! // WinResized trigger to prevent them from firing right after ! // the first autocmd is defined. ! if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED) ! && !(has_winscrolled() || has_winresized())) { tabpage_T *save_curtab = curtab; tabpage_T *tp; *************** *** 1811,1816 **** --- 1813,1827 ---- } /* + * Return TRUE when there is a WinResized autocommand defined. + */ + int + has_winresized(void) + { + return (first_autopat[(int)EVENT_WINRESIZED] != NULL); + } + + /* * Return TRUE when there is a WinScrolled autocommand defined. */ int *************** *** 2117,2122 **** --- 2128,2134 ---- || event == EVENT_MENUPOPUP || event == EVENT_USER || event == EVENT_WINCLOSED + || event == EVENT_WINRESIZED || event == EVENT_WINSCROLLED) { fname = vim_strsave(fname); *** ../vim-9.0.0916/src/proto/autocmd.pro 2022-06-27 23:14:56.000000000 +0100 --- src/proto/autocmd.pro 2022-11-22 12:25:27.714229336 +0000 *************** *** 16,21 **** --- 16,22 ---- int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap); int apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, int *retval); int trigger_cursorhold(void); + int has_winresized(void); int has_winscrolled(void); int has_cursormoved(void); int has_cursormovedI(void); *** ../vim-9.0.0916/src/window.c 2022-11-20 12:11:22.924741577 +0000 --- src/window.c 2022-11-21 23:27:46.994253281 +0000 *************** *** 2873,2918 **** } /* ! * Trigger WinScrolled if any window scrolled or changed size. */ void ! may_trigger_winscrolled(void) { static int recursive = FALSE; if (recursive ! || !has_winscrolled() || !did_initial_scroll_size_snapshot) return; ! win_T *wp; ! FOR_ALL_WINDOWS(wp) ! if (wp->w_last_topline != wp->w_topline ! || wp->w_last_leftcol != wp->w_leftcol ! || wp->w_last_skipcol != wp->w_skipcol ! || wp->w_last_width != wp->w_width ! || wp->w_last_height != wp->w_height) ! { ! // WinScrolled is triggered only once, even when multiple windows ! // scrolled or changed size. Store the current values before ! // triggering the event, if a scroll or resize happens as a side ! // effect then WinScrolled is triggered again later. ! snapshot_windows_scroll_size(); ! ! // "curwin" may be different from the actual current window, make ! // sure it can be restored. ! window_layout_lock(); ! recursive = TRUE; ! char_u winid[NUMBUFLEN]; ! vim_snprintf((char *)winid, sizeof(winid), "%d", wp->w_id); ! apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE, ! wp->w_buffer); ! recursive = FALSE; ! window_layout_unlock(); ! break; } } /* --- 2873,3145 ---- } /* ! * Create a dictionary with information about size and scroll changes in a ! * window. ! * Returns the dictionary with refcount set to one. ! * Returns NULL when out of memory. ! */ ! static dict_T * ! make_win_info_dict( ! int width, ! int height, ! int topline, ! int leftcol, ! int skipcol) ! { ! dict_T *d = dict_alloc(); ! if (d == NULL) ! return NULL; ! d->dv_refcount = 1; ! ! // not actually looping, for breaking out on error ! while (1) ! { ! typval_T tv; ! tv.v_lock = 0; ! tv.v_type = VAR_NUMBER; ! ! tv.vval.v_number = width; ! if (dict_add_tv(d, "width", &tv) == FAIL) ! break; ! tv.vval.v_number = height; ! if (dict_add_tv(d, "height", &tv) == FAIL) ! break; ! tv.vval.v_number = topline; ! if (dict_add_tv(d, "topline", &tv) == FAIL) ! break; ! tv.vval.v_number = leftcol; ! if (dict_add_tv(d, "leftcol", &tv) == FAIL) ! break; ! tv.vval.v_number = skipcol; ! if (dict_add_tv(d, "skipcol", &tv) == FAIL) ! break; ! return d; ! } ! dict_unref(d); ! return NULL; ! } ! ! // Return values of check_window_scroll_resize(): ! #define CWSR_SCROLLED 1 // at least one window scrolled ! #define CWSR_RESIZED 2 // at least one window size changed ! ! /* ! * This function is used for three purposes: ! * 1. Goes over all windows in the current tab page and returns: ! * 0 no scrolling and no size changes found ! * CWSR_SCROLLED at least one window scrolled ! * CWSR_RESIZED at least one window changed size ! * CWSR_SCROLLED + CWSR_RESIZED both ! * "size_count" is set to the nr of windows with size changes. ! * "first_scroll_win" is set to the first window with any relevant changes. ! * "first_size_win" is set to the first window with size changes. ! * ! * 2. When the first three arguments are NULL and "winlist" is not NULL, ! * "winlist" is set to the list of window IDs with size changes. ! * ! * 3. When the first three arguments are NULL and "v_event" is not NULL, ! * information about changed windows is added to "v_event". ! */ ! static int ! check_window_scroll_resize( ! int *size_count, ! win_T **first_scroll_win, ! win_T **first_size_win, ! list_T *winlist, ! dict_T *v_event) ! { ! int result = 0; ! int listidx = 0; ! int tot_width = 0; ! int tot_height = 0; ! int tot_topline = 0; ! int tot_leftcol = 0; ! int tot_skipcol = 0; ! ! win_T *wp; ! FOR_ALL_WINDOWS(wp) ! { ! int size_changed = wp->w_last_width != wp->w_width ! || wp->w_last_height != wp->w_height; ! if (size_changed) ! { ! result |= CWSR_RESIZED; ! if (winlist != NULL) ! { ! // Add this window to the list of changed windows. ! typval_T tv; ! tv.v_lock = 0; ! tv.v_type = VAR_NUMBER; ! tv.vval.v_number = wp->w_id; ! list_set_item(winlist, listidx++, &tv); ! } ! else if (size_count != NULL) ! { ! ++*size_count; ! if (*first_size_win == NULL) ! *first_size_win = wp; ! // For WinScrolled the first window with a size change is used ! // even when it didn't scroll. ! if (*first_scroll_win == NULL) ! *first_scroll_win = wp; ! } ! } ! ! int scroll_changed = wp->w_last_topline != wp->w_topline ! || wp->w_last_leftcol != wp->w_leftcol ! || wp->w_last_skipcol != wp->w_skipcol; ! if (scroll_changed) ! { ! result |= CWSR_SCROLLED; ! if (first_scroll_win != NULL && *first_scroll_win == NULL) ! *first_scroll_win = wp; ! } ! ! if ((size_changed || scroll_changed) && v_event != NULL) ! { ! // Add info about this window to the v:event dictionary. ! int width = wp->w_width - wp->w_last_width; ! int height = wp->w_height - wp->w_last_height; ! int topline = wp->w_topline - wp->w_last_topline; ! int leftcol = wp->w_leftcol - wp->w_last_leftcol; ! int skipcol = wp->w_skipcol - wp->w_last_skipcol; ! dict_T *d = make_win_info_dict(width, height, ! topline, leftcol, skipcol); ! if (d == NULL) ! break; ! char winid[NUMBUFLEN]; ! vim_snprintf(winid, sizeof(winid), "%d", wp->w_id); ! if (dict_add_dict(v_event, winid, d) == FAIL) ! { ! dict_unref(d); ! break; ! } ! --d->dv_refcount; ! ! tot_width += abs(width); ! tot_height += abs(height); ! tot_topline += abs(topline); ! tot_leftcol += abs(leftcol); ! tot_skipcol += abs(skipcol); ! } ! } ! ! if (v_event != NULL) ! { ! dict_T *alldict = make_win_info_dict(tot_width, tot_height, ! tot_topline, tot_leftcol, tot_skipcol); ! if (alldict != NULL) ! { ! if (dict_add_dict(v_event, "all", alldict) == FAIL) ! dict_unref(alldict); ! else ! --alldict->dv_refcount; ! } ! } ! ! return result; ! } ! ! /* ! * Trigger WinScrolled and/or WinResized if any window in the current tab page ! * scrolled or changed size. */ void ! may_trigger_win_scrolled_resized(void) { static int recursive = FALSE; + int do_resize = has_winresized(); + int do_scroll = has_winscrolled(); + // Do not trigger WinScrolled or WinResized recursively. Do not trigger + // before the initial snapshot of the w_last_ values was made. if (recursive ! || !(do_scroll || do_resize) || !did_initial_scroll_size_snapshot) return; ! int size_count = 0; ! win_T *first_scroll_win = NULL, *first_size_win = NULL; ! int cwsr = check_window_scroll_resize(&size_count, ! &first_scroll_win, &first_size_win, ! NULL, NULL); ! int trigger_resize = do_resize && size_count > 0; ! int trigger_scroll = do_scroll && cwsr != 0; ! if (!trigger_resize && !trigger_scroll) ! return; // no relevant changes ! list_T *windows_list = NULL; ! if (trigger_resize) ! { ! // Create the list for v:event.windows before making the snapshot. ! windows_list = list_alloc_with_items(size_count); ! (void)check_window_scroll_resize(NULL, NULL, NULL, windows_list, NULL); ! } ! dict_T *scroll_dict = NULL; ! if (trigger_scroll) ! { ! // Create the dict with entries for v:event before making the snapshot. ! scroll_dict = dict_alloc(); ! if (scroll_dict != NULL) ! { ! scroll_dict->dv_refcount = 1; ! (void)check_window_scroll_resize(NULL, NULL, NULL, NULL, ! scroll_dict); } + } + + // WinScrolled/WinResized are triggered only once, even when multiple + // windows scrolled or changed size. Store the current values before + // triggering the event, if a scroll or resize happens as a side effect + // then WinScrolled/WinResized is triggered for that later. + snapshot_windows_scroll_size(); + + // "curwin" may be different from the actual current window, make + // sure it can be restored. + window_layout_lock(); + recursive = TRUE; + + // If both are to be triggered do WinResized first. + if (trigger_resize) + { + save_v_event_T save_v_event; + dict_T *v_event = get_v_event(&save_v_event); + + if (dict_add_list(v_event, "windows", windows_list) == OK) + { + dict_set_items_ro(v_event); + + char_u winid[NUMBUFLEN]; + vim_snprintf((char *)winid, sizeof(winid), "%d", + first_size_win->w_id); + apply_autocmds(EVENT_WINRESIZED, winid, winid, FALSE, + first_size_win->w_buffer); + } + restore_v_event(v_event, &save_v_event); + } + + if (trigger_scroll) + { + save_v_event_T save_v_event; + dict_T *v_event = get_v_event(&save_v_event); + + // Move the entries from scroll_dict to v_event. + dict_extend(v_event, scroll_dict, (char_u *)"move", NULL); + dict_set_items_ro(v_event); + dict_unref(scroll_dict); + + char_u winid[NUMBUFLEN]; + vim_snprintf((char *)winid, sizeof(winid), "%d", + first_scroll_win->w_id); + apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE, + first_scroll_win->w_buffer); + + restore_v_event(v_event, &save_v_event); + } + + recursive = FALSE; + window_layout_unlock(); } /* *** ../vim-9.0.0916/src/proto/window.pro 2022-11-20 12:11:22.924741577 +0000 --- src/proto/window.pro 2022-11-22 12:25:20.594228565 +0000 *************** *** 20,26 **** int win_close(win_T *win, int free_buf); void snapshot_windows_scroll_size(void); void may_make_initial_scroll_size_snapshot(void); ! void may_trigger_winscrolled(void); 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); --- 20,26 ---- int win_close(win_T *win, int free_buf); void snapshot_windows_scroll_size(void); void may_make_initial_scroll_size_snapshot(void); ! void may_trigger_win_scrolled_resized(void); 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); *** ../vim-9.0.0916/src/vim.h 2022-11-05 14:23:09.229901363 +0000 --- src/vim.h 2022-11-21 12:51:20.583252778 +0000 *************** *** 1407,1413 **** 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 }; --- 1407,1414 ---- EVENT_WINCLOSED, // after closing a window EVENT_VIMSUSPEND, // before Vim is suspended EVENT_VIMRESUME, // after Vim is resumed ! EVENT_WINRESIZED, // after a window was resized ! EVENT_WINSCROLLED, // after a window was scrolled or resized NUM_EVENTS // MUST be the last one }; *** ../vim-9.0.0916/src/edit.c 2022-11-15 17:43:28.442135533 +0000 --- src/edit.c 2022-11-21 12:52:15.119192696 +0000 *************** *** 1510,1516 **** } if (ready) ! may_trigger_winscrolled(); // Trigger SafeState if nothing is pending. may_trigger_safestate(ready --- 1510,1516 ---- } if (ready) ! may_trigger_win_scrolled_resized(); // Trigger SafeState if nothing is pending. may_trigger_safestate(ready *** ../vim-9.0.0916/src/gui.c 2022-11-15 17:43:28.442135533 +0000 --- src/gui.c 2022-11-21 12:52:28.655177972 +0000 *************** *** 5097,5103 **** } if (!finish_op) ! may_trigger_winscrolled(); # ifdef FEAT_CONCEAL if (conceal_update_lines --- 5097,5103 ---- } if (!finish_op) ! may_trigger_win_scrolled_resized(); # ifdef FEAT_CONCEAL if (conceal_update_lines *** ../vim-9.0.0916/src/main.c 2022-11-19 21:17:48.841226535 +0000 --- src/main.c 2022-11-21 12:52:40.059165620 +0000 *************** *** 1358,1364 **** validate_cursor(); if (!finish_op) ! may_trigger_winscrolled(); // If nothing is pending and we are going to wait for the user to // type a character, trigger SafeState. --- 1358,1364 ---- validate_cursor(); if (!finish_op) ! may_trigger_win_scrolled_resized(); // If nothing is pending and we are going to wait for the user to // type a character, trigger SafeState. *** ../vim-9.0.0916/src/mouse.c 2022-11-19 14:31:04.356796241 +0000 --- src/mouse.c 2022-11-21 12:52:52.531152172 +0000 *************** *** 1171,1177 **** leftcol = 0; do_mousescroll_horiz((long_u)leftcol); } ! may_trigger_winscrolled(); } /* --- 1171,1177 ---- leftcol = 0; do_mousescroll_horiz((long_u)leftcol); } ! may_trigger_win_scrolled_resized(); } /* *** ../vim-9.0.0916/src/dict.c 2022-11-05 20:21:50.597151474 +0000 --- src/dict.c 2022-11-22 11:28:47.971367670 +0000 *************** *** 1082,1094 **** * Go over all entries in "d2" and add them to "d1". * When "action" is "error" then a duplicate key is an error. * When "action" is "force" then a duplicate key is overwritten. * Otherwise duplicate keys are ignored ("action" is "keep"). */ void dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name) { dictitem_T *di1; - hashitem_T *hi2; int todo; char_u *arg_errmsg = (char_u *)N_("extend() argument"); type_T *type; --- 1082,1095 ---- * Go over all entries in "d2" and add them to "d1". * When "action" is "error" then a duplicate key is an error. * When "action" is "force" then a duplicate key is overwritten. + * When "action" is "move" then move items instead of copying. * Otherwise duplicate keys are ignored ("action" is "keep"). + * "func_name" is used for reporting where an error occurred. */ void dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name) { dictitem_T *di1; int todo; char_u *arg_errmsg = (char_u *)N_("extend() argument"); type_T *type; *************** *** 1098,1105 **** else type = NULL; todo = (int)d2->dv_hashtab.ht_used; ! for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { if (!HASHITEM_EMPTY(hi2)) { --- 1099,1109 ---- else type = NULL; + if (*action == 'm') + hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove() + todo = (int)d2->dv_hashtab.ht_used; ! for (hashitem_T *hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2) { if (!HASHITEM_EMPTY(hi2)) { *************** *** 1116,1124 **** if (di1 == NULL) { ! di1 = dictitem_copy(HI2DI(hi2)); ! if (di1 != NULL && dict_add(d1, di1) == FAIL) ! dictitem_free(di1); } else if (*action == 'e') { --- 1120,1138 ---- if (di1 == NULL) { ! if (*action == 'm') ! { ! // cheap way to move a dict item from "d2" to "d1" ! di1 = HI2DI(hi2); ! dict_add(d1, di1); ! hash_remove(&d2->dv_hashtab, hi2); ! } ! else ! { ! di1 = dictitem_copy(HI2DI(hi2)); ! if (di1 != NULL && dict_add(d1, di1) == FAIL) ! dictitem_free(di1); ! } } else if (*action == 'e') { *************** *** 1138,1143 **** --- 1152,1160 ---- } } } + + if (*action == 'm') + hash_unlock(&d2->dv_hashtab); } /* *************** *** 1272,1278 **** action = (char_u *)"force"; if (type != NULL && check_typval_arg_type(type, &argvars[1], ! func_name, 2) == FAIL) return; dict_extend(d1, d2, action, func_name); --- 1289,1295 ---- action = (char_u *)"force"; if (type != NULL && check_typval_arg_type(type, &argvars[1], ! func_name, 2) == FAIL) return; dict_extend(d1, d2, action, func_name); *** ../vim-9.0.0916/src/testdir/test_autocmd.vim 2022-11-20 12:11:22.924741577 +0000 --- src/testdir/test_autocmd.vim 2022-11-22 12:23:02.490210044 +0000 *************** *** 306,311 **** --- 306,366 ---- unlet g:record endfunc + func Test_WinResized() + CheckRunVimInTerminal + + let lines =<< trim END + set scrolloff=0 + call setline(1, ['111', '222']) + vnew + call setline(1, ['aaa', 'bbb']) + new + call setline(1, ['foo', 'bar']) + + let g:resized = 0 + au WinResized * let g:resized += 1 + + func WriteResizedEvent() + call writefile([json_encode(v:event)], 'XresizeEvent') + endfunc + au WinResized * call WriteResizedEvent() + END + call writefile(lines, 'Xtest_winresized', 'D') + let buf = RunVimInTerminal('-S Xtest_winresized', {'rows': 10}) + + " redraw now to avoid a redraw after the :echo command + call term_sendkeys(buf, ":redraw!\") + call TermWait(buf) + + call term_sendkeys(buf, ":echo g:resized\") + call WaitForAssert({-> assert_match('^0$', term_getline(buf, 10))}, 1000) + + " increase window height, two windows will be reported + call term_sendkeys(buf, "\+") + call TermWait(buf) + call term_sendkeys(buf, ":echo g:resized\") + call WaitForAssert({-> assert_match('^1$', term_getline(buf, 10))}, 1000) + + let event = readfile('XresizeEvent')[0]->json_decode() + call assert_equal({ + \ 'windows': [1002, 1001], + \ }, event) + + " increase window width, three windows will be reported + call term_sendkeys(buf, "\>") + call TermWait(buf) + call term_sendkeys(buf, ":echo g:resized\") + call WaitForAssert({-> assert_match('^2$', term_getline(buf, 10))}, 1000) + + let event = readfile('XresizeEvent')[0]->json_decode() + call assert_equal({ + \ 'windows': [1002, 1001, 1000], + \ }, event) + + call delete('XresizeEvent') + call StopVimInTerminal(buf) + endfunc + func Test_WinScrolled() CheckRunVimInTerminal *************** *** 316,326 **** --- 371,385 ---- endfor let win_id = win_getid() let g:matched = v:false + func WriteScrollEvent() + call writefile([json_encode(v:event)], 'XscrollEvent') + endfunc 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('')) + au WinScrolled * call WriteScrollEvent() END call writefile(lines, 'Xtest_winscrolled', 'D') let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6}) *************** *** 332,346 **** --- 391,423 ---- call term_sendkeys(buf, "zlzh:echo g:scrolled\") call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000) + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 1, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': -1, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + " Scroll up/down in Normal mode. call term_sendkeys(buf, "\\:echo g:scrolled\") call WaitForAssert({-> assert_match('^4 ', term_getline(buf, 6))}, 1000) + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 0, 'topline': 1, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': 0, 'topline': -1, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + " 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) + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 0, 'topline': 1, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': 0, 'topline': -1, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + " 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. *************** *** 348,353 **** --- 425,436 ---- call term_sendkeys(buf, ":echo g:scrolled\") call WaitForAssert({-> assert_match('^8 ', term_getline(buf, 6))}, 1000) + let event = readfile('XscrollEvent')[0]->json_decode() + call assert_equal({ + \ 'all': {'leftcol': 5, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0}, + \ '1000': {'leftcol': -5, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0} + \ }, event) + " 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) *************** *** 356,361 **** --- 439,445 ---- 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 delete('XscrollEvent') call StopVimInTerminal(buf) endfunc *** ../vim-9.0.0916/src/version.c 2022-11-21 19:56:59.403412744 +0000 --- src/version.c 2022-11-21 22:18:31.256776712 +0000 *************** *** 697,698 **** --- 697,700 ---- { /* Add new patch number below this line */ + /**/ + 917, /**/ -- hundred-and-one symptoms of being an internet addict: 104. When people ask about the Presidential Election you ask "Which country?" /// 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 ///