diff --git a/src/nvim/eval/window.c b/src/nvim/eval/window.c index 47f4ac3123..f2f526fa30 100644 --- a/src/nvim/eval/window.c +++ b/src/nvim/eval/window.c @@ -48,6 +48,20 @@ bool win_has_winnr(win_T *wp, tabpage_T *tp) || (!wp->w_config.hide && wp->w_config.focusable); } +win_T *win_find_nr(int nr, tabpage_T *tp) +{ + if (tp == NULL) { + assert(curtab); + tp = curtab; + } + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + if ((nr -= win_has_winnr(wp, tp)) <= 0) { + return wp; + } + } + return NULL; +} + static int win_getid(typval_T *argvars) { if (argvars[0].v_type == VAR_UNKNOWN) { @@ -169,16 +183,17 @@ win_T *find_win_by_nr(typval_T *vp, tabpage_T *tp) tp = curtab; } - FOR_ALL_WINDOWS_IN_TAB(wp, tp) { - if (nr >= LOWEST_WIN_ID) { + if (nr >= LOWEST_WIN_ID) { + // window id + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (wp->handle == nr) { return wp; } - } else if (--nr <= 0) { - return wp; } + return NULL; } - return NULL; + + return win_find_nr(nr, tp); } /// Find a window: When using a Window ID in any tab page, when using a number diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 3fb7771ad6..b5202902d0 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -44,6 +44,7 @@ #include "nvim/eval/typval_defs.h" #include "nvim/eval/userfunc.h" #include "nvim/eval/vars.h" +#include "nvim/eval/window.h" #include "nvim/eval_defs.h" #include "nvim/event/loop.h" #include "nvim/event/multiqueue.h" @@ -1075,14 +1076,24 @@ static int compute_buffer_local_count(cmd_addr_T addr_type, linenr_T lnum, int o /// @return the window number of "win" or, /// the number of windows if "win" is NULL -static int current_win_nr(const win_T *win) +static int current_win_nr(const win_T *win, exarg_T *cmdarg) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { + // Whether to also match hidden / non-focusable windows (in current tab). + bool nonnr = cmdarg != NULL && cmdarg->cmdidx != CMD_quit; int nr = 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - nr++; + // only include non-hidden and focusable windows + // (so ui2 windows are excluded) + bool include_this = nonnr || win_has_winnr(wp, curtab); + nr += include_this; + if (wp == win) { + // same as in win_id2win() + if (!include_this) { + return 0; + } break; } } @@ -1102,8 +1113,8 @@ static int current_tab_nr(tabpage_T *tab) return nr; } -#define CURRENT_WIN_NR current_win_nr(curwin) -#define LAST_WIN_NR current_win_nr(NULL) +#define CURRENT_WIN_NR current_win_nr(curwin, NULL) +#define LAST_WIN_NR(cmdarg) current_win_nr(NULL, (cmdarg)) #define CURRENT_TAB_NR current_tab_nr(curtab) #define LAST_TAB_NR current_tab_nr(NULL) @@ -1304,7 +1315,7 @@ void set_cmd_dflall_range(exarg_T *eap) eap->line2 = lastbuf->b_fnum; break; case ADDR_WINDOWS: - eap->line2 = LAST_WIN_NR; + eap->line2 = LAST_WIN_NR(eap); break; case ADDR_TABS: eap->line2 = LAST_TAB_NR; @@ -2854,7 +2865,7 @@ int parse_cmd_address(exarg_T *eap, const char **errormsg, bool silent) if (IS_USER_CMDIDX(eap->cmdidx)) { eap->line1 = 1; eap->line2 = eap->addr_type == ADDR_WINDOWS - ? LAST_WIN_NR : LAST_TAB_NR; + ? LAST_WIN_NR(false) : LAST_TAB_NR; } else { // there is no Vim command which uses '%' and // ADDR_WINDOWS or ADDR_TABS @@ -3429,7 +3440,7 @@ linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, bool skip, lnum = curbuf->b_ml.ml_line_count; break; case ADDR_WINDOWS: - lnum = LAST_WIN_NR; + lnum = LAST_WIN_NR(eap); break; case ADDR_ARGUMENTS: lnum = ARGCOUNT; @@ -3773,7 +3784,7 @@ char *invalid_range(exarg_T *eap) } break; case ADDR_WINDOWS: - if (eap->line2 > LAST_WIN_NR) { + if (eap->line2 > LAST_WIN_NR(eap)) { return _(e_invrange); } break; @@ -5124,24 +5135,17 @@ fail_1: /// ":close": close current window, unless it is the last one static void ex_close(exarg_T *eap) { - win_T *win = NULL; - int winnr = 0; if (!text_locked() && !curbuf_locked()) { + win_T *win; if (eap->addr_count == 0) { - ex_win_close(eap->forceit, curwin, NULL); + win = curwin; } else { - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - winnr++; - if (winnr == eap->line2) { - win = wp; - break; - } - } + win = win_find_nr((int)eap->line2, curtab); if (win == NULL) { win = lastwin; } - ex_win_close(eap->forceit, win, NULL); } + ex_win_close(eap->forceit, win, NULL); } } @@ -5369,19 +5373,11 @@ static void ex_hide(exarg_T *eap) return; } - win_T *win = NULL; + win_T *win; if (eap->addr_count == 0) { win = curwin; } else { - int winnr = 0; - - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - winnr++; - if (winnr == eap->line2) { - win = wp; - break; - } - } + win = win_find_nr((int)eap->line2, curtab); if (win == NULL) { win = lastwin; } diff --git a/test/functional/ui/cmdline2_spec.lua b/test/functional/ui/cmdline2_spec.lua index 0aeed9ee9e..7564416192 100644 --- a/test/functional/ui/cmdline2_spec.lua +++ b/test/functional/ui/cmdline2_spec.lua @@ -324,6 +324,16 @@ describe('cmdline2', function() {16::}{15:call} {25:foo}{16:(}{25:bar}{16:(}^ | ]]) end) + + it("doesn't interfere with :$q", function() + exec('split b') + exec('wincmd J') + exec('wincmd p') + exec('$q') -- this would close a ui2 window, not the second visible window + + local nwins = vim.fn.winnr('$') + assert(nwins == 1) + end) end) describe('cmdline2', function()