Skip to content

Commit 35fc61c

Browse files
committed
patch 9.0.0917: the WinScrolled autocommand event is not enough
Problem: The WinScrolled autocommand event is not enough. Solution: Add WinResized and provide information about what changed. (closes #11576)
1 parent ce30ccc commit 35fc61c

File tree

14 files changed

+455
-54
lines changed

14 files changed

+455
-54
lines changed

runtime/doc/autocmd.txt

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1371,33 +1371,42 @@ WinNew When a new window was created. Not done for
13711371
Before a WinEnter event.
13721372

13731373
*WinScrolled*
1374-
WinScrolled After scrolling the content of a window or
1375-
resizing a window in the current tab page.
1376-
1377-
When more than one window scrolled or resized
1378-
only one WinScrolled event is triggered. You
1379-
can use the `winlayout()` and `getwininfo()`
1380-
functions to see what changed.
1374+
WinScrolled After any window in the current tab page
1375+
scrolled the text (horizontally or vertically)
1376+
or changed width or height. See
1377+
|win-scrolled-resized|.
13811378

13821379
The pattern is matched against the |window-ID|
13831380
of the first window that scrolled or resized.
13841381
Both <amatch> and <afile> are set to the
13851382
|window-ID|.
13861383

1384+
|v:event| is set with information about size
1385+
and scroll changes. |WinScrolled-event|
1386+
13871387
Only starts triggering after startup finished
13881388
and the first screen redraw was done.
1389+
Does not trigger when defining the first
1390+
WinScrolled or WinResized event, but may
1391+
trigger when adding more.
13891392

13901393
Non-recursive: the event will not trigger
13911394
while executing commands for the WinScrolled
13921395
event. However, if the command causes a
13931396
window to scroll or change size, then another
13941397
WinScrolled event will be triggered later.
13951398

1396-
Does not trigger when the command is added,
1397-
only after the first scroll or resize.
1398-
*E1312*
1399-
It is not allowed to change the window layout
1400-
here (split, close or move windows).
1399+
1400+
*WinResized*
1401+
WinResized After a window in the current tab page changed
1402+
width or height.
1403+
See |win-scrolled-resized|.
1404+
1405+
|v:event| is set with information about size
1406+
changes. |WinResized-event|
1407+
1408+
Same behavior as |WinScrolled| for the
1409+
pattern, triggering and recursiveness.
14011410

14021411
==============================================================================
14031412
6. Patterns *autocmd-patterns* *{aupat}*

runtime/doc/windows.txt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,54 @@ it).
631631
The minimal height and width of a window is set with 'winminheight' and
632632
'winminwidth'. These are hard values, a window will never become smaller.
633633

634+
635+
WinScrolled and WinResized autocommands ~
636+
*win-scrolled-resized*
637+
If you want to get notified of changes in window sizes, the |WinResized|
638+
autocommand event can be used.
639+
If you want to get notified of text in windows scrolling vertically or
640+
horizontally, the |WinScrolled| autocommand event can be used. This will also
641+
trigger in window size changes.
642+
*WinResized-event*
643+
The |WinResized| event is triggered after updating the display, several
644+
windows may have changed size then. A list of the IDs of windows that changed
645+
since last time is provided in the v:event.windows variable, for example:
646+
[1003, 1006]
647+
*WinScrolled-event*
648+
The |WinScrolled| event is triggered after |WinResized|, and also if a window
649+
was scrolled. That can be vertically (the text at the top of the window
650+
changed) or horizontally (when 'wrap' is off or when the first displayed part
651+
of the first line changes). Note that |WinScrolled| will trigger many more
652+
times than |WinResized|, it may slow down editing a bit.
653+
654+
The information provided by |WinScrolled| is a dictionary for each window that
655+
has changes, using the window ID as the key, and a total count of the changes
656+
with the key "all". Example value for |v:event| (|Vim9| syntax):
657+
{
658+
all: {width: 0, height: 2, leftcol: 0, topline: 1, skipcol: 0},
659+
1003: {width: 0, height: -1, leftcol: 0, topline: 0, skipcol: 0},
660+
1006: {width: 0, height: 1, leftcol: 0, topline: 1, skipcol: 0},
661+
}
662+
663+
Note that the "all" entry has the absolute values of the individual windows
664+
accumulated.
665+
666+
If you need more information about what changed, or you want to "debounce" the
667+
events (not handle every event to avoid doing too much work), you may want to
668+
use the `winlayout()` and `getwininfo()` functions.
669+
670+
|WinScrolled| and |WinResized| do not trigger when the first autocommand is
671+
added, only after the first scroll or resize. They may trigger when switching
672+
to another tab page.
673+
674+
The commands executed are expected to not cause window size or scroll changes.
675+
If this happens anyway, the event will trigger again very soon. In other
676+
words: Just before triggering the event, the current sizes and scroll
677+
positions are stored and used to decide whether there was a change.
678+
*E1312*
679+
It is not allowed to change the window layout here (split, close or move
680+
windows).
681+
634682
==============================================================================
635683
7. Argument and buffer list commands *buffer-list*
636684

src/autocmd.c

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,7 @@ static struct event_name
191191
{"WinClosed", EVENT_WINCLOSED},
192192
{"WinEnter", EVENT_WINENTER},
193193
{"WinLeave", EVENT_WINLEAVE},
194+
{"WinResized", EVENT_WINRESIZED},
194195
{"WinScrolled", EVENT_WINSCROLLED},
195196
{"VimResized", EVENT_VIMRESIZED},
196197
{"TextYankPost", EVENT_TEXTYANKPOST},
@@ -1263,10 +1264,11 @@ do_autocmd_event(
12631264
if (event == EVENT_MODECHANGED && !has_modechanged())
12641265
get_mode(last_mode);
12651266
#endif
1266-
// Initialize the fields checked by the WinScrolled trigger to
1267-
// prevent it from firing right after the first autocmd is
1268-
// defined.
1269-
if (event == EVENT_WINSCROLLED && !has_winscrolled())
1267+
// Initialize the fields checked by the WinScrolled and
1268+
// WinResized trigger to prevent them from firing right after
1269+
// the first autocmd is defined.
1270+
if ((event == EVENT_WINSCROLLED || event == EVENT_WINRESIZED)
1271+
&& !(has_winscrolled() || has_winresized()))
12701272
{
12711273
tabpage_T *save_curtab = curtab;
12721274
tabpage_T *tp;
@@ -1810,6 +1812,15 @@ trigger_cursorhold(void)
18101812
return FALSE;
18111813
}
18121814

1815+
/*
1816+
* Return TRUE when there is a WinResized autocommand defined.
1817+
*/
1818+
int
1819+
has_winresized(void)
1820+
{
1821+
return (first_autopat[(int)EVENT_WINRESIZED] != NULL);
1822+
}
1823+
18131824
/*
18141825
* Return TRUE when there is a WinScrolled autocommand defined.
18151826
*/
@@ -2117,6 +2128,7 @@ apply_autocmds_group(
21172128
|| event == EVENT_MENUPOPUP
21182129
|| event == EVENT_USER
21192130
|| event == EVENT_WINCLOSED
2131+
|| event == EVENT_WINRESIZED
21202132
|| event == EVENT_WINSCROLLED)
21212133
{
21222134
fname = vim_strsave(fname);

src/dict.c

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,13 +1082,14 @@ eval_dict(char_u **arg, typval_T *rettv, evalarg_T *evalarg, int literal)
10821082
* Go over all entries in "d2" and add them to "d1".
10831083
* When "action" is "error" then a duplicate key is an error.
10841084
* When "action" is "force" then a duplicate key is overwritten.
1085+
* When "action" is "move" then move items instead of copying.
10851086
* Otherwise duplicate keys are ignored ("action" is "keep").
1087+
* "func_name" is used for reporting where an error occurred.
10861088
*/
10871089
void
10881090
dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
10891091
{
10901092
dictitem_T *di1;
1091-
hashitem_T *hi2;
10921093
int todo;
10931094
char_u *arg_errmsg = (char_u *)N_("extend() argument");
10941095
type_T *type;
@@ -1098,8 +1099,11 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
10981099
else
10991100
type = NULL;
11001101

1102+
if (*action == 'm')
1103+
hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove()
1104+
11011105
todo = (int)d2->dv_hashtab.ht_used;
1102-
for (hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
1106+
for (hashitem_T *hi2 = d2->dv_hashtab.ht_array; todo > 0; ++hi2)
11031107
{
11041108
if (!HASHITEM_EMPTY(hi2))
11051109
{
@@ -1116,9 +1120,19 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
11161120

11171121
if (di1 == NULL)
11181122
{
1119-
di1 = dictitem_copy(HI2DI(hi2));
1120-
if (di1 != NULL && dict_add(d1, di1) == FAIL)
1121-
dictitem_free(di1);
1123+
if (*action == 'm')
1124+
{
1125+
// cheap way to move a dict item from "d2" to "d1"
1126+
di1 = HI2DI(hi2);
1127+
dict_add(d1, di1);
1128+
hash_remove(&d2->dv_hashtab, hi2);
1129+
}
1130+
else
1131+
{
1132+
di1 = dictitem_copy(HI2DI(hi2));
1133+
if (di1 != NULL && dict_add(d1, di1) == FAIL)
1134+
dictitem_free(di1);
1135+
}
11221136
}
11231137
else if (*action == 'e')
11241138
{
@@ -1138,6 +1152,9 @@ dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name)
11381152
}
11391153
}
11401154
}
1155+
1156+
if (*action == 'm')
1157+
hash_unlock(&d2->dv_hashtab);
11411158
}
11421159

11431160
/*
@@ -1272,7 +1289,7 @@ dict_extend_func(
12721289
action = (char_u *)"force";
12731290

12741291
if (type != NULL && check_typval_arg_type(type, &argvars[1],
1275-
func_name, 2) == FAIL)
1292+
func_name, 2) == FAIL)
12761293
return;
12771294
dict_extend(d1, d2, action, func_name);
12781295

src/edit.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1510,7 +1510,7 @@ ins_redraw(int ready) // not busy with something
15101510
}
15111511

15121512
if (ready)
1513-
may_trigger_winscrolled();
1513+
may_trigger_win_scrolled_resized();
15141514

15151515
// Trigger SafeState if nothing is pending.
15161516
may_trigger_safestate(ready

src/gui.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5097,7 +5097,7 @@ gui_update_screen(void)
50975097
}
50985098

50995099
if (!finish_op)
5100-
may_trigger_winscrolled();
5100+
may_trigger_win_scrolled_resized();
51015101

51025102
# ifdef FEAT_CONCEAL
51035103
if (conceal_update_lines

src/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1358,7 +1358,7 @@ main_loop(
13581358
validate_cursor();
13591359

13601360
if (!finish_op)
1361-
may_trigger_winscrolled();
1361+
may_trigger_win_scrolled_resized();
13621362

13631363
// If nothing is pending and we are going to wait for the user to
13641364
// type a character, trigger SafeState.

src/mouse.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1171,7 +1171,7 @@ do_mousescroll(cmdarg_T *cap)
11711171
leftcol = 0;
11721172
do_mousescroll_horiz((long_u)leftcol);
11731173
}
1174-
may_trigger_winscrolled();
1174+
may_trigger_win_scrolled_resized();
11751175
}
11761176

11771177
/*

src/proto/autocmd.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ int apply_autocmds(event_T event, char_u *fname, char_u *fname_io, int force, bu
1616
int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap);
1717
int apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, int *retval);
1818
int trigger_cursorhold(void);
19+
int has_winresized(void);
1920
int has_winscrolled(void);
2021
int has_cursormoved(void);
2122
int has_cursormovedI(void);

src/proto/window.pro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ int one_window(void);
2020
int win_close(win_T *win, int free_buf);
2121
void snapshot_windows_scroll_size(void);
2222
void may_make_initial_scroll_size_snapshot(void);
23-
void may_trigger_winscrolled(void);
23+
void may_trigger_win_scrolled_resized(void);
2424
void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp);
2525
void win_free_all(void);
2626
win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp);

0 commit comments

Comments
 (0)