diff options
Diffstat (limited to 'src/nvim/window.c')
-rw-r--r-- | src/nvim/window.c | 1138 |
1 files changed, 788 insertions, 350 deletions
diff --git a/src/nvim/window.c b/src/nvim/window.c index 1298248f1e..3cadfe612a 100644 --- a/src/nvim/window.c +++ b/src/nvim/window.c @@ -1,3 +1,6 @@ +// 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 <inttypes.h> #include <stdbool.h> @@ -28,7 +31,6 @@ #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/misc1.h" -#include "nvim/misc2.h" #include "nvim/file_search.h" #include "nvim/garray.h" #include "nvim/move.h" @@ -41,10 +43,12 @@ #include "nvim/regexp.h" #include "nvim/screen.h" #include "nvim/search.h" +#include "nvim/state.h" #include "nvim/strings.h" #include "nvim/syntax.h" #include "nvim/terminal.h" #include "nvim/undo.h" +#include "nvim/ui.h" #include "nvim/os/os.h" @@ -129,9 +133,10 @@ newwindow: vim_snprintf(cbuf, sizeof(cbuf) - 5, "%" PRId64, (int64_t)Prenum); else cbuf[0] = NUL; - if (nchar == 'v' || nchar == Ctrl_V) - strcat(cbuf, "v"); - strcat(cbuf, "new"); + if (nchar == 'v' || nchar == Ctrl_V) { + xstrlcat(cbuf, "v", sizeof(cbuf)); + } + xstrlcat(cbuf, "new", sizeof(cbuf)); do_cmdline_cmd(cbuf); break; @@ -160,13 +165,18 @@ newwindow: /* cursor to preview window */ case 'P': - for (wp = firstwin; wp != NULL; wp = wp->w_next) - if (wp->w_p_pvw) + wp = NULL; + FOR_ALL_WINDOWS_IN_TAB(wp2, curtab) { + if (wp2->w_p_pvw) { + wp = wp2; break; - if (wp == NULL) + } + } + if (wp == NULL) { EMSG(_("E441: There is no preview window")); - else + } else { win_goto(wp); + } break; /* close all but current window */ @@ -183,7 +193,7 @@ newwindow: /* cursor to previous window with wrap around */ case 'W': CHECK_CMDWIN - if (firstwin == lastwin && Prenum != 1) /* just one window */ + if (ONE_WINDOW && Prenum != 1) /* just one window */ beep_flush(); else { if (Prenum) { /* go to specified window */ @@ -278,10 +288,11 @@ newwindow: /* cursor to last accessed (previous) window */ case 'p': case Ctrl_P: - if (prevwin == NULL) + if (!win_valid(prevwin)) { beep_flush(); - else + } else { win_goto(prevwin); + } break; /* exchange current and next window */ @@ -356,7 +367,7 @@ newwindow: g_do_tagpreview = Prenum; else g_do_tagpreview = p_pvh; - /*FALLTHROUGH*/ + FALLTHROUGH; case ']': case Ctrl_RSB: CHECK_CMDWIN @@ -384,12 +395,16 @@ wingotofile: ptr = grab_file_name(Prenum1, &lnum); if (ptr != NULL) { + tabpage_T *oldtab = curtab; + win_T *oldwin = curwin; setpcmark(); if (win_split(0, 0) == OK) { RESET_BINDING(curwin); - (void)do_ecmd(0, ptr, NULL, NULL, ECMD_LASTL, - ECMD_HIDE, NULL); - if (nchar == 'F' && lnum >= 0) { + 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); + goto_tabpage_win(oldtab, oldwin); + } else if (nchar == 'F' && lnum >= 0) { curwin->w_cursor.lnum = lnum; check_cursor_lnum(); beginline(BL_SOL | BL_FIX); @@ -405,8 +420,8 @@ wingotofile: case 'i': /* Go to any match */ case Ctrl_I: type = FIND_ANY; - /* FALLTHROUGH */ - case 'd': /* Go to definition, using 'define' */ + FALLTHROUGH; + case 'd': // Go to definition, using 'define' case Ctrl_D: CHECK_CMDWIN if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0) @@ -436,13 +451,12 @@ wingotofile: case 'g': case Ctrl_G: CHECK_CMDWIN - ++ no_mapping; - ++allow_keys; /* no mapping for xchar, but allow key codes */ - if (xchar == NUL) + no_mapping++; + if (xchar == NUL) { xchar = plain_vgetc(); - LANGMAP_ADJUST(xchar, TRUE); - --no_mapping; - --allow_keys; + } + LANGMAP_ADJUST(xchar, true); + no_mapping--; (void)add_to_showcmd(xchar); switch (xchar) { case '}': @@ -451,7 +465,7 @@ wingotofile: g_do_tagpreview = Prenum; else g_do_tagpreview = p_pvh; - /*FALLTHROUGH*/ + FALLTHROUGH; case ']': case Ctrl_RSB: // Keep visual mode, can select words to use as a tag. @@ -482,7 +496,7 @@ wingotofile: } static void cmd_with_count(char *cmd, char_u *bufp, size_t bufsize, - long Prenum) + int64_t Prenum) { size_t len = xstrlcpy((char *)bufp, cmd, bufsize); @@ -551,6 +565,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) int before; int minheight; int wmh1; + bool did_set_fraction = false; if (flags & WSP_TOP) oldwin = firstwin; @@ -560,7 +575,7 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) oldwin = curwin; /* add a status line when p_ls == 1 and splitting the first window */ - if (lastwin == firstwin && p_ls == 1 && oldwin->w_status_height == 0) { + if (ONE_WINDOW && p_ls == 1 && oldwin->w_status_height == 0) { if (oldwin->w_height <= p_wmh && new_wp == NULL) { EMSG(_(e_noroom)); return FAIL; @@ -627,11 +642,12 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) if (oldwin->w_width - new_size - 1 < p_wmw) do_equal = TRUE; - /* We don't like to take lines for the new window from a - * 'winfixwidth' window. Take them from a window to the left or right - * instead, if possible. */ - if (oldwin->w_p_wfw) - win_setwidth_win(oldwin->w_width + new_size, oldwin); + // We don't like to take lines for the new window from a + // 'winfixwidth' window. Take them from a window to the left or right + // instead, if possible. Add one for the separator. + if (oldwin->w_p_wfw) { + win_setwidth_win(oldwin->w_width + new_size + 1, oldwin); + } /* Only make all windows the same width if one of them (except oldwin) * is wider than one of the split windows. */ @@ -714,6 +730,11 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) * 'winfixheight' window. Take them from a window above or below * instead, if possible. */ if (oldwin->w_p_wfh) { + // Set w_fraction now so that the cursor keeps the same relative + // vertical position using the old height. + set_fraction(oldwin); + did_set_fraction = true; + win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT, oldwin); oldwin_height = oldwin->w_height; @@ -828,8 +849,9 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) /* Set w_fraction now so that the cursor keeps the same relative * vertical position. */ - if (oldwin->w_height > 0) + if (!did_set_fraction) { set_fraction(oldwin); + } wp->w_fraction = oldwin->w_fraction; if (flags & WSP_VERT) { @@ -893,31 +915,31 @@ 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 (flags & (WSP_TOP | WSP_BOT)) - frame_new_height(curfrp, curfrp->fr_height - - (new_size + STATUS_HEIGHT), flags & WSP_TOP, FALSE); - else + if (flags & (WSP_TOP | WSP_BOT)) { + int new_fr_height = curfrp->fr_height - new_size; + + if (!((flags & WSP_BOT) && p_ls == 0)) { + new_fr_height -= STATUS_HEIGHT; + } + 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 */ + } + 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; } 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; - // Don't set the status_height for oldwin yet, this might break - // frame_fix_height(oldwin), therefore will be set below. + if (!(flags & WSP_BOT)) { + oldwin->w_status_height = STATUS_HEIGHT; + } } if (flags & WSP_BOT) frame_add_statusline(curfrp); frame_fix_height(wp); frame_fix_height(oldwin); - - if (!before) { - // New window above current one, set the status_height after - // frame_fix_height(oldwin) - oldwin->w_status_height = STATUS_HEIGHT; - } } if (flags & (WSP_TOP | WSP_BOT)) @@ -968,11 +990,15 @@ int win_split_ins(int size, int flags, win_T *new_wp, int dir) /* * make the new window the current window */ - win_enter(wp, false); - if (flags & WSP_VERT) + win_enter_ext(wp, false, false, true, true, true); + if (flags & WSP_VERT) { p_wiw = i; - else + } else { p_wh = i; + } + + // Send the window positions to the UI + oldwin->w_pos_changed = true; return OK; } @@ -1028,7 +1054,7 @@ static void win_init(win_T *newp, win_T *oldp, int flags) win_init_some(newp, oldp); - check_colorcolumn(newp); + didset_window_options(newp); } /* @@ -1064,6 +1090,23 @@ bool win_valid(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return false; } +/// Check if "win" is a pointer to an existing window in any tabpage. +/// +/// @param win window to check +bool win_valid_any_tab(win_T *win) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (win == NULL) { + return false; + } + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp == win) { + return true; + } + } + return false; +} + /* * Return the number of windows. */ @@ -1150,7 +1193,7 @@ static void win_exchange(long Prenum) win_T *wp2; int temp; - if (lastwin == firstwin) { /* just one window */ + if (ONE_WINDOW) { /* just one window */ beep_flush(); return; } @@ -1225,7 +1268,8 @@ static void win_exchange(long Prenum) (void)win_comp_pos(); /* recompute window positions */ win_enter(wp, true); - redraw_later(CLEAR); + redraw_later(NOT_VALID); + redraw_win_later(wp, NOT_VALID); } /* @@ -1239,7 +1283,7 @@ static void win_rotate(int upwards, int count) frame_T *frp; int n; - if (firstwin == lastwin) { /* nothing to do */ + if (ONE_WINDOW) { /* nothing to do */ beep_flush(); return; } @@ -1300,7 +1344,10 @@ static void win_rotate(int upwards, int count) (void)win_comp_pos(); } - redraw_later(CLEAR); + wp1->w_pos_changed = true; + wp2->w_pos_changed = true; + + redraw_all_later(NOT_VALID); } /* @@ -1311,7 +1358,7 @@ static void win_totop(int size, int flags) int dir; int height = curwin->w_height; - if (lastwin == firstwin) { + if (ONE_WINDOW) { beep_flush(); return; } @@ -1382,6 +1429,9 @@ void win_move_after(win_T *win1, win_T *win2) redraw_later(NOT_VALID); } win_enter(win1, false); + + win1->w_pos_changed = true; + win2->w_pos_changed = true; } /* @@ -1437,10 +1487,10 @@ static void win_equal_rec( || topfr->fr_width != width || topfr->fr_win->w_wincol != col ) { topfr->fr_win->w_winrow = row; - frame_new_height(topfr, height, FALSE, FALSE); + frame_new_height(topfr, height, false, false); topfr->fr_win->w_wincol = col; - frame_new_width(topfr, width, FALSE, FALSE); - redraw_all_later(CLEAR); + frame_new_width(topfr, width, false, false); + redraw_all_later(NOT_VALID); } } else if (topfr->fr_layout == FR_ROW) { topfr->fr_width = width; @@ -1685,25 +1735,23 @@ static void win_equal_rec( } } -/* - * close all windows for buffer 'buf' - */ -void -close_windows ( - buf_T *buf, - int keep_curwin /* don't close "curwin" */ -) +/// Closes all windows for buffer `buf`. +/// +/// @param keep_curwin don't close `curwin` +void close_windows(buf_T *buf, int keep_curwin) { tabpage_T *tp, *nexttp; int h = tabline_height(); ++RedrawingDisabled; - for (win_T *wp = firstwin; wp != NULL && lastwin != firstwin; ) { + for (win_T *wp = firstwin; wp != NULL && !ONE_WINDOW; ) { if (wp->w_buffer == buf && (!keep_curwin || wp != curwin) - && !(wp->w_closing || wp->w_buffer->b_closing) - ) { - win_close(wp, FALSE); + && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { + if (win_close(wp, 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; @@ -1717,9 +1765,8 @@ close_windows ( if (tp != curtab) { FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (wp->w_buffer == buf - && !(wp->w_closing || wp->w_buffer->b_closing) - ) { - win_close_othertab(wp, FALSE, tp); + && !(wp->w_closing || wp->w_buffer->b_locked > 0)) { + win_close_othertab(wp, false, tp); /* Start all over, the tab page may be closed and * autocommands may change the window layout. */ @@ -1732,9 +1779,10 @@ close_windows ( --RedrawingDisabled; - redraw_tabline = TRUE; - if (h != tabline_height()) + redraw_tabline = true; + if (h != tabline_height()) { shell_new_rows(); + } } /// Check that current window is the last one. @@ -1775,7 +1823,7 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, tabpage_T *prev_curtab) FUNC_ATTR_NONNULL_ARG(1) { - if (firstwin != lastwin) { + if (!ONE_WINDOW) { return false; } buf_T *old_curbuf = curbuf; @@ -1811,15 +1859,8 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, shell_new_rows(); } - if (term) { - // When a window containing a terminal buffer is closed, recalculate its - // size - terminal_resize(term, 0, 0); - } - // Since goto_tabpage_tp above did not trigger *Enter autocommands, do // that now. - apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, false, curbuf); apply_autocmds(EVENT_WINENTER, NULL, NULL, false, curbuf); apply_autocmds(EVENT_TABENTER, NULL, NULL, false, curbuf); if (old_curbuf != curbuf) { @@ -1828,29 +1869,30 @@ static bool close_last_window_tabpage(win_T *win, bool free_buf, return true; } -/* - * 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, int free_buf) +// 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) { win_T *wp; int other_buffer = FALSE; int close_curwin = FALSE; int dir; - int help_window = FALSE; + bool help_window = false; tabpage_T *prev_curtab = curtab; + frame_T *win_frame = win->w_frame->fr_parent; if (last_window()) { EMSG(_("E444: Cannot close last window")); return FAIL; } - if (win->w_closing || (win->w_buffer != NULL && win->w_buffer->b_closing)) - return FAIL; /* window is already being closed */ + if (win->w_closing + || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { + return FAIL; // window is already being closed + } if (win == aucmd_win) { EMSG(_("E813: Cannot close autocmd window")); return FAIL; @@ -1868,10 +1910,11 @@ int win_close(win_T *win, int free_buf) /* When closing the help window, try restoring a snapshot after closing * the window. Otherwise clear the snapshot, it's now invalid. */ - if (win->w_buffer != NULL && win->w_buffer->b_help) - help_window = TRUE; - else + if (bt_help(win->w_buffer)) { + help_window = true; + } else { clear_snapshot(curtab, SNAP_HELP_IDX); + } if (win == curwin) { /* @@ -1915,15 +1958,17 @@ int win_close(win_T *win, int free_buf) * 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(win)) { + if (win_valid_any_tab(win)) { win->w_closing = false; } // Make sure curbuf is valid. It can become invalid if 'bufhidden' is // "wipe". - if (!buf_valid(curbuf)) { + if (!bufref_valid(&bufref)) { curbuf = firstbuf; } } @@ -1931,17 +1976,26 @@ int win_close(win_T *win, int free_buf) if (only_one_window() && win_valid(win) && win->w_buffer == NULL && (last_window() || curtab != prev_curtab || close_last_window_tabpage(win, free_buf, prev_curtab))) { - /* Autocommands have close all windows, quit now. Restore - * curwin->w_buffer, otherwise writing ShaDa file may fail. */ - if (curwin->w_buffer == NULL) + // Autocommands have closed all windows, quit now. Restore + // curwin->w_buffer, otherwise writing ShaDa file may fail. + if (curwin->w_buffer == NULL) { curwin->w_buffer = curbuf; + } getout(0); } - /* Autocommands may have closed the window already, or closed the only - * other window or moved to another tab page. */ - else if (!win_valid(win) || last_window() || curtab != prev_curtab - || close_last_window_tabpage(win, free_buf, prev_curtab)) + // Autocommands may have moved to another tab page. + if (curtab != prev_curtab && win_valid_any_tab(win) + && win->w_buffer == NULL) { + // Need to close the window anyway, since the buffer is NULL. + win_close_othertab(win, false, prev_curtab); + return FAIL; + } + // Autocommands may have closed the window already, or closed the only + // other window or moved to another tab page. + if (!win_valid(win) || last_window() + || close_last_window_tabpage(win, free_buf, prev_curtab)) { return FAIL; + } // let terminal buffers know that this window dimensions may be ignored win->w_closing = true; @@ -1949,6 +2003,14 @@ int win_close(win_T *win, int free_buf) * the screen space. */ 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; + } + } + /* Make sure curwin isn't invalid. It can cause severe trouble when * printing an error message. For win_equal() curbuf needs to be valid * too. */ @@ -1974,20 +2036,25 @@ int win_close(win_T *win, int free_buf) } curbuf = curwin->w_buffer; close_curwin = TRUE; + + // The cursor position may be invalid if the buffer changed after last + // using the window. + check_cursor(); } - if (p_ea - && (*p_ead == 'b' || *p_ead == dir) - ) { - win_equal(curwin, true, dir); + if (p_ea && (*p_ead == 'b' || *p_ead == dir)) { + // If the frame of the closed window contains the new current window, + // only resize that frame. Otherwise resize all windows. + win_equal(curwin, curwin->w_frame->fr_parent == win_frame, dir); } else { win_comp_pos(); } if (close_curwin) { - win_enter_ext(wp, false, TRUE, TRUE, TRUE); - if (other_buffer) - /* careful: after this wp and win may be invalid! */ - apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); + win_enter_ext(wp, false, true, false, true, true); + if (other_buffer) { + // careful: after this wp and win may be invalid! + apply_autocmds(EVENT_BUFENTER, NULL, NULL, false, curbuf); + } } /* @@ -2001,6 +2068,7 @@ int win_close(win_T *win, int free_buf) if (help_window) restore_snapshot(SNAP_HELP_IDX, close_curwin); + curwin->w_pos_changed = true; redraw_all_later(NOT_VALID); return OK; } @@ -2018,12 +2086,17 @@ void win_close_othertab(win_T *win, int free_buf, tabpage_T *tp) tabpage_T *ptp = NULL; int free_tp = FALSE; - assert(win->w_buffer); // to avoid np dereference warning in next line - if (win->w_closing || win->w_buffer->b_closing) - return; /* window is already being closed */ + // Get here with win->w_buffer == NULL when win_close() detects the tab page + // changed. + if (win->w_closing + || (win->w_buffer != NULL && win->w_buffer->b_locked > 0)) { + return; // window is already being closed + } - /* Close the link to the buffer. */ - close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, FALSE); + if (win->w_buffer != NULL) { + // Close the link to the buffer. + close_buffer(win, win->w_buffer, free_buf ? DOBUF_UNLOAD : 0, false); + } /* Careful: Autocommands may have closed the tab page or made it the * current tab page. */ @@ -2048,19 +2121,29 @@ 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) { - if (tp == first_tabpage) + char_u prev_idx[NUMBUFLEN]; + if (has_event(EVENT_TABCLOSED)) { + vim_snprintf((char *)prev_idx, NUMBUFLEN, "%i", tabpage_index(tp)); + } + + if (tp == first_tabpage) { first_tabpage = tp->tp_next; - else { + } else { for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tp; - ptp = ptp->tp_next) - ; + ptp = ptp->tp_next) { + // loop + } if (ptp == NULL) { - EMSG2(_(e_intern2), "win_close_othertab()"); + internal_error("win_close_othertab()"); return; } ptp->tp_next = tp->tp_next; } - free_tp = TRUE; + free_tp = true; + + if (has_event(EVENT_TABCLOSED)) { + apply_autocmds(EVENT_TABCLOSED, prev_idx, prev_idx, false, win->w_buffer); + } } /* Free the memory used for the window. */ @@ -2139,7 +2222,7 @@ winframe_remove ( /* * If there is only one window there is nothing to remove. */ - if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin) + if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) return NULL; /* @@ -2231,6 +2314,9 @@ winframe_remove ( if (frp2->fr_win != NULL) frp2->fr_win->w_frame = frp2->fr_parent; frp = frp2->fr_parent; + if (topframe->fr_child == frp2) { + topframe->fr_child = frp; + } xfree(frp2); frp2 = frp->fr_parent; @@ -2252,6 +2338,9 @@ winframe_remove ( break; } } + if (topframe->fr_child == frp) { + topframe->fr_child = frp2; + } xfree(frp); } } @@ -2259,14 +2348,14 @@ winframe_remove ( return wp; } -/* - * Find out which frame is going to get the freed up space when "win" is - * closed. - * if 'splitbelow'/'splitleft' the space goes to the window above/left. - * if 'nosplitbelow'/'nosplitleft' the space goes to the window below/right. - * This makes opening a window and closing it immediately keep the same window - * layout. - */ +// Return a pointer to the frame that will receive the empty screen space that +// is left over after "win" is closed. +// +// If 'splitbelow' or 'splitright' is set, the space goes above or to the left +// by default. Otherwise, the free space goes below or to the right. The +// result is that opening a window and then immediately closing it will +// preserve the initial window layout. The 'wfh' and 'wfw' settings are +// respected when possible. static frame_T * win_altframe ( win_T *win, @@ -2274,20 +2363,40 @@ win_altframe ( ) { frame_T *frp; - int b; - if (tp == NULL ? firstwin == lastwin : tp->tp_firstwin == tp->tp_lastwin) - /* Last window in this tab page, will go to next tab page. */ + if (tp == NULL ? ONE_WINDOW : tp->tp_firstwin == tp->tp_lastwin) { return alt_tabpage()->tp_curwin->w_frame; + } frp = win->w_frame; - if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW) - b = p_spr; - else - b = p_sb; - if ((!b && frp->fr_next != NULL) || frp->fr_prev == NULL) + + if (frp->fr_prev == NULL) { return frp->fr_next; - return frp->fr_prev; + } + if (frp->fr_next == NULL) { + return frp->fr_prev; + } + + frame_T *target_fr = frp->fr_next; + frame_T *other_fr = frp->fr_prev; + if (p_spr || p_sb) { + target_fr = frp->fr_prev; + other_fr = frp->fr_next; + } + + // If 'wfh' or 'wfw' is set for the target and not for the alternate + // window, reverse the selection. + if (frp->fr_parent != NULL && frp->fr_parent->fr_layout == FR_ROW) { + if (frame_fixed_width(target_fr) && !frame_fixed_width(other_fr)) { + target_fr = other_fr; + } + } else { + if (frame_fixed_height(target_fr) && !frame_fixed_height(other_fr)) { + target_fr = other_fr; + } + } + + return target_fr; } /* @@ -2784,8 +2893,8 @@ close_others ( } if (!r) { if (message && (p_confirm || cmdmod.confirm) && p_write) { - dialog_changed(wp->w_buffer, FALSE); - if (!win_valid(wp)) { /* autocommands messed wp up */ + dialog_changed(wp->w_buffer, false); + if (!win_valid(wp)) { // autocommands messed wp up nextwp = firstwin; continue; } @@ -2793,10 +2902,10 @@ close_others ( if (bufIsChanged(wp->w_buffer)) continue; } - win_close(wp, !P_HID(wp->w_buffer) && !bufIsChanged(wp->w_buffer)); + win_close(wp, !buf_hide(wp->w_buffer) && !bufIsChanged(wp->w_buffer)); } - if (message && lastwin != firstwin) + if (message && !ONE_WINDOW) EMSG(_("E445: Other window contains changes")); } @@ -2874,8 +2983,9 @@ static int win_alloc_firstwin(win_T *oldwin) /* Very first window, need to create an empty buffer for it and * initialize from scratch. */ curbuf = buflist_new(NULL, NULL, 1L, BLN_LISTED); - if (curbuf == NULL) + if (curbuf == NULL) { return FAIL; + } curwin->w_buffer = curbuf; curwin->w_s = &(curbuf->b_s); curbuf->b_nwindows = 1; /* there is one window */ @@ -2893,7 +3003,6 @@ static int win_alloc_firstwin(win_T *oldwin) topframe = curwin->w_frame; topframe->fr_width = Columns; topframe->fr_height = Rows - p_ch; - topframe->fr_win = curwin; return OK; } @@ -2926,11 +3035,13 @@ void win_init_size(void) */ 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; handle_register_tabpage(tp); - /* init t: variables */ - tp->tp_vars = dict_alloc(); + // Init t: variables. + tp->tp_vars = tv_dict_alloc(); init_var_dict(tp->tp_vars, &tp->tp_winvar, VAR_SCOPE); tp->tp_diff_invalid = TRUE; tp->tp_ch_used = p_ch; @@ -2950,8 +3061,7 @@ void free_tabpage(tabpage_T *tp) hash_init(&tp->tp_vars->dv_hashtab); unref_var_dict(tp->tp_vars); - - xfree(tp->localdir); // Free tab-local working directory + xfree(tp->tp_localdir); xfree(tp); } @@ -2976,6 +3086,9 @@ int win_new_tabpage(int after, char_u *filename) xfree(newtp); return FAIL; } + + newtp->tp_localdir = tp->tp_localdir ? vim_strsave(tp->tp_localdir) : NULL; + curtab = newtp; /* Create a new empty window. */ @@ -3003,10 +3116,15 @@ int win_new_tabpage(int after, char_u *filename) newtp->tp_topframe = topframe; last_status(FALSE); - redraw_all_later(CLEAR); + redraw_all_later(NOT_VALID); - apply_autocmds(EVENT_TABNEW, filename, filename, false, curbuf); + if (ui_is_external(kUIMultigrid)) { + tabpage_check_windows(tp); + } + + 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_TABENTER, NULL, NULL, false, curbuf); return OK; @@ -3078,6 +3196,45 @@ bool valid_tabpage(tabpage_T *tpc) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT return false; } +/// Returns true when `tpc` is valid and at least one window is valid. +int valid_tabpage_win(tabpage_T *tpc) +{ + FOR_ALL_TABS(tp) { + if (tp == tpc) { + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + if (win_valid_any_tab(wp)) { + return true; + } + } + return false; + } + } + // shouldn't happen + return false; +} + +/// Close tabpage `tab`, assuming it has no windows in it. +/// There must be another tabpage or this will crash. +void close_tabpage(tabpage_T *tab) +{ + tabpage_T *ptp; + + if (tab == first_tabpage) { + first_tabpage = tab->tp_next; + ptp = first_tabpage; + } else { + for (ptp = first_tabpage; ptp != NULL && ptp->tp_next != tab; + ptp = ptp->tp_next) { + // do nothing + } + assert(ptp != NULL); + ptp->tp_next = tab->tp_next; + } + + goto_tabpage_tp(ptp, false, false); + free_tabpage(tab); +} + /* * Find tab page "n" (first one is 1). Returns NULL when not found. */ @@ -3156,22 +3313,26 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au 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; topframe = tp->tp_topframe; + if (old_curtab != curtab && ui_is_external(kUIMultigrid)) { + tabpage_check_windows(old_curtab); + } + /* We would like doing the TabEnter event first, but we don't have a * valid current window yet, which may break some commands. * This triggers autocommands, thus may make "tp" invalid. */ - win_enter_ext(tp->tp_curwin, false, TRUE, - trigger_enter_autocmds, trigger_leave_autocmds); + win_enter_ext(tp->tp_curwin, false, true, false, + trigger_enter_autocmds, trigger_leave_autocmds); prevwin = next_prevwin; - last_status(FALSE); /* status line may appear or disappear */ - (void)win_comp_pos(); /* recompute w_winrow for all windows */ - must_redraw = CLEAR; /* need to redraw everything */ - diff_need_scrollbind = TRUE; + last_status(false); // status line may appear or disappear + (void)win_comp_pos(); // recompute w_winrow for all windows + diff_need_scrollbind = true; /* The tabpage line may have appeared or disappeared, may need to resize * the frames for that. When the Vim window was resized need to update @@ -3193,7 +3354,21 @@ static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf, int trigger_enter_au apply_autocmds(EVENT_BUFENTER, NULL, NULL, FALSE, curbuf); } - redraw_all_later(CLEAR); + redraw_all_later(NOT_VALID); +} + +/// called when changing current tabpage from old_curtab to curtab +static void tabpage_check_windows(tabpage_T *old_curtab) +{ + win_T *next_wp; + for (win_T *wp = old_curtab->tp_firstwin; wp; wp = next_wp) { + next_wp = wp->w_next; + wp->w_pos_changed = true; + } + + for (win_T *wp = firstwin; wp; wp = wp->w_next) { + wp->w_pos_changed = true; + } } /* @@ -3207,11 +3382,8 @@ void goto_tabpage(int n) int i; if (text_locked()) { - /* Not allowed when editing the command line. */ - if (cmdwin_type != 0) - EMSG(_(e_cmdwin)); - else - EMSG(_(e_secure)); + // Not allowed when editing the command line. + text_locked_msg(); return; } @@ -3296,8 +3468,11 @@ void tabpage_move(int nr) tabpage_T *tp; tabpage_T *tp_dst; - if (first_tabpage->tp_next == NULL) + assert(curtab != NULL); + + if (first_tabpage->tp_next == NULL) { return; + } for (tp = first_tabpage; tp->tp_next != NULL && n < nr; tp = tp->tp_next) { ++n; @@ -3310,15 +3485,20 @@ void tabpage_move(int nr) tp_dst = tp; - /* Remove the current tab page from the list of tab pages. */ - if (curtab == first_tabpage) + // Remove the current tab page from the list of tab pages. + if (curtab == first_tabpage) { first_tabpage = curtab->tp_next; - else { - for (tp = first_tabpage; tp != NULL; tp = tp->tp_next) - if (tp->tp_next == curtab) + } else { + tp = NULL; + FOR_ALL_TABS(tp2) { + if (tp2->tp_next == curtab) { + tp = tp2; break; - if (tp == NULL) /* "cannot happen" */ + } + } + if (tp == NULL) { // "cannot happen" return; + } tp->tp_next = curtab->tp_next; } @@ -3362,11 +3542,13 @@ void win_goto(win_T *wp) win_enter(wp, true); - /* Conceal cursor line in previous window, unconceal in current window. */ - if (win_valid(owp) && owp->w_p_cole > 0 && !msg_scrolled) - update_single_line(owp, owp->w_cursor.lnum); - if (curwin->w_p_cole > 0 && !msg_scrolled) - need_cursor_line_redraw = TRUE; + // Conceal cursor line in previous window, unconceal in current window. + if (win_valid(owp) && owp->w_p_cole > 0 && !msg_scrolled) { + redrawWinline(owp, owp->w_cursor.lnum); + } + if (curwin->w_p_cole > 0 && !msg_scrolled) { + redrawWinline(curwin, curwin->w_cursor.lnum); + } } @@ -3506,7 +3688,7 @@ end: */ void win_enter(win_T *wp, bool undo_sync) { - win_enter_ext(wp, undo_sync, FALSE, TRUE, TRUE); + win_enter_ext(wp, undo_sync, false, false, true, true); } /* @@ -3514,7 +3696,9 @@ void win_enter(win_T *wp, bool undo_sync) * Can be called with "curwin_invalid" TRUE, which means that curwin has just * been closed and isn't valid. */ -static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int trigger_enter_autocmds, int trigger_leave_autocmds) +static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, + int trigger_new_autocmds, int trigger_enter_autocmds, + int trigger_leave_autocmds) { int other_buffer = FALSE; @@ -3563,33 +3747,46 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri curwin->w_cursor.coladd = 0; changed_line_abv_curs(); /* assume cursor position needs updating */ - // The new directory is either the local directory of the window, of the tab - // or NULL. - char_u *new_dir = curwin->w_localdir ? curwin->w_localdir : curtab->localdir; + // 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 cwd[MAXPATHL]; + if (os_dirname((char_u *)cwd, MAXPATHL) != OK) { + cwd[0] = NUL; + } if (new_dir) { // Window/tab has a local directory: Save current directory as global - // directory (unless that was done already) and change to the local - // directory. + // (unless that was done already) and change to the local directory. if (globaldir == NULL) { - char_u cwd[MAXPATHL]; - - if (os_dirname(cwd, MAXPATHL) == OK) { - globaldir = vim_strsave(cwd); + if (cwd[0] != NUL) { + globaldir = (char_u *)xstrdup(cwd); } } - if (os_chdir((char *)new_dir) == 0) { + if (os_chdir(new_dir) == 0) { + if (!p_acd && !strequal(new_dir, cwd)) { + do_autocmd_dirchanged(new_dir, curwin->w_localdir + ? kCdScopeWindow : kCdScopeTab); + } 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. */ - ignored = os_chdir((char *)globaldir); + // 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 && !strequal((char *)globaldir, cwd)) { + do_autocmd_dirchanged((char *)globaldir, kCdScopeGlobal); + } + } xfree(globaldir); globaldir = NULL; shorten_fnames(TRUE); } + if (trigger_new_autocmds) { + apply_autocmds(EVENT_WINNEW, NULL, NULL, false, curbuf); + } if (trigger_enter_autocmds) { apply_autocmds(EVENT_WINENTER, NULL, NULL, FALSE, curbuf); if (other_buffer) @@ -3602,6 +3799,12 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri if (restart_edit) redraw_later(VALID); /* causes status line redraw */ + if (HL_ATTR(HLF_INACTIVE) + || (prevwin && prevwin->w_hl_ids[HLF_INACTIVE]) + || curwin->w_hl_ids[HLF_INACTIVE]) { + redraw_all_later(NOT_VALID); + } + /* set window height to desired minimal value */ if (curwin->w_height < p_wh && !curwin->w_p_wfh) win_setheight((int)p_wh); @@ -3616,10 +3819,6 @@ static void win_enter_ext(win_T *wp, bool undo_sync, int curwin_invalid, int tri /* Change directories when the 'acd' option is set. */ do_autochdir(); - - if (curbuf->terminal) { - terminal_resize(curbuf->terminal, curwin->w_width, curwin->w_height); - } } @@ -3686,15 +3885,19 @@ win_T *buf_jump_open_tab(buf_T *buf) */ static win_T *win_alloc(win_T *after, int hidden) { - /* - * allocate window structure and linesizes arrays - */ + static int last_win_id = LOWEST_WIN_ID - 1; + + // allocate window structure and linesizes arrays win_T *new_wp = xcalloc(1, sizeof(win_T)); - handle_register_window(new_wp); win_alloc_lines(new_wp); - /* init w: variables */ - new_wp->w_vars = dict_alloc(); + new_wp->handle = ++last_win_id; + handle_register_window(new_wp); + + grid_assign_handle(&new_wp->w_grid); + + // Init w: variables. + new_wp->w_vars = tv_dict_alloc(); init_var_dict(new_wp->w_vars, &new_wp->w_winvar, VAR_SCOPE); /* Don't execute autocommands while the window is not properly @@ -3758,8 +3961,15 @@ win_free ( hash_init(&wp->w_vars->dv_hashtab); unref_var_dict(wp->w_vars); - if (prevwin == wp) + if (prevwin == wp) { prevwin = NULL; + } + FOR_ALL_TABS(ttp) { + if (ttp->tp_prevwin == wp) { + ttp->tp_prevwin = NULL; + } + } + win_free_lsize(wp); for (i = 0; i < wp->w_tagstacklen; ++i) @@ -3784,6 +3994,8 @@ win_free ( xfree(wp->w_p_cc_cols); + win_free_grid(wp, false); + if (wp != aucmd_win) win_remove(wp, tp); if (autocmd_busy) { @@ -3796,6 +4008,20 @@ win_free ( unblock_autocmds(); } +void win_free_grid(win_T *wp, bool reinit) +{ + if (wp->w_grid.handle != 0 && ui_is_external(kUIMultigrid)) { + ui_call_grid_destroy(wp->w_grid.handle); + wp->w_grid.handle = 0; + } + grid_free(&wp->w_grid); + if (reinit) { + // if a float is turned into a split and back into a float, the grid + // data structure will be reused + memset(&wp->w_grid, 0, sizeof(wp->w_grid)); + } +} + /* * Append window "wp" in the window list after window "after". */ @@ -3829,18 +4055,20 @@ win_remove ( tabpage_T *tp /* tab page "win" is in, NULL for current */ ) { - if (wp->w_prev != NULL) + if (wp->w_prev != NULL) { wp->w_prev->w_next = wp->w_next; - else if (tp == NULL) - firstwin = wp->w_next; - else + } else if (tp == NULL) { + firstwin = curtab->tp_firstwin = wp->w_next; + } else { tp->tp_firstwin = wp->w_next; - if (wp->w_next != NULL) + } + if (wp->w_next != NULL) { wp->w_next->w_prev = wp->w_prev; - else if (tp == NULL) - lastwin = wp->w_prev; - else + } else if (tp == NULL) { + lastwin = curtab->tp_lastwin = wp->w_prev; + } else { tp->tp_lastwin = wp->w_prev; + } } /* @@ -3874,12 +4102,18 @@ static void frame_insert(frame_T *before, frame_T *frp) */ static void frame_remove(frame_T *frp) { - if (frp->fr_prev != NULL) + if (frp->fr_prev != NULL) { frp->fr_prev->fr_next = frp->fr_next; - else + } else { frp->fr_parent->fr_child = frp->fr_next; - if (frp->fr_next != NULL) + // special case: topframe->fr_child == frp + if (topframe->fr_child == frp) { + topframe->fr_child = frp->fr_next; + } + } + if (frp->fr_next != NULL) { frp->fr_next->fr_prev = frp->fr_prev; + } } @@ -3889,8 +4123,8 @@ static void frame_remove(frame_T *frp) void win_alloc_lines(win_T *wp) { wp->w_lines_valid = 0; - assert(Rows >= 0); - wp->w_lines = xcalloc(Rows, sizeof(wline_T)); + assert(wp->w_grid.Rows >= 0); + wp->w_lines = xcalloc(MAX(wp->w_grid.Rows + 1, Rows), sizeof(wline_T)); } /* @@ -4020,7 +4254,8 @@ static void frame_comp_pos(frame_T *topfrp, int *row, int *col) wp->w_winrow = *row; wp->w_wincol = *col; redraw_win_later(wp, NOT_VALID); - wp->w_redr_status = TRUE; + wp->w_redr_status = true; + wp->w_pos_changed = true; } *row += wp->w_height + wp->w_status_height; *col += wp->w_width + wp->w_vsep_width; @@ -4073,8 +4308,9 @@ void win_setheight_win(int height, win_T *win) * If there is extra space created between the last window and the command * line, clear it. */ - if (full_screen && msg_scrolled == 0 && row < cmdline_row) - screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); + if (full_screen && msg_scrolled == 0 && row < cmdline_row) { + grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); + } cmdline_row = row; msg_row = row; msg_col = 0; @@ -4157,11 +4393,11 @@ static void frame_setheight(frame_T *curfrp, int height) room_cmdline = 0; } - if (height <= room + room_cmdline) + if (height <= room + room_cmdline) { break; + } if (run == 2 || curfrp->fr_width == Columns) { - if (height > room + room_cmdline) - height = room + room_cmdline; + height = room + room_cmdline; break; } frame_setheight(curfrp->fr_parent, height @@ -4325,8 +4561,7 @@ static void frame_setwidth(frame_T *curfrp, int width) if (width <= room) break; if (run == 2 || curfrp->fr_height >= ROWS_AVAIL) { - if (width > room) - width = room; + width = room; break; } frame_setwidth(curfrp->fr_parent, width @@ -4525,7 +4760,7 @@ void win_drag_status_line(win_T *dragwin, int offset) fr = fr->fr_next; } row = win_comp_pos(); - screen_fill(row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); + grid_fill(&default_grid, row, cmdline_row, 0, (int)Columns, ' ', ' ', 0); cmdline_row = row; p_ch = Rows - cmdline_row; if (p_ch < 1) @@ -4638,10 +4873,13 @@ void win_drag_vsep_line(win_T *dragwin, int offset) #define FRACTION_MULT 16384L // Set wp->w_fraction for the current w_wrow and w_height. +// Has no effect when the window is less than two lines. void set_fraction(win_T *wp) { - wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height / 2) + if (wp->w_height > 1) { + wp->w_fraction = ((long)wp->w_wrow * FRACTION_MULT + wp->w_height / 2) / (long)wp->w_height; + } } /* @@ -4651,8 +4889,6 @@ void set_fraction(win_T *wp) */ void win_new_height(win_T *wp, int height) { - linenr_T lnum; - int sline, line_size; int prev_height = wp->w_height; /* Don't want a negative height. Happens when splitting a tiny window. @@ -4668,7 +4904,7 @@ void win_new_height(win_T *wp, int height) // call win_new_height() recursively. validate_cursor(); } - if (wp->w_height != prev_height) { + if (wp->w_height != prev_height) { // -V547 return; // Recursive call already changed the size, bail out. } if (wp->w_wrow != wp->w_prev_fraction_row) { @@ -4679,6 +4915,21 @@ void win_new_height(win_T *wp, int height) wp->w_height = height; wp->w_skipcol = 0; + // There is no point in adjusting the scroll position when exiting. Some + // values might be invalid. + if (!exiting) { + scroll_to_fraction(wp, prev_height); + } + + wp->w_pos_changed = true; +} + +void scroll_to_fraction(win_T *wp, int prev_height) +{ + linenr_T lnum; + int sline, line_size; + int height = wp->w_height; + /* Don't change w_topline when height is zero. Don't set w_topline when * 'scrollbind' is set and this isn't the current window. */ if (height > 0 @@ -4697,8 +4948,8 @@ void win_new_height(win_T *wp, int height) sline = wp->w_wrow - line_size; if (sline >= 0) { - /* Make sure the whole cursor line is visible, if possible. */ - int rows = plines_win(wp, lnum, FALSE); + // Make sure the whole cursor line is visible, if possible. + const int rows = plines_win(wp, lnum, false); if (sline > wp->w_height - rows) { sline = wp->w_height - rows; @@ -4733,12 +4984,13 @@ void win_new_height(win_T *wp, int height) --sline; break; } - --lnum; - if (lnum == wp->w_topline) - line_size = plines_win_nofill(wp, lnum, TRUE) + lnum--; + if (lnum == wp->w_topline) { + line_size = plines_win_nofill(wp, lnum, true) + wp->w_topfill; - else - line_size = plines_win(wp, lnum, TRUE); + } else { + line_size = plines_win(wp, lnum, true); + } sline -= line_size; } @@ -4775,13 +5027,11 @@ void win_new_height(win_T *wp, int height) if (wp->w_buffer->terminal) { terminal_resize(wp->w_buffer->terminal, 0, wp->w_height); - redraw_win_later(wp, CLEAR); + redraw_win_later(wp, NOT_VALID); } } -/* - * Set the width of a window. - */ +/// Set the width of a window. void win_new_width(win_T *wp, int width) { wp->w_width = width; @@ -4797,10 +5047,12 @@ void win_new_width(win_T *wp, int width) if (wp->w_buffer->terminal) { if (wp->w_height != 0) { - terminal_resize(wp->w_buffer->terminal, wp->w_width, 0); + terminal_resize(wp->w_buffer->terminal, + (uint16_t)(MAX(0, wp->w_width - win_col_off(wp))), + 0); } - redraw_win_later(wp, CLEAR); } + wp->w_pos_changed = true; } void win_comp_scroll(win_T *wp) @@ -4857,10 +5109,11 @@ void command_height(void) /* Recompute window positions. */ (void)win_comp_pos(); - /* clear the lines added to cmdline */ - if (full_screen) - screen_fill(cmdline_row, (int)Rows, 0, - (int)Columns, ' ', ' ', 0); + // clear the lines added to cmdline + if (full_screen) { + grid_fill(&default_grid, cmdline_row, (int)Rows, 0, (int)Columns, ' ', + ' ', 0); + } msg_row = cmdline_row; redraw_cmdline = TRUE; return; @@ -4946,16 +5199,20 @@ file_name_in_line ( { char_u *ptr; size_t len; + bool in_type = true; + bool is_url = false; /* * search forward for what could be the start of a file name */ ptr = line + col; - while (*ptr != NUL && !vim_isfilec(*ptr)) - mb_ptr_adv(ptr); - if (*ptr == NUL) { /* nothing found */ - if (options & FNAME_MESS) + while (*ptr != NUL && !vim_isfilec(*ptr)) { + MB_PTR_ADV(ptr); + } + if (*ptr == NUL) { // nothing found + if (options & FNAME_MESS) { EMSG(_("E446: No file name under cursor")); + } return NULL; } @@ -4964,13 +5221,14 @@ file_name_in_line ( * Go one char back to ":" before "//" even when ':' is not in 'isfname'. */ while (ptr > line) { - if (has_mbyte && (len = (size_t)((*mb_head_off)(line, ptr - 1))) > 0) + if ((len = (size_t)(utf_head_off(line, ptr - 1))) > 0) { ptr -= len + 1; - else if (vim_isfilec(ptr[-1]) - || ((options & FNAME_HYP) && path_is_url((char *)ptr - 1))) - --ptr; - else + } else if (vim_isfilec(ptr[-1]) + || ((options & FNAME_HYP) && path_is_url((char *)ptr - 1))) { + ptr--; + } else { break; + } } /* @@ -4979,7 +5237,19 @@ file_name_in_line ( */ len = 0; while (vim_isfilec(ptr[len]) || (ptr[len] == '\\' && ptr[len + 1] == ' ') - || ((options & FNAME_HYP) && path_is_url((char *)ptr + len))) { + || ((options & FNAME_HYP) && path_is_url((char *)ptr + len)) + || (is_url && vim_strchr((char_u *)"?&=", ptr[len]) != NULL)) { + // After type:// we also include ?, & and = as valid characters, so that + // http://google.com?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)) { + is_url = true; + } + } else { + in_type = false; + } + if (ptr[len] == '\\' && ptr[len + 1] == ' ') { // Skip over the "\" in "\ ". ++len; @@ -5028,7 +5298,7 @@ last_status ( { /* Don't make a difference between horizontal or vertical split. */ last_status_rec(topframe, (p_ls == 2 - || (p_ls == 1 && (morewin || lastwin != firstwin)))); + || (p_ls == 1 && (morewin || !ONE_WINDOW)))); } static void last_status_rec(frame_T *fr, int statusline) @@ -5085,6 +5355,9 @@ static void last_status_rec(frame_T *fr, int statusline) */ int tabline_height(void) { + if (ui_is_external(kUITabline)) { + return 0; + } assert(first_tabpage); switch (p_stal) { case 0: return 0; @@ -5127,7 +5400,7 @@ bool only_one_window(void) FUNC_ATTR_PURE FUNC_ATTR_WARN_UNUSED_RESULT int count = 0; FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { if (wp->w_buffer != NULL - && (!((wp->w_buffer->b_help && !curbuf->b_help) + && (!((bt_help(wp->w_buffer) && !bt_help(curbuf)) || wp->w_p_pvw) || wp == curwin) && wp != aucmd_win) { count++; } @@ -5229,15 +5502,13 @@ restore_snapshot ( win_comp_pos(); if (wp != NULL && close_curwin) win_goto(wp); - redraw_all_later(CLEAR); + redraw_all_later(NOT_VALID); } clear_snapshot(curtab, idx); } -/* - * Check if frames "sn" and "fr" have the same layout, same following frames - * and same children. - */ +/// Check if frames "sn" and "fr" have the same layout, same following frames +/// and same children. And the window pointer is valid. static int check_snapshot_rec(frame_T *sn, frame_T *fr) { if (sn->fr_layout != fr->fr_layout @@ -5246,7 +5517,8 @@ static int check_snapshot_rec(frame_T *sn, frame_T *fr) || (sn->fr_next != NULL && check_snapshot_rec(sn->fr_next, fr->fr_next) == FAIL) || (sn->fr_child != NULL - && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL)) + && check_snapshot_rec(sn->fr_child, fr->fr_child) == FAIL) + || (sn->fr_win != NULL && !win_valid(sn->fr_win))) return FAIL; return OK; } @@ -5281,6 +5553,27 @@ 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 sn->fr_win; +} /* * Set "win" to be the curwin and "tp" to be the current tab page. @@ -5313,12 +5606,10 @@ int switch_win(win_T **save_curwin, tabpage_T **save_curtab, win_T *win, tabpage return OK; } -/* - * 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, int no_display) +// 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) { if (save_curtab != NULL && valid_tabpage(save_curtab)) { if (no_display) { @@ -5337,44 +5628,45 @@ void restore_win(win_T *save_curwin, tabpage_T *save_curtab, int no_display) unblock_autocmds(); } -/* - * Make "buf" the current buffer. restore_buffer() MUST be called to undo. - * No autocommands will be executed. Use aucmd_prepbuf() if there are any. - */ -void switch_buffer(buf_T **save_curbuf, buf_T *buf) +/// Make "buf" the current buffer. +/// +/// restore_buffer() MUST be called to undo. +/// No autocommands will be executed. Use aucmd_prepbuf() if there are any. +void switch_buffer(bufref_T *save_curbuf, buf_T *buf) { block_autocmds(); - *save_curbuf = curbuf; - --curbuf->b_nwindows; + set_bufref(save_curbuf, curbuf); + curbuf->b_nwindows--; curbuf = buf; curwin->w_buffer = buf; - ++curbuf->b_nwindows; + curbuf->b_nwindows++; } -/* - * Restore the current buffer after using switch_buffer(). - */ -void restore_buffer(buf_T *save_curbuf) +/// Restore the current buffer after using switch_buffer(). +void restore_buffer(bufref_T *save_curbuf) { unblock_autocmds(); - /* Check for valid buffer, just in case. */ - if (buf_valid(save_curbuf)) { - --curbuf->b_nwindows; - curwin->w_buffer = save_curbuf; - curbuf = save_curbuf; - ++curbuf->b_nwindows; + // Check for valid buffer, just in case. + if (bufref_valid(save_curbuf)) { + curbuf->b_nwindows--; + curwin->w_buffer = save_curbuf->br_buf; + curbuf = save_curbuf->br_buf; + curbuf->b_nwindows++; } } -// 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). -// If no particular ID is desired, -1 must be specified for 'id'. -// Return ID of added match, -1 on failure. -int match_add(win_T *wp, char_u *grp, char_u *pat, +/// 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 +/// @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, - char_u *conceal_char) + const char *const conceal_char) { matchitem_T *cur; matchitem_T *prev; @@ -5387,8 +5679,8 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, return -1; } if (id < -1 || id == 0) { - EMSGN("E799: Invalid ID: %" PRId64 - " (must be greater than or equal to 1)", + EMSGN(_("E799: Invalid ID: %" PRId64 + " (must be greater than or equal to 1)"), id); return -1; } @@ -5396,17 +5688,17 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, cur = wp->w_match_head; while (cur != NULL) { if (cur->id == id) { - EMSGN("E801: ID already taken: %" PRId64, id); + EMSGN(_("E801: ID already taken: %" PRId64), id); return -1; } cur = cur->next; } } - if ((hlg_id = syn_namen2id(grp, (int)STRLEN(grp))) == 0) { + if ((hlg_id = syn_name2id((const char_u *)grp)) == 0) { EMSG2(_(e_nogroup), grp); return -1; } - if (pat != NULL && (regprog = vim_regcomp(pat, RE_MAGIC)) == NULL) { + if (pat != NULL && (regprog = vim_regcomp((char_u *)pat, RE_MAGIC)) == NULL) { EMSG2(_(e_invarg2), pat); return -1; } @@ -5425,76 +5717,76 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, m = xcalloc(1, sizeof(matchitem_T)); m->id = id; m->priority = prio; - m->pattern = pat == NULL ? NULL: vim_strsave(pat); + 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 = (*mb_ptr2char)(conceal_char); + 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; - listitem_T *li; - int i; - - for (i = 0, li = pos_list->lv_first; li != NULL && i < MAXPOSMATCH; - i++, li = li->li_next) { - linenr_T lnum = 0; - colnr_T col = 0; - int len = 1; - list_T *subl; - listitem_T *subli; - int error = false; - - if (li->li_tv.v_type == VAR_LIST) { - subl = li->li_tv.vval.v_list; - if (subl == NULL) { - goto fail; - } - subli = subl->lv_first; + 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) { + emsgf(_("E5030: Empty list at position %d"), + (int)tv_list_idx_of_item(pos_list, li)); goto fail; } - lnum = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) { + lnum = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); + if (error) { goto fail; } - if (lnum == 0) { - --i; + if (lnum <= 0) { continue; } m->pos.pos[i].lnum = lnum; - subli = subli->li_next; + subli = TV_LIST_ITEM_NEXT(subl, subli); if (subli != NULL) { - col = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) + col = tv_get_number_chk(TV_LIST_ITEM_TV(subli), &error); + if (error) { goto fail; - subli = subli->li_next; + } + if (col < 0) { + continue; + } + subli = TV_LIST_ITEM_NEXT(subl, subli); if (subli != NULL) { - len = get_tv_number_chk(&subli->li_tv, &error); - if (error == true) { + 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 (li->li_tv.v_type == VAR_NUMBER) { - if (li->li_tv.vval.v_number == 0) { - --i; + } 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 = li->li_tv.vval.v_number; + 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 { - EMSG(_("List or number required")); + emsgf(_("E5031: List or number required at position %d"), + (int)tv_list_idx_of_item(pos_list, li)); goto fail; } if (toplnum == 0 || lnum < toplnum) { @@ -5503,7 +5795,11 @@ int match_add(win_T *wp, char_u *grp, char_u *pat, if (botlnum == 0 || lnum >= botlnum) { botlnum = lnum + 1; } - } + i++; + if (i >= MAXPOSMATCH) { + break; + } + }); // Calculate top and bottom lines for redrawing area if (toplnum != 0){ @@ -5548,10 +5844,9 @@ fail: return -1; } -/* - * Delete match with ID 'id' in the match list of window 'wp'. - * Print error messages if 'perr' is TRUE. - */ + +/// Delete match with ID 'id' in the match list of window 'wp'. +/// Print error messages if 'perr' is TRUE. int match_delete(win_T *wp, int id, int perr) { matchitem_T *cur = wp->w_match_head; @@ -5559,10 +5854,11 @@ int match_delete(win_T *wp, int id, int perr) int rtype = SOME_VALID; if (id < 1) { - if (perr == TRUE) - EMSGN("E802: Invalid ID: %" PRId64 - " (must be greater than or equal to 1)", + if (perr) { + EMSGN(_("E802: Invalid ID: %" PRId64 + " (must be greater than or equal to 1)"), id); + } return -1; } while (cur != NULL && cur->id != id) { @@ -5570,8 +5866,9 @@ int match_delete(win_T *wp, int id, int perr) cur = cur->next; } if (cur == NULL) { - if (perr == TRUE) - EMSGN("E803: ID not found: %" PRId64, id); + if (perr) { + EMSGN(_("E803: ID not found: %" PRId64), id); + } return -1; } if (cur == prev) @@ -5671,3 +5968,144 @@ static bool frame_check_width(frame_T *topfrp, int width) } return true; } + +int win_getid(typval_T *argvars) +{ + if (argvars[0].v_type == VAR_UNKNOWN) { + return curwin->handle; + } + int winnr = tv_get_number(&argvars[0]); + win_T *wp; + if (winnr > 0) { + if (argvars[1].v_type == VAR_UNKNOWN) { + wp = firstwin; + } else { + tabpage_T *tp = NULL; + int tabnr = tv_get_number(&argvars[1]); + FOR_ALL_TABS(tp2) { + if (--tabnr == 0) { + tp = tp2; + break; + } + } + if (tp == NULL) { + return -1; + } + if (tp == curtab) { + wp = firstwin; + } else { + wp = tp->tp_firstwin; + } + } + for ( ; wp != NULL; wp = wp->w_next) { + if (--winnr == 0) { + return wp->handle; + } + } + } + return 0; +} + +int win_gotoid(typval_T *argvars) +{ + int id = tv_get_number(&argvars[0]); + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->handle == id) { + goto_tabpage_win(tp, wp); + return 1; + } + } + return 0; +} + +void win_get_tabwin(handle_T id, int *tabnr, int *winnr) +{ + *tabnr = 0; + *winnr = 0; + + int tnum = 1, wnum = 1; + FOR_ALL_TABS(tp) { + FOR_ALL_WINDOWS_IN_TAB(wp, tp) { + if (wp->handle == id) { + *winnr = wnum; + *tabnr = tnum; + return; + } + wnum++; + } + tnum++; + wnum = 1; + } +} + +void win_id2tabwin(typval_T *const argvars, typval_T *const rettv) +{ + int winnr = 1; + int tabnr = 1; + handle_T id = (handle_T)tv_get_number(&argvars[0]); + + win_get_tabwin(id, &tabnr, &winnr); + + list_T *const list = tv_list_alloc_ret(rettv, 2); + tv_list_append_number(list, tabnr); + tv_list_append_number(list, winnr); +} + +win_T * win_id2wp(typval_T *argvars) +{ + int id = tv_get_number(&argvars[0]); + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->handle == id) { + return wp; + } + } + + return NULL; +} + +int win_id2win(typval_T *argvars) +{ + int nr = 1; + int id = tv_get_number(&argvars[0]); + + FOR_ALL_WINDOWS_IN_TAB(wp, curtab) { + if (wp->handle == id) { + return nr; + } + nr++; + } + return 0; +} + +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_buffer->b_fnum == bufnr) { + tv_list_append_number(list, wp->handle); + } + } +} + +void win_ui_flush(void) +{ + if (!ui_is_external(kUIMultigrid)) { + return; + } + + FOR_ALL_TAB_WINDOWS(tp, wp) { + if (wp->w_pos_changed && wp->w_grid.chars != NULL) { + if (tp == curtab) { + ui_call_win_pos(wp->w_grid.handle, wp->handle, wp->w_winrow, + wp->w_wincol, wp->w_width, wp->w_height); + } else { + ui_call_win_hide(wp->w_grid.handle); + } + wp->w_pos_changed = false; + } + } + +} |