Patch 9.0.0917

9 views
Skip to first unread message

Bram Moolenaar

unread,
Nov 22, 2022, 7:41:37 AM11/22/22
to vim...@googlegroups.com

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 <amatch> and <afile> 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 <amatch> and <afile> 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!\<CR>")
+ call TermWait(buf)
+
+ call term_sendkeys(buf, ":echo g:resized\<CR>")
+ call WaitForAssert({-> assert_match('^0$', term_getline(buf, 10))}, 1000)
+
+ " increase window height, two windows will be reported
+ call term_sendkeys(buf, "\<C-W>+")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":echo g:resized\<CR>")
+ 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, "\<C-W>>")
+ call TermWait(buf)
+ call term_sendkeys(buf, ":echo g:resized\<CR>")
+ 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('<amatch>'))
au WinScrolled * let g:afile = str2nr(expand('<afile>'))
+ 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\<CR>")
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, "\<c-e>\<c-y>:echo g:scrolled\<CR>")
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\<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)

+ 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\<CR>")
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\<CR>")
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\<CR>")
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 -- Br...@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 ///

Bram Moolenaar

unread,
Nov 22, 2022, 11:54:14 AM11/22/22
to vim...@googlegroups.com, Bram Moolenaar

I wrote:

> 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

Please have a critical look at how these two autocmd events work now.
If anything should change, we better do this right now, before it is
widely used.

One thing I have been wondering is whether we should reduce the number
of events generated. E.g. only trigger when there is no typeahead.
That would avoid overhead, but also means there is only one event for
several changes in a row.

--
hundred-and-one symptoms of being an internet addict:
108. While reading a magazine, you look for the Zoom icon for a better
look at a photograph.
Reply all
Reply to author
Forward
0 new messages