aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSean Dewar <seandewar@users.noreply.github.com>2024-02-04 02:59:26 +0000
committerSean Dewar <6256228+seandewar@users.noreply.github.com>2024-03-08 23:23:55 +0000
commita873f33993ef84e3f954127038e559e1ac1cac43 (patch)
tree732e38f3a4db9af8403ee9633bbc88f53e16d570 /src
parent233649bc757743f7677b2ae414779192a94aa2ae (diff)
downloadrneovim-a873f33993ef84e3f954127038e559e1ac1cac43.tar.gz
rneovim-a873f33993ef84e3f954127038e559e1ac1cac43.tar.bz2
rneovim-a873f33993ef84e3f954127038e559e1ac1cac43.zip
fix(api): open_win fire BufWinEnter for other buffer when !enter && !noautocmd
Problem: BufWinEnter is not fired when not entering a new window, even when a different buffer is specified and buffer-related autocommands are unblocked (!noautocmd). Solution: fire it in the context of the new window and buffer. Do not do it if the buffer is unchanged, like :{s}buffer. Be wary of autocommands! For example, it's possible for nvim_win_set_config to be used in an autocommand to move a window to a different tabpage (in contrast, things like wincmd T actually create a *new* window, so it may not have been possible before, meaning other parts of Nvim could assume windows can't do this... I'd be especially cautious of logic that restores curwin and curtab without checking if curwin is still valid in curtab, if any such logic exists). Also, bail early from win_set_buf if setting the temp curwin fails; this shouldn't be possible, as the callers check that wp is valid, but in case that's not true, win_set_buf will no longer continue setting a buffer for the wrong window. Note that pum_create_float_preview also uses win_set_buf, but from a glance, doesn't look like it properly checks for autocmds screwing things up (win_enter, nvim_create_buf...). I haven't addressed that here. Also adds some test coverage for nvim_open_win autocommands. Closes #27121.
Diffstat (limited to 'src')
-rw-r--r--src/nvim/api/win_config.c39
-rw-r--r--src/nvim/window.c6
2 files changed, 35 insertions, 10 deletions
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index e53e13e2a3..9d63a1997c 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -261,8 +261,8 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err
switchwin_T switchwin;
// `parent` is valid in `tp`, so switch_win should not fail.
const int result = switch_win(&switchwin, parent, tp, true);
- (void)result;
assert(result == OK);
+ (void)result;
wp = win_split_ins(0, flags, NULL, 0);
restore_win(&switchwin, true);
}
@@ -276,18 +276,41 @@ Window nvim_open_win(Buffer buffer, Boolean enter, Dict(win_config) *config, Err
api_set_error(err, kErrorTypeException, "Failed to create window");
return 0;
}
+
+ // Autocommands may close `wp` or move it to another tabpage, so update and check `tp` after each
+ // event. In each case, `wp` should already be valid in `tp`, so switch_win should not fail.
switchwin_T switchwin;
- if (switch_win_noblock(&switchwin, wp, tp, true) == OK) {
- apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf);
+ {
+ const int result = switch_win_noblock(&switchwin, wp, tp, true);
+ assert(result == OK);
+ (void)result;
+ if (apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf)) {
+ tp = win_find_tabpage(wp);
+ }
+ restore_win_noblock(&switchwin, true);
}
- restore_win_noblock(&switchwin, true);
- if (enter) {
+ if (tp && enter) {
goto_tabpage_win(tp, wp);
+ tp = win_find_tabpage(wp);
}
- if (win_valid_any_tab(wp) && buf != wp->w_buffer) {
- win_set_buf(wp, buf, !enter || fconfig.noautocmd, err);
+ if (tp && buf != wp->w_buffer) {
+ const bool noautocmd = !enter || fconfig.noautocmd;
+ win_set_buf(wp, buf, noautocmd, err);
+ if (!noautocmd) {
+ tp = win_find_tabpage(wp);
+ }
+ // win_set_buf autocommands were blocked if we didn't enter, but we still want BufWinEnter.
+ if (noautocmd && !fconfig.noautocmd && wp->w_buffer == buf) {
+ const int result = switch_win_noblock(&switchwin, wp, tp, true);
+ assert(result == OK);
+ (void)result;
+ if (apply_autocmds(EVENT_BUFWINENTER, NULL, NULL, false, buf)) {
+ tp = win_find_tabpage(wp);
+ }
+ restore_win_noblock(&switchwin, true);
+ }
}
- if (!win_valid_any_tab(wp)) {
+ if (!tp) {
api_set_error(err, kErrorTypeException, "Window was closed immediately");
return 0;
}
diff --git a/src/nvim/window.c b/src/nvim/window.c
index e2c4524eaa..d5a6e347e7 100644
--- a/src/nvim/window.c
+++ b/src/nvim/window.c
@@ -718,6 +718,7 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err)
kErrorTypeException,
"Failed to switch to window %d",
win->handle);
+ goto cleanup;
}
try_start();
@@ -729,10 +730,11 @@ void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err)
buf->handle);
}
- // If window is not current, state logic will not validate its cursor.
- // So do it now.
+ // If window is not current, state logic will not validate its cursor. So do it now.
+ // Still needed if do_buffer returns FAIL (e.g: autocmds abort script after buffer was set).
validate_cursor();
+cleanup:
restore_win_noblock(&switchwin, true);
if (noautocmd) {
unblock_autocmds();