Mirror of the Vim source from https://github.com/vim/vim
Revision | a86ee6c0309ec6b8f81239956e4d128da5ab31ef (tree) |
---|---|
Time | 2022-11-22 21:45:04 |
Author | Bram Moolenaar <Bram@vim....> |
Commiter | Bram Moolenaar |
patch 9.0.0917: the WinScrolled autocommand event is not enough
Commit: https://github.com/vim/vim/commit/35fc61cb5b5eba8bbb9d8f0700332fbab38f40ca
Author: Bram Moolenaar <Bram@vim.org>
Date: Tue Nov 22 12:40:50 2022 +0000
@@ -1371,21 +1371,24 @@ | ||
1371 | 1371 | Before a WinEnter event. |
1372 | 1372 | |
1373 | 1373 | *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|. | |
1381 | 1378 | |
1382 | 1379 | The pattern is matched against the |window-ID| |
1383 | 1380 | of the first window that scrolled or resized. |
1384 | 1381 | Both <amatch> and <afile> are set to the |
1385 | 1382 | |window-ID|. |
1386 | 1383 | |
1384 | + |v:event| is set with information about size | |
1385 | + and scroll changes. |WinScrolled-event| | |
1386 | + | |
1387 | 1387 | Only starts triggering after startup finished |
1388 | 1388 | 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. | |
1389 | 1392 | |
1390 | 1393 | Non-recursive: the event will not trigger |
1391 | 1394 | while executing commands for the WinScrolled |
@@ -1393,11 +1396,17 @@ | ||
1393 | 1396 | window to scroll or change size, then another |
1394 | 1397 | WinScrolled event will be triggered later. |
1395 | 1398 | |
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. | |
1401 | 1410 | |
1402 | 1411 | ============================================================================== |
1403 | 1412 | 6. Patterns *autocmd-patterns* *{aupat}* |
@@ -631,6 +631,54 @@ | ||
631 | 631 | The minimal height and width of a window is set with 'winminheight' and |
632 | 632 | 'winminwidth'. These are hard values, a window will never become smaller. |
633 | 633 | |
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 | + | |
634 | 682 | ============================================================================== |
635 | 683 | 7. Argument and buffer list commands *buffer-list* |
636 | 684 |
@@ -191,6 +191,7 @@ | ||
191 | 191 | {"WinClosed", EVENT_WINCLOSED}, |
192 | 192 | {"WinEnter", EVENT_WINENTER}, |
193 | 193 | {"WinLeave", EVENT_WINLEAVE}, |
194 | + {"WinResized", EVENT_WINRESIZED}, | |
194 | 195 | {"WinScrolled", EVENT_WINSCROLLED}, |
195 | 196 | {"VimResized", EVENT_VIMRESIZED}, |
196 | 197 | {"TextYankPost", EVENT_TEXTYANKPOST}, |
@@ -1263,10 +1264,11 @@ | ||
1263 | 1264 | if (event == EVENT_MODECHANGED && !has_modechanged()) |
1264 | 1265 | get_mode(last_mode); |
1265 | 1266 | #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())) | |
1270 | 1272 | { |
1271 | 1273 | tabpage_T *save_curtab = curtab; |
1272 | 1274 | tabpage_T *tp; |
@@ -1811,6 +1813,15 @@ | ||
1811 | 1813 | } |
1812 | 1814 | |
1813 | 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 | + | |
1824 | +/* | |
1814 | 1825 | * Return TRUE when there is a WinScrolled autocommand defined. |
1815 | 1826 | */ |
1816 | 1827 | int |
@@ -2117,6 +2128,7 @@ | ||
2117 | 2128 | || event == EVENT_MENUPOPUP |
2118 | 2129 | || event == EVENT_USER |
2119 | 2130 | || event == EVENT_WINCLOSED |
2131 | + || event == EVENT_WINRESIZED | |
2120 | 2132 | || event == EVENT_WINSCROLLED) |
2121 | 2133 | { |
2122 | 2134 | fname = vim_strsave(fname); |
@@ -1082,13 +1082,14 @@ | ||
1082 | 1082 | * Go over all entries in "d2" and add them to "d1". |
1083 | 1083 | * When "action" is "error" then a duplicate key is an error. |
1084 | 1084 | * When "action" is "force" then a duplicate key is overwritten. |
1085 | + * When "action" is "move" then move items instead of copying. | |
1085 | 1086 | * Otherwise duplicate keys are ignored ("action" is "keep"). |
1087 | + * "func_name" is used for reporting where an error occurred. | |
1086 | 1088 | */ |
1087 | 1089 | void |
1088 | 1090 | dict_extend(dict_T *d1, dict_T *d2, char_u *action, char *func_name) |
1089 | 1091 | { |
1090 | 1092 | dictitem_T *di1; |
1091 | - hashitem_T *hi2; | |
1092 | 1093 | int todo; |
1093 | 1094 | char_u *arg_errmsg = (char_u *)N_("extend() argument"); |
1094 | 1095 | type_T *type; |
@@ -1098,8 +1099,11 @@ | ||
1098 | 1099 | else |
1099 | 1100 | type = NULL; |
1100 | 1101 | |
1102 | + if (*action == 'm') | |
1103 | + hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove() | |
1104 | + | |
1101 | 1105 | 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) | |
1103 | 1107 | { |
1104 | 1108 | if (!HASHITEM_EMPTY(hi2)) |
1105 | 1109 | { |
@@ -1116,9 +1120,19 @@ | ||
1116 | 1120 | |
1117 | 1121 | if (di1 == NULL) |
1118 | 1122 | { |
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 | + } | |
1122 | 1136 | } |
1123 | 1137 | else if (*action == 'e') |
1124 | 1138 | { |
@@ -1138,6 +1152,9 @@ | ||
1138 | 1152 | } |
1139 | 1153 | } |
1140 | 1154 | } |
1155 | + | |
1156 | + if (*action == 'm') | |
1157 | + hash_unlock(&d2->dv_hashtab); | |
1141 | 1158 | } |
1142 | 1159 | |
1143 | 1160 | /* |
@@ -1272,7 +1289,7 @@ | ||
1272 | 1289 | action = (char_u *)"force"; |
1273 | 1290 | |
1274 | 1291 | if (type != NULL && check_typval_arg_type(type, &argvars[1], |
1275 | - func_name, 2) == FAIL) | |
1292 | + func_name, 2) == FAIL) | |
1276 | 1293 | return; |
1277 | 1294 | dict_extend(d1, d2, action, func_name); |
1278 | 1295 |
@@ -1510,7 +1510,7 @@ | ||
1510 | 1510 | } |
1511 | 1511 | |
1512 | 1512 | if (ready) |
1513 | - may_trigger_winscrolled(); | |
1513 | + may_trigger_win_scrolled_resized(); | |
1514 | 1514 | |
1515 | 1515 | // Trigger SafeState if nothing is pending. |
1516 | 1516 | may_trigger_safestate(ready |
@@ -5097,7 +5097,7 @@ | ||
5097 | 5097 | } |
5098 | 5098 | |
5099 | 5099 | if (!finish_op) |
5100 | - may_trigger_winscrolled(); | |
5100 | + may_trigger_win_scrolled_resized(); | |
5101 | 5101 | |
5102 | 5102 | # ifdef FEAT_CONCEAL |
5103 | 5103 | if (conceal_update_lines |
@@ -1358,7 +1358,7 @@ | ||
1358 | 1358 | validate_cursor(); |
1359 | 1359 | |
1360 | 1360 | if (!finish_op) |
1361 | - may_trigger_winscrolled(); | |
1361 | + may_trigger_win_scrolled_resized(); | |
1362 | 1362 | |
1363 | 1363 | // If nothing is pending and we are going to wait for the user to |
1364 | 1364 | // type a character, trigger SafeState. |
@@ -1171,7 +1171,7 @@ | ||
1171 | 1171 | leftcol = 0; |
1172 | 1172 | do_mousescroll_horiz((long_u)leftcol); |
1173 | 1173 | } |
1174 | - may_trigger_winscrolled(); | |
1174 | + may_trigger_win_scrolled_resized(); | |
1175 | 1175 | } |
1176 | 1176 | |
1177 | 1177 | /* |
@@ -16,6 +16,7 @@ | ||
16 | 16 | int apply_autocmds_exarg(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, exarg_T *eap); |
17 | 17 | int apply_autocmds_retval(event_T event, char_u *fname, char_u *fname_io, int force, buf_T *buf, int *retval); |
18 | 18 | int trigger_cursorhold(void); |
19 | +int has_winresized(void); | |
19 | 20 | int has_winscrolled(void); |
20 | 21 | int has_cursormoved(void); |
21 | 22 | int has_cursormovedI(void); |
@@ -20,7 +20,7 @@ | ||
20 | 20 | int win_close(win_T *win, int free_buf); |
21 | 21 | void snapshot_windows_scroll_size(void); |
22 | 22 | void may_make_initial_scroll_size_snapshot(void); |
23 | -void may_trigger_winscrolled(void); | |
23 | +void may_trigger_win_scrolled_resized(void); | |
24 | 24 | void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp); |
25 | 25 | void win_free_all(void); |
26 | 26 | win_T *winframe_remove(win_T *win, int *dirp, tabpage_T *tp); |
@@ -306,6 +306,61 @@ | ||
306 | 306 | unlet g:record |
307 | 307 | endfunc |
308 | 308 | |
309 | +func Test_WinResized() | |
310 | + CheckRunVimInTerminal | |
311 | + | |
312 | + let lines =<< trim END | |
313 | + set scrolloff=0 | |
314 | + call setline(1, ['111', '222']) | |
315 | + vnew | |
316 | + call setline(1, ['aaa', 'bbb']) | |
317 | + new | |
318 | + call setline(1, ['foo', 'bar']) | |
319 | + | |
320 | + let g:resized = 0 | |
321 | + au WinResized * let g:resized += 1 | |
322 | + | |
323 | + func WriteResizedEvent() | |
324 | + call writefile([json_encode(v:event)], 'XresizeEvent') | |
325 | + endfunc | |
326 | + au WinResized * call WriteResizedEvent() | |
327 | + END | |
328 | + call writefile(lines, 'Xtest_winresized', 'D') | |
329 | + let buf = RunVimInTerminal('-S Xtest_winresized', {'rows': 10}) | |
330 | + | |
331 | + " redraw now to avoid a redraw after the :echo command | |
332 | + call term_sendkeys(buf, ":redraw!\<CR>") | |
333 | + call TermWait(buf) | |
334 | + | |
335 | + call term_sendkeys(buf, ":echo g:resized\<CR>") | |
336 | + call WaitForAssert({-> assert_match('^0$', term_getline(buf, 10))}, 1000) | |
337 | + | |
338 | + " increase window height, two windows will be reported | |
339 | + call term_sendkeys(buf, "\<C-W>+") | |
340 | + call TermWait(buf) | |
341 | + call term_sendkeys(buf, ":echo g:resized\<CR>") | |
342 | + call WaitForAssert({-> assert_match('^1$', term_getline(buf, 10))}, 1000) | |
343 | + | |
344 | + let event = readfile('XresizeEvent')[0]->json_decode() | |
345 | + call assert_equal({ | |
346 | + \ 'windows': [1002, 1001], | |
347 | + \ }, event) | |
348 | + | |
349 | + " increase window width, three windows will be reported | |
350 | + call term_sendkeys(buf, "\<C-W>>") | |
351 | + call TermWait(buf) | |
352 | + call term_sendkeys(buf, ":echo g:resized\<CR>") | |
353 | + call WaitForAssert({-> assert_match('^2$', term_getline(buf, 10))}, 1000) | |
354 | + | |
355 | + let event = readfile('XresizeEvent')[0]->json_decode() | |
356 | + call assert_equal({ | |
357 | + \ 'windows': [1002, 1001, 1000], | |
358 | + \ }, event) | |
359 | + | |
360 | + call delete('XresizeEvent') | |
361 | + call StopVimInTerminal(buf) | |
362 | +endfunc | |
363 | + | |
309 | 364 | func Test_WinScrolled() |
310 | 365 | CheckRunVimInTerminal |
311 | 366 |
@@ -316,11 +371,15 @@ | ||
316 | 371 | endfor |
317 | 372 | let win_id = win_getid() |
318 | 373 | let g:matched = v:false |
374 | + func WriteScrollEvent() | |
375 | + call writefile([json_encode(v:event)], 'XscrollEvent') | |
376 | + endfunc | |
319 | 377 | execute 'au WinScrolled' win_id 'let g:matched = v:true' |
320 | 378 | let g:scrolled = 0 |
321 | 379 | au WinScrolled * let g:scrolled += 1 |
322 | 380 | au WinScrolled * let g:amatch = str2nr(expand('<amatch>')) |
323 | 381 | au WinScrolled * let g:afile = str2nr(expand('<afile>')) |
382 | + au WinScrolled * call WriteScrollEvent() | |
324 | 383 | END |
325 | 384 | call writefile(lines, 'Xtest_winscrolled', 'D') |
326 | 385 | let buf = RunVimInTerminal('-S Xtest_winscrolled', {'rows': 6}) |
@@ -332,15 +391,33 @@ | ||
332 | 391 | call term_sendkeys(buf, "zlzh:echo g:scrolled\<CR>") |
333 | 392 | call WaitForAssert({-> assert_match('^2 ', term_getline(buf, 6))}, 1000) |
334 | 393 | |
394 | + let event = readfile('XscrollEvent')[0]->json_decode() | |
395 | + call assert_equal({ | |
396 | + \ 'all': {'leftcol': 1, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0}, | |
397 | + \ '1000': {'leftcol': -1, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0} | |
398 | + \ }, event) | |
399 | + | |
335 | 400 | " Scroll up/down in Normal mode. |
336 | 401 | call term_sendkeys(buf, "\<c-e>\<c-y>:echo g:scrolled\<CR>") |
337 | 402 | call WaitForAssert({-> assert_match('^4 ', term_getline(buf, 6))}, 1000) |
338 | 403 | |
404 | + let event = readfile('XscrollEvent')[0]->json_decode() | |
405 | + call assert_equal({ | |
406 | + \ 'all': {'leftcol': 0, 'topline': 1, 'width': 0, 'height': 0, 'skipcol': 0}, | |
407 | + \ '1000': {'leftcol': 0, 'topline': -1, 'width': 0, 'height': 0, 'skipcol': 0} | |
408 | + \ }, event) | |
409 | + | |
339 | 410 | " Scroll up/down in Insert mode. |
340 | 411 | call term_sendkeys(buf, "Mi\<c-x>\<c-e>\<Esc>i\<c-x>\<c-y>\<Esc>") |
341 | 412 | call term_sendkeys(buf, ":echo g:scrolled\<CR>") |
342 | 413 | call WaitForAssert({-> assert_match('^6 ', term_getline(buf, 6))}, 1000) |
343 | 414 | |
415 | + let event = readfile('XscrollEvent')[0]->json_decode() | |
416 | + call assert_equal({ | |
417 | + \ 'all': {'leftcol': 0, 'topline': 1, 'width': 0, 'height': 0, 'skipcol': 0}, | |
418 | + \ '1000': {'leftcol': 0, 'topline': -1, 'width': 0, 'height': 0, 'skipcol': 0} | |
419 | + \ }, event) | |
420 | + | |
344 | 421 | " Scroll the window horizontally to focus the last letter of the third line |
345 | 422 | " containing only six characters. Moving to the previous and shorter lines |
346 | 423 | " should trigger another autocommand as Vim has to make them visible. |
@@ -348,6 +425,12 @@ | ||
348 | 425 | call term_sendkeys(buf, ":echo g:scrolled\<CR>") |
349 | 426 | call WaitForAssert({-> assert_match('^8 ', term_getline(buf, 6))}, 1000) |
350 | 427 | |
428 | + let event = readfile('XscrollEvent')[0]->json_decode() | |
429 | + call assert_equal({ | |
430 | + \ 'all': {'leftcol': 5, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0}, | |
431 | + \ '1000': {'leftcol': -5, 'topline': 0, 'width': 0, 'height': 0, 'skipcol': 0} | |
432 | + \ }, event) | |
433 | + | |
351 | 434 | " Ensure the command was triggered for the specified window ID. |
352 | 435 | call term_sendkeys(buf, ":echo g:matched\<CR>") |
353 | 436 | call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000) |
@@ -356,6 +439,7 @@ | ||
356 | 439 | call term_sendkeys(buf, ":echo g:amatch == win_id && g:afile == win_id\<CR>") |
357 | 440 | call WaitForAssert({-> assert_match('^v:true ', term_getline(buf, 6))}, 1000) |
358 | 441 | |
442 | + call delete('XscrollEvent') | |
359 | 443 | call StopVimInTerminal(buf) |
360 | 444 | endfunc |
361 | 445 |
@@ -696,6 +696,8 @@ | ||
696 | 696 | static int included_patches[] = |
697 | 697 | { /* Add new patch number below this line */ |
698 | 698 | /**/ |
699 | + 917, | |
700 | +/**/ | |
699 | 701 | 916, |
700 | 702 | /**/ |
701 | 703 | 915, |
@@ -1407,7 +1407,8 @@ | ||
1407 | 1407 | EVENT_WINCLOSED, // after closing a window |
1408 | 1408 | EVENT_VIMSUSPEND, // before Vim is suspended |
1409 | 1409 | EVENT_VIMRESUME, // after Vim is resumed |
1410 | - EVENT_WINSCROLLED, // after Vim window was scrolled | |
1410 | + EVENT_WINRESIZED, // after a window was resized | |
1411 | + EVENT_WINSCROLLED, // after a window was scrolled or resized | |
1411 | 1412 | |
1412 | 1413 | NUM_EVENTS // MUST be the last one |
1413 | 1414 | }; |
@@ -2873,46 +2873,273 @@ | ||
2873 | 2873 | } |
2874 | 2874 | |
2875 | 2875 | /* |
2876 | - * Trigger WinScrolled if any window scrolled or changed size. | |
2877 | - */ | |
2878 | - void | |
2879 | -may_trigger_winscrolled(void) | |
2880 | -{ | |
2881 | - static int recursive = FALSE; | |
2882 | - | |
2883 | - if (recursive | |
2884 | - || !has_winscrolled() | |
2885 | - || !did_initial_scroll_size_snapshot) | |
2886 | - return; | |
2876 | + * Create a dictionary with information about size and scroll changes in a | |
2877 | + * window. | |
2878 | + * Returns the dictionary with refcount set to one. | |
2879 | + * Returns NULL when out of memory. | |
2880 | + */ | |
2881 | + static dict_T * | |
2882 | +make_win_info_dict( | |
2883 | + int width, | |
2884 | + int height, | |
2885 | + int topline, | |
2886 | + int leftcol, | |
2887 | + int skipcol) | |
2888 | +{ | |
2889 | + dict_T *d = dict_alloc(); | |
2890 | + if (d == NULL) | |
2891 | + return NULL; | |
2892 | + d->dv_refcount = 1; | |
2893 | + | |
2894 | + // not actually looping, for breaking out on error | |
2895 | + while (1) | |
2896 | + { | |
2897 | + typval_T tv; | |
2898 | + tv.v_lock = 0; | |
2899 | + tv.v_type = VAR_NUMBER; | |
2900 | + | |
2901 | + tv.vval.v_number = width; | |
2902 | + if (dict_add_tv(d, "width", &tv) == FAIL) | |
2903 | + break; | |
2904 | + tv.vval.v_number = height; | |
2905 | + if (dict_add_tv(d, "height", &tv) == FAIL) | |
2906 | + break; | |
2907 | + tv.vval.v_number = topline; | |
2908 | + if (dict_add_tv(d, "topline", &tv) == FAIL) | |
2909 | + break; | |
2910 | + tv.vval.v_number = leftcol; | |
2911 | + if (dict_add_tv(d, "leftcol", &tv) == FAIL) | |
2912 | + break; | |
2913 | + tv.vval.v_number = skipcol; | |
2914 | + if (dict_add_tv(d, "skipcol", &tv) == FAIL) | |
2915 | + break; | |
2916 | + return d; | |
2917 | + } | |
2918 | + dict_unref(d); | |
2919 | + return NULL; | |
2920 | +} | |
2921 | + | |
2922 | +// Return values of check_window_scroll_resize(): | |
2923 | +#define CWSR_SCROLLED 1 // at least one window scrolled | |
2924 | +#define CWSR_RESIZED 2 // at least one window size changed | |
2925 | + | |
2926 | +/* | |
2927 | + * This function is used for three purposes: | |
2928 | + * 1. Goes over all windows in the current tab page and returns: | |
2929 | + * 0 no scrolling and no size changes found | |
2930 | + * CWSR_SCROLLED at least one window scrolled | |
2931 | + * CWSR_RESIZED at least one window changed size | |
2932 | + * CWSR_SCROLLED + CWSR_RESIZED both | |
2933 | + * "size_count" is set to the nr of windows with size changes. | |
2934 | + * "first_scroll_win" is set to the first window with any relevant changes. | |
2935 | + * "first_size_win" is set to the first window with size changes. | |
2936 | + * | |
2937 | + * 2. When the first three arguments are NULL and "winlist" is not NULL, | |
2938 | + * "winlist" is set to the list of window IDs with size changes. | |
2939 | + * | |
2940 | + * 3. When the first three arguments are NULL and "v_event" is not NULL, | |
2941 | + * information about changed windows is added to "v_event". | |
2942 | + */ | |
2943 | + static int | |
2944 | +check_window_scroll_resize( | |
2945 | + int *size_count, | |
2946 | + win_T **first_scroll_win, | |
2947 | + win_T **first_size_win, | |
2948 | + list_T *winlist, | |
2949 | + dict_T *v_event) | |
2950 | +{ | |
2951 | + int result = 0; | |
2952 | + int listidx = 0; | |
2953 | + int tot_width = 0; | |
2954 | + int tot_height = 0; | |
2955 | + int tot_topline = 0; | |
2956 | + int tot_leftcol = 0; | |
2957 | + int tot_skipcol = 0; | |
2887 | 2958 | |
2888 | 2959 | win_T *wp; |
2889 | 2960 | FOR_ALL_WINDOWS(wp) |
2890 | - if (wp->w_last_topline != wp->w_topline | |
2891 | - || wp->w_last_leftcol != wp->w_leftcol | |
2892 | - || wp->w_last_skipcol != wp->w_skipcol | |
2893 | - || wp->w_last_width != wp->w_width | |
2894 | - || wp->w_last_height != wp->w_height) | |
2961 | + { | |
2962 | + int size_changed = wp->w_last_width != wp->w_width | |
2963 | + || wp->w_last_height != wp->w_height; | |
2964 | + if (size_changed) | |
2895 | 2965 | { |
2896 | - // WinScrolled is triggered only once, even when multiple windows | |
2897 | - // scrolled or changed size. Store the current values before | |
2898 | - // triggering the event, if a scroll or resize happens as a side | |
2899 | - // effect then WinScrolled is triggered again later. | |
2900 | - snapshot_windows_scroll_size(); | |
2901 | - | |
2902 | - // "curwin" may be different from the actual current window, make | |
2903 | - // sure it can be restored. | |
2904 | - window_layout_lock(); | |
2905 | - | |
2906 | - recursive = TRUE; | |
2966 | + result |= CWSR_RESIZED; | |
2967 | + if (winlist != NULL) | |
2968 | + { | |
2969 | + // Add this window to the list of changed windows. | |
2970 | + typval_T tv; | |
2971 | + tv.v_lock = 0; | |
2972 | + tv.v_type = VAR_NUMBER; | |
2973 | + tv.vval.v_number = wp->w_id; | |
2974 | + list_set_item(winlist, listidx++, &tv); | |
2975 | + } | |
2976 | + else if (size_count != NULL) | |
2977 | + { | |
2978 | + ++*size_count; | |
2979 | + if (*first_size_win == NULL) | |
2980 | + *first_size_win = wp; | |
2981 | + // For WinScrolled the first window with a size change is used | |
2982 | + // even when it didn't scroll. | |
2983 | + if (*first_scroll_win == NULL) | |
2984 | + *first_scroll_win = wp; | |
2985 | + } | |
2986 | + } | |
2987 | + | |
2988 | + int scroll_changed = wp->w_last_topline != wp->w_topline | |
2989 | + || wp->w_last_leftcol != wp->w_leftcol | |
2990 | + || wp->w_last_skipcol != wp->w_skipcol; | |
2991 | + if (scroll_changed) | |
2992 | + { | |
2993 | + result |= CWSR_SCROLLED; | |
2994 | + if (first_scroll_win != NULL && *first_scroll_win == NULL) | |
2995 | + *first_scroll_win = wp; | |
2996 | + } | |
2997 | + | |
2998 | + if ((size_changed || scroll_changed) && v_event != NULL) | |
2999 | + { | |
3000 | + // Add info about this window to the v:event dictionary. | |
3001 | + int width = wp->w_width - wp->w_last_width; | |
3002 | + int height = wp->w_height - wp->w_last_height; | |
3003 | + int topline = wp->w_topline - wp->w_last_topline; | |
3004 | + int leftcol = wp->w_leftcol - wp->w_last_leftcol; | |
3005 | + int skipcol = wp->w_skipcol - wp->w_last_skipcol; | |
3006 | + dict_T *d = make_win_info_dict(width, height, | |
3007 | + topline, leftcol, skipcol); | |
3008 | + if (d == NULL) | |
3009 | + break; | |
3010 | + char winid[NUMBUFLEN]; | |
3011 | + vim_snprintf(winid, sizeof(winid), "%d", wp->w_id); | |
3012 | + if (dict_add_dict(v_event, winid, d) == FAIL) | |
3013 | + { | |
3014 | + dict_unref(d); | |
3015 | + break; | |
3016 | + } | |
3017 | + --d->dv_refcount; | |
3018 | + | |
3019 | + tot_width += abs(width); | |
3020 | + tot_height += abs(height); | |
3021 | + tot_topline += abs(topline); | |
3022 | + tot_leftcol += abs(leftcol); | |
3023 | + tot_skipcol += abs(skipcol); | |
3024 | + } | |
3025 | + } | |
3026 | + | |
3027 | + if (v_event != NULL) | |
3028 | + { | |
3029 | + dict_T *alldict = make_win_info_dict(tot_width, tot_height, | |
3030 | + tot_topline, tot_leftcol, tot_skipcol); | |
3031 | + if (alldict != NULL) | |
3032 | + { | |
3033 | + if (dict_add_dict(v_event, "all", alldict) == FAIL) | |
3034 | + dict_unref(alldict); | |
3035 | + else | |
3036 | + --alldict->dv_refcount; | |
3037 | + } | |
3038 | + } | |
3039 | + | |
3040 | + return result; | |
3041 | +} | |
3042 | + | |
3043 | +/* | |
3044 | + * Trigger WinScrolled and/or WinResized if any window in the current tab page | |
3045 | + * scrolled or changed size. | |
3046 | + */ | |
3047 | + void | |
3048 | +may_trigger_win_scrolled_resized(void) | |
3049 | +{ | |
3050 | + static int recursive = FALSE; | |
3051 | + int do_resize = has_winresized(); | |
3052 | + int do_scroll = has_winscrolled(); | |
3053 | + | |
3054 | + // Do not trigger WinScrolled or WinResized recursively. Do not trigger | |
3055 | + // before the initial snapshot of the w_last_ values was made. | |
3056 | + if (recursive | |
3057 | + || !(do_scroll || do_resize) | |
3058 | + || !did_initial_scroll_size_snapshot) | |
3059 | + return; | |
3060 | + | |
3061 | + int size_count = 0; | |
3062 | + win_T *first_scroll_win = NULL, *first_size_win = NULL; | |
3063 | + int cwsr = check_window_scroll_resize(&size_count, | |
3064 | + &first_scroll_win, &first_size_win, | |
3065 | + NULL, NULL); | |
3066 | + int trigger_resize = do_resize && size_count > 0; | |
3067 | + int trigger_scroll = do_scroll && cwsr != 0; | |
3068 | + if (!trigger_resize && !trigger_scroll) | |
3069 | + return; // no relevant changes | |
3070 | + | |
3071 | + list_T *windows_list = NULL; | |
3072 | + if (trigger_resize) | |
3073 | + { | |
3074 | + // Create the list for v:event.windows before making the snapshot. | |
3075 | + windows_list = list_alloc_with_items(size_count); | |
3076 | + (void)check_window_scroll_resize(NULL, NULL, NULL, windows_list, NULL); | |
3077 | + } | |
3078 | + | |
3079 | + dict_T *scroll_dict = NULL; | |
3080 | + if (trigger_scroll) | |
3081 | + { | |
3082 | + // Create the dict with entries for v:event before making the snapshot. | |
3083 | + scroll_dict = dict_alloc(); | |
3084 | + if (scroll_dict != NULL) | |
3085 | + { | |
3086 | + scroll_dict->dv_refcount = 1; | |
3087 | + (void)check_window_scroll_resize(NULL, NULL, NULL, NULL, | |
3088 | + scroll_dict); | |
3089 | + } | |
3090 | + } | |
3091 | + | |
3092 | + // WinScrolled/WinResized are triggered only once, even when multiple | |
3093 | + // windows scrolled or changed size. Store the current values before | |
3094 | + // triggering the event, if a scroll or resize happens as a side effect | |
3095 | + // then WinScrolled/WinResized is triggered for that later. | |
3096 | + snapshot_windows_scroll_size(); | |
3097 | + | |
3098 | + // "curwin" may be different from the actual current window, make | |
3099 | + // sure it can be restored. | |
3100 | + window_layout_lock(); | |
3101 | + recursive = TRUE; | |
3102 | + | |
3103 | + // If both are to be triggered do WinResized first. | |
3104 | + if (trigger_resize) | |
3105 | + { | |
3106 | + save_v_event_T save_v_event; | |
3107 | + dict_T *v_event = get_v_event(&save_v_event); | |
3108 | + | |
3109 | + if (dict_add_list(v_event, "windows", windows_list) == OK) | |
3110 | + { | |
3111 | + dict_set_items_ro(v_event); | |
3112 | + | |
2907 | 3113 | char_u winid[NUMBUFLEN]; |
2908 | - vim_snprintf((char *)winid, sizeof(winid), "%d", wp->w_id); | |
2909 | - apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE, | |
2910 | - wp->w_buffer); | |
2911 | - recursive = FALSE; | |
2912 | - window_layout_unlock(); | |
2913 | - | |
2914 | - break; | |
3114 | + vim_snprintf((char *)winid, sizeof(winid), "%d", | |
3115 | + first_size_win->w_id); | |
3116 | + apply_autocmds(EVENT_WINRESIZED, winid, winid, FALSE, | |
3117 | + first_size_win->w_buffer); | |
2915 | 3118 | } |
3119 | + restore_v_event(v_event, &save_v_event); | |
3120 | + } | |
3121 | + | |
3122 | + if (trigger_scroll) | |
3123 | + { | |
3124 | + save_v_event_T save_v_event; | |
3125 | + dict_T *v_event = get_v_event(&save_v_event); | |
3126 | + | |
3127 | + // Move the entries from scroll_dict to v_event. | |
3128 | + dict_extend(v_event, scroll_dict, (char_u *)"move", NULL); | |
3129 | + dict_set_items_ro(v_event); | |
3130 | + dict_unref(scroll_dict); | |
3131 | + | |
3132 | + char_u winid[NUMBUFLEN]; | |
3133 | + vim_snprintf((char *)winid, sizeof(winid), "%d", | |
3134 | + first_scroll_win->w_id); | |
3135 | + apply_autocmds(EVENT_WINSCROLLED, winid, winid, FALSE, | |
3136 | + first_scroll_win->w_buffer); | |
3137 | + | |
3138 | + restore_v_event(v_event, &save_v_event); | |
3139 | + } | |
3140 | + | |
3141 | + recursive = FALSE; | |
3142 | + window_layout_unlock(); | |
2916 | 3143 | } |
2917 | 3144 | |
2918 | 3145 | /* |