diff options
author | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:40:31 +0000 |
---|---|---|
committer | Josh Rahm <joshuarahm@gmail.com> | 2023-11-29 22:40:31 +0000 |
commit | 339e2d15cc26fe86988ea06468d912a46c8d6f29 (patch) | |
tree | a6167fc8fcfc6ae2dc102f57b2473858eac34063 /src/nvim/window.c | |
parent | 067dc73729267c0262438a6fdd66e586f8496946 (diff) | |
parent | 4a8bf24ac690004aedf5540fa440e788459e5e34 (diff) | |
download | rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.tar.gz rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.tar.bz2 rneovim-339e2d15cc26fe86988ea06468d912a46c8d6f29.zip |
Merge remote-tracking branch 'upstream/master' into fix_repeatcmdline
Diffstat (limited to 'src/nvim/window.c')
-rw-r--r-- | src/nvim/window.c | 859 |
1 files changed, 386 insertions, 473 deletions
diff --git a/src/nvim/window.c b/src/nvim/window.c index 05f84b5a91..bcf245ef93 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1,12 +1,8 @@ -// This is an open source non-commercial project. Dear PVS-Studio, please check -// it. PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com - #include <assert.h> #include <ctype.h> #include <inttypes.h> #include <limits.h> #include <stdbool.h> -#include <stddef.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -15,7 +11,7 @@ #include "nvim/api/private/defs.h" #include "nvim/api/private/helpers.h" #include "nvim/arglist.h" -#include "nvim/ascii.h" +#include "nvim/ascii_defs.h" #include "nvim/autocmd.h" #include "nvim/buffer.h" #include "nvim/buffer_defs.h" @@ -27,7 +23,6 @@ #include "nvim/edit.h" #include "nvim/eval.h" #include "nvim/eval/typval.h" -#include "nvim/eval/typval_defs.h" #include "nvim/eval/vars.h" #include "nvim/eval/window.h" #include "nvim/ex_cmds.h" @@ -38,6 +33,7 @@ #include "nvim/file_search.h" #include "nvim/fileio.h" #include "nvim/fold.h" +#include "nvim/func_attr.h" #include "nvim/garray.h" #include "nvim/getchar.h" #include "nvim/gettext.h" @@ -45,40 +41,39 @@ #include "nvim/grid.h" #include "nvim/hashtab.h" #include "nvim/keycodes.h" -#include "nvim/macros.h" +#include "nvim/macros_defs.h" #include "nvim/main.h" -#include "nvim/map.h" +#include "nvim/map_defs.h" #include "nvim/mapping.h" // IWYU pragma: keep (langmap_adjust_mb) #include "nvim/mark.h" #include "nvim/match.h" #include "nvim/mbyte.h" -#include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/mouse.h" #include "nvim/move.h" #include "nvim/normal.h" #include "nvim/option.h" -#include "nvim/optionstr.h" -#include "nvim/os/os.h" -#include "nvim/os/os_defs.h" +#include "nvim/option_defs.h" +#include "nvim/option_vars.h" +#include "nvim/os/fs.h" #include "nvim/path.h" #include "nvim/plines.h" -#include "nvim/pos.h" +#include "nvim/pos_defs.h" #include "nvim/quickfix.h" -#include "nvim/screen.h" #include "nvim/search.h" #include "nvim/state.h" #include "nvim/statusline.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/terminal.h" -#include "nvim/types.h" +#include "nvim/types_defs.h" #include "nvim/ui.h" #include "nvim/ui_compositor.h" #include "nvim/undo.h" -#include "nvim/vim.h" +#include "nvim/vim_defs.h" #include "nvim/window.h" +#include "nvim/winfloat.h" #ifdef INCLUDE_GENERATED_DECLARATIONS # include "window.c.generated.h" @@ -97,7 +92,7 @@ typedef enum { WEE_TRIGGER_LEAVE_AUTOCMDS = 0x10, } wee_flags_T; -static char e_cannot_split_window_when_closing_buffer[] +static const 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"); @@ -140,15 +135,41 @@ win_T *prevwin_curwin(void) return is_in_cmdwin() && prevwin != NULL ? prevwin : curwin; } +/// If the 'switchbuf' option contains "useopen" or "usetab", then try to jump +/// to a window containing "buf". +/// Returns the pointer to the window that was jumped to or NULL. +win_T *swbuf_goto_win_with_buf(buf_T *buf) +{ + win_T *wp = NULL; + + if (buf == NULL) { + return wp; + } + + // If 'switchbuf' contains "useopen": jump to first window in the current + // tab page containing "buf" if one exists. + if (swb_flags & SWB_USEOPEN) { + wp = buf_jump_open_win(buf); + } + + // If 'switchbuf' contains "usetab": jump to first window in any tab page + // containing "buf" if one exists. + if (wp == NULL && (swb_flags & SWB_USETAB)) { + wp = buf_jump_open_tab(buf); + } + + return wp; +} + /// all CTRL-W window commands are handled here, called from normal_cmd(). /// /// @param xchar extra char from ":wincmd gx" or NUL -void do_window(int nchar, long Prenum, int xchar) +void do_window(int nchar, int Prenum, int xchar) { int type = FIND_DEFINE; char cbuf[40]; - long Prenum1 = Prenum == 0 ? 1 : Prenum; + int Prenum1 = Prenum == 0 ? 1 : Prenum; #define CHECK_CMDWIN \ do { \ @@ -170,7 +191,7 @@ void do_window(int nchar, long Prenum, int xchar) if (bt_quickfix(curbuf)) { goto newwindow; } - (void)win_split((int)Prenum, 0); + (void)win_split(Prenum, 0); break; // split current window in two parts, vertically @@ -183,7 +204,7 @@ void do_window(int nchar, long Prenum, int xchar) if (bt_quickfix(curbuf)) { goto newwindow; } - (void)win_split((int)Prenum, WSP_VERT); + (void)win_split(Prenum, WSP_VERT); break; // split current window and edit alternate file @@ -192,7 +213,7 @@ void do_window(int nchar, long Prenum, int xchar) CHECK_CMDWIN; reset_VIsual_and_resel(); // stop Visual mode - if (buflist_findnr(Prenum == 0 ? curwin->w_alt_fnum : (int)Prenum) == NULL) { + if (buflist_findnr(Prenum == 0 ? curwin->w_alt_fnum : Prenum) == NULL) { if (Prenum == 0) { emsg(_(e_noalt)); } else { @@ -202,8 +223,8 @@ void do_window(int nchar, long Prenum, int xchar) } if (!curbuf_locked() && win_split(0, 0) == OK) { - (void)buflist_getfile(Prenum == 0 ? curwin->w_alt_fnum : (int)Prenum, - (linenr_T)0, GETF_ALT, false); + (void)buflist_getfile(Prenum == 0 ? curwin->w_alt_fnum : Prenum, + 0, GETF_ALT, false); } break; @@ -355,17 +376,16 @@ newwindow: case 'T': CHECK_CMDWIN; if (one_window(curwin)) { - msg(_(m_onlyone)); + msg(_(m_onlyone), 0); } else { tabpage_T *oldtab = curtab; - tabpage_T *newtab; // First create a new tab with the window, then go back to // the old tab and close the window there. win_T *wp = curwin; - if (win_new_tabpage((int)Prenum, NULL) == OK + if (win_new_tabpage(Prenum, NULL) == OK && valid_tabpage(oldtab)) { - newtab = curtab; + tabpage_T *newtab = curtab; goto_tabpage_tp(oldtab, true, true); if (curwin == wp) { win_close(curwin, false, false); @@ -412,14 +432,14 @@ newwindow: case 'r': CHECK_CMDWIN; reset_VIsual_and_resel(); // stop Visual mode - win_rotate(false, (int)Prenum1); // downwards + win_rotate(false, Prenum1); // downwards break; // rotate windows upwards case 'R': CHECK_CMDWIN; reset_VIsual_and_resel(); // stop Visual mode - win_rotate(true, (int)Prenum1); // upwards + win_rotate(true, Prenum1); // upwards break; // move window to the very top/bottom/left/right @@ -428,7 +448,7 @@ newwindow: case 'H': case 'L': CHECK_CMDWIN; - win_totop((int)Prenum, + win_totop(Prenum, ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0) | ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT)); break; @@ -442,40 +462,40 @@ newwindow: // increase current window height case '+': - win_setheight(curwin->w_height + (int)Prenum1); + win_setheight(curwin->w_height + Prenum1); break; // decrease current window height case '-': - win_setheight(curwin->w_height - (int)Prenum1); + win_setheight(curwin->w_height - Prenum1); break; // set current window height case Ctrl__: case '_': - win_setheight(Prenum ? (int)Prenum : Rows - 1); + win_setheight(Prenum ? Prenum : Rows - 1); break; // increase current window width case '>': - win_setwidth(curwin->w_width + (int)Prenum1); + win_setwidth(curwin->w_width + Prenum1); break; // decrease current window width case '<': - win_setwidth(curwin->w_width - (int)Prenum1); + win_setwidth(curwin->w_width - Prenum1); break; // set current window width case '|': - win_setwidth(Prenum != 0 ? (int)Prenum : Columns); + win_setwidth(Prenum != 0 ? Prenum : Columns); break; // jump to tag and split window if tag exists (in preview window) case '}': CHECK_CMDWIN; if (Prenum) { - g_do_tagpreview = (int)Prenum; + g_do_tagpreview = Prenum; } else { g_do_tagpreview = (int)p_pvh; } @@ -485,7 +505,7 @@ newwindow: CHECK_CMDWIN; // Keep visual mode, can select words to use as a tag. if (Prenum) { - postponed_split = (int)Prenum; + postponed_split = Prenum; } else { postponed_split = -1; } @@ -506,6 +526,9 @@ newwindow: case Ctrl_F: { wingotofile: CHECK_CMDWIN; + if (check_text_or_curbuf_locked(NULL)) { + break; + } linenr_T lnum = -1; char *ptr = grab_file_name(Prenum1, &lnum); @@ -513,18 +536,31 @@ wingotofile: tabpage_T *oldtab = curtab; win_T *oldwin = curwin; setpcmark(); - if (win_split(0, 0) == OK) { + + // If 'switchbuf' is set to 'useopen' or 'usetab' and the + // file is already opened in a window, then jump to it. + win_T *wp = NULL; + if ((swb_flags & (SWB_USEOPEN | SWB_USETAB)) + && cmdmod.cmod_tab == 0) { + wp = swbuf_goto_win_with_buf(buflist_findname_exp(ptr)); + } + + if (wp == NULL && win_split(0, 0) == OK) { RESET_BINDING(curwin); if (do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL, ECMD_HIDE, NULL) == FAIL) { // Failed to open the file, close the window opened for it. win_close(curwin, false, false); goto_tabpage_win(oldtab, oldwin); - } else if (nchar == 'F' && lnum >= 0) { - curwin->w_cursor.lnum = lnum; - check_cursor_lnum(); - beginline(BL_SOL | BL_FIX); + } else { + wp = curwin; } } + + if (wp != NULL && nchar == 'F' && lnum >= 0) { + curwin->w_cursor.lnum = lnum; + check_cursor_lnum(curwin); + beginline(BL_SOL | BL_FIX); + } xfree(ptr); } break; @@ -546,7 +582,7 @@ wingotofile: } // Make a copy, if the line was changed it will be freed. - ptr = xstrnsave(ptr, len); + ptr = xmemdupz(ptr, len); find_pattern_in_path(ptr, 0, len, true, Prenum == 0, type, Prenum1, ACTION_SPLIT, 1, MAXLNUM); @@ -581,7 +617,7 @@ wingotofile: case '}': xchar = Ctrl_RSB; if (Prenum) { - g_do_tagpreview = (int)Prenum; + g_do_tagpreview = Prenum; } else { g_do_tagpreview = (int)p_pvh; } @@ -590,7 +626,7 @@ wingotofile: case Ctrl_RSB: // Keep visual mode, can select words to use as a tag. if (Prenum) { - postponed_split = (int)Prenum; + postponed_split = Prenum; } else { postponed_split = -1; } @@ -608,11 +644,11 @@ wingotofile: goto wingotofile; case 't': // CTRL-W gt: go to next tab page - goto_tabpage((int)Prenum); + goto_tabpage(Prenum); break; case 'T': // CTRL-W gT: go to previous tab page - goto_tabpage(-(int)Prenum1); + goto_tabpage(-Prenum1); break; case TAB: // CTRL-W g<Tab>: go to last used tab page @@ -658,17 +694,13 @@ static void cmd_with_count(char *cmd, char *bufp, size_t bufsize, int64_t Prenum } } -void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) +void win_set_buf(win_T *win, buf_T *buf, bool noautocmd, Error *err) + FUNC_ATTR_NONNULL_ALL { - win_T *win = find_window_by_handle(window, err); - buf_T *buf = find_buffer_by_handle(buffer, err); - - if (!win || !buf) { - return; - } - tabpage_T *tab = win_find_tabpage(win); + // no redrawing and don't set the window title + RedrawingDisabled++; if (noautocmd) { block_autocmds(); } @@ -678,7 +710,7 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) api_set_error(err, kErrorTypeException, "Failed to switch to window %d", - window); + win->handle); } try_start(); @@ -687,7 +719,7 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) api_set_error(err, kErrorTypeException, "Failed to set buffer %d", - buffer); + buf->handle); } // If window is not current, state logic will not validate its cursor. @@ -698,210 +730,13 @@ void win_set_buf(Window window, Buffer buffer, bool noautocmd, Error *err) if (noautocmd) { unblock_autocmds(); } -} - -/// Create a new float. -/// -/// @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(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 - api_set_error(err, kErrorTypeException, - "Cannot change last window into float"); - return NULL; - } else if (!win_valid(wp)) { - api_set_error(err, kErrorTypeException, - "Cannot change window from different tabpage into float"); - return NULL; - } - int dir; - winframe_remove(wp, &dir, NULL); - XFREE_CLEAR(wp->w_frame); - (void)win_comp_pos(); // recompute window positions - win_remove(wp, NULL); - win_append(lastwin_nofloating(), wp); - } - wp->w_floating = true; - 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); - win_set_inner_size(wp, true); - wp->w_pos_changed = true; - redraw_later(wp, UPD_VALID); - return wp; -} - -void win_set_minimal_style(win_T *wp) -{ - wp->w_p_nu = false; - wp->w_p_rnu = false; - wp->w_p_cul = false; - wp->w_p_cuc = false; - wp->w_p_spell = false; - wp->w_p_list = false; - - // Hide EOB region: use " " fillchar and cleared highlighting - if (wp->w_p_fcs_chars.eob != ' ') { - char *old = wp->w_p_fcs; - wp->w_p_fcs = ((*old == NUL) - ? xstrdup("eob: ") - : concat_str(old, ",eob: ")); - free_string_option(old); - } - - // TODO(bfredl): this could use a highlight namespace directly, - // and avoid peculiarities around window options - char *old = wp->w_p_winhl; - wp->w_p_winhl = ((*old == NUL) - ? xstrdup("EndOfBuffer:") - : concat_str(old, ",EndOfBuffer:")); - free_string_option(old); - parse_winhl_opt(wp); - - // signcolumn: use 'auto' - if (wp->w_p_scl[0] != 'a' || strlen(wp->w_p_scl) >= 8) { - free_string_option(wp->w_p_scl); - wp->w_p_scl = xstrdup("auto"); - } - - // foldcolumn: use '0' - if (wp->w_p_fdc[0] != '0') { - free_string_option(wp->w_p_fdc); - wp->w_p_fdc = xstrdup("0"); - } - - // colorcolumn: cleared - if (wp->w_p_cc != NULL && *wp->w_p_cc != NUL) { - free_string_option(wp->w_p_cc); - wp->w_p_cc = xstrdup(""); - } - - // statuscolumn: cleared - if (wp->w_p_stc != NULL && *wp->w_p_stc != NUL) { - free_string_option(wp->w_p_stc); - wp->w_p_stc = xstrdup(""); - } -} - -void win_config_float(win_T *wp, FloatConfig fconfig) -{ - wp->w_width = MAX(fconfig.width, 1); - wp->w_height = MAX(fconfig.height, 1); - - if (fconfig.relative == kFloatRelativeCursor) { - fconfig.relative = kFloatRelativeWindow; - fconfig.row += curwin->w_wrow; - fconfig.col += curwin->w_wcol; - fconfig.window = curwin->handle; - } else if (fconfig.relative == kFloatRelativeMouse) { - int row = mouse_row, col = mouse_col, grid = mouse_grid; - win_T *mouse_win = mouse_find_win(&grid, &row, &col); - if (mouse_win != NULL) { - fconfig.relative = kFloatRelativeWindow; - fconfig.row += row; - fconfig.col += col; - fconfig.window = mouse_win->handle; - } - } - - bool change_external = fconfig.external != wp->w_float_config.external; - bool change_border = (fconfig.border != wp->w_float_config.border - || memcmp(fconfig.border_hl_ids, - wp->w_float_config.border_hl_ids, - sizeof fconfig.border_hl_ids) != 0); - - wp->w_float_config = fconfig; - - bool has_border = wp->w_floating && wp->w_float_config.border; - for (int i = 0; i < 4; i++) { - int new_adj = has_border && wp->w_float_config.border_chars[2 * i + 1][0]; - if (new_adj != wp->w_border_adj[i]) { - change_border = true; - wp->w_border_adj[i] = new_adj; - } - } - - if (!ui_has(kUIMultigrid)) { - 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, true); - must_redraw = MAX(must_redraw, UPD_VALID); - - wp->w_pos_changed = true; - if (change_external || change_border) { - wp->w_hl_needs_update = true; - redraw_later(wp, UPD_NOT_VALID); - } - - // compute initial position - if (wp->w_float_config.relative == kFloatRelativeWindow) { - int row = (int)wp->w_float_config.row; - int col = (int)wp->w_float_config.col; - Error dummy = ERROR_INIT; - win_T *parent = find_window_by_handle(wp->w_float_config.window, &dummy); - if (parent) { - row += parent->w_winrow; - col += parent->w_wincol; - ScreenGrid *grid = &parent->w_grid; - int row_off = 0, col_off = 0; - grid_adjust(&grid, &row_off, &col_off); - row += row_off; - col += col_off; - if (wp->w_float_config.bufpos.lnum >= 0) { - pos_T pos = { wp->w_float_config.bufpos.lnum + 1, - wp->w_float_config.bufpos.col, 0 }; - int trow, tcol, tcolc, tcole; - textpos2screenpos(parent, &pos, &trow, &tcol, &tcolc, &tcole, true); - row += trow - 1; - col += tcol - 1; - } - } - api_clear_error(&dummy); - wp->w_winrow = row; - wp->w_wincol = col; - } else { - wp->w_winrow = (int)fconfig.row; - wp->w_wincol = (int)fconfig.col; - } - - // changing border style while keeping border only requires redrawing border - if (fconfig.border) { - wp->w_redr_border = true; - redraw_later(wp, UPD_VALID); - } -} - -void win_check_anchored_floats(win_T *win) -{ - for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { - // float might be anchored to moved window - if (wp->w_float_config.relative == kFloatRelativeWindow - && wp->w_float_config.window == win->handle) { - wp->w_pos_changed = true; - } - } + RedrawingDisabled--; } /// Return the number of fold columns to display int win_fdccol_count(win_T *wp) { - const char *fdc = (const char *)wp->w_p_fdc; + const char *fdc = wp->w_p_fdc; // auto:<NUM> if (strncmp(fdc, "auto", 4) == 0) { @@ -928,7 +763,13 @@ void ui_ext_win_position(win_T *wp, bool validate) if (c.relative == kFloatRelativeWindow) { Error dummy = ERROR_INIT; win_T *win = find_window_by_handle(c.window, &dummy); - if (win) { + api_clear_error(&dummy); + if (win != NULL) { + // When a floating window is anchored to another window, + // update the position of its anchored window first. + if (win->w_pos_changed && win->w_grid_alloc.chars != NULL && win_valid(win)) { + ui_ext_win_position(win, validate); + } grid = &win->w_grid; int row_off = 0, col_off = 0; grid_adjust(&grid, &row_off, &col_off); @@ -942,15 +783,18 @@ void ui_ext_win_position(win_T *wp, bool validate) col += tcol - 1; } } - api_clear_error(&dummy); } wp->w_grid_alloc.zindex = wp->w_float_config.zindex; if (ui_has(kUIMultigrid)) { 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); + if (!c.hide) { + ui_call_win_float_pos(wp->w_grid_alloc.handle, wp->handle, anchor, + grid->handle, row, col, c.focusable, + wp->w_grid_alloc.zindex); + } else { + ui_call_win_hide(wp->w_grid_alloc.handle); + } } else { bool valid = (wp->w_redr_type == 0); if (!valid && !validate) { @@ -967,16 +811,23 @@ void ui_ext_win_position(win_T *wp, bool validate) comp_row += grid->comp_row; comp_col += grid->comp_col; 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); + if (!c.fixed || east) { + comp_col = MAX(MIN(comp_col, Columns - wp->w_width_outer), 0); + } wp->w_winrow = comp_row; wp->w_wincol = comp_col; - ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col, - wp->w_height_outer, wp->w_width_outer, valid, false); - ui_check_cursor_grid(wp->w_grid_alloc.handle); - wp->w_grid_alloc.focusable = wp->w_float_config.focusable; - if (!valid) { - wp->w_grid_alloc.valid = false; - redraw_later(wp, UPD_NOT_VALID); + + if (!c.hide) { + ui_comp_put_grid(&wp->w_grid_alloc, comp_row, comp_col, + wp->w_height_outer, wp->w_width_outer, valid, false); + ui_check_cursor_grid(wp->w_grid_alloc.handle); + wp->w_grid_alloc.focusable = wp->w_float_config.focusable; + if (!valid) { + wp->w_grid_alloc.valid = false; + redraw_later(wp, UPD_NOT_VALID); + } + } else { + ui_comp_remove_grid(&wp->w_grid_alloc); } } } else { @@ -986,22 +837,63 @@ void ui_ext_win_position(win_T *wp, bool validate) 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) { + // NOTE: The win_viewport command is delayed until the next flush when there are pending updates. + // This ensures that the updates and the viewport are sent together. + if ((wp == curwin || ui_has(kUIMultigrid)) && wp->w_viewport_invalid && wp->w_redr_type == 0) { + const linenr_T line_count = wp->w_buffer->b_ml.ml_line_count; + // Avoid ml_get errors when producing "scroll_delta". + const linenr_T cur_topline = MIN(wp->w_topline, line_count); + const linenr_T cur_botline = MIN(wp->w_botline, line_count); + int64_t delta = 0; + linenr_T last_topline = wp->w_viewport_last_topline; + linenr_T last_botline = wp->w_viewport_last_botline; + int last_topfill = wp->w_viewport_last_topfill; + int64_t last_skipcol = wp->w_viewport_last_skipcol; + if (last_topline > line_count) { + delta -= last_topline - line_count; + last_topline = line_count; + last_topfill = 0; + last_skipcol = MAXCOL; + } + last_botline = MIN(last_botline, line_count); + if (cur_topline < last_topline + || (cur_topline == last_topline && wp->w_skipcol < last_skipcol)) { + if (last_topline > 0 && cur_botline < last_topline) { + // Scrolling too many lines: only give an approximate "scroll_delta". + delta -= win_text_height(wp, cur_topline, wp->w_skipcol, cur_botline, 0, NULL); + delta -= last_topline - cur_botline; + } else { + delta -= win_text_height(wp, cur_topline, wp->w_skipcol, last_topline, last_skipcol, NULL); + } + } else if (cur_topline > last_topline + || (cur_topline == last_topline && wp->w_skipcol > last_skipcol)) { + if (last_botline > 0 && cur_topline > last_botline) { + // Scrolling too many lines: only give an approximate "scroll_delta". + delta += win_text_height(wp, last_topline, last_skipcol, last_botline, 0, NULL); + delta += cur_topline - last_botline; + } else { + delta += win_text_height(wp, last_topline, last_skipcol, cur_topline, wp->w_skipcol, NULL); + } + } + delta += last_topfill; + delta -= wp->w_topfill; + linenr_T ev_botline = wp->w_botline; + if (ev_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; + ev_botline = 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, - line_count); + ui_call_win_viewport(wp->w_grid_alloc.handle, wp->handle, wp->w_topline - 1, ev_botline, + wp->w_cursor.lnum - 1, wp->w_cursor.col, line_count, delta); wp->w_viewport_invalid = false; + wp->w_viewport_last_topline = wp->w_topline; + wp->w_viewport_last_botline = wp->w_botline; + wp->w_viewport_last_topfill = wp->w_topfill; + wp->w_viewport_last_skipcol = wp->w_skipcol; } } -/// If "split_disallowed" is set given an error and return FAIL. +/// If "split_disallowed" is set give an error and return FAIL. /// Otherwise return OK. static int check_split_disallowed(void) { @@ -1058,10 +950,10 @@ int win_split(int size, int flags) return win_split_ins(size, flags, NULL, 0); } -// When "new_wp" is NULL: split the current window in two. -// When "new_wp" is not NULL: insert this window at the far -// top/left/right/bottom. -// return FAIL for failure, OK otherwise +/// When "new_wp" is NULL: split the current window in two. +/// When "new_wp" is not NULL: insert this window at the far +/// top/left/right/bottom. +/// @return FAIL for failure, OK otherwise int win_split_ins(int size, int flags, win_T *new_wp, int dir) { win_T *wp = new_wp; @@ -1435,7 +1327,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; + const 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; @@ -1451,6 +1343,7 @@ 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); + const int old_status_height = oldwin->w_status_height; if (before) { wp->w_hsep_height = is_stl_global ? 1 : 0; } else { @@ -1459,15 +1352,19 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) } if (flags & (WSP_TOP | WSP_BOT)) { int new_fr_height = curfrp->fr_height - new_size; - - if (!((flags & WSP_BOT) && p_ls == 0) && !is_stl_global) { - new_fr_height -= STATUS_HEIGHT; - } else if (is_stl_global) { + if (is_stl_global) { if (flags & WSP_BOT) { frame_add_hsep(curfrp); } else { new_fr_height -= 1; } + } else { + if (!((flags & WSP_BOT) && p_ls == 0)) { + new_fr_height -= STATUS_HEIGHT; + } + if (flags & WSP_BOT) { + frame_add_statusline(curfrp); + } } frame_new_height(curfrp, new_fr_height, flags & WSP_TOP, false); } else { @@ -1476,7 +1373,6 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (before) { // new window above current one wp->w_winrow = oldwin->w_winrow; - if (is_stl_global) { wp->w_status_height = 0; oldwin->w_winrow += wp->w_height + 1; @@ -1490,15 +1386,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) 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; + wp->w_status_height = old_status_height; if (!(flags & WSP_BOT)) { oldwin->w_status_height = STATUS_HEIGHT; } } } - if ((flags & WSP_BOT) && !is_stl_global) { - frame_add_statusline(curfrp); - } frame_fix_height(wp); frame_fix_height(oldwin); } @@ -1525,7 +1418,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')); - } else if (*p_spk != 'c' && !is_aucmd_win(wp)) { + } else if (!is_aucmd_win(wp)) { win_fix_scroll(false); } @@ -1567,7 +1460,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) // The windows will both edit the same buffer. // WSP_NEWLOC may be specified in flags to prevent the location list from // being copied. -static void win_init(win_T *newp, win_T *oldp, int flags) +void win_init(win_T *newp, win_T *oldp, int flags) { newp->w_buffer = oldp->w_buffer; newp->w_s = &(oldp->w_buffer->b_s); @@ -1599,6 +1492,9 @@ static void win_init(win_T *newp, win_T *oldp, int flags) ? NULL : xstrdup(oldp->w_prevdir); if (*p_spk != 'c') { + if (*p_spk == 't') { + newp->w_skipcol = oldp->w_skipcol; + } newp->w_botline = oldp->w_botline; newp->w_prev_height = oldp->w_height; newp->w_prev_winrow = oldp->w_winrow; @@ -1641,24 +1537,6 @@ static void win_init_some(win_T *newp, win_T *oldp) win_copy_options(oldp, newp); } -/// Return true if "win" is floating window in the current tab page. -/// -/// @param win window to check -bool win_valid_floating(const win_T *win) - FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (win == NULL) { - return false; - } - - FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { - if (wp == win) { - return wp->w_floating; - } - } - return false; -} - /// Check if "win" is a pointer to an existing window in the current tabpage. /// /// @param win window to check @@ -1790,7 +1668,7 @@ int make_windows(int count, bool vertical) } // Exchange current and next window -static void win_exchange(long Prenum) +static void win_exchange(int Prenum) { if (curwin->w_floating) { emsg(e_floatexchange); @@ -1802,6 +1680,10 @@ static void win_exchange(long Prenum) beep_flush(); return; } + if (text_or_buf_locked()) { + beep_flush(); + return; + } frame_T *frp; @@ -1900,8 +1782,8 @@ static void win_rotate(bool upwards, int count) } } - win_T *wp1; - win_T *wp2; + win_T *wp1 = NULL; + win_T *wp2 = NULL; while (count--) { if (upwards) { // first window becomes last window @@ -2115,7 +1997,7 @@ void win_equal(win_T *next_curwin, bool current, int dir) win_equal_rec(next_curwin == NULL ? curwin : next_curwin, current, topframe, dir, 0, tabline_height(), Columns, topframe->fr_height); - if (*p_spk != 'c' && !is_aucmd_win(next_curwin)) { + if (!is_aucmd_win(next_curwin)) { win_fix_scroll(true); } } @@ -2258,6 +2140,9 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } if (hnc) { // add next_curwin size next_curwin_size -= (int)p_wiw - (m - n); + if (next_curwin_size < 0) { + next_curwin_size = 0; + } new_size += next_curwin_size; room -= new_size - next_curwin_size; } else { @@ -2407,7 +2292,7 @@ static void win_equal_rec(win_T *next_curwin, bool current, frame_T *topfr, int } } -static void leaving_window(win_T *const win) +void leaving_window(win_T *const win) FUNC_ATTR_NONNULL_ALL { // Only matters for a prompt window. @@ -2585,6 +2470,23 @@ static bool can_close_floating_windows(void) return true; } +/// @return true if, considering the cmdwin, `win` is safe to close. +/// If false and `win` is the cmdwin, it is closed; otherwise, `err` is set. +bool can_close_in_cmdwin(win_T *win, Error *err) + FUNC_ATTR_NONNULL_ALL +{ + if (cmdwin_type != 0) { + if (win == curwin) { + cmdwin_result = Ctrl_C; + return false; + } else if (win == cmdwin_old_curwin) { + api_set_error(err, kErrorTypeException, "%s", e_cmdwin); + return false; + } + } + return true; +} + /// Close the possibly last window in a tab page. /// /// @param win window to close @@ -2599,6 +2501,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev if (!ONE_WINDOW) { return false; } + buf_T *old_curbuf = curbuf; Terminal *term = win->w_buffer ? win->w_buffer->terminal : NULL; @@ -2636,10 +2539,11 @@ 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. +/// Close the buffer of "win" and unload it if "action" is DOBUF_UNLOAD. +/// "action" can also be zero (do nothing). /// "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) +static void win_close_buffer(win_T *win, int action, bool abort_if_last) { // Free independent synblock before the buffer is freed. if (win->w_buffer != NULL) { @@ -2658,7 +2562,7 @@ static void win_close_buffer(win_T *win, bool free_buf, bool abort_if_last) 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); + close_buffer(win, win->w_buffer, action, abort_if_last, true); if (win_valid_any_tab(win)) { win->w_closing = false; } @@ -2756,6 +2660,9 @@ int win_close(win_T *win, bool free_buf, bool force) reset_VIsual_and_resel(); // stop Visual mode other_buffer = true; + if (!win_valid(win)) { + return FAIL; + } win->w_closing = true; apply_autocmds(EVENT_BUFLEAVE, NULL, NULL, false, curbuf); if (!win_valid(win)) { @@ -2788,7 +2695,7 @@ int win_close(win_T *win, bool free_buf, bool force) return OK; } - win_close_buffer(win, free_buf, true); + win_close_buffer(win, free_buf ? DOBUF_UNLOAD : 0, true); if (only_one_window() && win_valid(win) && win->w_buffer == NULL && (last_window(win) || curtab != prev_curtab @@ -2872,7 +2779,7 @@ int win_close(win_T *win, bool free_buf, bool force) if (wp->w_p_pvw || bt_quickfix(wp->w_buffer)) { // If the cursor goes to the preview or the quickfix window, try // finding another window to go to. - for (;;) { + while (true) { if (wp->w_next == NULL) { wp = firstwin; } else { @@ -2907,9 +2814,7 @@ int win_close(win_T *win, bool free_buf, bool force) win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir); } else { (void)win_comp_pos(); - if (*p_spk != 'c') { - win_fix_scroll(false); - } + win_fix_scroll(false); } } @@ -3326,20 +3231,20 @@ static frame_T *win_altframe(win_T *win, tabpage_T *tp) // 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; + frame_T *other_fr = frp->fr_prev; // 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; + 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; + other_fr = frp->fr_next; } // If 'wfh' or 'wfw' is set for the target and not for the alternate @@ -3576,12 +3481,7 @@ static void frame_add_statusline(frame_T *frp) { if (frp->fr_layout == FR_LEAF) { win_T *wp = frp->fr_win; - if (wp->w_status_height == 0) { - if (wp->w_height - STATUS_HEIGHT >= 0) { // don't make it negative - wp->w_height -= STATUS_HEIGHT; - } - wp->w_status_height = STATUS_HEIGHT; - } + wp->w_status_height = STATUS_HEIGHT; } else if (frp->fr_layout == FR_ROW) { // Handle all the frames in the row. FOR_ALL_FRAMES(frp, frp->fr_child) { @@ -3728,12 +3628,7 @@ static void frame_add_hsep(const frame_T *frp) { if (frp->fr_layout == FR_LEAF) { win_T *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; - } + 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) { @@ -3870,7 +3765,7 @@ void close_others(int message, int forceit) if (one_nonfloat() && !lastwin->w_floating) { if (message && !autocmd_busy) { - msg(_(m_onlyone)); + msg(_(m_onlyone), 0); } return; } @@ -3883,6 +3778,12 @@ void close_others(int message, int forceit) continue; } + // autoccommands messed this one up + if (!buf_valid(wp->w_buffer) && win_valid(wp)) { + wp->w_buffer = NULL; + win_close(wp, false, false); + continue; + } // Check if it's allowed to abandon this window int r = can_abandon(wp->w_buffer, forceit); if (!win_valid(wp)) { // autocommands messed wp up @@ -3968,7 +3869,7 @@ static int win_alloc_firstwin(win_T *oldwin) if (oldwin == NULL) { // Very first window, need to create an empty buffer for it and // initialize from scratch. - curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED); + curbuf = buflist_new(NULL, NULL, 1, BLN_LISTED); if (curbuf == NULL) { return FAIL; } @@ -4024,7 +3925,7 @@ static tabpage_T *alloc_tabpage(void) static int last_tp_handle = 0; tabpage_T *tp = xcalloc(1, sizeof(tabpage_T)); tp->handle = ++last_tp_handle; - pmap_put(handle_T)(&tabpage_handles, tp->handle, tp); + pmap_put(int)(&tabpage_handles, tp->handle, tp); // Init t: variables. tp->tp_vars = tv_dict_alloc(); @@ -4037,7 +3938,7 @@ static tabpage_T *alloc_tabpage(void) void free_tabpage(tabpage_T *tp) { - pmap_del(handle_T)(&tabpage_handles, tp->handle); + pmap_del(int)(&tabpage_handles, tp->handle, NULL); diff_clear(tp); for (int idx = 0; idx < SNAP_COUNT; idx++) { clear_snapshot(tp, idx); @@ -4081,7 +3982,7 @@ int win_new_tabpage(int after, char *filename) } newtp->tp_localdir = old_curtab->tp_localdir - ? xstrdup(old_curtab->tp_localdir) : NULL; + ? xstrdup(old_curtab->tp_localdir) : NULL; curtab = newtp; @@ -4144,12 +4045,17 @@ int may_open_tabpage(void) { int n = (cmdmod.cmod_tab == 0) ? postponed_split_tab : cmdmod.cmod_tab; - if (n != 0) { - cmdmod.cmod_tab = 0; // reset it to avoid doing it twice - postponed_split_tab = 0; - return win_new_tabpage(n, NULL); + if (n == 0) { + return FAIL; + } + + cmdmod.cmod_tab = 0; // reset it to avoid doing it twice + postponed_split_tab = 0; + int status = win_new_tabpage(n, NULL); + if (status == OK) { + apply_autocmds(EVENT_TABNEWENTERED, NULL, NULL, false, curbuf); } - return FAIL; + return status; } // Create up to "maxcount" tabpages with empty windows. @@ -4494,11 +4400,12 @@ void goto_tabpage_tp(tabpage_T *tp, bool trigger_enter_autocmds, bool trigger_le /// @return true if the tab page is valid, false otherwise. bool goto_tabpage_lastused(void) { - if (valid_tabpage(lastused_tabpage)) { - goto_tabpage_tp(lastused_tabpage, true, true); - return true; + if (!valid_tabpage(lastused_tabpage)) { + return false; } - return false; + + goto_tabpage_tp(lastused_tabpage, true, true); + return true; } // Enter window "wp" in tab page "tp". @@ -4615,7 +4522,7 @@ tabpage_T *win_find_tabpage(win_T *win) /// @param count nth neighbor window /// /// @return found window -win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count) +win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, int count) { frame_T *foundfr = wp->w_frame; @@ -4628,7 +4535,7 @@ win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count) // First go upwards in the tree of frames until we find an upwards or // downwards neighbor. frame_T *fr = foundfr; - for (;;) { + while (true) { if (fr == tp->tp_topframe) { goto end; } @@ -4644,7 +4551,7 @@ win_T *win_vert_neighbor(tabpage_T *tp, win_T *wp, bool up, long count) } // Now go downwards to find the bottom or top frame in it. - for (;;) { + while (true) { if (nfr->fr_layout == FR_LEAF) { foundfr = nfr; break; @@ -4674,7 +4581,7 @@ end: /// /// @param up true to go to win above /// @param count go count times into direction -static void win_goto_ver(bool up, long count) +static void win_goto_ver(bool up, int count) { win_T *win = win_vert_neighbor(curtab, curwin, up, count); if (win != NULL) { @@ -4691,7 +4598,7 @@ static void win_goto_ver(bool up, long count) /// @param count nth neighbor window /// /// @return found window -win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count) +win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, int count) { frame_T *foundfr = wp->w_frame; @@ -4704,7 +4611,7 @@ win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count) // First go upwards in the tree of frames until we find a left or // right neighbor. frame_T *fr = foundfr; - for (;;) { + while (true) { if (fr == tp->tp_topframe) { goto end; } @@ -4720,7 +4627,7 @@ win_T *win_horz_neighbor(tabpage_T *tp, win_T *wp, bool left, long count) } // Now go downwards to find the leftmost or rightmost frame in it. - for (;;) { + while (true) { if (nfr->fr_layout == FR_LEAF) { foundfr = nfr; break; @@ -4750,7 +4657,7 @@ end: /// /// @param left true to go to left window /// @param count go count times into direction -static void win_goto_hor(bool left, long count) +static void win_goto_hor(bool left, int count) { win_T *win = win_horz_neighbor(curtab, curwin, left, count); if (win != NULL) { @@ -4811,7 +4718,7 @@ static void win_enter_ext(win_T *const wp, const int flags) // Might need to scroll the old window before switching, e.g., when the // cursor was moved. - if (*p_spk == 'c') { + if (*p_spk == 'c' && !curwin_invalid) { update_topline(curwin); } @@ -4833,7 +4740,9 @@ static void win_enter_ext(win_T *const wp, const int flags) if (*p_spk == 'c') { changed_line_abv_curs(); // assume cursor position needs updating } else { - win_fix_cursor(true); + // Make sure the cursor position is valid, either by moving the cursor + // or by scrolling the text. + win_fix_cursor(get_real_state() & (MODE_NORMAL|MODE_CMDLINE|MODE_TERMINAL)); } fix_current_dir(); @@ -4848,8 +4757,6 @@ static void win_enter_ext(win_T *const wp, const int flags) if (other_buffer) { apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf); } - apply_autocmds(EVENT_CURSORMOVED, NULL, NULL, false, curbuf); - curwin->w_last_cursormoved = curwin->w_cursor; } maketitle(); @@ -4997,7 +4904,7 @@ win_T *buf_jump_open_tab(buf_T *buf) /// @param hidden allocate a window structure and link it in the window if // false. -static win_T *win_alloc(win_T *after, bool hidden) +win_T *win_alloc(win_T *after, bool hidden) { static int last_win_id = LOWEST_WIN_ID - 1; @@ -5005,7 +4912,7 @@ static win_T *win_alloc(win_T *after, bool hidden) win_T *new_wp = xcalloc(1, sizeof(win_T)); new_wp->handle = ++last_win_id; - pmap_put(handle_T)(&window_handles, new_wp->handle, new_wp); + pmap_put(int)(&window_handles, new_wp->handle, new_wp); grid_assign_handle(&new_wp->w_grid_alloc); @@ -5034,12 +4941,13 @@ static win_T *win_alloc(win_T *after, bool hidden) new_wp->w_floating = 0; new_wp->w_float_config = FLOAT_CONFIG_INIT; new_wp->w_viewport_invalid = true; + new_wp->w_viewport_last_topline = 1; new_wp->w_ns_hl = -1; // use global option for global-local options - new_wp->w_p_so = -1; - new_wp->w_p_siso = -1; + new_wp->w_allbuf_opt.wo_so = new_wp->w_p_so = -1; + new_wp->w_allbuf_opt.wo_siso = new_wp->w_p_siso = -1; // We won't calculate w_fraction until resizing the window new_wp->w_fraction = 0; @@ -5066,14 +4974,13 @@ void free_wininfo(wininfo_T *wip, buf_T *bp) /// @param tp tab page "win" is in, NULL for current static void win_free(win_T *wp, tabpage_T *tp) { - pmap_del(handle_T)(&window_handles, wp->handle); + pmap_del(int)(&window_handles, wp->handle, NULL); clearFolding(wp); // reduce the reference count to the argument list. alist_unlink(wp->w_alist); // Don't execute autocommands while the window is halfway being deleted. - // gui_mch_destroy_scrollbar() may trigger a FocusGained event. block_autocmds(); clear_winopt(&wp->w_onebuf_opt); @@ -5145,8 +5052,9 @@ static void win_free(win_T *wp, tabpage_T *tp) } } - // free the border title text + // free the border text clear_virttext(&wp->w_float_config.title_chunks); + clear_virttext(&wp->w_float_config.footer_chunks); clear_matches(wp); @@ -5263,8 +5171,8 @@ static void frame_remove(frame_T *frp) void win_new_screensize(void) { - static long old_Rows = 0; - static long old_Columns = 0; + static int old_Rows = 0; + static int old_Columns = 0; if (old_Rows != Rows) { // If 'window' uses the whole screen, keep it using that. @@ -5306,7 +5214,7 @@ void win_new_screen_rows(void) compute_cmdrow(); curtab->tp_ch_used = p_ch; - if (*p_spk != 'c' && !skip_win_fix_scroll) { + if (!skip_win_fix_scroll) { win_fix_scroll(true); } } @@ -5364,7 +5272,7 @@ static dict_T *make_win_info_dict(int width, int height, int topline, int topfil d->dv_refcount = 1; // not actually looping, for breaking out on error - while (1) { + while (true) { typval_T tv = { .v_lock = VAR_UNLOCKED, .v_type = VAR_NUMBER, @@ -5617,8 +5525,9 @@ void win_size_save(garray_T *gap) { ga_init(gap, (int)sizeof(int), 1); ga_grow(gap, win_count() * 2 + 1); - // first entry is value of 'lines' - ((int *)gap->ga_data)[gap->ga_len++] = Rows; + // first entry is the total lines available for windows + ((int *)gap->ga_data)[gap->ga_len++] = + (int)ROWS_AVAIL + global_stl_height() - last_stl_height(false); FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { ((int *)gap->ga_data)[gap->ga_len++] = @@ -5628,13 +5537,14 @@ void win_size_save(garray_T *gap) } // Restore window sizes, but only if the number of windows is still the same -// and 'lines' didn't change. +// and total lines available for windows didn't change. // Does not free the growarray. void win_size_restore(garray_T *gap) FUNC_ATTR_NONNULL_ALL { if (win_count() * 2 + 1 == gap->ga_len - && ((int *)gap->ga_data)[0] == Rows) { + && ((int *)gap->ga_data)[0] == + ROWS_AVAIL + global_stl_height() - last_stl_height(false)) { // The order matters, because frames contain other frames, but it's // difficult to get right. The easy way out is to do it twice. for (int j = 0; j < 2; j++) { @@ -5672,13 +5582,6 @@ int win_comp_pos(void) return row + global_stl_height(); } -void win_reconfig_floats(void) -{ - for (win_T *wp = lastwin; wp && wp->w_floating; wp = wp->w_prev) { - win_config_float(wp, wp->w_float_config); - } -} - // Update the position of the windows in frame "topfrp", using the width and // height of the frames. // "*row" and "*col" are the top-left position of the frame. They are updated @@ -5753,9 +5656,7 @@ void win_setheight_win(int height, win_T *win) msg_row = row; msg_col = 0; - if (*p_spk != 'c') { - win_fix_scroll(true); - } + win_fix_scroll(true); redraw_all_later(UPD_NOT_VALID); redraw_cmdline = true; @@ -6079,7 +5980,7 @@ static void frame_setwidth(frame_T *curfrp, int width) } // Check 'winminheight' for a valid value and reduce it if needed. -void win_setminheight(void) +const char *did_set_winminheight(optset_T *args FUNC_ATTR_UNUSED) { bool first = true; @@ -6096,10 +5997,11 @@ void win_setminheight(void) first = false; } } + return NULL; } // Check 'winminwidth' for a valid value and reduce it if needed. -void win_setminwidth(void) +const char *did_set_winminwidth(optset_T *args FUNC_ATTR_UNUSED) { bool first = true; @@ -6116,6 +6018,7 @@ void win_setminwidth(void) first = false; } } + return NULL; } /// Status line of dragwin is dragged "offset" lines down (negative is up). @@ -6233,9 +6136,7 @@ void win_drag_status_line(win_T *dragwin, int offset) p_ch = MAX(Rows - cmdline_row, p_ch_was_zero ? 0 : 1); curtab->tp_ch_used = p_ch; - if (*p_spk != 'c') { - win_fix_scroll(true); - } + win_fix_scroll(true); redraw_all_later(UPD_SOME_VALID); showmode(); @@ -6340,7 +6241,7 @@ void win_drag_vsep_line(win_T *dragwin, int offset) redraw_all_later(UPD_NOT_VALID); } -#define FRACTION_MULT 16384L +#define FRACTION_MULT 16384 // Set wp->w_fraction for the current w_wrow and w_height. // Has no effect when the window is less than two lines. @@ -6350,45 +6251,56 @@ void set_fraction(win_T *wp) // When cursor is in the first line the percentage is computed as if // it's halfway that line. Thus with two lines it is 25%, with three // lines 17%, etc. Similarly for the last line: 75%, 83%, etc. - wp->w_fraction = (int)(wp->w_wrow * FRACTION_MULT + FRACTION_MULT / 2) / wp->w_height_inner; + wp->w_fraction = (wp->w_wrow * FRACTION_MULT + FRACTION_MULT / 2) / wp->w_height_inner; } } -/// Handle scroll position for 'splitkeep'. Replaces scroll_to_fraction() -/// call from win_set_inner_size(). Instead we iterate over all windows in a -/// tabpage and calculate the new scroll position. -/// TODO(luukvbaal): Ensure this also works with wrapped lines. -/// Requires topline to be able to be set to a bufferline with some -/// offset(row-wise scrolling/smoothscroll). +/// Handle scroll position, depending on 'splitkeep'. Replaces the +/// scroll_to_fraction() call from win_new_height() if 'splitkeep' is "screen" +/// or "topline". Instead we iterate over all windows in a tabpage and +/// calculate the new scroll position. +/// TODO(vim): Ensure this also works with wrapped lines. +/// Requires a not fully visible cursor line to be allowed at the bottom of +/// a window("zb"), probably only when 'smoothscroll' is also set. void win_fix_scroll(int resize) { - linenr_T lnum; + if (*p_spk == 'c') { + return; // 'splitkeep' is "cursor" + } skip_update_topline = true; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { // Skip when window height has not changed or when floating. if (!wp->w_floating && wp->w_height != wp->w_prev_height) { + // Cursor position in this window may now be invalid. It is kept + // potentially invalid until the window is made the current window. + wp->w_do_win_fix_cursor = true; + // If window has moved update botline to keep the same screenlines. if (*p_spk == 's' && wp->w_winrow != wp->w_prev_winrow && wp->w_botline - 1 <= wp->w_buffer->b_ml.ml_line_count) { - lnum = wp->w_cursor.lnum; int diff = (wp->w_winrow - wp->w_prev_winrow) + (wp->w_height - wp->w_prev_height); + linenr_T lnum = wp->w_cursor.lnum; wp->w_cursor.lnum = wp->w_botline - 1; + // Add difference in height and row to botline. if (diff > 0) { cursor_down_inner(wp, diff); } else { cursor_up_inner(wp, -diff); } - // Bring the new cursor position to the bottom of the screen. + + // Scroll to put the new cursor position at the bottom of the + // screen. wp->w_fraction = FRACTION_MULT; scroll_to_fraction(wp, wp->w_prev_height); wp->w_cursor.lnum = lnum; } else if (wp == curwin) { wp->w_valid &= ~VALID_CROW; } - invalidate_botline_win(wp); + + invalidate_botline(wp); validate_botline(wp); } wp->w_prev_height = wp->w_height; @@ -6405,40 +6317,45 @@ void win_fix_scroll(int resize) /// Make sure the cursor position is valid for 'splitkeep'. /// If it is not, put the cursor position in the jumplist and move it. -/// If we are not in normal mode, scroll to make valid instead. -static void win_fix_cursor(int normal) +/// If we are not in normal mode ("normal" is false), make it valid by scrolling +/// instead. +static void win_fix_cursor(bool normal) { win_T *wp = curwin; - long so = get_scrolloff_value(wp); - linenr_T nlnum = 0; - linenr_T lnum = wp->w_cursor.lnum; - if (wp->w_buffer->b_ml.ml_line_count < wp->w_height - || skip_win_fix_cursor) { + if (skip_win_fix_cursor + || !wp->w_do_win_fix_cursor + || wp->w_buffer->b_ml.ml_line_count < wp->w_height_inner) { return; } + wp->w_do_win_fix_cursor = false; // Determine valid cursor range. - so = MIN(wp->w_height_inner / 2, so); + int so = MIN(wp->w_height_inner / 2, get_scrolloff_value(wp)); + linenr_T lnum = wp->w_cursor.lnum; + wp->w_cursor.lnum = wp->w_topline; - linenr_T top = cursor_down_inner(wp, so); + cursor_down_inner(wp, so); + linenr_T top = wp->w_cursor.lnum; + wp->w_cursor.lnum = wp->w_botline - 1; - linenr_T bot = cursor_up_inner(wp, so); + cursor_up_inner(wp, so); + linenr_T bot = wp->w_cursor.lnum; + wp->w_cursor.lnum = lnum; // Check if cursor position is above or below valid cursor range. + linenr_T nlnum = 0; if (lnum > bot && (wp->w_botline - wp->w_buffer->b_ml.ml_line_count) != 1) { nlnum = bot; } else if (lnum < top && wp->w_topline != 1) { - nlnum = (so == wp->w_height / 2) ? bot : top; + nlnum = (so == wp->w_height_inner / 2) ? bot : top; } - if (nlnum) { // Cursor is invalid for current scroll position. - if (normal) { - // Save to jumplist and set cursor to avoid scrolling. + if (nlnum != 0) { // Cursor is invalid for current scroll position. + if (normal) { // Save to jumplist and set cursor to avoid scrolling. setmark('\''); wp->w_cursor.lnum = nlnum; - } else { - // Scroll instead when not in normal mode. + } else { // Scroll instead when not in normal mode. wp->w_fraction = (nlnum == bot) ? FRACTION_MULT : 0; scroll_to_fraction(wp, wp->w_prev_height); validate_botline(curwin); @@ -6485,8 +6402,8 @@ void scroll_to_fraction(win_T *wp, int prev_height) if (lnum < 1) { // can happen when starting up lnum = 1; } - wp->w_wrow = (int)((long)wp->w_fraction * (long)height - 1L) / FRACTION_MULT; - int line_size = plines_win_col(wp, lnum, (long)(wp->w_cursor.col)) - 1; + wp->w_wrow = (wp->w_fraction * height - 1) / FRACTION_MULT; + int line_size = plines_win_col(wp, lnum, wp->w_cursor.col) - 1; int sline = wp->w_wrow - line_size; if (sline >= 0) { @@ -6549,9 +6466,6 @@ void scroll_to_fraction(win_T *wp, int prev_height) } if (wp == curwin) { - if (get_scrolloff_value(wp)) { - update_topline(wp); - } curs_columns(wp, false); // validate w_wrow } if (prev_height > 0) { @@ -6559,7 +6473,7 @@ void scroll_to_fraction(win_T *wp, int prev_height) } redraw_later(wp, UPD_SOME_VALID); - invalidate_botline_win(wp); + invalidate_botline(wp); } void win_set_inner_size(win_T *wp, bool valid_cursor) @@ -6582,20 +6496,20 @@ void win_set_inner_size(win_T *wp, bool valid_cursor) // call win_new_height() recursively. validate_cursor(); } - if (wp->w_height_inner != prev_height) { // -V547 + if (wp->w_height_inner != prev_height) { return; // Recursive call already changed the size, bail out. } if (wp->w_wrow != wp->w_prev_fraction_row) { set_fraction(wp); } } - wp->w_skipcol = 0; wp->w_height_inner = height; win_comp_scroll(wp); // There is no point in adjusting the scroll position when exiting. Some // values might be invalid. if (valid_cursor && !exiting && *p_spk == 'c') { + wp->w_skipcol = 0; scroll_to_fraction(wp, prev_height); } redraw_later(wp, UPD_SOME_VALID); @@ -6606,12 +6520,9 @@ void win_set_inner_size(win_T *wp, bool valid_cursor) wp->w_lines_valid = 0; if (valid_cursor) { changed_line_abv_curs_win(wp); - invalidate_botline_win(wp); - if (wp == curwin) { - skip_update_topline = (*p_spk != 'c'); - update_topline(wp); + invalidate_botline(wp); + if (wp == curwin && *p_spk == 'c') { curs_columns(wp, true); // validate w_wrow - skip_update_topline = false; } } redraw_later(wp, UPD_NOT_VALID); @@ -6628,27 +6539,18 @@ void win_set_inner_size(win_T *wp, bool valid_cursor) wp->w_redr_status = true; } -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. void win_new_width(win_T *wp, int width) { - wp->w_width = width; + // Should we give an error if width < 0? + wp->w_width = width < 0 ? 0 : width; wp->w_pos_changed = true; win_set_inner_size(wp, true); } void win_comp_scroll(win_T *wp) { - const long old_w_p_scr = wp->w_p_scr; + const OptInt old_w_p_scr = wp->w_p_scr; wp->w_p_scr = wp->w_height_inner / 2; if (wp->w_p_scr == 0) { @@ -6744,7 +6646,7 @@ void command_height(void) static void frame_add_height(frame_T *frp, int n) { frame_new_height(frp, frp->fr_height + n, false, false); - for (;;) { + while (true) { frp = frp->fr_parent; if (frp == NULL) { break; @@ -6756,7 +6658,7 @@ static void frame_add_height(frame_T *frp, int n) // Get the file name at the cursor. // If Visual mode is active, use the selected text if it's in one line. // Returns the name in allocated memory, NULL for failure. -char *grab_file_name(long count, linenr_T *file_lnum) +char *grab_file_name(int count, linenr_T *file_lnum) { int options = FNAME_MESS | FNAME_EXP | FNAME_REL | FNAME_UNESC; if (VIsual_active) { @@ -6769,7 +6671,7 @@ char *grab_file_name(long count, linenr_T *file_lnum) if (file_lnum != NULL && ptr[len] == ':' && isdigit((uint8_t)ptr[len + 1])) { char *p = ptr + len + 1; - *file_lnum = (linenr_T)getdigits_long(&p, false, 0); + *file_lnum = getdigits_int32(&p, false, 0); } return find_file_name_in_path(ptr, len, options, count, curbuf->b_ffname); } @@ -6787,7 +6689,7 @@ char *grab_file_name(long count, linenr_T *file_lnum) // FNAME_EXP expand to path // FNAME_HYP check for hypertext link // FNAME_INCL apply "includeexpr" -char *file_name_at_cursor(int options, long count, linenr_T *file_lnum) +char *file_name_at_cursor(int options, int count, linenr_T *file_lnum) { return file_name_in_line(get_cursor_line_ptr(), curwin->w_cursor.col, options, count, curbuf->b_ffname, @@ -6798,7 +6700,7 @@ char *file_name_at_cursor(int options, long count, linenr_T *file_lnum) /// @param file_lnum line number after the file name /// /// @return the name of the file under or after ptr[col]. Otherwise like file_name_at_cursor(). -char *file_name_in_line(char *line, int col, int options, long count, char *rel_fname, +char *file_name_in_line(char *line, int col, int options, int count, char *rel_fname, linenr_T *file_lnum) { // search forward for what could be the start of a file name @@ -6818,12 +6720,12 @@ char *file_name_in_line(char *line, int col, int options, long count, char *rel_ bool is_url = false; // Search backward for first char of the file name. - // Go one char back to ":" before "//" even when ':' is not in 'isfname'. + // Go one char back to ":" before "//", or to the drive letter before ":\" (even if ":" + // is not in 'isfname'). while (ptr > line) { if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) { ptr -= len + 1; - } else if (vim_isfilec((uint8_t)ptr[-1]) - || ((options & FNAME_HYP) && path_is_url(ptr - 1))) { + } else if (vim_isfilec((uint8_t)ptr[-1]) || ((options & FNAME_HYP) && path_is_url(ptr - 1))) { ptr--; } else { break; @@ -6832,14 +6734,13 @@ char *file_name_in_line(char *line, int col, int options, long count, char *rel_ // Search forward for the last char of the file name. // Also allow ":/" when ':' is not in 'isfname'. - len = 0; + len = path_has_drive_letter(ptr) ? 2 : 0; while (vim_isfilec((uint8_t)ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ') || ((options & FNAME_HYP) && path_is_url(ptr + len)) || (is_url && vim_strchr(":?&=", (uint8_t)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 ((ptr[len] >= 'A' && ptr[len] <= 'Z') || (ptr[len] >= 'a' && ptr[len] <= 'z')) { if (in_type && path_is_url(ptr + len + 1)) { is_url = true; } @@ -6897,8 +6798,7 @@ char *file_name_in_line(char *line, int col, int options, long count, char *rel_ 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_nonfloat()))), - global_stl_height() > 0); + last_status_rec(topframe, last_stl_height(morewin) > 0, global_stl_height() > 0); } // Remove status line from window, replacing it with a horizontal separator if needed. @@ -7097,6 +6997,14 @@ int global_stl_height(void) return (p_ls == 3) ? STATUS_HEIGHT : 0; } +/// Return the height of the last window's statusline, or the global statusline if set. +/// +/// @param morewin pretend there are two or more windows if true. +int last_stl_height(bool morewin) +{ + return (p_ls > 1 || (p_ls == 1 && (morewin || !one_nonfloat()))) ? 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) @@ -7193,8 +7101,9 @@ void reset_lnums(void) { FOR_ALL_TAB_WINDOWS(tp, wp) { if (wp->w_buffer == curbuf) { - // Restore the value if the autocommand didn't change it and it was - // set. + // Restore the value if the autocommand didn't change it and it was set. + // Note: This triggers e.g. on BufReadPre, when the buffer is not yet + // loaded, so cannot validate the buffer line if (equalpos(wp->w_save_cursor.w_cursor_corr, wp->w_cursor) && wp->w_save_cursor.w_cursor_save.lnum != 0) { wp->w_cursor = wp->w_save_cursor.w_cursor_save; @@ -7203,6 +7112,9 @@ void reset_lnums(void) && wp->w_save_cursor.w_topline_save != 0) { wp->w_topline = wp->w_save_cursor.w_topline_save; } + if (wp->w_save_cursor.w_topline_save > wp->w_buffer->b_ml.ml_line_count) { + wp->w_valid &= ~VALID_TOPLINE; + } } } } @@ -7250,11 +7162,12 @@ static void clear_snapshot(tabpage_T *tp, int idx) static void clear_snapshot_rec(frame_T *fr) { - if (fr != NULL) { - clear_snapshot_rec(fr->fr_next); - clear_snapshot_rec(fr->fr_child); - xfree(fr); + if (fr == NULL) { + return; } + clear_snapshot_rec(fr->fr_next); + clear_snapshot_rec(fr->fr_child); + xfree(fr); } /// Traverse a snapshot to find the previous curwin. @@ -7404,13 +7317,13 @@ static int int_cmp(const void *a, const void *b) /// Handle setting 'colorcolumn' or 'textwidth' in window "wp". /// /// @return error message, NULL if it's OK. -char *check_colorcolumn(win_T *wp) +const char *check_colorcolumn(win_T *wp) { if (wp->w_buffer == NULL) { return NULL; // buffer was closed } - unsigned int count = 0; + unsigned count = 0; int color_cols[256]; for (char *s = wp->w_p_cc; *s != NUL && count < 255;) { int col; @@ -7463,7 +7376,7 @@ skip: qsort(color_cols, count, sizeof(int), int_cmp); int j = 0; - for (unsigned int i = 0; i < count; i++) { + for (unsigned i = 0; i < count; i++) { // skip duplicates if (j == 0 || wp->w_p_cc_cols[j - 1] != color_cols[i]) { wp->w_p_cc_cols[j++] = color_cols[i]; |