mirror of
https://github.com/neovim/neovim.git
synced 2025-09-05 19:08:15 +00:00
vim-patch:partial:9.0.0917: the WinScrolled autocommand event is not enough (#21161)
Problem: The WinScrolled autocommand event is not enough. Solution: Add WinResized and provide information about what changed. (closes vim/vim#11576)35fc61cb5b
Omit "func_name" comment in tv_dict_extend(): Vim9 script only. Skip layout locking and E1312. Skip list_alloc_with_items() and list_set_item(). Since this overrides remaining changes in patch 9.0.0913, that patch can now be marked as fully ported: vim-patch:9.0.0913: only change in current window triggers the WinScrolled event N/A patches for version.c: vim-patch:9.0.0919: build failure with tiny features Problem: Build failure with tiny features. Solution: Adjust #ifdef's.9c5b7cb4cf
Co-authored-by: Bram Moolenaar <Bram@vim.org>
This commit is contained in:
@@ -1095,21 +1095,24 @@ WinNew When a new window was created. Not done for
|
||||
Before WinEnter.
|
||||
|
||||
*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.
|
||||
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
|
||||
@@ -1117,8 +1120,17 @@ WinScrolled After scrolling the content of a window or
|
||||
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.
|
||||
|
||||
*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-pattern* *{aupat}*
|
||||
|
@@ -607,6 +607,51 @@ it).
|
||||
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|:
|
||||
{
|
||||
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.
|
||||
|
||||
==============================================================================
|
||||
7. Argument and buffer list commands *buffer-list*
|
||||
|
||||
|
@@ -123,7 +123,8 @@ return {
|
||||
'WinEnter', -- after entering a window
|
||||
'WinLeave', -- before leaving a window
|
||||
'WinNew', -- when entering a new window
|
||||
'WinScrolled', -- after scrolling a window
|
||||
'WinResized', -- after a window was resized
|
||||
'WinScrolled', -- after a window was scrolled or resized
|
||||
},
|
||||
aliases = {
|
||||
BufCreate = 'BufAdd',
|
||||
|
@@ -1141,10 +1141,11 @@ int autocmd_register(int64_t id, event_T event, char *pat, int patlen, int group
|
||||
curwin->w_last_cursormoved = curwin->w_cursor;
|
||||
}
|
||||
|
||||
// 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_event(EVENT_WINSCROLLED)) {
|
||||
// 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_event(EVENT_WINSCROLLED) || has_event(EVENT_WINRESIZED))) {
|
||||
tabpage_T *save_curtab = curtab;
|
||||
FOR_ALL_TABS(tp) {
|
||||
unuse_tabpage(curtab);
|
||||
@@ -1781,7 +1782,7 @@ bool apply_autocmds_group(event_T event, char *fname, char *fname_io, bool force
|
||||
|| event == EVENT_SIGNAL || event == EVENT_SPELLFILEMISSING
|
||||
|| event == EVENT_SYNTAX || event == EVENT_TABCLOSED
|
||||
|| event == EVENT_USER || event == EVENT_WINCLOSED
|
||||
|| event == EVENT_WINSCROLLED) {
|
||||
|| event == EVENT_WINRESIZED || event == EVENT_WINSCROLLED) {
|
||||
fname = xstrdup(fname);
|
||||
} else {
|
||||
fname = FullName_save(fname, false);
|
||||
|
@@ -1339,8 +1339,7 @@ void ins_redraw(bool ready)
|
||||
}
|
||||
|
||||
if (ready) {
|
||||
// Trigger Scroll if viewport changed.
|
||||
may_trigger_winscrolled();
|
||||
may_trigger_win_scrolled_resized();
|
||||
}
|
||||
|
||||
// Trigger BufModified if b_changed_invalid is set.
|
||||
|
@@ -2446,10 +2446,10 @@ void tv_dict_clear(dict_T *const d)
|
||||
///
|
||||
/// @param d1 Dictionary to extend.
|
||||
/// @param[in] d2 Dictionary to extend with.
|
||||
/// @param[in] action "error", "force", "keep":
|
||||
///
|
||||
/// @param[in] action "error", "force", "move", "keep":
|
||||
/// e*, including "error": duplicate key gives an error.
|
||||
/// f*, including "force": duplicate d2 keys override d1.
|
||||
/// m*, including "move": move items instead of copying.
|
||||
/// other, including "keep": duplicate d2 keys ignored.
|
||||
void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
@@ -2458,19 +2458,31 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
|
||||
const char *const arg_errmsg = _("extend() argument");
|
||||
const size_t arg_errmsg_len = strlen(arg_errmsg);
|
||||
|
||||
TV_DICT_ITER(d2, di2, {
|
||||
if (*action == 'm') {
|
||||
hash_lock(&d2->dv_hashtab); // don't rehash on hash_remove()
|
||||
}
|
||||
|
||||
HASHTAB_ITER(&d2->dv_hashtab, hi2, {
|
||||
dictitem_T *const di2 = TV_DICT_HI2DI(hi2);
|
||||
dictitem_T *const di1 = tv_dict_find(d1, (const char *)di2->di_key, -1);
|
||||
// Check the key to be valid when adding to any scope.
|
||||
if (d1->dv_scope != VAR_NO_SCOPE && !valid_varname((const char *)di2->di_key)) {
|
||||
break;
|
||||
}
|
||||
if (di1 == NULL) {
|
||||
dictitem_T *const new_di = tv_dict_item_copy(di2);
|
||||
if (tv_dict_add(d1, new_di) == FAIL) {
|
||||
tv_dict_item_free(new_di);
|
||||
} else if (watched) {
|
||||
tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv,
|
||||
NULL);
|
||||
if (*action == 'm') {
|
||||
// cheap way to move a dict item from "d2" to "d1"
|
||||
dictitem_T *const new_di = di2;
|
||||
tv_dict_add(d1, new_di);
|
||||
hash_remove(&d2->dv_hashtab, hi2);
|
||||
tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, NULL);
|
||||
} else {
|
||||
dictitem_T *const new_di = tv_dict_item_copy(di2);
|
||||
if (tv_dict_add(d1, new_di) == FAIL) {
|
||||
tv_dict_item_free(new_di);
|
||||
} else if (watched) {
|
||||
tv_dict_watcher_notify(d1, (const char *)new_di->di_key, &new_di->di_tv, NULL);
|
||||
}
|
||||
}
|
||||
} else if (*action == 'e') {
|
||||
semsg(_("E737: Key already exists: %s"), di2->di_key);
|
||||
@@ -2501,6 +2513,10 @@ void tv_dict_extend(dict_T *const d1, dict_T *const d2, const char *const action
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (*action == 'm') {
|
||||
hash_unlock(&d2->dv_hashtab);
|
||||
}
|
||||
}
|
||||
|
||||
/// Compare two dictionaries
|
||||
|
@@ -1230,8 +1230,7 @@ static void normal_check_interrupt(NormalState *s)
|
||||
static void normal_check_window_scrolled(NormalState *s)
|
||||
{
|
||||
if (!finish_op) {
|
||||
// Trigger Scroll if the viewport changed.
|
||||
may_trigger_winscrolled();
|
||||
may_trigger_win_scrolled_resized();
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -295,6 +295,61 @@ func Test_win_tab_autocmd()
|
||||
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
|
||||
|
||||
@@ -305,11 +360,15 @@ func Test_WinScrolled()
|
||||
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})
|
||||
@@ -321,15 +380,33 @@ func Test_WinScrolled()
|
||||
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.
|
||||
@@ -337,6 +414,12 @@ func Test_WinScrolled()
|
||||
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)
|
||||
@@ -345,6 +428,7 @@ func Test_WinScrolled()
|
||||
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
|
||||
|
||||
|
@@ -5301,39 +5301,241 @@ void may_make_initial_scroll_size_snapshot(void)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trigger WinScrolled if any window scrolled or changed size.
|
||||
void may_trigger_winscrolled(void)
|
||||
/// Create a dictionary with information about size and scroll changes in a
|
||||
/// window.
|
||||
/// Returns the dictionary with refcount set to one.
|
||||
/// Returns NULL on internal error.
|
||||
static dict_T *make_win_info_dict(int width, int height, int topline, int leftcol, int skipcol)
|
||||
{
|
||||
dict_T *const d = tv_dict_alloc();
|
||||
d->dv_refcount = 1;
|
||||
|
||||
// not actually looping, for breaking out on error
|
||||
while (1) {
|
||||
typval_T tv = {
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.v_type = VAR_NUMBER,
|
||||
};
|
||||
|
||||
tv.vval.v_number = width;
|
||||
if (tv_dict_add_tv(d, S_LEN("width"), &tv) == FAIL) {
|
||||
break;
|
||||
}
|
||||
tv.vval.v_number = height;
|
||||
if (tv_dict_add_tv(d, S_LEN("height"), &tv) == FAIL) {
|
||||
break;
|
||||
}
|
||||
tv.vval.v_number = topline;
|
||||
if (tv_dict_add_tv(d, S_LEN("topline"), &tv) == FAIL) {
|
||||
break;
|
||||
}
|
||||
tv.vval.v_number = leftcol;
|
||||
if (tv_dict_add_tv(d, S_LEN("leftcol"), &tv) == FAIL) {
|
||||
break;
|
||||
}
|
||||
tv.vval.v_number = skipcol;
|
||||
if (tv_dict_add_tv(d, S_LEN("skipcol"), &tv) == FAIL) {
|
||||
break;
|
||||
}
|
||||
return d;
|
||||
}
|
||||
tv_dict_unref(d);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/// Return values of check_window_scroll_resize():
|
||||
enum {
|
||||
CWSR_SCROLLED = 1, ///< at least one window scrolled
|
||||
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;
|
||||
|
||||
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
||||
const bool 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 = {
|
||||
.v_lock = VAR_UNLOCKED,
|
||||
.v_type = VAR_NUMBER,
|
||||
.vval.v_number = wp->handle,
|
||||
};
|
||||
// tv_list_set_item(winlist, listidx++, &tv);
|
||||
tv_list_append_owned_tv(winlist, 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const bool 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];
|
||||
int key_len = vim_snprintf(winid, sizeof(winid), "%d", wp->handle);
|
||||
if (tv_dict_add_dict(v_event, winid, (size_t)key_len, d) == FAIL) {
|
||||
tv_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 (tv_dict_add_dict(v_event, S_LEN("all"), alldict) == FAIL) {
|
||||
tv_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 bool recursive = false;
|
||||
const bool do_resize = has_event(EVENT_WINRESIZED);
|
||||
const bool do_scroll = has_event(EVENT_WINSCROLLED);
|
||||
|
||||
if (recursive
|
||||
|| !has_event(EVENT_WINSCROLLED)
|
||||
|| !(do_scroll || do_resize)
|
||||
|| !did_initial_scroll_size_snapshot) {
|
||||
return;
|
||||
}
|
||||
|
||||
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
|
||||
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();
|
||||
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 = tv_list_alloc_with_items(size_count);
|
||||
windows_list = tv_list_alloc(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 = tv_dict_alloc();
|
||||
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();
|
||||
|
||||
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 (tv_dict_add_list(v_event, S_LEN("windows"), windows_list) == OK) {
|
||||
tv_dict_set_keys_readonly(v_event);
|
||||
|
||||
char winid[NUMBUFLEN];
|
||||
vim_snprintf(winid, sizeof(winid), "%d", wp->handle);
|
||||
|
||||
recursive = true;
|
||||
apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, wp->w_buffer);
|
||||
recursive = false;
|
||||
|
||||
break;
|
||||
vim_snprintf(winid, sizeof(winid), "%d", first_size_win->handle);
|
||||
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.
|
||||
tv_dict_extend(v_event, scroll_dict, "move");
|
||||
tv_dict_set_keys_readonly(v_event);
|
||||
tv_dict_unref(scroll_dict);
|
||||
|
||||
char winid[NUMBUFLEN];
|
||||
vim_snprintf(winid, sizeof(winid), "%d", first_scroll_win->handle);
|
||||
apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, first_scroll_win->w_buffer);
|
||||
|
||||
restore_v_event(v_event, &save_v_event);
|
||||
}
|
||||
|
||||
recursive = false;
|
||||
}
|
||||
|
||||
// Save the size of all windows in "gap".
|
||||
|
285
test/functional/autocmd/win_scrolled_resized_spec.lua
Normal file
285
test/functional/autocmd/win_scrolled_resized_spec.lua
Normal file
@@ -0,0 +1,285 @@
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
local clear = helpers.clear
|
||||
local eq = helpers.eq
|
||||
local eval = helpers.eval
|
||||
local exec = helpers.exec
|
||||
local command = helpers.command
|
||||
local feed = helpers.feed
|
||||
local meths = helpers.meths
|
||||
local assert_alive = helpers.assert_alive
|
||||
|
||||
before_each(clear)
|
||||
|
||||
describe('WinResized', function()
|
||||
-- oldtest: Test_WinResized()
|
||||
it('works', function()
|
||||
exec([[
|
||||
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
|
||||
au WinResized * let g:v_event = deepcopy(v:event)
|
||||
]])
|
||||
eq(0, eval('g:resized'))
|
||||
|
||||
-- increase window height, two windows will be reported
|
||||
feed('<C-W>+')
|
||||
eq(1, eval('g:resized'))
|
||||
eq({windows = {1002, 1001}}, eval('g:v_event'))
|
||||
|
||||
-- increase window width, three windows will be reported
|
||||
feed('<C-W>>')
|
||||
eq(2, eval('g:resized'))
|
||||
eq({windows = {1002, 1001, 1000}}, eval('g:v_event'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('WinScrolled', function()
|
||||
local win_id
|
||||
|
||||
before_each(function()
|
||||
win_id = meths.get_current_win().id
|
||||
command(string.format('autocmd WinScrolled %d let g:matched = v:true', win_id))
|
||||
exec([[
|
||||
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 * let g:v_event = deepcopy(v:event)
|
||||
]])
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
eq(true, eval('g:matched'))
|
||||
eq(win_id, eval('g:amatch'))
|
||||
eq(win_id, eval('g:afile'))
|
||||
end)
|
||||
|
||||
it('is triggered by scrolling vertically', function()
|
||||
local lines = {'123', '123'}
|
||||
meths.buf_set_lines(0, 0, -1, true, lines)
|
||||
eq(0, eval('g:scrolled'))
|
||||
|
||||
feed('<C-E>')
|
||||
eq(1, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 0, topline = 1, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = 0, topline = 1, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
|
||||
feed('<C-Y>')
|
||||
eq(2, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 0, topline = 1, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = 0, topline = -1, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
end)
|
||||
|
||||
it('is triggered by scrolling horizontally', function()
|
||||
command('set nowrap')
|
||||
local width = meths.win_get_width(0)
|
||||
local line = '123' .. ('*'):rep(width * 2)
|
||||
local lines = {line, line}
|
||||
meths.buf_set_lines(0, 0, -1, true, lines)
|
||||
eq(0, eval('g:scrolled'))
|
||||
|
||||
feed('zl')
|
||||
eq(1, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = 1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
|
||||
feed('zh')
|
||||
eq(2, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = -1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
end)
|
||||
|
||||
it('is triggered by horizontal scrolling from cursor move', function()
|
||||
command('set nowrap')
|
||||
local lines = {'', '', 'Foo'}
|
||||
meths.buf_set_lines(0, 0, -1, true, lines)
|
||||
meths.win_set_cursor(0, {3, 0})
|
||||
eq(0, eval('g:scrolled'))
|
||||
|
||||
feed('zl')
|
||||
eq(1, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = 1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
|
||||
feed('zl')
|
||||
eq(2, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = 1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
|
||||
feed('h')
|
||||
eq(3, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = -1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
|
||||
feed('zh')
|
||||
eq(4, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = -1, topline = 0, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
end)
|
||||
|
||||
-- oldtest: Test_WinScrolled_long_wrapped()
|
||||
it('is triggered by scrolling on a long wrapped line #19968', function()
|
||||
local height = meths.win_get_height(0)
|
||||
local width = meths.win_get_width(0)
|
||||
meths.buf_set_lines(0, 0, -1, true, {('foo'):rep(height * width)})
|
||||
meths.win_set_cursor(0, {1, height * width - 1})
|
||||
eq(0, eval('g:scrolled'))
|
||||
|
||||
feed('gj')
|
||||
eq(1, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 0, topline = 0, width = 0, height = 0, skipcol = width},
|
||||
['1000'] = {leftcol = 0, topline = 0, width = 0, height = 0, skipcol = width},
|
||||
}, eval('g:v_event'))
|
||||
|
||||
feed('0')
|
||||
eq(2, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 0, topline = 0, width = 0, height = 0, skipcol = width},
|
||||
['1000'] = {leftcol = 0, topline = 0, width = 0, height = 0, skipcol = -width},
|
||||
}, eval('g:v_event'))
|
||||
|
||||
feed('$')
|
||||
eq(3, eval('g:scrolled'))
|
||||
end)
|
||||
|
||||
it('is triggered when the window scrolls in Insert mode', function()
|
||||
local height = meths.win_get_height(0)
|
||||
local lines = {}
|
||||
for i = 1, height * 2 do
|
||||
lines[i] = tostring(i)
|
||||
end
|
||||
meths.buf_set_lines(0, 0, -1, true, lines)
|
||||
|
||||
feed('M')
|
||||
eq(0, eval('g:scrolled'))
|
||||
|
||||
feed('i<C-X><C-E><Esc>')
|
||||
eq(1, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 0, topline = 1, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = 0, topline = 1, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
|
||||
feed('i<C-X><C-Y><Esc>')
|
||||
eq(2, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 0, topline = 1, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = 0, topline = -1, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
|
||||
feed('L')
|
||||
eq(2, eval('g:scrolled'))
|
||||
|
||||
feed('A<CR><Esc>')
|
||||
eq(3, eval('g:scrolled'))
|
||||
eq({
|
||||
all = {leftcol = 0, topline = 1, width = 0, height = 0, skipcol = 0},
|
||||
['1000'] = {leftcol = 0, topline = 1, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('WinScrolled', function()
|
||||
-- oldtest: Test_WinScrolled_mouse()
|
||||
it('is triggered by mouse scrolling in another window', function()
|
||||
local screen = Screen.new(75, 10)
|
||||
screen:attach()
|
||||
exec([[
|
||||
set nowrap scrolloff=0
|
||||
set mouse=a
|
||||
call setline(1, ['foo']->repeat(32))
|
||||
split
|
||||
let g:scrolled = 0
|
||||
au WinScrolled * let g:scrolled += 1
|
||||
]])
|
||||
eq(0, eval('g:scrolled'))
|
||||
|
||||
-- With the upper split focused, send a scroll-down event to the unfocused one.
|
||||
meths.input_mouse('wheel', 'down', '', 0, 6, 0)
|
||||
eq(1, eval('g:scrolled'))
|
||||
|
||||
-- Again, but this time while we're in insert mode.
|
||||
feed('i')
|
||||
meths.input_mouse('wheel', 'down', '', 0, 6, 0)
|
||||
feed('<Esc>')
|
||||
eq(2, eval('g:scrolled'))
|
||||
end)
|
||||
|
||||
-- oldtest: Test_WinScrolled_close_curwin()
|
||||
it('closing window does not cause use-after-free #13265', function()
|
||||
exec([[
|
||||
set nowrap scrolloff=0
|
||||
call setline(1, ['aaa', 'bbb'])
|
||||
vsplit
|
||||
au WinScrolled * close
|
||||
]])
|
||||
|
||||
-- This was using freed memory
|
||||
feed('<C-E>')
|
||||
assert_alive()
|
||||
end)
|
||||
|
||||
it('is triggered by mouse scrolling in unfocused floating window #18222', function()
|
||||
local screen = Screen.new(80, 24)
|
||||
screen:attach()
|
||||
local buf = meths.create_buf(true, true)
|
||||
meths.buf_set_lines(buf, 0, -1, false, {'a', 'b', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n'})
|
||||
local win = meths.open_win(buf, false, {
|
||||
height = 5,
|
||||
width = 10,
|
||||
col = 0,
|
||||
row = 1,
|
||||
relative = 'editor',
|
||||
style = 'minimal'
|
||||
})
|
||||
local winid_str = tostring(win.id)
|
||||
exec([[
|
||||
let g:scrolled = 0
|
||||
autocmd WinScrolled * let g:scrolled += 1
|
||||
autocmd WinScrolled * let g:amatch = expand('<amatch>')
|
||||
autocmd WinScrolled * let g:v_event = deepcopy(v:event)
|
||||
]])
|
||||
eq(0, eval('g:scrolled'))
|
||||
|
||||
meths.input_mouse('wheel', 'down', '', 0, 3, 3)
|
||||
eq(1, eval('g:scrolled'))
|
||||
eq(winid_str, eval('g:amatch'))
|
||||
eq({
|
||||
all = {leftcol = 0, topline = 3, width = 0, height = 0, skipcol = 0},
|
||||
[winid_str] = {leftcol = 0, topline = 3, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
|
||||
meths.input_mouse('wheel', 'up', '', 0, 3, 3)
|
||||
eq(2, eval('g:scrolled'))
|
||||
eq(tostring(win.id), eval('g:amatch'))
|
||||
eq({
|
||||
all = {leftcol = 0, topline = 3, width = 0, height = 0, skipcol = 0},
|
||||
[winid_str] = {leftcol = 0, topline = -3, width = 0, height = 0, skipcol = 0},
|
||||
}, eval('g:v_event'))
|
||||
end)
|
||||
end)
|
@@ -1,162 +0,0 @@
|
||||
local helpers = require('test.functional.helpers')(after_each)
|
||||
local Screen = require('test.functional.ui.screen')
|
||||
|
||||
local clear = helpers.clear
|
||||
local eq = helpers.eq
|
||||
local eval = helpers.eval
|
||||
local exec = helpers.exec
|
||||
local command = helpers.command
|
||||
local feed = helpers.feed
|
||||
local meths = helpers.meths
|
||||
local assert_alive = helpers.assert_alive
|
||||
|
||||
before_each(clear)
|
||||
|
||||
describe('WinScrolled', function()
|
||||
local win_id
|
||||
|
||||
before_each(function()
|
||||
win_id = meths.get_current_win().id
|
||||
command(string.format('autocmd WinScrolled %d let g:matched = v:true', win_id))
|
||||
command('let g:scrolled = 0')
|
||||
command('autocmd WinScrolled * let g:scrolled += 1')
|
||||
command([[autocmd WinScrolled * let g:amatch = str2nr(expand('<amatch>'))]])
|
||||
command([[autocmd WinScrolled * let g:afile = str2nr(expand('<afile>'))]])
|
||||
end)
|
||||
|
||||
after_each(function()
|
||||
eq(true, eval('g:matched'))
|
||||
eq(win_id, eval('g:amatch'))
|
||||
eq(win_id, eval('g:afile'))
|
||||
end)
|
||||
|
||||
it('is triggered by scrolling vertically', function()
|
||||
local lines = {'123', '123'}
|
||||
meths.buf_set_lines(0, 0, -1, true, lines)
|
||||
eq(0, eval('g:scrolled'))
|
||||
feed('<C-E>')
|
||||
eq(1, eval('g:scrolled'))
|
||||
end)
|
||||
|
||||
it('is triggered by scrolling horizontally', function()
|
||||
command('set nowrap')
|
||||
local width = meths.win_get_width(0)
|
||||
local line = '123' .. ('*'):rep(width * 2)
|
||||
local lines = {line, line}
|
||||
meths.buf_set_lines(0, 0, -1, true, lines)
|
||||
eq(0, eval('g:scrolled'))
|
||||
feed('zl')
|
||||
eq(1, eval('g:scrolled'))
|
||||
end)
|
||||
|
||||
it('is triggered by horizontal scrolling from cursor move', function()
|
||||
command('set nowrap')
|
||||
local lines = {'', '', 'Foo'}
|
||||
meths.buf_set_lines(0, 0, -1, true, lines)
|
||||
meths.win_set_cursor(0, {3, 0})
|
||||
eq(0, eval('g:scrolled'))
|
||||
feed('zl')
|
||||
eq(1, eval('g:scrolled'))
|
||||
feed('zl')
|
||||
eq(2, eval('g:scrolled'))
|
||||
feed('h')
|
||||
eq(3, eval('g:scrolled'))
|
||||
end)
|
||||
|
||||
it('is triggered by scrolling on a long wrapped line #19968', function()
|
||||
local height = meths.win_get_height(0)
|
||||
local width = meths.win_get_width(0)
|
||||
meths.buf_set_lines(0, 0, -1, true, {('foo'):rep(height * width)})
|
||||
meths.win_set_cursor(0, {1, height * width - 1})
|
||||
eq(0, eval('g:scrolled'))
|
||||
feed('gj')
|
||||
eq(1, eval('g:scrolled'))
|
||||
feed('0')
|
||||
eq(2, eval('g:scrolled'))
|
||||
feed('$')
|
||||
eq(3, eval('g:scrolled'))
|
||||
end)
|
||||
|
||||
it('is triggered when the window scrolls in Insert mode', function()
|
||||
local height = meths.win_get_height(0)
|
||||
local lines = {}
|
||||
for i = 1, height * 2 do
|
||||
lines[i] = tostring(i)
|
||||
end
|
||||
meths.buf_set_lines(0, 0, -1, true, lines)
|
||||
feed('L')
|
||||
eq(0, eval('g:scrolled'))
|
||||
feed('A<CR><Esc>')
|
||||
eq(1, eval('g:scrolled'))
|
||||
end)
|
||||
end)
|
||||
|
||||
describe('WinScrolled', function()
|
||||
-- oldtest: Test_WinScrolled_mouse()
|
||||
it('is triggered by mouse scrolling in another window', function()
|
||||
local screen = Screen.new(75, 10)
|
||||
screen:attach()
|
||||
exec([[
|
||||
set nowrap scrolloff=0
|
||||
set mouse=a
|
||||
call setline(1, ['foo']->repeat(32))
|
||||
split
|
||||
let g:scrolled = 0
|
||||
au WinScrolled * let g:scrolled += 1
|
||||
]])
|
||||
eq(0, eval('g:scrolled'))
|
||||
|
||||
-- With the upper split focused, send a scroll-down event to the unfocused one.
|
||||
meths.input_mouse('wheel', 'down', '', 0, 6, 0)
|
||||
eq(1, eval('g:scrolled'))
|
||||
|
||||
-- Again, but this time while we're in insert mode.
|
||||
feed('i')
|
||||
meths.input_mouse('wheel', 'down', '', 0, 6, 0)
|
||||
feed('<Esc>')
|
||||
eq(2, eval('g:scrolled'))
|
||||
end)
|
||||
|
||||
-- oldtest: Test_WinScrolled_close_curwin()
|
||||
it('closing window does not cause use-after-free #13265', function()
|
||||
exec([[
|
||||
set nowrap scrolloff=0
|
||||
call setline(1, ['aaa', 'bbb'])
|
||||
vsplit
|
||||
au WinScrolled * close
|
||||
]])
|
||||
|
||||
-- This was using freed memory
|
||||
feed('<C-E>')
|
||||
assert_alive()
|
||||
end)
|
||||
|
||||
it('is triggered by mouse scrolling in unfocused floating window #18222', function()
|
||||
local screen = Screen.new(80, 24)
|
||||
screen:attach()
|
||||
local buf = meths.create_buf(true, true)
|
||||
meths.buf_set_lines(buf, 0, -1, false, {'a', 'b', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n'})
|
||||
local win = meths.open_win(buf, false, {
|
||||
height = 5,
|
||||
width = 10,
|
||||
col = 0,
|
||||
row = 1,
|
||||
relative = 'editor',
|
||||
style = 'minimal'
|
||||
})
|
||||
exec([[
|
||||
let g:scrolled = 0
|
||||
autocmd WinScrolled * let g:scrolled += 1
|
||||
autocmd WinScrolled * let g:amatch = expand('<amatch>')
|
||||
]])
|
||||
eq(0, eval('g:scrolled'))
|
||||
|
||||
meths.input_mouse('wheel', 'down', '', 0, 3, 3)
|
||||
eq(1, eval('g:scrolled'))
|
||||
eq(tostring(win.id), eval('g:amatch'))
|
||||
|
||||
meths.input_mouse('wheel', 'down', '', 0, 3, 3)
|
||||
eq(2, eval('g:scrolled'))
|
||||
eq(tostring(win.id), eval('g:amatch'))
|
||||
end)
|
||||
end)
|
Reference in New Issue
Block a user