diff options
Diffstat (limited to 'src/nvim/window.c')
-rw-r--r-- | src/nvim/window.c | 1628 |
1 files changed, 887 insertions, 741 deletions
diff --git a/src/nvim/window.c b/src/nvim/window.c index 343d279b1d..2bffe2055f 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -26,7 +26,9 @@ #include "nvim/globals.h" #include "nvim/hashtab.h" #include "nvim/main.h" +#include "nvim/mapping.h" #include "nvim/mark.h" +#include "nvim/match.h" #include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" @@ -52,15 +54,13 @@ #include "nvim/vim.h" #include "nvim/window.h" - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "window.c.generated.h" #endif +#define NOWIN ((win_T *)-1) // non-existing window -#define NOWIN (win_T *)-1 // non-existing window - -#define ROWS_AVAIL (Rows - p_ch - tabline_height()) +#define ROWS_AVAIL (Rows - p_ch - tabline_height() - global_stl_height()) /// flags for win_enter_ext() typedef enum { @@ -71,8 +71,49 @@ typedef enum { WEE_TRIGGER_LEAVE_AUTOCMDS = 0x10, } wee_flags_T; +static char e_cannot_split_window_when_closing_buffer[] + = N_("E1159: Cannot split a window when closing the buffer"); + static char *m_onlyone = N_("Already only one window"); +/// When non-zero splitting a window is forbidden. Used to avoid that nasty +/// autocommands mess up the window structure. +static int split_disallowed = 0; + +// #define WIN_DEBUG +#ifdef WIN_DEBUG +/// Call this method to log the current window layout. +static void log_frame_layout(frame_T *frame) +{ + DLOG("layout %s, wi: %d, he: %d, wwi: %d, whe: %d, id: %d", + frame->fr_layout == FR_LEAF ? "LEAF" : frame->fr_layout == FR_ROW ? "ROW" : "COL", + frame->fr_width, + frame->fr_height, + frame->fr_win == NULL ? -1 : frame->fr_win->w_width, + frame->fr_win == NULL ? -1 : frame->fr_win->w_height, + frame->fr_win == NULL ? -1 : frame->fr_win->w_id); + if (frame->fr_child != NULL) { + DLOG("children"); + log_frame_layout(frame->fr_child); + if (frame->fr_next != NULL) { + DLOG("END of children"); + } + } + if (frame->fr_next != NULL) { + log_frame_layout(frame->fr_next); + } +} +#endif + +/// @return the current window, unless in the cmdline window and "prevwin" is +/// set, then return "prevwin". +win_T *prevwin_curwin(void) + FUNC_ATTR_WARN_UNUSED_RESULT +{ + // In cmdwin, the alternative buffer should be used. + return is_in_cmdwin() && prevwin != NULL ? prevwin : curwin; +} + /// all CTRL-W window commands are handled here, called from normal_cmd(). /// /// @param xchar extra char from ":wincmd gx" or NUL @@ -290,7 +331,8 @@ newwindow: // move window to new tab page case 'T': - if (one_window()) { + CHECK_CMDWIN; + if (one_window(curwin)) { msg(_(m_onlyone)); } else { tabpage_T *oldtab = curtab; @@ -304,7 +346,7 @@ newwindow: newtab = curtab; goto_tabpage_tp(oldtab, true, true); if (curwin == wp) { - win_close(curwin, false); + win_close(curwin, false, false); } if (valid_tabpage(newtab)) { goto_tabpage_tp(newtab, true, true); @@ -387,7 +429,7 @@ newwindow: // set current window height case Ctrl__: case '_': - win_setheight(Prenum ? (int)Prenum : Rows-1); + win_setheight(Prenum ? (int)Prenum : Rows - 1); break; // increase current window width @@ -447,9 +489,9 @@ wingotofile: setpcmark(); if (win_split(0, 0) == OK) { RESET_BINDING(curwin); - if (do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL, ECMD_HIDE, NULL) == FAIL) { + if (do_ecmd(0, (char *)ptr, NULL, NULL, ECMD_LASTL, ECMD_HIDE, NULL) == FAIL) { // Failed to open the file, close the window opened for it. - win_close(curwin, false); + win_close(curwin, false, false); goto_tabpage_win(oldtab, oldwin); } else if (nchar == 'F' && lnum >= 0) { curwin->w_cursor.lnum = lnum; @@ -473,9 +515,14 @@ wingotofile: if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) { break; } + + // Make a copy, if the line was changed it will be freed. + ptr = vim_strnsave(ptr, len); + find_pattern_in_path(ptr, 0, len, true, Prenum == 0, type, Prenum1, ACTION_SPLIT, 1, MAXLNUM); - curwin->w_set_curswant = TRUE; + xfree(ptr); + curwin->w_set_curswant = true; break; // Quickfix window only: view the result under the cursor in a new split. @@ -486,17 +533,18 @@ wingotofile: } break; - // CTRL-W g extended commands case 'g': case Ctrl_G: CHECK_CMDWIN; no_mapping++; + allow_keys++; // no mapping for xchar, but allow key codes if (xchar == NUL) { xchar = plain_vgetc(); } LANGMAP_ADJUST(xchar, true); no_mapping--; + allow_keys--; (void)add_to_showcmd(xchar); switch (xchar) { case '}': @@ -521,13 +569,9 @@ wingotofile: do_nv_ident('g', xchar); break; - case TAB: - goto_tabpage_lastused(); - break; - case 'f': // CTRL-W gf: "gf" in a new tab page case 'F': // CTRL-W gF: "gF" in a new tab page - cmdmod.tab = tabpage_index(curtab) + 1; + cmdmod.cmod_tab = tabpage_index(curtab) + 1; nchar = xchar; goto wingotofile; case 't': // CTRL-W gt: go to next tab page @@ -538,6 +582,12 @@ wingotofile: goto_tabpage(-(int)Prenum1); break; + case TAB: // CTRL-W g<Tab>: go to last used tab page + if (!goto_tabpage_lastused()) { + beep_flush(); + } + break; + case 'e': if (curwin->w_floating || !ui_has(kUIMultigrid)) { beep_flush(); @@ -548,7 +598,7 @@ wingotofile: config.height = curwin->w_height; config.external = true; Error err = ERROR_INIT; - if (!win_new_float(curwin, config, &err)) { + if (!win_new_float(curwin, false, config, &err)) { emsg(err.msg); api_clear_error(&err); beep_flush(); @@ -577,18 +627,19 @@ static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, int64_t Pren void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) { - win_T *win = find_window_by_handle(window, err), *save_curwin = curwin; + win_T *win = find_window_by_handle(window, err); buf_T *buf = find_buffer_by_handle(buffer, err); - tabpage_T *tab = win_find_tabpage(win), *save_curtab = curtab; + tabpage_T *tab = win_find_tabpage(win); if (!win || !buf) { return; } - if (noautocmd) { block_autocmds(); } - if (switch_win_noblock(&save_curwin, &save_curtab, win, tab, false) == FAIL) { + + switchwin_T switchwin; + if (switch_win_noblock(&switchwin, win, tab, false) == FAIL) { api_set_error(err, kErrorTypeException, "Failed to switch to window %d", @@ -608,7 +659,7 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) // So do it now. validate_cursor(); - restore_win_noblock(save_curwin, save_curtab, false); + restore_win_noblock(&switchwin, false); if (noautocmd) { unblock_autocmds(); } @@ -616,16 +667,18 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) /// Create a new float. /// -/// if wp == NULL allocate a new window, otherwise turn existing window into a -/// float. It must then already belong to the current tabpage! -/// -/// config must already have been validated! -win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err) +/// @param wp if NULL, allocate a new window, otherwise turn existing window into a float. +/// It must then already belong to the current tabpage! +/// @param last make the window the last one in the window list. +/// Only used when allocating the autocommand window. +/// @param config must already have been validated! +win_T *win_new_float(win_T *wp, bool last, FloatConfig fconfig, Error *err) { if (wp == NULL) { - wp = win_alloc(lastwin_nofloating(), false); + wp = win_alloc(last ? lastwin : lastwin_nofloating(), false); win_init(wp, curwin, 0); } else { + assert(!last); assert(!wp->w_floating); if (firstwin == wp && lastwin_nofloating() == wp) { // last non-float @@ -646,6 +699,8 @@ win_T *win_new_float(win_T *wp, FloatConfig fconfig, Error *err) } wp->w_floating = 1; wp->w_status_height = 0; + wp->w_winbar_height = 0; + wp->w_hsep_height = 0; wp->w_vsep_width = 0; win_config_float(wp, fconfig); @@ -717,7 +772,6 @@ void win_config_float(win_T *wp, FloatConfig fconfig) wp->w_float_config.border_hl_ids, sizeof fconfig.border_hl_ids)); - wp->w_float_config = fconfig; bool has_border = wp->w_floating && wp->w_float_config.border; @@ -730,10 +784,8 @@ void win_config_float(win_T *wp, FloatConfig fconfig) } if (!ui_has(kUIMultigrid)) { - wp->w_height = MIN(wp->w_height, - Rows - 1 - (wp->w_border_adj[0] + wp->w_border_adj[2])); - wp->w_width = MIN(wp->w_width, - Columns - (wp->w_border_adj[1] + wp->w_border_adj[3])); + wp->w_height = MIN(wp->w_height, Rows - 1 - win_border_height(wp)); + wp->w_width = MIN(wp->w_width, Columns - win_border_width(wp)); } win_set_inner_size(wp); @@ -756,7 +808,7 @@ void win_config_float(win_T *wp, FloatConfig fconfig) col += parent->w_wincol; ScreenGrid *grid = &parent->w_grid; int row_off = 0, col_off = 0; - screen_adjust_grid(&grid, &row_off, &col_off); + grid_adjust(&grid, &row_off, &col_off); row += row_off; col += col_off; } @@ -820,22 +872,22 @@ void ui_ext_win_position(win_T *wp) FloatConfig c = wp->w_float_config; if (!c.external) { ScreenGrid *grid = &default_grid; - float row = c.row, col = c.col; + Float row = c.row, col = c.col; if (c.relative == kFloatRelativeWindow) { Error dummy = ERROR_INIT; win_T *win = find_window_by_handle(c.window, &dummy); if (win) { grid = &win->w_grid; int row_off = 0, col_off = 0; - screen_adjust_grid(&grid, &row_off, &col_off); + grid_adjust(&grid, &row_off, &col_off); row += row_off; col += col_off; if (c.bufpos.lnum >= 0) { - pos_T pos = { c.bufpos.lnum+1, c.bufpos.col, 0 }; + pos_T pos = { c.bufpos.lnum + 1, c.bufpos.col, 0 }; int trow, tcol, tcolc, tcole; textpos2screenpos(win, &pos, &trow, &tcol, &tcolc, &tcole, true); - row += trow-1; - col += tcol-1; + row += trow - 1; + col += tcol - 1; } } api_clear_error(&dummy); @@ -843,7 +895,7 @@ void ui_ext_win_position(win_T *wp) wp->w_grid_alloc.zindex = wp->w_float_config.zindex; if (ui_has(kUIMultigrid)) { - String anchor = cstr_to_string(float_anchor_str[c.anchor]); + String anchor = cstr_as_string((char *)float_anchor_str[c.anchor]); ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor, grid->handle, row, col, c.focusable, wp->w_grid_alloc.zindex); @@ -857,7 +909,7 @@ void ui_ext_win_position(win_T *wp) int comp_col = (int)col - (east ? wp->w_width_outer : 0); comp_row += grid->comp_row; comp_col += grid->comp_col; - comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - 1), 0); + comp_row = MAX(MIN(comp_row, Rows - wp->w_height_outer - (p_ch > 0 ? 1 : 0)), 0); comp_col = MAX(MIN(comp_col, Columns - wp->w_width_outer), 0); wp->w_winrow = comp_row; wp->w_wincol = comp_col; @@ -881,18 +933,33 @@ void ui_ext_win_viewport(win_T *wp) if ((wp == curwin || ui_has(kUIMultigrid)) && wp->w_viewport_invalid) { int botline = wp->w_botline; int line_count = wp->w_buffer->b_ml.ml_line_count; - if (botline == line_count+1 && wp->w_empty_rows == 0) { + if (botline == line_count + 1 && wp->w_empty_rows == 0) { // TODO(bfredl): The might be more cases to consider, like how does this // interact with incomplete final line? Diff filler lines? botline = wp->w_buffer->b_ml.ml_line_count; } - ui_call_win_viewport(wp->w_grid_alloc.handle, wp->handle, wp->w_topline-1, - botline, wp->w_cursor.lnum-1, wp->w_cursor.col, + ui_call_win_viewport(wp->w_grid_alloc.handle, wp->handle, wp->w_topline - 1, + botline, wp->w_cursor.lnum - 1, wp->w_cursor.col, line_count); wp->w_viewport_invalid = false; } } +/// If "split_disallowed" is set given an error and return FAIL. +/// Otherwise return OK. +static int check_split_disallowed(void) +{ + if (split_disallowed > 0) { + emsg(_("E242: Can't split a window while closing another")); + return FAIL; + } + if (curwin->w_buffer->b_locked_split) { + emsg(_(e_cannot_split_window_when_closing_buffer)); + return FAIL; + } + return OK; +} + /* * split the current window, implements CTRL-W s and :split * @@ -902,21 +969,25 @@ void ui_ext_win_viewport(win_T *wp) * "flags": * WSP_ROOM: require enough room for new window * WSP_VERT: vertical split. - * WSP_TOP: open window at the top-left of the shell (help window). - * WSP_BOT: open window at the bottom-right of the shell (quickfix window). + * WSP_TOP: open window at the top-left of the screen (help window). + * WSP_BOT: open window at the bottom-right of the screen (quickfix window). * WSP_HELP: creating the help window, keep layout snapshot * * return FAIL for failure, OK otherwise */ int win_split(int size, int flags) { + if (check_split_disallowed() == FAIL) { + return FAIL; + } + // When the ":tab" modifier was used open a new tab page instead. if (may_open_tabpage() == OK) { return OK; } // Add flags from ":vertical", ":topleft" and ":botright". - flags |= cmdmod.split; + flags |= cmdmod.cmod_split; if ((flags & WSP_TOP) && (flags & WSP_BOT)) { emsg(_("E442: Can't split topleft and botright at the same time")); return FAIL; @@ -957,6 +1028,11 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) int wmh1; bool did_set_fraction = false; + // aucmd_win should always remain floating + if (new_wp != NULL && new_wp == aucmd_win) { + return FAIL; + } + if (flags & WSP_TOP) { oldwin = firstwin; } else if (flags & WSP_BOT || curwin->w_floating) { @@ -977,7 +1053,6 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) need_status = STATUS_HEIGHT; } - if (flags & WSP_VERT) { int wmw1; int minwidth; @@ -1064,15 +1139,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } else { layout = FR_COL; - /* - * Check if we are able to split the current window and compute its - * height. - */ - // Current window requires at least 1 space. - wmh1 = p_wmh == 0 ? 1 : p_wmh; + // Check if we are able to split the current window and compute its height. + // Current window requires at least 1 space plus space for the window bar. + wmh1 = MAX(p_wmh, 1) + oldwin->w_winbar_height; needed = wmh1 + STATUS_HEIGHT; if (flags & WSP_ROOM) { - needed += p_wh - wmh1; + needed += p_wh - wmh1 + oldwin->w_winbar_height; + } + if (p_ch < 1) { + needed += 1; // Adjust for cmdheight=0. } if (flags & (WSP_BOT | WSP_TOP)) { minheight = frame_minheight(topframe, NOWIN) + need_status; @@ -1081,8 +1156,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } else if (p_ea) { minheight = frame_minheight(oldwin->w_frame, NOWIN) + need_status; prevfrp = oldwin->w_frame; - for (frp = oldwin->w_frame->fr_parent; frp != NULL; - frp = frp->fr_parent) { + for (frp = oldwin->w_frame->fr_parent; frp != NULL; frp = frp->fr_parent) { if (frp->fr_layout == FR_COL) { FOR_ALL_FRAMES(frp2, frp->fr_child) { if (frp2 != prevfrp) { @@ -1150,8 +1224,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) while (frp != NULL) { if (frp->fr_win != oldwin && frp->fr_win != NULL && (frp->fr_win->w_height > new_size - || frp->fr_win->w_height > oldwin_height - new_size - - STATUS_HEIGHT)) { + || frp->fr_win->w_height > oldwin_height - new_size - STATUS_HEIGHT)) { do_equal = true; break; } @@ -1194,8 +1267,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } else if (wp->w_floating) { new_frame(wp); wp->w_floating = false; - // non-floating window doesn't store float config. + // non-floating window doesn't store float config or have a border. wp->w_float_config = FLOAT_CONFIG_INIT; + memset(wp->w_border_adj, 0, sizeof(wp->w_border_adj)); } /* @@ -1277,13 +1351,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (flags & (WSP_TOP | WSP_BOT)) { // set height and row of new window to full height wp->w_winrow = tabline_height(); - win_new_height(wp, curfrp->fr_height - (p_ls > 0)); - wp->w_status_height = (p_ls > 0); + win_new_height(wp, curfrp->fr_height - (p_ls == 1 || p_ls == 2)); + wp->w_status_height = (p_ls == 1 || p_ls == 2); + wp->w_hsep_height = 0; } else { // height and row of new window is same as current window wp->w_winrow = oldwin->w_winrow; win_new_height(wp, oldwin->w_height); wp->w_status_height = oldwin->w_status_height; + wp->w_hsep_height = oldwin->w_hsep_height; } frp->fr_height = curfrp->fr_height; @@ -1316,6 +1392,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) frame_fix_width(oldwin); frame_fix_width(wp); } else { + bool is_stl_global = global_stl_height() > 0; // width and column of new window is same as current window if (flags & (WSP_TOP | WSP_BOT)) { wp->w_wincol = 0; @@ -1331,28 +1408,52 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // "new_size" of the current window goes to the new window, use // one row for the status line win_new_height(wp, new_size); + if (before) { + wp->w_hsep_height = is_stl_global ? 1 : 0; + } else { + wp->w_hsep_height = oldwin->w_hsep_height; + oldwin->w_hsep_height = is_stl_global ? 1 : 0; + } if (flags & (WSP_TOP | WSP_BOT)) { int new_fr_height = curfrp->fr_height - new_size; - if (!((flags & WSP_BOT) && p_ls == 0)) { + if (!((flags & WSP_BOT) && p_ls == 0) && !is_stl_global) { new_fr_height -= STATUS_HEIGHT; + } else if (is_stl_global) { + if (flags & WSP_BOT) { + frame_add_hsep(curfrp); + } else { + new_fr_height -= 1; + } } frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false); } else { win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT)); } + if (before) { // new window above current one wp->w_winrow = oldwin->w_winrow; - wp->w_status_height = STATUS_HEIGHT; - oldwin->w_winrow += wp->w_height + STATUS_HEIGHT; + + if (is_stl_global) { + wp->w_status_height = 0; + oldwin->w_winrow += wp->w_height + 1; + } else { + wp->w_status_height = STATUS_HEIGHT; + oldwin->w_winrow += wp->w_height + STATUS_HEIGHT; + } } else { // new window below current one - wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT; - wp->w_status_height = oldwin->w_status_height; - if (!(flags & WSP_BOT)) { - oldwin->w_status_height = STATUS_HEIGHT; + if (is_stl_global) { + wp->w_winrow = oldwin->w_winrow + oldwin->w_height + 1; + wp->w_status_height = 0; + } else { + wp->w_winrow = oldwin->w_winrow + oldwin->w_height + STATUS_HEIGHT; + wp->w_status_height = oldwin->w_status_height; + if (!(flags & WSP_BOT)) { + oldwin->w_status_height = STATUS_HEIGHT; + } } } - if (flags & WSP_BOT) { + if ((flags & WSP_BOT) && !is_stl_global) { frame_add_statusline(curfrp); } frame_fix_height(wp); @@ -1382,10 +1483,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) * equalize the window sizes. */ if (do_equal || dir != 0) { - win_equal(wp, true, - (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') - : dir == 'h' ? 'b' : - 'v'); + win_equal(wp, true, (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h') : (dir == 'h' ? 'b' : 'v')); } // Don't change the window height/width to 'winheight' / 'winwidth' if a @@ -1414,17 +1512,14 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) p_wh = i; } - if (!win_valid(oldwin)) { - return FAIL; + if (win_valid(oldwin)) { + // Send the window positions to the UI + oldwin->w_pos_changed = true; } - // Send the window positions to the UI - oldwin->w_pos_changed = true; - return OK; } - /* * Initialize window "newp" from window "oldp". * Used when splitting a window and when creating a new tab page. @@ -1461,9 +1556,9 @@ static void win_init(win_T *newp, win_T *oldp, int flags) copy_loclist_stack(oldp, newp); } newp->w_localdir = (oldp->w_localdir == NULL) - ? NULL : vim_strsave(oldp->w_localdir); + ? NULL : xstrdup(oldp->w_localdir); newp->w_prevdir = (oldp->w_prevdir == NULL) - ? NULL : vim_strsave(oldp->w_prevdir); + ? NULL : xstrdup(oldp->w_prevdir); // copy tagstack and folds for (i = 0; i < oldp->w_tagstacklen; i++) { @@ -1482,7 +1577,7 @@ static void win_init(win_T *newp, win_T *oldp, int flags) win_init_some(newp, oldp); - didset_window_options(newp); + newp->w_winbar_height = oldp->w_winbar_height; } /* @@ -1579,10 +1674,10 @@ int win_count(void) } /// Make "count" windows on the screen. -/// Must be called when there is just one window, filling the whole screen +/// Must be called when there is just one window, filling the whole screen. /// (excluding the command line). /// -/// @param vertical split windows vertically if true +/// @param vertical split windows vertically if true. /// /// @return actual number of windows on the screen. int make_windows(int count, bool vertical) @@ -1591,15 +1686,15 @@ int make_windows(int count, bool vertical) int todo; if (vertical) { - // Each window needs at least 'winminwidth' lines and a separator - // column. + // Each window needs at least 'winminwidth' lines and a separator column. maxcount = (curwin->w_width + curwin->w_vsep_width - (p_wiw - p_wmw)) / (p_wmw + 1); } else { - // Each window needs at least 'winminheight' lines and a status line. - maxcount = (curwin->w_height - + curwin->w_status_height - - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT); + // Each window needs at least 'winminheight' lines. + // If statusline isn't global, each window also needs a statusline. + // If 'winbar' is set, each window also needs a winbar. + maxcount = (curwin->w_height + curwin->w_hsep_height + curwin->w_status_height + - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT + global_winbar_height()); } if (maxcount < 2) { @@ -1609,17 +1704,13 @@ int make_windows(int count, bool vertical) count = maxcount; } - /* - * add status line now, otherwise first window will be too big - */ + // add status line now, otherwise first window will be too big if (count > 1) { last_status(true); } - /* - * Don't execute autocommands while creating the windows. Must do that - * when putting the buffers in the windows. - */ + // Don't execute autocommands while creating the windows. Must do that + // when putting the buffers in the windows. block_autocmds(); // todo is number of windows left to create @@ -1666,7 +1757,6 @@ static void win_exchange(long Prenum) return; } - /* * find window to exchange with */ @@ -1694,7 +1784,7 @@ static void win_exchange(long Prenum) * if wp != wp2 * 3. remove wp from the list * 4. insert wp after wp2 - * 5. exchange the status line height and vsep width. + * 5. exchange the status line height, winbar height, hsep height and vsep width. */ wp2 = curwin->w_prev; frp2 = curwin->w_frame->fr_prev; @@ -1720,6 +1810,9 @@ static void win_exchange(long Prenum) temp = curwin->w_vsep_width; curwin->w_vsep_width = wp->w_vsep_width; wp->w_vsep_width = temp; + temp = curwin->w_hsep_height; + curwin->w_hsep_height = wp->w_hsep_height; + wp->w_hsep_height = temp; frame_fix_height(curwin); frame_fix_height(wp); @@ -1728,6 +1821,12 @@ static void win_exchange(long Prenum) (void)win_comp_pos(); // recompute window positions + if (wp->w_buffer != curbuf) { + reset_VIsual_and_resel(); + } else if (VIsual_active) { + wp->w_cursor = curwin->w_cursor; + } + win_enter(wp, true); redraw_later(curwin, NOT_VALID); redraw_later(wp, NOT_VALID); @@ -1772,8 +1871,7 @@ static void win_rotate(bool upwards, int count) assert(frp->fr_parent->fr_child); // find last frame and append removed window/frame after it - for (; frp->fr_next != NULL; frp = frp->fr_next) { - } + for (; frp->fr_next != NULL; frp = frp->fr_next) {} win_append(frp->fr_win, wp1); frame_append(frp, wp1->w_frame); @@ -1781,8 +1879,7 @@ static void win_rotate(bool upwards, int count) } else { // last window becomes first window // find last window/frame in the list and remove it for (frp = curwin->w_frame; frp->fr_next != NULL; - frp = frp->fr_next) { - } + frp = frp->fr_next) {} wp1 = frp->fr_win; wp2 = wp1->w_prev; // will become last window win_remove(wp1, NULL); @@ -1794,10 +1891,13 @@ static void win_rotate(bool upwards, int count) frame_insert(frp->fr_parent->fr_child, frp); } - // exchange status height and vsep width of old and new last window + // exchange status height, winbar height, hsep height and vsep width of old and new last window n = wp2->w_status_height; wp2->w_status_height = wp1->w_status_height; wp1->w_status_height = n; + n = wp2->w_hsep_height; + wp2->w_hsep_height = wp1->w_hsep_height; + wp1->w_hsep_height = n; frame_fix_height(wp1); frame_fix_height(wp2); n = wp2->w_vsep_width; @@ -1828,6 +1928,12 @@ static void win_totop(int size, int flags) beep_flush(); return; } + if (curwin == aucmd_win) { + return; + } + if (check_split_disallowed() == FAIL) { + return; + } if (curwin->w_floating) { ui_comp_remove_grid(&curwin->w_grid_alloc); @@ -1836,7 +1942,7 @@ static void win_totop(int size, int flags) } else { // No longer a float, a non-multigrid UI shouldn't draw it as such ui_call_win_hide(curwin->w_grid_alloc.handle); - win_free_grid(curwin, false); + win_free_grid(curwin, true); } } else { // Remove the window and frame from the tree of frames. @@ -1871,11 +1977,22 @@ void win_move_after(win_T *win1, win_T *win2) // check if there is something to do if (win2->w_next != win1) { - // may need move the status line/vertical separator of the last window + if (win1->w_frame->fr_parent != win2->w_frame->fr_parent) { + iemsg("INTERNAL: trying to move a window into another frame"); + return; + } + + // may need to move the status line, window bar, horizontal or vertical separator of the last + // window if (win1 == lastwin) { height = win1->w_prev->w_status_height; win1->w_prev->w_status_height = win1->w_status_height; win1->w_status_height = height; + + height = win1->w_prev->w_hsep_height; + win1->w_prev->w_hsep_height = win1->w_hsep_height; + win1->w_hsep_height = height; + if (win1->w_prev->w_vsep_width == 1) { // Remove the vertical separator from the last-but-one window, // add it to the last window. Adjust the frame widths. @@ -1888,6 +2005,11 @@ void win_move_after(win_T *win1, win_T *win2) height = win1->w_status_height; win1->w_status_height = win2->w_status_height; win2->w_status_height = height; + + height = win1->w_hsep_height; + win1->w_hsep_height = win2->w_hsep_height; + win2->w_hsep_height = height; + if (win1->w_vsep_width == 1) { // Remove the vertical separator from win1, add it to the last // window, win2. Adjust the frame widths. @@ -1911,6 +2033,37 @@ void win_move_after(win_T *win1, win_T *win2) win2->w_pos_changed = true; } +/// Compute maximum number of windows that can fit within "height" in frame "fr". +static int get_maximum_wincount(frame_T *fr, int height) +{ + if (fr->fr_layout != FR_COL) { + return (height / (p_wmh + STATUS_HEIGHT + frame2win(fr)->w_winbar_height)); + } else if (global_winbar_height()) { + // If winbar is globally enabled, no need to check each window for it. + return (height / (p_wmh + STATUS_HEIGHT + 1)); + } + + frame_T *frp; + int total_wincount = 0; + + // First, try to fit all child frames of "fr" into "height" + FOR_ALL_FRAMES(frp, fr->fr_child) { + win_T *wp = frame2win(frp); + + if (height < (p_wmh + STATUS_HEIGHT + wp->w_winbar_height)) { + break; + } + height -= p_wmh + STATUS_HEIGHT + wp->w_winbar_height; + total_wincount += 1; + } + + // If we still have enough room for more windows, just use the default winbar height (which is 0) + // in order to get the amount of windows that'd fit in the remaining space + total_wincount += height / (p_wmh + STATUS_HEIGHT); + + return total_wincount; +} + /// Make all windows the same height. ///'next_curwin' will soon be the current window, make sure it has enough rows. /// @@ -2102,13 +2255,15 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int if (dir != 'h') { // equalize frame heights // Compute maximum number of windows vertically in this frame. n = frame_minheight(topfr, NOWIN); - // add one for the bottom window if it doesn't have a statusline + // add one for the bottom window if it doesn't have a statusline or separator if (row + height == cmdline_row && p_ls == 0) { + extra_sep = STATUS_HEIGHT; + } else if (global_stl_height() > 0) { extra_sep = 1; } else { extra_sep = 0; } - totwincount = (n + extra_sep) / (p_wmh + 1); + totwincount = get_maximum_wincount(topfr, n + extra_sep); has_next_curwin = frame_has_win(topfr, next_curwin); /* @@ -2142,8 +2297,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } } else { // These windows don't use up room. - totwincount -= (n + (fr->fr_next == NULL - ? extra_sep : 0)) / (p_wmh + 1); + totwincount -= get_maximum_wincount(fr, (n + (fr->fr_next == NULL ? extra_sep : 0))); } room -= new_size - n; if (room < 0) { @@ -2188,8 +2342,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } else { // Compute the maximum number of windows vert. in "fr". n = frame_minheight(fr, NOWIN); - wincount = (n + (fr->fr_next == NULL ? extra_sep : 0)) - / (p_wmh + 1); + wincount = get_maximum_wincount(fr, (n + (fr->fr_next == NULL ? extra_sep : 0))); m = frame_minheight(fr, next_curwin); if (has_next_curwin) { hnc = frame_has_win(fr, next_curwin); @@ -2247,7 +2400,7 @@ static void leaving_window(win_T *const win) // When leaving the window (or closing the window) was done from a // callback we need to break out of the Insert mode loop and restart Insert // mode when entering the window again. - if (State & INSERT) { + if (State & MODE_INSERT) { stop_insert_mode = true; if (win->w_buffer->b_prompt_insert == NUL) { win->w_buffer->b_prompt_insert = 'A'; @@ -2271,33 +2424,59 @@ void entering_window(win_T *const win) // When entering the prompt window restart Insert mode if we were in Insert // mode when we left it and not already in Insert mode. - if ((State & INSERT) == 0) { + if ((State & MODE_INSERT) == 0) { restart_edit = win->w_buffer->b_prompt_insert; } } -/// Closes all windows for buffer `buf`. +void win_init_empty(win_T *wp) +{ + redraw_later(wp, NOT_VALID); + wp->w_lines_valid = 0; + wp->w_cursor.lnum = 1; + wp->w_curswant = wp->w_cursor.col = 0; + wp->w_cursor.coladd = 0; + wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1 + wp->w_pcmark.col = 0; + wp->w_prev_pcmark.lnum = 0; + wp->w_prev_pcmark.col = 0; + wp->w_topline = 1; + wp->w_topfill = 0; + wp->w_botline = 2; + wp->w_s = &wp->w_buffer->b_s; +} + +/// Init the current window "curwin". +/// Called when a new file is being edited. +void curwin_init(void) +{ + win_init_empty(curwin); +} + +/// Closes all windows for buffer `buf` unless there is only one non-floating window. /// -/// @param keep_curwin don't close `curwin` -void close_windows(buf_T *buf, int keep_curwin) +/// @param keep_curwin don't close `curwin` +void close_windows(buf_T *buf, bool keep_curwin) { tabpage_T *tp, *nexttp; int h = tabline_height(); ++RedrawingDisabled; - for (win_T *wp = firstwin; wp != NULL && !ONE_WINDOW;) { + // Start from lastwin to close floating windows with the same buffer first. + // When the autocommand window is involved win_close() may need to print an error message. + for (win_T *wp = lastwin; wp != NULL && (lastwin == aucmd_win || !one_window(wp));) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { - if (win_close(wp, false) == FAIL) { + if (win_close(wp, false, false) == FAIL) { // If closing the window fails give up, to avoid looping forever. break; } // Start all over, autocommands may change the window layout. - wp = firstwin; + wp = lastwin; } else { - wp = wp->w_next; + wp = wp->w_prev; } } @@ -2323,27 +2502,28 @@ void close_windows(buf_T *buf, int keep_curwin) redraw_tabline = true; if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } -/// Check that current window is the last one. +/// Check that the specified window is the last one. +/// @param win counted even if floating /// -/// @return true if the current window is the only window that exists, false if -/// there is another, possibly in another tab page. -static bool last_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +/// @return true if the specified window is the only window that exists, +/// false if there is another, possibly in another tab page. +bool last_window(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { - return one_window() && first_tabpage->tp_next == NULL; + return one_window(win) && first_tabpage->tp_next == NULL; } -/// Check that current tab page contains no more then one window other than -/// "aucmd_win". Only counts floating window if it is current. -bool one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +/// Check that current tab page contains no more then one window other than `aucmd_win`. +/// @param counted_float counted even if floating, but not if it is `aucmd_win` +bool one_window(win_T *counted_float) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT { bool seen_one = false; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp != aucmd_win && (!wp->w_floating || wp == curwin)) { + if (wp != aucmd_win && (!wp->w_floating || wp == counted_float)) { if (seen_one) { return false; } @@ -2367,6 +2547,24 @@ bool last_nonfloat(win_T *wp) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return wp != NULL && firstwin == wp && !(wp->w_next && !wp->w_floating); } +/// Check if floating windows in the current tab can be closed. +/// Do not call this when the autocommand window is in use! +/// +/// @return true if all floating windows can be closed +static bool can_close_floating_windows(void) +{ + assert(lastwin != aucmd_win); + for (win_T *wp = lastwin; wp->w_floating; wp = wp->w_prev) { + buf_T *buf = wp->w_buffer; + int need_hide = (bufIsChanged(buf) && buf->b_nwindows <= 1); + + if (need_hide && !buf_hide(buf)) { + return false; + } + } + return true; +} + /// Close the possibly last window in a tab page. /// /// @param win window to close @@ -2411,7 +2609,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev win_close_othertab(win, free_buf, prev_curtab); if (h != tabline_height()) { - shell_new_rows(); + win_new_screen_rows(); } } entering_window(curwin); @@ -2426,12 +2624,47 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev return true; } +/// Close the buffer of "win" and unload it if "free_buf" is true. +/// "abort_if_last" is passed to close_buffer(): abort closing if all other +/// windows are closed. +static void win_close_buffer(win_T *win, bool free_buf, bool abort_if_last) +{ + // Free independent synblock before the buffer is freed. + if (win->w_buffer != NULL) { + reset_synblock(win); + } + + // When a quickfix/location list window is closed and the buffer is + // displayed in only one window, then unlist the buffer. + if (win->w_buffer != NULL && bt_quickfix(win->w_buffer) + && win->w_buffer->b_nwindows == 1) { + win->w_buffer->b_p_bl = false; + } + + // Close the link to the buffer. + if (win->w_buffer != NULL) { + bufref_T bufref; + set_bufref(&bufref, curbuf); + win->w_closing = true; + close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, abort_if_last, true); + if (win_valid_any_tab(win)) { + win->w_closing = false; + } + + // Make sure curbuf is valid. It can become invalid if 'bufhidden' is + // "wipe". + if (!bufref_valid(&bufref)) { + curbuf = firstbuf; + } + } +} + // Close window "win". Only works for the current tab page. // If "free_buf" is true related buffer may be unloaded. // // Called by :quit, :close, :xit, :wq and findtag(). // Returns FAIL when the window was not closed. -int win_close(win_T *win, bool free_buf) +int win_close(win_T *win, bool free_buf, bool force) { win_T *wp; bool other_buffer = false; @@ -2442,7 +2675,7 @@ int win_close(win_T *win, bool free_buf) frame_T *win_frame = win->w_floating ? NULL : win->w_frame->fr_parent; const bool had_diffmode = win->w_p_diff; - if (last_window() && !win->w_floating) { + if (last_window(win)) { emsg(_("E444: Cannot close last window")); return FAIL; } @@ -2455,15 +2688,24 @@ int win_close(win_T *win, bool free_buf) emsg(_(e_autocmd_close)); return FAIL; } - if ((firstwin == aucmd_win || lastwin == aucmd_win) && one_window()) { - emsg(_("E814: Cannot close window, only autocmd window would remain")); - return FAIL; - } - if ((firstwin == win && lastwin_nofloating() == win) - && lastwin->w_floating) { - // TODO(bfredl): we might close the float also instead - emsg(e_floatonly); - return FAIL; + if (lastwin->w_floating && one_window(win)) { + if (lastwin == aucmd_win) { + emsg(_("E814: Cannot close window, only autocmd window would remain")); + return FAIL; + } + if (force || can_close_floating_windows()) { + // close the last window until the there are no floating windows + while (lastwin->w_floating) { + // `force` flag isn't actually used when closing a floating window. + if (win_close(lastwin, free_buf, true) == FAIL) { + // If closing the window fails give up, to avoid looping forever. + return FAIL; + } + } + } else { + emsg(e_floatonly); + return FAIL; + } } // When closing the last window in a tab page first go to another tab page @@ -2501,6 +2743,8 @@ int win_close(win_T *win, bool free_buf) * to be the last one left, return now. */ if (wp->w_buffer != curbuf) { + reset_VIsual_and_resel(); // stop Visual mode + other_buffer = true; win->w_closing = true; apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf); @@ -2508,7 +2752,7 @@ int win_close(win_T *win, bool free_buf) return FAIL; } win->w_closing = false; - if (last_window()) { + if (last_window(win)) { return FAIL; } } @@ -2518,7 +2762,7 @@ int win_close(win_T *win, bool free_buf) return FAIL; } win->w_closing = false; - if (last_window()) { + if (last_window(win)) { return FAIL; } // autocmds may abort script processing @@ -2555,32 +2799,10 @@ int win_close(win_T *win, bool free_buf) return OK; } - // Free independent synblock before the buffer is freed. - if (win->w_buffer != NULL) { - reset_synblock(win); - } - - /* - * Close the link to the buffer. - */ - if (win->w_buffer != NULL) { - bufref_T bufref; - set_bufref(&bufref, curbuf); - win->w_closing = true; - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, true); - if (win_valid_any_tab(win)) { - win->w_closing = false; - } - - // Make sure curbuf is valid. It can become invalid if 'bufhidden' is - // "wipe". - if (!bufref_valid(&bufref)) { - curbuf = firstbuf; - } - } + win_close_buffer(win, free_buf, true); if (only_one_window() && win_valid(win) && win->w_buffer == NULL - && (last_window() || curtab != prev_curtab + && (last_window(win) || curtab != prev_curtab || close_last_window_tabpage(win, free_buf, prev_curtab)) && !win->w_floating) { // Autocommands have closed all windows, quit now. Restore @@ -2600,11 +2822,15 @@ int win_close(win_T *win, bool free_buf) // Autocommands may have closed the window already, or closed the only // other window or moved to another tab page. - if (!win_valid(win) || (!win->w_floating && last_window()) + if (!win_valid(win) || (!win->w_floating && last_window(win)) || close_last_window_tabpage(win, free_buf, prev_curtab)) { return FAIL; } + // Now we are really going to close the window. Disallow any autocommand + // to split a window to avoid trouble. + split_disallowed++; + // let terminal buffers know that this window dimensions may be ignored win->w_closing = true; @@ -2613,10 +2839,11 @@ int win_close(win_T *win, bool free_buf) wp = win_free_mem(win, &dir, NULL); if (help_window) { - // Closing the help window moves the cursor back to the original window. - win_T *tmpwp = get_snapshot_focus(SNAP_HELP_IDX); - if (tmpwp != NULL) { - wp = tmpwp; + // Closing the help window moves the cursor back to the current window + // of the snapshot. + win_T *prev_win = get_snapshot_curwin(SNAP_HELP_IDX); + if (win_valid(prev_win)) { + wp = prev_win; } } @@ -2672,6 +2899,8 @@ int win_close(win_T *win, bool free_buf) } } + split_disallowed--; + /* * If last window has a status line now and we don't want one, * remove the status line. @@ -2713,8 +2942,8 @@ static void do_autocmd_winclosed(win_T *win) return; } recursive = true; - char_u winid[NUMBUFLEN]; - vim_snprintf((char *)winid, sizeof(winid), "%i", win->handle); + char winid[NUMBUFLEN]; + vim_snprintf(winid, sizeof(winid), "%d", win->handle); apply_autocmds(EVENT_WINCLOSED, winid, winid, false, win->w_buffer); recursive = false; } @@ -2748,14 +2977,20 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) if (win->w_buffer != NULL) { // Close the link to the buffer. - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false); + close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false, true); } // Careful: Autocommands may have closed the tab page or made it the // current tab page. - for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next) { - } + for (ptp = first_tabpage; ptp != NULL && ptp != tp; ptp = ptp->tp_next) {} if (ptp == NULL || tp == curtab) { + // If the buffer was removed from the window we have to give it any + // buffer. + if (win_valid_any_tab(win) && win->w_buffer == NULL) { + win->w_buffer = firstbuf; + firstbuf->b_nwindows++; + win_init_empty(win); + } return; } @@ -2775,9 +3010,9 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) // When closing the last window in a tab page remove the tab page. if (tp->tp_firstwin == tp->tp_lastwin) { - char_u prev_idx[NUMBUFLEN]; + char prev_idx[NUMBUFLEN]; if (has_event(EVENT_TABCLOSED)) { - vim_snprintf((char *)prev_idx, NUMBUFLEN, "%i", tabpage_index(tp)); + vim_snprintf(prev_idx, NUMBUFLEN, "%i", tabpage_index(tp)); } if (tp == first_tabpage) { @@ -2857,6 +3092,9 @@ void win_free_all(void) { int dummy; + // avoid an error for switching tabpage with the cmdline window open + cmdwin_type = 0; + while (first_tabpage->tp_next != NULL) { tabpage_close(TRUE); } @@ -3060,9 +3298,21 @@ static frame_T *win_altframe(win_T *win, tabpage_T *tp) return frp->fr_prev; } + // By default the next window will get the space that was abandoned by this + // window frame_T *target_fr = frp->fr_next; frame_T *other_fr = frp->fr_prev; - if (p_spr || p_sb) { + + // If this is part of a column of windows and 'splitbelow' is true then the + // previous window will get the space. + if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_COL && p_sb) { + target_fr = frp->fr_prev; + other_fr = frp->fr_next; + } + + // If this is part of a row of windows, and 'splitright' is true then the + // previous window will get the space. + if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW && p_spr) { target_fr = frp->fr_prev; other_fr = frp->fr_next; } @@ -3095,15 +3345,14 @@ static tabpage_T *alt_tabpage(void) } // Find the last but one tab page. - for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) { - } + for (tp = first_tabpage; tp->tp_next != curtab; tp = tp->tp_next) {} return tp; } /* * Find the left-upper window in frame "frp". */ -static win_T *frame2win(frame_T *frp) +win_T *frame2win(frame_T *frp) { while (frp->fr_win == NULL) { frp = frp->fr_child; @@ -3130,23 +3379,40 @@ static bool frame_has_win(const frame_T *frp, const win_T *wp) return false; } +/// Check if current window is at the bottom +/// Returns true if there are no windows below current window +static bool is_bottom_win(win_T *wp) + FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_ALL +{ + frame_T *frp; + for (frp = wp->w_frame; frp->fr_parent != NULL; frp = frp->fr_parent) { + if (frp->fr_parent->fr_layout == FR_COL && frp->fr_next != NULL) { + return false; + } + } + return true; +} /// Set a new height for a frame. Recursively sets the height for contained /// frames and windows. Caller must take care of positions. /// /// @param topfirst resize topmost contained frame first. /// @param wfh obey 'winfixheight' when there is a choice; /// may cause the height not to be set. -static void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh) +void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wfh) FUNC_ATTR_NONNULL_ALL { frame_T *frp; int extra_lines; int h; + win_T *wp; if (topfrp->fr_win != NULL) { // Simple case: just one window. - win_new_height(topfrp->fr_win, - height - topfrp->fr_win->w_status_height); + wp = topfrp->fr_win; + if (is_bottom_win(wp)) { + wp->w_hsep_height = 0; + } + win_new_height(wp, height - wp->w_hsep_height - wp->w_status_height); } else if (topfrp->fr_layout == FR_ROW) { do { // All frames in this row get the same new height. @@ -3202,13 +3468,11 @@ static void frame_new_height(frame_T *topfrp, int height, bool topfirst, bool wf if (topfirst) { do { frp = frp->fr_next; - } - while (wfh && frp != NULL && frame_fixed_height(frp)); + } while (wfh && frp != NULL && frame_fixed_height(frp)); } else { do { frp = frp->fr_prev; - } - while (wfh && frp != NULL && frame_fixed_height(frp)); + } while (wfh && frp != NULL && frame_fixed_height(frp)); } // Increase "height" if we could not reduce enough frames. if (frp == NULL) { @@ -3302,8 +3566,8 @@ static void frame_add_statusline(frame_T *frp) if (frp->fr_layout == FR_LEAF) { wp = frp->fr_win; if (wp->w_status_height == 0) { - if (wp->w_height > 0) { // don't make it negative - --wp->w_height; + if (wp->w_height - STATUS_HEIGHT >= 0) { // don't make it negative + wp->w_height -= STATUS_HEIGHT; } wp->w_status_height = STATUS_HEIGHT; } @@ -3315,8 +3579,7 @@ static void frame_add_statusline(frame_T *frp) } else { assert(frp->fr_layout == FR_COL); // Only need to handle the last frame in the column. - for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next) { - } + for (frp = frp->fr_child; frp->fr_next != NULL; frp = frp->fr_next) {} frame_add_statusline(frp); } } @@ -3402,13 +3665,11 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw if (leftfirst) { do { frp = frp->fr_next; - } - while (wfw && frp != NULL && frame_fixed_width(frp)); + } while (wfw && frp != NULL && frame_fixed_width(frp)); } else { do { frp = frp->fr_prev; - } - while (wfw && frp != NULL && frame_fixed_width(frp)); + } while (wfw && frp != NULL && frame_fixed_width(frp)); } // Increase "width" if we could not reduce enough frames. if (frp == NULL) { @@ -3423,10 +3684,8 @@ static void frame_new_width(frame_T *topfrp, int width, bool leftfirst, bool wfw topfrp->fr_width = width; } -/* - * Add the vertical separator to windows at the right side of "frp". - * Note: Does not check if there is room! - */ +/// Add the vertical separator to windows at the right side of "frp". +/// Note: Does not check if there is room! static void frame_add_vsep(const frame_T *frp) FUNC_ATTR_NONNULL_ARG(1) { @@ -3456,6 +3715,37 @@ static void frame_add_vsep(const frame_T *frp) } } +/// Add the horizontal separator to windows at the bottom of "frp". +/// Note: Does not check if there is room or whether the windows have a statusline! +static void frame_add_hsep(const frame_T *frp) + FUNC_ATTR_NONNULL_ARG(1) +{ + win_T *wp; + + if (frp->fr_layout == FR_LEAF) { + wp = frp->fr_win; + if (wp->w_hsep_height == 0) { + if (wp->w_height > 0) { // don't make it negative + wp->w_height++; + } + wp->w_hsep_height = 1; + } + } else if (frp->fr_layout == FR_ROW) { + // Handle all the frames in the row. + FOR_ALL_FRAMES(frp, frp->fr_child) { + frame_add_hsep(frp); + } + } else { + assert(frp->fr_layout == FR_COL); + // Only need to handle the last frame in the column. + frp = frp->fr_child; + while (frp->fr_next != NULL) { + frp = frp->fr_next; + } + frame_add_hsep(frp); + } +} + /* * Set frame width from the window it contains. */ @@ -3470,16 +3760,12 @@ static void frame_fix_width(win_T *wp) static void frame_fix_height(win_T *wp) FUNC_ATTR_NONNULL_ALL { - wp->w_frame->fr_height = wp->w_height + wp->w_status_height; + wp->w_frame->fr_height = wp->w_height + wp->w_hsep_height + wp->w_status_height; } -/* - * Compute the minimal height for frame "topfrp". - * Uses the 'winminheight' option. - * When "next_curwin" isn't NULL, use p_wh for this window. - * When "next_curwin" is NOWIN, don't use at least one line for the current - * window. - */ +/// Compute the minimal height for frame "topfrp". Uses the 'winminheight' option. +/// When "next_curwin" isn't NULL, use p_wh for this window. +/// When "next_curwin" is NOWIN, don't use at least one line for the current window. static int frame_minheight(frame_T *topfrp, win_T *next_curwin) { frame_T *frp; @@ -3487,11 +3773,14 @@ static int frame_minheight(frame_T *topfrp, win_T *next_curwin) int n; if (topfrp->fr_win != NULL) { + // Combined height of window bar and separator column or status line. + int extra_height = topfrp->fr_win->w_winbar_height + topfrp->fr_win->w_hsep_height + + topfrp->fr_win->w_status_height; + if (topfrp->fr_win == next_curwin) { - m = p_wh + topfrp->fr_win->w_status_height; + m = p_wh + extra_height; } else { - // window: minimal height of the window plus status line - m = p_wmh + topfrp->fr_win->w_status_height; + m = p_wmh + extra_height; if (topfrp->fr_win == curwin && next_curwin == NULL) { // Current window is minimal one line high. if (p_wmh == 0) { @@ -3561,7 +3850,6 @@ static int frame_minwidth(frame_T *topfrp, win_T *next_curwin) return m; } - /// Try to close all windows except current one. /// Buffers in the other windows become hidden if 'hidden' is set, or '!' is /// used and the buffer was modified. @@ -3582,7 +3870,7 @@ void close_others(int message, int forceit) return; } - if (one_window() && !lastwin->w_floating) { + if (one_nonfloat() && !lastwin->w_floating) { if (message && !autocmd_busy) { msg(_(m_onlyone)); @@ -3604,7 +3892,7 @@ void close_others(int message, int forceit) continue; } if (!r) { - if (message && (p_confirm || cmdmod.confirm) && p_write) { + if (message && (p_confirm || (cmdmod.cmod_flags & CMOD_CONFIRM)) && p_write) { dialog_changed(wp->w_buffer, false); if (!win_valid(wp)) { // autocommands messed wp up nextwp = firstwin; @@ -3615,7 +3903,9 @@ void close_others(int message, int forceit) continue; } } - win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer)); + win_close(wp, + !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer), + false); } if (message && !ONE_WINDOW) { @@ -3623,33 +3913,6 @@ void close_others(int message, int forceit) } } - -/* - * Init the current window "curwin". - * Called when a new file is being edited. - */ -void curwin_init(void) -{ - win_init_empty(curwin); -} - -void win_init_empty(win_T *wp) -{ - redraw_later(wp, NOT_VALID); - wp->w_lines_valid = 0; - wp->w_cursor.lnum = 1; - wp->w_curswant = wp->w_cursor.col = 0; - wp->w_cursor.coladd = 0; - wp->w_pcmark.lnum = 1; // pcmark not cleared but set to line 1 - wp->w_pcmark.col = 0; - wp->w_prev_pcmark.lnum = 0; - wp->w_prev_pcmark.col = 0; - wp->w_topline = 1; - wp->w_topfill = 0; - wp->w_botline = 2; - wp->w_s = &wp->w_buffer->b_s; -} - /* * Allocate the first window and put an empty buffer in it. * Called from main(). @@ -3681,7 +3944,7 @@ void win_alloc_aucmd_win(void) fconfig.width = Columns; fconfig.height = 5; fconfig.focusable = false; - aucmd_win = win_new_float(NULL, fconfig, &err); + aucmd_win = win_new_float(NULL, true, fconfig, &err); aucmd_win->w_buffer->b_nwindows--; RESET_BINDING(aucmd_win); } @@ -3718,7 +3981,7 @@ static int win_alloc_firstwin(win_T *oldwin) new_frame(curwin); topframe = curwin->w_frame; topframe->fr_width = Columns; - topframe->fr_height = Rows - p_ch; + topframe->fr_height = Rows - p_ch - global_stl_height(); return OK; } @@ -3741,8 +4004,9 @@ static void new_frame(win_T *wp) void win_init_size(void) { firstwin->w_height = ROWS_AVAIL; - firstwin->w_height_inner = firstwin->w_height; + firstwin->w_height_inner = firstwin->w_height - firstwin->w_winbar_height; firstwin->w_height_outer = firstwin->w_height; + firstwin->w_winrow_off = firstwin->w_winbar_height; topframe->fr_height = ROWS_AVAIL; firstwin->w_width = Columns; firstwin->w_width_inner = firstwin->w_width; @@ -3805,6 +4069,11 @@ int win_new_tabpage(int after, char_u *filename) tabpage_T *newtp; int n; + if (cmdwin_type != 0) { + emsg(_(e_cmdwin)); + return FAIL; + } + newtp = alloc_tabpage(); // Remember the current windows in this Tab page. @@ -3814,7 +4083,7 @@ int win_new_tabpage(int after, char_u *filename) } newtp->tp_localdir = old_curtab->tp_localdir - ? vim_strsave(old_curtab->tp_localdir) : NULL; + ? xstrdup(old_curtab->tp_localdir) : NULL; curtab = newtp; @@ -3847,6 +4116,7 @@ int win_new_tabpage(int after, char_u *filename) newtp->tp_topframe = topframe; last_status(false); + set_winbar(); redraw_all_later(NOT_VALID); @@ -3858,7 +4128,7 @@ int win_new_tabpage(int after, char_u *filename) apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); - apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); + apply_autocmds(EVENT_TABNEW, (char *)filename, (char *)filename, false, curbuf); apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf); return OK; @@ -3876,10 +4146,10 @@ int win_new_tabpage(int after, char_u *filename) */ int may_open_tabpage(void) { - int n = (cmdmod.tab == 0) ? postponed_split_tab : cmdmod.tab; + int n = (cmdmod.cmod_tab == 0) ? postponed_split_tab : cmdmod.cmod_tab; if (n != 0) { - cmdmod.tab = 0; // reset it to avoid doing it twice + cmdmod.cmod_tab = 0; // reset it to avoid doing it twice postponed_split_tab = 0; return win_new_tabpage(n, NULL); } @@ -4050,8 +4320,8 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a { int old_off = tp->tp_firstwin->w_winrow; win_T *next_prevwin = tp->tp_prevwin; - tabpage_T *old_curtab = curtab; + curtab = tp; firstwin = tp->tp_firstwin; lastwin = tp->tp_lastwin; @@ -4090,10 +4360,10 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, bool trigger_enter_a } if (curtab->tp_old_Rows != Rows || (old_off != firstwin->w_winrow)) { - shell_new_rows(); + win_new_screen_rows(); } if (curtab->tp_old_Columns != Columns && starting == 0) { - shell_new_columns(); // update window widths + win_new_screen_cols(); // update window widths } lastused_tabpage = old_curtab; @@ -4176,14 +4446,12 @@ void goto_tabpage(int n) ttp = curtab; for (i = n; i < 0; ++i) { for (tp = first_tabpage; tp->tp_next != ttp && tp->tp_next != NULL; - tp = tp->tp_next) { - } + tp = tp->tp_next) {} ttp = tp; } } else if (n == 9999) { // Go to last tab page. - for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next) { - } + for (tp = first_tabpage; tp->tp_next != NULL; tp = tp->tp_next) {} } else { // Go to tab page "n". tp = find_tabpage(n); @@ -4203,6 +4471,10 @@ void goto_tabpage(int n) /// @param trigger_leave_autocmds when true trigger *Leave autocommands. void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_leave_autocmds) { + if (trigger_enter_autocmds || trigger_leave_autocmds) { + CHECK_CMDWIN; + } + // Don't repeat a message in another tab page. set_keep_msg(NULL, 0); @@ -4218,13 +4490,15 @@ void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_le } } -// Go to the last accessed tab page, if there is one. -void goto_tabpage_lastused(void) +/// Go to the last accessed tab page, if there is one. +/// @return true if the tab page is valid, false otherwise. +bool goto_tabpage_lastused(void) { - int index = tabpage_index(lastused_tabpage); - if (index < tabpage_index(NULL)) { - goto_tabpage(index); + if (valid_tabpage(lastused_tabpage)) { + goto_tabpage_tp(lastused_tabpage, true, true); + return true; } + return false; } /* @@ -4293,7 +4567,6 @@ void tabpage_move(int nr) redraw_tabline = true; } - /* * Go to another window. * When jumping to another buffer, stop Visual mode. Do this before @@ -4305,12 +4578,8 @@ void win_goto(win_T *wp) { win_T *owp = curwin; - if (text_locked()) { + if (text_or_buf_locked()) { beep_flush(); - text_locked_msg(); - return; - } - if (curbuf_locked()) { return; } @@ -4331,7 +4600,6 @@ void win_goto(win_T *wp) } } - /* * Find the tabpage for window "win". */ @@ -4634,8 +4902,7 @@ static void win_enter_ext(win_T *const wp, const int flags) void fix_current_dir(void) { // New directory is either the local directory of the window, tab or NULL. - char *new_dir = (char *)(curwin->w_localdir - ? curwin->w_localdir : curtab->tp_localdir); + char *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->tp_localdir; char cwd[MAXPATHL]; if (os_dirname((char_u *)cwd, MAXPATHL) != OK) { cwd[0] = NUL; @@ -4646,23 +4913,32 @@ void fix_current_dir(void) // (unless that was done already) and change to the local directory. if (globaldir == NULL) { if (cwd[0] != NUL) { - globaldir = (char_u *)xstrdup(cwd); + globaldir = xstrdup(cwd); } } + bool dir_differs = pathcmp(new_dir, cwd, -1) != 0; + if (!p_acd && dir_differs) { + do_autocmd_dirchanged(new_dir, curwin->w_localdir ? kCdScopeWindow : kCdScopeTabpage, + kCdCauseWindow, true); + } if (os_chdir(new_dir) == 0) { - if (!p_acd && pathcmp(new_dir, cwd, -1) != 0) { - do_autocmd_dirchanged(new_dir, curwin->w_localdir - ? kCdScopeWindow : kCdScopeTabpage, kCdCauseWindow); + if (!p_acd && dir_differs) { + do_autocmd_dirchanged(new_dir, curwin->w_localdir ? kCdScopeWindow : kCdScopeTabpage, + kCdCauseWindow, false); } - last_chdir_reason = NULL; - shorten_fnames(true); } + last_chdir_reason = NULL; + shorten_fnames(true); } else if (globaldir != NULL) { // Window doesn't have a local directory and we are not in the global // directory: Change to the global directory. - if (os_chdir((char *)globaldir) == 0) { - if (!p_acd && pathcmp((char *)globaldir, cwd, -1) != 0) { - do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal, kCdCauseWindow); + bool dir_differs = pathcmp(globaldir, cwd, -1) != 0; + if (!p_acd && dir_differs) { + do_autocmd_dirchanged(globaldir, kCdScopeGlobal, kCdCauseWindow, true); + } + if (os_chdir(globaldir) == 0) { + if (!p_acd && dir_differs) { + do_autocmd_dirchanged(globaldir, kCdScopeGlobal, kCdCauseWindow, false); } } XFREE_CLEAR(globaldir); @@ -4785,7 +5061,6 @@ static win_T *win_alloc(win_T *after, bool hidden) return new_wp; } - // Free one wininfo_T. void free_wininfo(wininfo_T *wip, buf_T *bp) { @@ -4796,7 +5071,6 @@ void free_wininfo(wininfo_T *wip, buf_T *bp) xfree(wip); } - /// Remove window 'wp' from the window list and free the structure. /// /// @param tp tab page "win" is in, NULL for current @@ -4819,6 +5093,7 @@ static void win_free(win_T *wp, tabpage_T *tp) clear_winopt(&wp->w_allbuf_opt); xfree(wp->w_p_lcs_chars.multispace); + xfree(wp->w_p_lcs_chars.leadmultispace); vars_clear(&wp->w_vars->dv_hashtab); // free all w: variables hash_init(&wp->w_vars->dv_hashtab); @@ -4843,6 +5118,12 @@ static void win_free(win_T *wp, tabpage_T *tp) xfree(wp->w_localdir); xfree(wp->w_prevdir); + stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); + xfree(wp->w_status_click_defs); + + stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); + xfree(wp->w_winbar_click_defs); + // Remove the window from the b_wininfo lists, it may happen that the // freed memory is re-used for another window. FOR_ALL_BUFFERS(buf) { @@ -4904,8 +5185,7 @@ void win_free_grid(win_T *wp, bool reinit) } grid_free(&wp->w_grid_alloc); if (reinit) { - // if a float is turned into a split and back into a float, the grid - // data structure will be reused + // if a float is turned into a split, the grid data structure will be reused memset(&wp->w_grid_alloc, 0, sizeof(wp->w_grid_alloc)); } } @@ -5001,12 +5281,29 @@ static void frame_remove(frame_T *frp) } } +void win_new_screensize(void) +{ + static long old_Rows = 0; + static long old_Columns = 0; -/* - * Called from win_new_shellsize() after Rows changed. - * This only does the current tab page, others must be done when made active. - */ -void shell_new_rows(void) + if (old_Rows != Rows) { + // If 'window' uses the whole screen, keep it using that. + // Don't change it when set with "-w size" on the command line. + if (p_window == old_Rows - 1 || (old_Rows == 0 && p_window == 0)) { + p_window = Rows - 1; + } + old_Rows = Rows; + win_new_screen_rows(); // update window sizes + } + if (old_Columns != Columns) { + old_Columns = Columns; + win_new_screen_cols(); // update window sizes + } +} +/// Called from win_new_screensize() after Rows changed. +/// +/// This only does the current tab page, others must be done when made active. +void win_new_screen_rows(void) { int h = (int)ROWS_AVAIL; @@ -5030,10 +5327,8 @@ void shell_new_rows(void) curtab->tp_ch_used = p_ch; } -/* - * Called from win_new_shellsize() after Columns changed. - */ -void shell_new_columns(void) +/// Called from win_new_screensize() after Columns changed. +void win_new_screen_cols(void) { if (firstwin == NULL) { // not initialized yet return; @@ -5050,25 +5345,35 @@ void shell_new_columns(void) win_reconfig_floats(); // The size of floats might change } -/// Check if "wp" has scrolled since last time it was checked -/// @param wp the window to check -bool win_did_scroll(win_T *wp) +/// Trigger WinScrolled for "curwin" if needed. +void may_trigger_winscrolled(void) { - return (curwin->w_last_topline != curwin->w_topline - || curwin->w_last_leftcol != curwin->w_leftcol - || curwin->w_last_width != curwin->w_width - || curwin->w_last_height != curwin->w_height); -} + static bool recursive = false; -/// Trigger WinScrolled autocmd -void do_autocmd_winscrolled(win_T *wp) -{ - apply_autocmds(EVENT_WINSCROLLED, NULL, NULL, false, curbuf); + if (recursive || !has_event(EVENT_WINSCROLLED)) { + return; + } + + win_T *wp = curwin; + if (wp->w_last_topline != wp->w_topline + || wp->w_last_leftcol != wp->w_leftcol + || wp->w_last_width != wp->w_width + || wp->w_last_height != wp->w_height) { + char winid[NUMBUFLEN]; + vim_snprintf(winid, sizeof(winid), "%d", wp->handle); - wp->w_last_topline = wp->w_topline; - wp->w_last_leftcol = wp->w_leftcol; - wp->w_last_width = wp->w_width; - wp->w_last_height = wp->w_height; + recursive = true; + apply_autocmds(EVENT_WINSCROLLED, winid, winid, false, wp->w_buffer); + recursive = false; + + // an autocmd may close the window, "wp" may be invalid now + if (win_valid_any_tab(wp)) { + wp->w_last_topline = wp->w_topline; + wp->w_last_leftcol = wp->w_leftcol; + wp->w_last_width = wp->w_width; + wp->w_last_height = wp->w_height; + } + } } /* @@ -5114,11 +5419,8 @@ void win_size_restore(garray_T *gap) } } -/* - * Update the position for all windows, using the width and height of the - * frames. - * Returns the row just after the last window. - */ +// Update the position for all windows, using the width and height of the frames. +// Returns the row just after the last window and global statusline (if there is one). int win_comp_pos(void) { int row = tabline_height(); @@ -5133,7 +5435,7 @@ int win_comp_pos(void) } } - return row; + return row + global_stl_height(); } void win_reconfig_floats(void) @@ -5167,7 +5469,7 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) wp->w_redr_status = true; wp->w_pos_changed = true; } - const int h = wp->w_height + wp->w_status_height; + const int h = wp->w_height + wp->w_hsep_height + wp->w_status_height; *row += h > topfrp->fr_height ? topfrp->fr_height : h; *col += wp->w_width + wp->w_vsep_width; } else { @@ -5184,7 +5486,6 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) } } - /* * Set current window height and take care of repositioning other windows to * fit around it. @@ -5200,23 +5501,16 @@ void win_setheight(int height) */ void win_setheight_win(int height, win_T *win) { - if (win == curwin) { - // Always keep current window at least one line high, even when - // 'winminheight' is zero. - if (height < p_wmh) { - height = p_wmh; - } - if (height == 0) { - height = 1; - } - } + // Always keep current window at least one line high, even when 'winminheight' is zero. + // Keep window at least two lines high if 'winbar' is enabled. + height = MAX(height, (win == curwin ? MAX(p_wmh, 1) : p_wmh) + win->w_winbar_height); if (win->w_floating) { win->w_float_config.height = height; win_config_float(win, win->w_float_config); redraw_later(win, NOT_VALID); } else { - frame_setheight(win->w_frame, height + win->w_status_height); + frame_setheight(win->w_frame, height + win->w_hsep_height + win->w_status_height); // recompute the window positions int row = win_comp_pos(); @@ -5225,15 +5519,19 @@ void win_setheight_win(int height, win_T *win) // line, clear it. if (full_screen && msg_scrolled == 0 && row < cmdline_row) { grid_fill(&default_grid, row, cmdline_row, 0, Columns, ' ', ' ', 0); + if (msg_grid.chars) { + clear_cmdline = true; + } } cmdline_row = row; + p_ch = MAX(Rows - cmdline_row, 0); + curtab->tp_ch_used = p_ch; msg_row = row; msg_col = 0; redraw_all_later(NOT_VALID); } } - /* * Set the height of a frame to "height" and take care that all frames and * windows inside it are resized. Also resize frames on the left and right if @@ -5265,7 +5563,10 @@ static void frame_setheight(frame_T *curfrp, int height) if (curfrp->fr_parent == NULL) { // topframe: can only change the command line if (height > ROWS_AVAIL) { - height = ROWS_AVAIL; + // If height is greater than the available space, try to create space for + // the frame by reducing 'cmdheight' if possible, while making sure + // `cmdheight` doesn't go below 1. + height = MIN((p_ch > 0 ? ROWS_AVAIL + (p_ch - 1) : ROWS_AVAIL), height); } if (height > 0) { frame_new_height(curfrp, height, false, false); @@ -5307,8 +5608,8 @@ static void frame_setheight(frame_T *curfrp, int height) room_cmdline = 0; } else { win_T *wp = lastwin_nofloating(); - room_cmdline = Rows - p_ch - - (wp->w_winrow + wp->w_height + wp->w_status_height); + room_cmdline = Rows - p_ch - global_stl_height() + - (wp->w_winrow + wp->w_height + wp->w_hsep_height + wp->w_status_height); if (room_cmdline < 0) { room_cmdline = 0; } @@ -5671,10 +5972,8 @@ void win_drag_status_line(win_T *dragwin, int offset) up = false; // Only dragging the last status line can reduce p_ch. room = Rows - cmdline_row; - if (curfr->fr_next == NULL) { - room -= 1; - } else { - room -= p_ch; + if (curfr->fr_next != NULL) { + room -= p_ch + global_stl_height(); } if (room < 0) { room = 0; @@ -5730,10 +6029,7 @@ void win_drag_status_line(win_T *dragwin, int offset) clear_cmdline = true; } cmdline_row = row; - p_ch = Rows - cmdline_row; - if (p_ch < 1) { - p_ch = 1; - } + p_ch = MAX(Rows - cmdline_row, 0); curtab->tp_ch_used = p_ch; redraw_all_later(SOME_VALID); showmode(); @@ -5800,7 +6096,6 @@ void win_drag_vsep_line(win_T *dragwin, int offset) } fr = curfr; // put fr at window that grows } - assert(fr); // Not enough room if (room < offset) { @@ -5813,7 +6108,9 @@ void win_drag_vsep_line(win_T *dragwin, int offset) } if (fr == NULL) { - return; // Safety check, should not happen. + // This can happen when calling win_move_separator() on the rightmost + // window. Just don't do anything. + return; } // grow frame fr by offset lines @@ -5844,7 +6141,6 @@ void win_drag_vsep_line(win_T *dragwin, int offset) redraw_all_later(NOT_VALID); } - #define FRACTION_MULT 16384L // Set wp->w_fraction for the current w_wrow and w_height. @@ -5996,7 +6292,7 @@ void win_set_inner_size(win_T *wp) int prev_height = wp->w_height_inner; int height = wp->w_height_request; if (height == 0) { - height = wp->w_height; + height = wp->w_height - wp->w_winbar_height; } if (height != prev_height) { @@ -6013,8 +6309,8 @@ void win_set_inner_size(win_T *wp) set_fraction(wp); } } - wp->w_height_inner = height; wp->w_skipcol = 0; + wp->w_height_inner = height; // There is no point in adjusting the scroll position when exiting. Some // values might be invalid. @@ -6040,10 +6336,20 @@ void win_set_inner_size(win_T *wp) terminal_check_size(wp->w_buffer->terminal); } - wp->w_height_outer = (wp->w_height_inner - + wp->w_border_adj[0] + wp->w_border_adj[2]); - wp->w_width_outer = (wp->w_width_inner - + wp->w_border_adj[1] + wp->w_border_adj[3]); + wp->w_height_outer = (wp->w_height_inner + win_border_height(wp) + wp->w_winbar_height); + wp->w_width_outer = (wp->w_width_inner + win_border_width(wp)); + wp->w_winrow_off = wp->w_border_adj[0] + wp->w_winbar_height; + wp->w_wincol_off = wp->w_border_adj[3]; +} + +static int win_border_height(win_T *wp) +{ + return wp->w_border_adj[0] + wp->w_border_adj[2]; +} + +static int win_border_width(win_T *wp) +{ + return wp->w_border_adj[1] + wp->w_border_adj[3]; } /// Set the width of a window. @@ -6179,7 +6485,7 @@ char_u *grab_file_name(long count, linenr_T *file_lnum) *file_lnum = getdigits_long(&p, false, 0); } - return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname); + return find_file_name_in_path(ptr, len, options, count, (char_u *)curbuf->b_ffname); } return file_name_at_cursor(options | FNAME_HYP, count, file_lnum); } @@ -6200,7 +6506,7 @@ char_u *grab_file_name(long count, linenr_T *file_lnum) char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum) { return file_name_in_line(get_cursor_line_ptr(), - curwin->w_cursor.col, options, count, curbuf->b_ffname, + curwin->w_cursor.col, options, count, (char_u *)curbuf->b_ffname, file_lnum); } @@ -6211,7 +6517,7 @@ char_u *file_name_at_cursor(int options, long count, linenr_T *file_lnum) char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u *rel_fname, linenr_T *file_lnum) { - char_u *ptr; + char *ptr; size_t len; bool in_type = true; bool is_url = false; @@ -6219,7 +6525,7 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u /* * search forward for what could be the start of a file name */ - ptr = line + col; + ptr = (char *)line + col; while (*ptr != NUL && !vim_isfilec(*ptr)) { MB_PTR_ADV(ptr); } @@ -6234,11 +6540,10 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u * Search backward for first char of the file name. * Go one char back to ":" before "//" even when ':' is not in 'isfname'. */ - while (ptr > line) { - if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) { + while ((char_u *)ptr > line) { + if ((len = (size_t)(utf_head_off(line, (char_u *)ptr - 1))) > 0) { ptr -= len + 1; - } else if (vim_isfilec(ptr[-1]) - || ((options & FNAME_HYP) && path_is_url((char *)ptr - 1))) { + } else if (vim_isfilec(ptr[-1]) || ((options & FNAME_HYP) && path_is_url(ptr - 1))) { ptr--; } else { break; @@ -6251,13 +6556,13 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u */ len = 0; while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ') - || ((options & FNAME_HYP) && path_is_url((char *)ptr + len)) - || (is_url && vim_strchr((char_u *)":?&=", ptr[len]) != NULL)) { + || ((options & FNAME_HYP) && path_is_url(ptr + len)) + || (is_url && vim_strchr(":?&=", ptr[len]) != NULL)) { // After type:// we also include :, ?, & and = as valid characters, so that // http://google.com:8080?q=this&that=ok works. if ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z')) { - if (in_type && path_is_url((char *)ptr + len + 1)) { + if (in_type && path_is_url(ptr + len + 1)) { is_url = true; } } else { @@ -6275,13 +6580,13 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u * If there is trailing punctuation, remove it. * But don't remove "..", could be a directory name. */ - if (len > 2 && vim_strchr((char_u *)".,:;!", ptr[len - 1]) != NULL + if (len > 2 && vim_strchr(".,:;!", ptr[len - 1]) != NULL && ptr[len - 2] != '.') { --len; } if (file_lnum != NULL) { - char_u *p; + char *p; const char *line_english = " line "; const char *line_transl = _(line_msg); @@ -6302,80 +6607,177 @@ char_u *file_name_in_line(char_u *line, int col, int options, long count, char_u } p = skipwhite(p); if (isdigit(*p)) { - *file_lnum = getdigits_long(&p, false, 0); + *file_lnum = getdigits_long((char_u **)&p, false, 0); } } } - return find_file_name_in_path(ptr, len, options, count, rel_fname); + return find_file_name_in_path((char_u *)ptr, len, options, count, rel_fname); } -/// Add or remove a status line for the bottom window(s), according to the +/// Add or remove a status line from window(s), according to the /// value of 'laststatus'. /// /// @param morewin pretend there are two or more windows if true. void last_status(bool morewin) { // Don't make a difference between horizontal or vertical split. - last_status_rec(topframe, (p_ls == 2 - || (p_ls == 1 && (morewin || !one_window())))); + last_status_rec(topframe, (p_ls == 2 || (p_ls == 1 && (morewin || !one_nonfloat()))), + global_stl_height() > 0); +} + +// Remove status line from window, replacing it with a horizontal separator if needed. +static void win_remove_status_line(win_T *wp, bool add_hsep) +{ + wp->w_status_height = 0; + if (add_hsep) { + wp->w_hsep_height = 1; + } else { + win_new_height(wp, wp->w_height + STATUS_HEIGHT); + } + comp_col(); + + stl_clear_click_defs(wp->w_status_click_defs, wp->w_status_click_defs_size); + xfree(wp->w_status_click_defs); + wp->w_status_click_defs_size = 0; + wp->w_status_click_defs = NULL; +} + +// Look for a horizontally resizable frame, starting with frame "fr". +// Returns NULL if there are no resizable frames. +static frame_T *find_horizontally_resizable_frame(frame_T *fr) +{ + frame_T *fp = fr; + + while (fp->fr_height <= frame_minheight(fp, NULL)) { + if (fp == topframe) { + return NULL; + } + // In a column of frames: go to frame above. If already at + // the top or in a row of frames: go to parent. + if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) { + fp = fp->fr_prev; + } else { + fp = fp->fr_parent; + } + } + + return fp; } -static void last_status_rec(frame_T *fr, bool statusline) +// Look for resizable frames and take lines from them to make room for the statusline. +// @return Success or failure. +static bool resize_frame_for_status(frame_T *fr) +{ + win_T *wp = fr->fr_win; + frame_T *fp = find_horizontally_resizable_frame(fr); + + if (fp == NULL) { + emsg(_(e_noroom)); + return false; + } else if (fp != fr) { + frame_new_height(fp, fp->fr_height - 1, false, false); + frame_fix_height(wp); + (void)win_comp_pos(); + } else { + win_new_height(wp, wp->w_height - 1); + } + + return true; +} + +// Look for resizable frames and take lines from them to make room for the winbar. +// @return Success or failure. +static bool resize_frame_for_winbar(frame_T *fr) +{ + win_T *wp = fr->fr_win; + frame_T *fp = find_horizontally_resizable_frame(fr); + + if (fp == NULL || fp == fr) { + emsg(_(e_noroom)); + return false; + } else { + frame_new_height(fp, fp->fr_height - 1, false, false); + win_new_height(wp, wp->w_height + 1); + frame_fix_height(wp); + (void)win_comp_pos(); + } + + return true; +} + +static void last_status_rec(frame_T *fr, bool statusline, bool is_stl_global) { frame_T *fp; win_T *wp; if (fr->fr_layout == FR_LEAF) { wp = fr->fr_win; - if (wp->w_status_height != 0 && !statusline) { - // remove status line - win_new_height(wp, wp->w_height + 1); - wp->w_status_height = 0; - comp_col(); - } else if (wp->w_status_height == 0 && statusline) { - // Find a frame to take a line from. - fp = fr; - while (fp->fr_height <= frame_minheight(fp, NULL)) { - if (fp == topframe) { - emsg(_(e_noroom)); + bool is_last = is_bottom_win(wp); + + if (is_last) { + if (wp->w_status_height != 0 && (!statusline || is_stl_global)) { + win_remove_status_line(wp, false); + } else if (wp->w_status_height == 0 && !is_stl_global && statusline) { + // Add statusline to window if needed + wp->w_status_height = STATUS_HEIGHT; + if (!resize_frame_for_status(fr)) { return; } - // In a column of frames: go to frame above. If already at - // the top or in a row of frames: go to parent. - if (fp->fr_parent->fr_layout == FR_COL && fp->fr_prev != NULL) { - fp = fp->fr_prev; - } else { - fp = fp->fr_parent; - } - } - wp->w_status_height = 1; - if (fp != fr) { - frame_new_height(fp, fp->fr_height - 1, false, false); - frame_fix_height(wp); - (void)win_comp_pos(); - } else { - win_new_height(wp, wp->w_height - 1); - } + comp_col(); + } + } else if (wp->w_status_height != 0 && is_stl_global) { + // If statusline is global and the window has a statusline, replace it with a horizontal + // separator + win_remove_status_line(wp, true); + } else if (wp->w_status_height == 0 && !is_stl_global) { + // If statusline isn't global and the window doesn't have a statusline, re-add it + wp->w_status_height = STATUS_HEIGHT; + wp->w_hsep_height = 0; comp_col(); - redraw_all_later(SOME_VALID); } - } else if (fr->fr_layout == FR_ROW) { - // vertically split windows, set status line for each one + redraw_all_later(SOME_VALID); + } else { + // For a column or row frame, recursively call this function for all child frames FOR_ALL_FRAMES(fp, fr->fr_child) { - last_status_rec(fp, statusline); + last_status_rec(fp, statusline, is_stl_global); } - } else { - // horizontally split window, set status line for last one - for (fp = fr->fr_child; fp->fr_next != NULL; fp = fp->fr_next) { + } +} + +// Add or remove window bars from windows depending on the value of 'winbar'. +void set_winbar(void) +{ + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + // Require the local value to be set in order to show winbar on a floating window. + int winbar_height = wp->w_floating ? ((*wp->w_p_wbr != NUL) ? 1 : 0) + : ((*p_wbr != NUL || *wp->w_p_wbr != NUL) ? 1 : 0); + + if (wp->w_winbar_height != winbar_height) { + if (winbar_height == 1 && wp->w_height_inner <= 1) { + if (wp->w_floating) { + emsg(_(e_noroom)); + continue; + } else if (!resize_frame_for_winbar(wp->w_frame)) { + return; + } + } + wp->w_winbar_height = winbar_height; + win_set_inner_size(wp); + wp->w_redr_status = wp->w_redr_status || winbar_height; + + if (winbar_height == 0) { + // When removing winbar, deallocate the w_winbar_click_defs array + stl_clear_click_defs(wp->w_winbar_click_defs, wp->w_winbar_click_defs_size); + xfree(wp->w_winbar_click_defs); + wp->w_winbar_click_defs_size = 0; + wp->w_winbar_click_defs = NULL; + } } - last_status_rec(fp, statusline); } } -/* - * Return the number of lines used by the tab page line. - */ +/// Return the number of lines used by the tab page line. int tabline_height(void) { if (ui_has(kUITabline)) { @@ -6391,10 +6793,20 @@ int tabline_height(void) return 1; } -/* - * Return the minimal number of rows that is needed on the screen to display - * the current number of windows. - */ +/// Return the number of lines used by default by the window bar. +int global_winbar_height(void) +{ + return *p_wbr != NUL ? 1 : 0; +} + +/// Return the number of lines used by the global statusline +int global_stl_height(void) +{ + return (p_ls == 3) ? STATUS_HEIGHT : 0; +} + +/// Return the minimal number of rows that is needed on the screen to display +/// the current number of windows. int min_rows(void) { if (firstwin == NULL) { // not initialized yet @@ -6408,7 +6820,7 @@ int min_rows(void) total = n; } } - total += tabline_height(); + total += tabline_height() + global_stl_height(); total += 1; // count the room for the command line return total; } @@ -6533,6 +6945,35 @@ static void clear_snapshot_rec(frame_T *fr) } } +/// Traverse a snapshot to find the previous curwin. +static win_T *get_snapshot_curwin_rec(frame_T *ft) +{ + win_T *wp; + + if (ft->fr_next != NULL) { + if ((wp = get_snapshot_curwin_rec(ft->fr_next)) != NULL) { + return wp; + } + } + if (ft->fr_child != NULL) { + if ((wp = get_snapshot_curwin_rec(ft->fr_child)) != NULL) { + return wp; + } + } + + return ft->fr_win; +} + +/// @return the current window stored in the snapshot or NULL. +static win_T *get_snapshot_curwin(int idx) +{ + if (curtab->tp_snapshot[idx] == NULL) { + return NULL; + } + + return get_snapshot_curwin_rec(curtab->tp_snapshot[idx]); +} + /// Restore a previously created snapshot, if there is any. /// This is only done if the screen size didn't change and the window layout is /// still the same. @@ -6605,28 +7046,6 @@ static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr) return wp; } -/// Gets the focused window (the one holding the cursor) of the snapshot. -static win_T *get_snapshot_focus(int idx) -{ - if (curtab->tp_snapshot[idx] == NULL) { - return NULL; - } - - frame_T *sn = curtab->tp_snapshot[idx]; - // This should be equivalent to the recursive algorithm found in - // restore_snapshot as far as traveling nodes go. - while (sn->fr_child != NULL || sn->fr_next != NULL) { - while (sn->fr_child != NULL) { - sn = sn->fr_child; - } - if (sn->fr_next != NULL) { - sn = sn->fr_next; - } - } - - return win_valid(sn->fr_win) ? sn->fr_win : NULL; -} - /// Set "win" to be the curwin and "tp" to be the current tab page. /// restore_win() MUST be called to undo, also when FAIL is returned. /// No autocommands will be executed until restore_win() is called. @@ -6635,20 +7054,27 @@ static win_T *get_snapshot_focus(int idx) /// triggered, another tabpage access is limited. /// /// @return FAIL if switching to "win" failed. -int switch_win(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp, - bool no_display) +int switch_win(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display) { block_autocmds(); - return switch_win_noblock(save_curwin, save_curtab, win, tp, no_display); + return switch_win_noblock(switchwin, win, tp, no_display); } // As switch_win() but without blocking autocommands. -int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage_T *tp, - bool no_display) +int switch_win_noblock(switchwin_T *switchwin, win_T *win, tabpage_T *tp, bool no_display) { - *save_curwin = curwin; + memset(switchwin, 0, sizeof(switchwin_T)); + switchwin->sw_curwin = curwin; + if (win == curwin) { + switchwin->sw_same_win = true; + } else { + // Disable Visual selection, because redrawing may fail. + switchwin->sw_visual_active = VIsual_active; + VIsual_active = false; + } + if (tp != NULL) { - *save_curtab = curtab; + switchwin->sw_curtab = curtab; if (no_display) { curtab->tp_firstwin = firstwin; curtab->tp_lastwin = lastwin; @@ -6670,33 +7096,35 @@ int switch_win_noblock(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, // Restore current tabpage and window saved by switch_win(), if still valid. // When "no_display" is true the display won't be affected, no redraw is // triggered. -void restore_win(win_T *save_curwin, tabpage_T *save_curtab, bool no_display) +void restore_win(switchwin_T *switchwin, bool no_display) { - restore_win_noblock(save_curwin, save_curtab, no_display); + restore_win_noblock(switchwin, no_display); unblock_autocmds(); } // As restore_win() but without unblocking autocommands. -void restore_win_noblock(win_T *save_curwin, tabpage_T *save_curtab, bool no_display) +void restore_win_noblock(switchwin_T *switchwin, bool no_display) { - if (save_curtab != NULL && valid_tabpage(save_curtab)) { + if (switchwin->sw_curtab != NULL && valid_tabpage(switchwin->sw_curtab)) { if (no_display) { curtab->tp_firstwin = firstwin; curtab->tp_lastwin = lastwin; - curtab = save_curtab; + curtab = switchwin->sw_curtab; firstwin = curtab->tp_firstwin; lastwin = curtab->tp_lastwin; } else { - goto_tabpage_tp(save_curtab, false, false); + goto_tabpage_tp(switchwin->sw_curtab, false, false); } } - if (win_valid(save_curwin)) { - curwin = save_curwin; + + if (!switchwin->sw_same_win) { + VIsual_active = switchwin->sw_visual_active; + } + + if (win_valid(switchwin->sw_curwin)) { + curwin = switchwin->sw_curwin; curbuf = curwin->w_buffer; } - // If called by win_execute() and executing the command changed the - // directory, it now has to be restored. - fix_current_dir(); } /// Make "buf" the current buffer. @@ -6726,286 +7154,6 @@ void restore_buffer(bufref_T *save_curbuf) } } - -/// Add match to the match list of window 'wp'. The pattern 'pat' will be -/// highlighted with the group 'grp' with priority 'prio'. -/// Optionally, a desired ID 'id' can be specified (greater than or equal to 1). -/// -/// @param[in] id a desired ID 'id' can be specified -/// (greater than or equal to 1). -1 must be specified if no -/// particular ID is desired -/// @param[in] conceal_char pointer to conceal replacement char -/// @return ID of added match, -1 on failure. -int match_add(win_T *wp, const char *const grp, const char *const pat, int prio, int id, - list_T *pos_list, const char *const conceal_char) - FUNC_ATTR_NONNULL_ARG(1, 2) -{ - matchitem_T *cur; - matchitem_T *prev; - matchitem_T *m; - int hlg_id; - regprog_T *regprog = NULL; - int rtype = SOME_VALID; - - if (*grp == NUL || (pat != NULL && *pat == NUL)) { - return -1; - } - if (id < -1 || id == 0) { - semsg(_("E799: Invalid ID: %" PRId64 - " (must be greater than or equal to 1)"), - (int64_t)id); - return -1; - } - if (id != -1) { - cur = wp->w_match_head; - while (cur != NULL) { - if (cur->id == id) { - semsg(_("E801: ID already taken: %" PRId64), (int64_t)id); - return -1; - } - cur = cur->next; - } - } - if ((hlg_id = syn_check_group(grp, strlen(grp))) == 0) { - return -1; - } - if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { - semsg(_(e_invarg2), pat); - return -1; - } - - // Find available match ID. - while (id == -1) { - cur = wp->w_match_head; - while (cur != NULL && cur->id != wp->w_next_match_id) { - cur = cur->next; - } - if (cur == NULL) { - id = wp->w_next_match_id; - } - wp->w_next_match_id++; - } - - // Build new match. - m = xcalloc(1, sizeof(matchitem_T)); - m->id = id; - m->priority = prio; - m->pattern = pat == NULL ? NULL: (char_u *)xstrdup(pat); - m->hlg_id = hlg_id; - m->match.regprog = regprog; - m->match.rmm_ic = FALSE; - m->match.rmm_maxcol = 0; - m->conceal_char = 0; - if (conceal_char != NULL) { - m->conceal_char = utf_ptr2char((const char_u *)conceal_char); - } - - // Set up position matches - if (pos_list != NULL) { - linenr_T toplnum = 0; - linenr_T botlnum = 0; - - int i = 0; - TV_LIST_ITER(pos_list, li, { - linenr_T lnum = 0; - colnr_T col = 0; - int len = 1; - bool error = false; - - if (TV_LIST_ITEM_TV(li)->v_type == VAR_LIST) { - const list_T *const subl = TV_LIST_ITEM_TV(li)->vval.v_list; - const listitem_T *subli = tv_list_first(subl); - if (subli == NULL) { - semsg(_("E5030: Empty list at position %d"), - (int)tv_list_idx_of_item(pos_list, li)); - goto fail; - } - lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); - if (error) { - goto fail; - } - if (lnum <= 0) { - continue; - } - m->pos.pos[i].lnum = lnum; - subli = TV_LIST_ITEM_NEXT(subl, subli); - if (subli != NULL) { - col = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); - if (error) { - goto fail; - } - if (col < 0) { - continue; - } - subli = TV_LIST_ITEM_NEXT(subl, subli); - if (subli != NULL) { - len = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); - if (len < 0) { - continue; - } - if (error) { - goto fail; - } - } - } - m->pos.pos[i].col = col; - m->pos.pos[i].len = len; - } else if (TV_LIST_ITEM_TV(li)->v_type == VAR_NUMBER) { - if (TV_LIST_ITEM_TV(li)->vval.v_number <= 0) { - continue; - } - m->pos.pos[i].lnum = TV_LIST_ITEM_TV(li)->vval.v_number; - m->pos.pos[i].col = 0; - m->pos.pos[i].len = 0; - } else { - semsg(_("E5031: List or number required at position %d"), - (int)tv_list_idx_of_item(pos_list, li)); - goto fail; - } - if (toplnum == 0 || lnum < toplnum) { - toplnum = lnum; - } - if (botlnum == 0 || lnum >= botlnum) { - botlnum = lnum + 1; - } - i++; - if (i >= MAXPOSMATCH) { - break; - } - }); - - // Calculate top and bottom lines for redrawing area - if (toplnum != 0) { - if (wp->w_buffer->b_mod_set) { - if (wp->w_buffer->b_mod_top > toplnum) { - wp->w_buffer->b_mod_top = toplnum; - } - if (wp->w_buffer->b_mod_bot < botlnum) { - wp->w_buffer->b_mod_bot = botlnum; - } - } else { - wp->w_buffer->b_mod_set = true; - wp->w_buffer->b_mod_top = toplnum; - wp->w_buffer->b_mod_bot = botlnum; - wp->w_buffer->b_mod_xlines = 0; - } - m->pos.toplnum = toplnum; - m->pos.botlnum = botlnum; - rtype = VALID; - } - } - - // Insert new match. The match list is in ascending order with regard to - // the match priorities. - cur = wp->w_match_head; - prev = cur; - while (cur != NULL && prio >= cur->priority) { - prev = cur; - cur = cur->next; - } - if (cur == prev) { - wp->w_match_head = m; - } else { - prev->next = m; - } - m->next = cur; - - redraw_later(wp, rtype); - return id; - -fail: - xfree(m); - return -1; -} - - -/// Delete match with ID 'id' in the match list of window 'wp'. -/// -/// @param perr print error messages if true. -int match_delete(win_T *wp, int id, bool perr) -{ - matchitem_T *cur = wp->w_match_head; - matchitem_T *prev = cur; - int rtype = SOME_VALID; - - if (id < 1) { - if (perr) { - semsg(_("E802: Invalid ID: %" PRId64 - " (must be greater than or equal to 1)"), - (int64_t)id); - } - return -1; - } - while (cur != NULL && cur->id != id) { - prev = cur; - cur = cur->next; - } - if (cur == NULL) { - if (perr) { - semsg(_("E803: ID not found: %" PRId64), (int64_t)id); - } - return -1; - } - if (cur == prev) { - wp->w_match_head = cur->next; - } else { - prev->next = cur->next; - } - vim_regfree(cur->match.regprog); - xfree(cur->pattern); - if (cur->pos.toplnum != 0) { - if (wp->w_buffer->b_mod_set) { - if (wp->w_buffer->b_mod_top > cur->pos.toplnum) { - wp->w_buffer->b_mod_top = cur->pos.toplnum; - } - if (wp->w_buffer->b_mod_bot < cur->pos.botlnum) { - wp->w_buffer->b_mod_bot = cur->pos.botlnum; - } - } else { - wp->w_buffer->b_mod_set = true; - wp->w_buffer->b_mod_top = cur->pos.toplnum; - wp->w_buffer->b_mod_bot = cur->pos.botlnum; - wp->w_buffer->b_mod_xlines = 0; - } - rtype = VALID; - } - xfree(cur); - redraw_later(wp, rtype); - return 0; -} - -/* - * Delete all matches in the match list of window 'wp'. - */ -void clear_matches(win_T *wp) -{ - matchitem_T *m; - - while (wp->w_match_head != NULL) { - m = wp->w_match_head->next; - vim_regfree(wp->w_match_head->match.regprog); - xfree(wp->w_match_head->pattern); - xfree(wp->w_match_head); - wp->w_match_head = m; - } - redraw_later(wp, SOME_VALID); -} - -/* - * Get match from ID 'id' in window 'wp'. - * Return NULL if match not found. - */ -matchitem_T *get_match(win_T *wp, int id) -{ - matchitem_T *cur = wp->w_match_head; - - while (cur != NULL && cur->id != id) { - cur = cur->next; - } - return cur; -} - - /// Check that "topfrp" and its children are at the right height. /// /// @param topfrp top frame pointer @@ -7131,16 +7279,14 @@ void win_id2tabwin(typval_T *const argvars, typval_T *const rettv) tv_list_append_number(list, winnr); } -win_T *win_id2wp(typval_T *argvars) +win_T *win_id2wp(int id) { - return win_id2wp_tp(argvars, NULL); + return win_id2wp_tp(id, NULL); } // Return the window and tab pointer of window "id". -win_T *win_id2wp_tp(typval_T *argvars, tabpage_T **tpp) +win_T *win_id2wp_tp(int id, tabpage_T **tpp) { - int id = tv_get_number(&argvars[0]); - FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->handle == id) { if (tpp != NULL) { @@ -7172,7 +7318,7 @@ void win_findbuf(typval_T *argvars, list_T *list) int bufnr = tv_get_number(&argvars[0]); FOR_ALL_TAB_WINDOWS(tp, wp) { - if (!wp->w_closing && wp->w_buffer->b_fnum == bufnr) { + if (wp->w_buffer->b_fnum == bufnr) { tv_list_append_number(list, wp->handle); } } |