aboutsummaryrefslogtreecommitdiff
path: root/src/nvim/window.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/nvim/window.c')
-rw-r--r--src/nvim/window.c5394
1 files changed, 5394 insertions, 0 deletions
diff --git a/src/nvim/window.c b/src/nvim/window.c
new file mode 100644
index 0000000000..27582184d7
--- /dev/null
+++ b/src/nvim/window.c
@@ -0,0 +1,5394 @@
+/*
+ * VIM - Vi IMproved by Bram Moolenaar
+ *
+ * Do ":help uganda" in Vim to read a list of people who contributed.
+ * Do ":help credits" in Vim to see a list of people who contributed.
+ * See README.txt for an overview of the Vim source code.
+ */
+
+#include "vim.h"
+#include "window.h"
+#include "buffer.h"
+#include "charset.h"
+#include "diff.h"
+#include "edit.h"
+#include "eval.h"
+#include "ex_cmds.h"
+#include "ex_cmds2.h"
+#include "ex_docmd.h"
+#include "ex_eval.h"
+#include "ex_getln.h"
+#include "farsi.h"
+#include "fileio.h"
+#include "fold.h"
+#include "getchar.h"
+#include "hashtab.h"
+#include "main.h"
+#include "mark.h"
+#include "memline.h"
+#include "memory.h"
+#include "message.h"
+#include "misc1.h"
+#include "misc2.h"
+#include "file_search.h"
+#include "garray.h"
+#include "move.h"
+#include "normal.h"
+#include "option.h"
+#include "os_unix.h"
+#include "path.h"
+#include "quickfix.h"
+#include "regexp.h"
+#include "screen.h"
+#include "search.h"
+#include "syntax.h"
+#include "term.h"
+#include "undo.h"
+#include "os/os.h"
+
+static void win_init(win_T *newp, win_T *oldp, int flags);
+static void win_init_some(win_T *newp, win_T *oldp);
+static void frame_comp_pos(frame_T *topfrp, int *row, int *col);
+static void frame_setheight(frame_T *curfrp, int height);
+static void frame_setwidth(frame_T *curfrp, int width);
+static void win_exchange(long);
+static void win_rotate(int, int);
+static void win_totop(int size, int flags);
+static void win_equal_rec(win_T *next_curwin, int current, frame_T *topfr,
+ int dir, int col, int row, int width,
+ int height);
+static int last_window(void);
+static int close_last_window_tabpage(win_T *win, int free_buf,
+ tabpage_T *prev_curtab);
+static win_T *win_free_mem(win_T *win, int *dirp, tabpage_T *tp);
+static frame_T *win_altframe(win_T *win, tabpage_T *tp);
+static tabpage_T *alt_tabpage(void);
+static win_T *frame2win(frame_T *frp);
+static int frame_has_win(frame_T *frp, win_T *wp);
+static void frame_new_height(frame_T *topfrp, int height, int topfirst,
+ int wfh);
+static int frame_fixed_height(frame_T *frp);
+static int frame_fixed_width(frame_T *frp);
+static void frame_add_statusline(frame_T *frp);
+static void frame_new_width(frame_T *topfrp, int width, int leftfirst,
+ int wfw);
+static void frame_add_vsep(frame_T *frp);
+static int frame_minwidth(frame_T *topfrp, win_T *next_curwin);
+static void frame_fix_width(win_T *wp);
+static int win_alloc_firstwin(win_T *oldwin);
+static void new_frame(win_T *wp);
+static tabpage_T *alloc_tabpage(void);
+static int leave_tabpage(buf_T *new_curbuf, int trigger_leave_autocmds);
+static void enter_tabpage(tabpage_T *tp, buf_T *old_curbuf,
+ int trigger_enter_autocmds,
+ int trigger_leave_autocmds);
+static void frame_fix_height(win_T *wp);
+static int frame_minheight(frame_T *topfrp, win_T *next_curwin);
+static void win_enter_ext(win_T *wp, int undo_sync, int no_curwin,
+ int trigger_enter_autocmds,
+ int trigger_leave_autocmds);
+static void win_free(win_T *wp, tabpage_T *tp);
+static void frame_append(frame_T *after, frame_T *frp);
+static void frame_insert(frame_T *before, frame_T *frp);
+static void frame_remove(frame_T *frp);
+static void win_goto_ver(int up, long count);
+static void win_goto_hor(int left, long count);
+static void frame_add_height(frame_T *frp, int n);
+static void last_status_rec(frame_T *fr, int statusline);
+
+static void make_snapshot_rec(frame_T *fr, frame_T **frp);
+static void clear_snapshot(tabpage_T *tp, int idx);
+static void clear_snapshot_rec(frame_T *fr);
+static int check_snapshot_rec(frame_T *sn, frame_T *fr);
+static win_T *restore_snapshot_rec(frame_T *sn, frame_T *fr);
+
+static int frame_check_height(frame_T *topfrp, int height);
+static int frame_check_width(frame_T *topfrp, int width);
+
+
+static win_T *win_alloc(win_T *after, int hidden);
+static void set_fraction(win_T *wp);
+
+#define NOWIN (win_T *)-1 /* non-existing window */
+
+# define ROWS_AVAIL (Rows - p_ch - tabline_height())
+
+
+static char *m_onlyone = N_("Already only one window");
+
+/*
+ * all CTRL-W window commands are handled here, called from normal_cmd().
+ */
+void
+do_window (
+ int nchar,
+ long Prenum,
+ int xchar /* extra char from ":wincmd gx" or NUL */
+)
+{
+ long Prenum1;
+ win_T *wp;
+ char_u *ptr;
+ linenr_T lnum = -1;
+ int type = FIND_DEFINE;
+ int len;
+ char_u cbuf[40];
+
+ if (Prenum == 0)
+ Prenum1 = 1;
+ else
+ Prenum1 = Prenum;
+
+# define CHECK_CMDWIN if (cmdwin_type != 0) { EMSG(_(e_cmdwin)); break; }
+
+ switch (nchar) {
+ /* split current window in two parts, horizontally */
+ case 'S':
+ case Ctrl_S:
+ case 's':
+ CHECK_CMDWIN reset_VIsual_and_resel(); /* stop Visual mode */
+ /* When splitting the quickfix window open a new buffer in it,
+ * don't replicate the quickfix buffer. */
+ if (bt_quickfix(curbuf))
+ goto newwindow;
+ win_split((int)Prenum, 0);
+ break;
+
+ /* split current window in two parts, vertically */
+ case Ctrl_V:
+ case 'v':
+ CHECK_CMDWIN reset_VIsual_and_resel(); /* stop Visual mode */
+ /* When splitting the quickfix window open a new buffer in it,
+ * don't replicate the quickfix buffer. */
+ if (bt_quickfix(curbuf))
+ goto newwindow;
+ win_split((int)Prenum, WSP_VERT);
+ break;
+
+ /* split current window and edit alternate file */
+ case Ctrl_HAT:
+ case '^':
+ CHECK_CMDWIN reset_VIsual_and_resel(); /* stop Visual mode */
+ STRCPY(cbuf, "split #");
+ if (Prenum)
+ vim_snprintf((char *)cbuf + 7, sizeof(cbuf) - 7,
+ "%" PRId64, (int64_t)Prenum);
+ do_cmdline_cmd(cbuf);
+ break;
+
+ /* open new window */
+ case Ctrl_N:
+ case 'n':
+ CHECK_CMDWIN reset_VIsual_and_resel(); /* stop Visual mode */
+newwindow:
+ if (Prenum)
+ /* window height */
+ vim_snprintf((char *)cbuf, sizeof(cbuf) - 5, "%" PRId64, (int64_t)Prenum);
+ else
+ cbuf[0] = NUL;
+ if (nchar == 'v' || nchar == Ctrl_V)
+ STRCAT(cbuf, "v");
+ STRCAT(cbuf, "new");
+ do_cmdline_cmd(cbuf);
+ break;
+
+ /* quit current window */
+ case Ctrl_Q:
+ case 'q':
+ reset_VIsual_and_resel(); /* stop Visual mode */
+ do_cmdline_cmd((char_u *)"quit");
+ break;
+
+ /* close current window */
+ case Ctrl_C:
+ case 'c':
+ reset_VIsual_and_resel(); /* stop Visual mode */
+ do_cmdline_cmd((char_u *)"close");
+ break;
+
+ /* close preview window */
+ case Ctrl_Z:
+ case 'z':
+ CHECK_CMDWIN reset_VIsual_and_resel(); /* stop Visual mode */
+ do_cmdline_cmd((char_u *)"pclose");
+ break;
+
+ /* cursor to preview window */
+ case 'P':
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (wp->w_p_pvw)
+ break;
+ if (wp == NULL)
+ EMSG(_("E441: There is no preview window"));
+ else
+ win_goto(wp);
+ break;
+
+ /* close all but current window */
+ case Ctrl_O:
+ case 'o':
+ CHECK_CMDWIN reset_VIsual_and_resel(); /* stop Visual mode */
+ do_cmdline_cmd((char_u *)"only");
+ break;
+
+ /* cursor to next window with wrap around */
+ case Ctrl_W:
+ case 'w':
+ /* cursor to previous window with wrap around */
+ case 'W':
+ CHECK_CMDWIN
+ if (firstwin == lastwin && Prenum != 1) /* just one window */
+ beep_flush();
+ else {
+ if (Prenum) { /* go to specified window */
+ for (wp = firstwin; --Prenum > 0; ) {
+ if (wp->w_next == NULL)
+ break;
+ else
+ wp = wp->w_next;
+ }
+ } else {
+ if (nchar == 'W') { /* go to previous window */
+ wp = curwin->w_prev;
+ if (wp == NULL)
+ wp = lastwin; /* wrap around */
+ } else { /* go to next window */
+ wp = curwin->w_next;
+ if (wp == NULL)
+ wp = firstwin; /* wrap around */
+ }
+ }
+ win_goto(wp);
+ }
+ break;
+
+ /* cursor to window below */
+ case 'j':
+ case K_DOWN:
+ case Ctrl_J:
+ CHECK_CMDWIN win_goto_ver(FALSE, Prenum1);
+ break;
+
+ /* cursor to window above */
+ case 'k':
+ case K_UP:
+ case Ctrl_K:
+ CHECK_CMDWIN win_goto_ver(TRUE, Prenum1);
+ break;
+
+ /* cursor to left window */
+ case 'h':
+ case K_LEFT:
+ case Ctrl_H:
+ case K_BS:
+ CHECK_CMDWIN win_goto_hor(TRUE, Prenum1);
+ break;
+
+ /* cursor to right window */
+ case 'l':
+ case K_RIGHT:
+ case Ctrl_L:
+ CHECK_CMDWIN win_goto_hor(FALSE, Prenum1);
+ break;
+
+ /* move window to new tab page */
+ case 'T':
+ if (one_window())
+ MSG(_(m_onlyone));
+ 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. */
+ wp = curwin;
+ if (win_new_tabpage((int)Prenum) == OK
+ && valid_tabpage(oldtab)) {
+ newtab = curtab;
+ goto_tabpage_tp(oldtab, TRUE, TRUE);
+ if (curwin == wp)
+ win_close(curwin, FALSE);
+ if (valid_tabpage(newtab))
+ goto_tabpage_tp(newtab, TRUE, TRUE);
+ }
+ }
+ break;
+
+ /* cursor to top-left window */
+ case 't':
+ case Ctrl_T:
+ win_goto(firstwin);
+ break;
+
+ /* cursor to bottom-right window */
+ case 'b':
+ case Ctrl_B:
+ win_goto(lastwin);
+ break;
+
+ /* cursor to last accessed (previous) window */
+ case 'p':
+ case Ctrl_P:
+ if (prevwin == NULL)
+ beep_flush();
+ else
+ win_goto(prevwin);
+ break;
+
+ /* exchange current and next window */
+ case 'x':
+ case Ctrl_X:
+ CHECK_CMDWIN win_exchange(Prenum);
+ break;
+
+ /* rotate windows downwards */
+ case Ctrl_R:
+ case 'r':
+ CHECK_CMDWIN reset_VIsual_and_resel(); /* stop Visual mode */
+ win_rotate(FALSE, (int)Prenum1); /* downwards */
+ break;
+
+ /* rotate windows upwards */
+ case 'R':
+ CHECK_CMDWIN reset_VIsual_and_resel(); /* stop Visual mode */
+ win_rotate(TRUE, (int)Prenum1); /* upwards */
+ break;
+
+ /* move window to the very top/bottom/left/right */
+ case 'K':
+ case 'J':
+ case 'H':
+ case 'L':
+ CHECK_CMDWIN win_totop((int)Prenum,
+ ((nchar == 'H' || nchar == 'L') ? WSP_VERT : 0)
+ | ((nchar == 'H' || nchar == 'K') ? WSP_TOP : WSP_BOT));
+ break;
+
+ /* make all windows the same height */
+ case '=':
+ win_equal(NULL, FALSE, 'b');
+ break;
+
+ /* increase current window height */
+ case '+':
+ win_setheight(curwin->w_height + (int)Prenum1);
+ break;
+
+ /* decrease current window height */
+ case '-':
+ win_setheight(curwin->w_height - (int)Prenum1);
+ break;
+
+ /* set current window height */
+ case Ctrl__:
+ case '_':
+ win_setheight(Prenum ? (int)Prenum : 9999);
+ break;
+
+ /* increase current window width */
+ case '>':
+ win_setwidth(curwin->w_width + (int)Prenum1);
+ break;
+
+ /* decrease current window width */
+ case '<':
+ win_setwidth(curwin->w_width - (int)Prenum1);
+ break;
+
+ /* set current window width */
+ case '|':
+ win_setwidth(Prenum != 0 ? (int)Prenum : 9999);
+ break;
+
+ /* jump to tag and split window if tag exists (in preview window) */
+ case '}':
+ CHECK_CMDWIN
+ if (Prenum)
+ g_do_tagpreview = Prenum;
+ else
+ g_do_tagpreview = p_pvh;
+ /*FALLTHROUGH*/
+ case ']':
+ case Ctrl_RSB:
+ CHECK_CMDWIN reset_VIsual_and_resel(); /* stop Visual mode */
+ if (Prenum)
+ postponed_split = Prenum;
+ else
+ postponed_split = -1;
+
+ /* Execute the command right here, required when
+ * "wincmd ]" was used in a function. */
+ do_nv_ident(Ctrl_RSB, NUL);
+ break;
+
+ /* edit file name under cursor in a new window */
+ case 'f':
+ case 'F':
+ case Ctrl_F:
+wingotofile:
+ CHECK_CMDWIN
+
+ ptr = grab_file_name(Prenum1, &lnum);
+ if (ptr != NULL) {
+ 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) {
+ curwin->w_cursor.lnum = lnum;
+ check_cursor_lnum();
+ beginline(BL_SOL | BL_FIX);
+ }
+ }
+ free(ptr);
+ }
+ break;
+
+ /* Go to the first occurrence of the identifier under cursor along path in a
+ * new window -- webb
+ */
+ case 'i': /* Go to any match */
+ case Ctrl_I:
+ type = FIND_ANY;
+ /* FALLTHROUGH */
+ case 'd': /* Go to definition, using 'define' */
+ case Ctrl_D:
+ CHECK_CMDWIN
+ if ((len = find_ident_under_cursor(&ptr, FIND_IDENT)) == 0)
+ break;
+ find_pattern_in_path(ptr, 0, len, TRUE,
+ Prenum == 0 ? TRUE : FALSE, type,
+ Prenum1, ACTION_SPLIT, (linenr_T)1, (linenr_T)MAXLNUM);
+ curwin->w_set_curswant = TRUE;
+ break;
+
+ case K_KENTER:
+ case CAR:
+ /*
+ * In a quickfix window a <CR> jumps to the error under the
+ * cursor in a new window.
+ */
+ if (bt_quickfix(curbuf)) {
+ sprintf((char *)cbuf, "split +%" PRId64 "%s",
+ (int64_t)curwin->w_cursor.lnum,
+ (curwin->w_llist_ref == NULL) ? "cc" : "ll");
+ do_cmdline_cmd(cbuf);
+ }
+ break;
+
+
+ /* CTRL-W g extended commands */
+ case 'g':
+ case Ctrl_G:
+ CHECK_CMDWIN
+#ifdef USE_ON_FLY_SCROLL
+ dont_scroll = TRUE; /* disallow scrolling here */
+#endif
+ ++ no_mapping;
+ ++allow_keys; /* no mapping for xchar, but allow key codes */
+ if (xchar == NUL)
+ xchar = plain_vgetc();
+ LANGMAP_ADJUST(xchar, TRUE);
+ --no_mapping;
+ --allow_keys;
+ (void)add_to_showcmd(xchar);
+ switch (xchar) {
+ case '}':
+ xchar = Ctrl_RSB;
+ if (Prenum)
+ g_do_tagpreview = Prenum;
+ else
+ g_do_tagpreview = p_pvh;
+ /*FALLTHROUGH*/
+ case ']':
+ case Ctrl_RSB:
+ reset_VIsual_and_resel(); /* stop Visual mode */
+ if (Prenum)
+ postponed_split = Prenum;
+ else
+ postponed_split = -1;
+
+ /* Execute the command right here, required when
+ * "wincmd g}" was used in a function. */
+ do_nv_ident('g', xchar);
+ break;
+
+ case 'f': /* CTRL-W gf: "gf" in a new tab page */
+ case 'F': /* CTRL-W gF: "gF" in a new tab page */
+ cmdmod.tab = tabpage_index(curtab) + 1;
+ nchar = xchar;
+ goto wingotofile;
+ default:
+ beep_flush();
+ break;
+ }
+ break;
+
+ default: beep_flush();
+ break;
+ }
+}
+
+/*
+ * split the current window, implements CTRL-W s and :split
+ *
+ * "size" is the height or width for the new window, 0 to use half of current
+ * height or width.
+ *
+ * "flags":
+ * WSP_ROOM: require enough room for new window
+ * WSP_VERT: vertical split.
+ * WSP_TOP: open window at the top-left of the shell (help window).
+ * WSP_BOT: open window at the bottom-right of the shell (quickfix window).
+ * WSP_HELP: creating the help window, keep layout snapshot
+ *
+ * return FAIL for failure, OK otherwise
+ */
+int win_split(int size, int flags)
+{
+ /* When the ":tab" modifier was used open a new tab page instead. */
+ if (may_open_tabpage() == OK)
+ return OK;
+
+ /* Add flags from ":vertical", ":topleft" and ":botright". */
+ flags |= cmdmod.split;
+ if ((flags & WSP_TOP) && (flags & WSP_BOT)) {
+ EMSG(_("E442: Can't split topleft and botright at the same time"));
+ return FAIL;
+ }
+
+ /* When creating the help window make a snapshot of the window layout.
+ * Otherwise clear the snapshot, it's now invalid. */
+ if (flags & WSP_HELP)
+ make_snapshot(SNAP_HELP_IDX);
+ else
+ clear_snapshot(curtab, SNAP_HELP_IDX);
+
+ 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
+ */
+int win_split_ins(int size, int flags, win_T *new_wp, int dir)
+{
+ win_T *wp = new_wp;
+ win_T *oldwin;
+ int new_size = size;
+ int i;
+ int need_status = 0;
+ int do_equal = FALSE;
+ int needed;
+ int available;
+ int oldwin_height = 0;
+ int layout;
+ frame_T *frp, *curfrp;
+ int before;
+
+ if (flags & WSP_TOP)
+ oldwin = firstwin;
+ else if (flags & WSP_BOT)
+ oldwin = lastwin;
+ else
+ 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 (oldwin->w_height <= p_wmh && new_wp == NULL) {
+ EMSG(_(e_noroom));
+ return FAIL;
+ }
+ need_status = STATUS_HEIGHT;
+ }
+
+
+ if (flags & WSP_VERT) {
+ layout = FR_ROW;
+
+ /*
+ * Check if we are able to split the current window and compute its
+ * width.
+ */
+ needed = p_wmw + 1;
+ if (flags & WSP_ROOM)
+ needed += p_wiw - p_wmw;
+ if (p_ea || (flags & (WSP_BOT | WSP_TOP))) {
+ available = topframe->fr_width;
+ needed += frame_minwidth(topframe, NULL);
+ } else
+ available = oldwin->w_width;
+ if (available < needed && new_wp == NULL) {
+ EMSG(_(e_noroom));
+ return FAIL;
+ }
+ if (new_size == 0)
+ new_size = oldwin->w_width / 2;
+ if (new_size > oldwin->w_width - p_wmw - 1)
+ new_size = oldwin->w_width - p_wmw - 1;
+ if (new_size < p_wmw)
+ new_size = p_wmw;
+
+ /* if it doesn't fit in the current window, need win_equal() */
+ 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);
+
+ /* Only make all windows the same width if one of them (except oldwin)
+ * is wider than one of the split windows. */
+ if (!do_equal && p_ea && size == 0 && *p_ead != 'v'
+ && oldwin->w_frame->fr_parent != NULL) {
+ frp = oldwin->w_frame->fr_parent->fr_child;
+ while (frp != NULL) {
+ if (frp->fr_win != oldwin && frp->fr_win != NULL
+ && (frp->fr_win->w_width > new_size
+ || frp->fr_win->w_width > oldwin->w_width
+ - new_size - STATUS_HEIGHT)) {
+ do_equal = TRUE;
+ break;
+ }
+ frp = frp->fr_next;
+ }
+ }
+ } else {
+ layout = FR_COL;
+
+ /*
+ * Check if we are able to split the current window and compute its
+ * height.
+ */
+ needed = p_wmh + STATUS_HEIGHT + need_status;
+ if (flags & WSP_ROOM)
+ needed += p_wh - p_wmh;
+ if (p_ea || (flags & (WSP_BOT | WSP_TOP))) {
+ available = topframe->fr_height;
+ needed += frame_minheight(topframe, NULL);
+ } else {
+ available = oldwin->w_height;
+ needed += p_wmh;
+ }
+ if (available < needed && new_wp == NULL) {
+ EMSG(_(e_noroom));
+ return FAIL;
+ }
+ oldwin_height = oldwin->w_height;
+ if (need_status) {
+ oldwin->w_status_height = STATUS_HEIGHT;
+ oldwin_height -= STATUS_HEIGHT;
+ }
+ if (new_size == 0)
+ new_size = oldwin_height / 2;
+
+ if (new_size > oldwin_height - p_wmh - STATUS_HEIGHT)
+ new_size = oldwin_height - p_wmh - STATUS_HEIGHT;
+ if (new_size < p_wmh)
+ new_size = p_wmh;
+
+ /* if it doesn't fit in the current window, need win_equal() */
+ if (oldwin_height - new_size - STATUS_HEIGHT < p_wmh)
+ do_equal = TRUE;
+
+ /* We don't like to take lines for the new window from a
+ * 'winfixheight' window. Take them from a window above or below
+ * instead, if possible. */
+ if (oldwin->w_p_wfh) {
+ win_setheight_win(oldwin->w_height + new_size + STATUS_HEIGHT,
+ oldwin);
+ oldwin_height = oldwin->w_height;
+ if (need_status)
+ oldwin_height -= STATUS_HEIGHT;
+ }
+
+ /* Only make all windows the same height if one of them (except oldwin)
+ * is higher than one of the split windows. */
+ if (!do_equal && p_ea && size == 0
+ && *p_ead != 'h'
+ && oldwin->w_frame->fr_parent != NULL) {
+ frp = oldwin->w_frame->fr_parent->fr_child;
+ while (frp != NULL) {
+ if (frp->fr_win != oldwin && frp->fr_win != NULL
+ && (frp->fr_win->w_height > new_size
+ || frp->fr_win->w_height > oldwin_height - new_size
+ - STATUS_HEIGHT)) {
+ do_equal = TRUE;
+ break;
+ }
+ frp = frp->fr_next;
+ }
+ }
+ }
+
+ /*
+ * allocate new window structure and link it in the window list
+ */
+ if ((flags & WSP_TOP) == 0
+ && ((flags & WSP_BOT)
+ || (flags & WSP_BELOW)
+ || (!(flags & WSP_ABOVE)
+ && (
+ (flags & WSP_VERT) ? p_spr :
+ p_sb)))) {
+ /* new window below/right of current one */
+ if (new_wp == NULL)
+ wp = win_alloc(oldwin, FALSE);
+ else
+ win_append(oldwin, wp);
+ } else {
+ if (new_wp == NULL)
+ wp = win_alloc(oldwin->w_prev, FALSE);
+ else
+ win_append(oldwin->w_prev, wp);
+ }
+
+ if (new_wp == NULL) {
+ if (wp == NULL)
+ return FAIL;
+
+ new_frame(wp);
+
+ /* make the contents of the new window the same as the current one */
+ win_init(wp, curwin, flags);
+ }
+
+ /*
+ * Reorganise the tree of frames to insert the new window.
+ */
+ if (flags & (WSP_TOP | WSP_BOT)) {
+ if ((topframe->fr_layout == FR_COL && (flags & WSP_VERT) == 0)
+ || (topframe->fr_layout == FR_ROW && (flags & WSP_VERT) != 0)) {
+ curfrp = topframe->fr_child;
+ if (flags & WSP_BOT)
+ while (curfrp->fr_next != NULL)
+ curfrp = curfrp->fr_next;
+ } else
+ curfrp = topframe;
+ before = (flags & WSP_TOP);
+ } else {
+ curfrp = oldwin->w_frame;
+ if (flags & WSP_BELOW)
+ before = FALSE;
+ else if (flags & WSP_ABOVE)
+ before = TRUE;
+ else if (flags & WSP_VERT)
+ before = !p_spr;
+ else
+ before = !p_sb;
+ }
+ if (curfrp->fr_parent == NULL || curfrp->fr_parent->fr_layout != layout) {
+ /* Need to create a new frame in the tree to make a branch. */
+ frp = xcalloc(1, sizeof(frame_T));
+ *frp = *curfrp;
+ curfrp->fr_layout = layout;
+ frp->fr_parent = curfrp;
+ frp->fr_next = NULL;
+ frp->fr_prev = NULL;
+ curfrp->fr_child = frp;
+ curfrp->fr_win = NULL;
+ curfrp = frp;
+ if (frp->fr_win != NULL)
+ oldwin->w_frame = frp;
+ else
+ for (frp = frp->fr_child; frp != NULL; frp = frp->fr_next)
+ frp->fr_parent = curfrp;
+ }
+
+ if (new_wp == NULL)
+ frp = wp->w_frame;
+ else
+ frp = new_wp->w_frame;
+ frp->fr_parent = curfrp->fr_parent;
+
+ /* Insert the new frame at the right place in the frame list. */
+ if (before)
+ frame_insert(curfrp, frp);
+ else
+ frame_append(curfrp, frp);
+
+ /* Set w_fraction now so that the cursor keeps the same relative
+ * vertical position. */
+ if (oldwin->w_height > 0)
+ set_fraction(oldwin);
+ wp->w_fraction = oldwin->w_fraction;
+
+ if (flags & WSP_VERT) {
+ wp->w_p_scr = curwin->w_p_scr;
+
+ if (need_status) {
+ win_new_height(oldwin, oldwin->w_height - 1);
+ oldwin->w_status_height = need_status;
+ }
+ if (flags & (WSP_TOP | WSP_BOT)) {
+ /* set height and row of new window to full height */
+ wp->w_winrow = tabline_height();
+ win_new_height(wp, curfrp->fr_height - (p_ls > 0));
+ wp->w_status_height = (p_ls > 0);
+ } else {
+ /* height and row of new window is same as current window */
+ wp->w_winrow = oldwin->w_winrow;
+ win_new_height(wp, oldwin->w_height);
+ wp->w_status_height = oldwin->w_status_height;
+ }
+ frp->fr_height = curfrp->fr_height;
+
+ /* "new_size" of the current window goes to the new window, use
+ * one column for the vertical separator */
+ win_new_width(wp, new_size);
+ if (before)
+ wp->w_vsep_width = 1;
+ else {
+ wp->w_vsep_width = oldwin->w_vsep_width;
+ oldwin->w_vsep_width = 1;
+ }
+ if (flags & (WSP_TOP | WSP_BOT)) {
+ if (flags & WSP_BOT)
+ frame_add_vsep(curfrp);
+ /* Set width of neighbor frame */
+ frame_new_width(curfrp, curfrp->fr_width
+ - (new_size + ((flags & WSP_TOP) != 0)), flags & WSP_TOP,
+ FALSE);
+ } else
+ win_new_width(oldwin, oldwin->w_width - (new_size + 1));
+ if (before) { /* new window left of current one */
+ wp->w_wincol = oldwin->w_wincol;
+ oldwin->w_wincol += new_size + 1;
+ } else /* new window right of current one */
+ wp->w_wincol = oldwin->w_wincol + oldwin->w_width + 1;
+ frame_fix_width(oldwin);
+ frame_fix_width(wp);
+ } else {
+ /* width and column of new window is same as current window */
+ if (flags & (WSP_TOP | WSP_BOT)) {
+ wp->w_wincol = 0;
+ win_new_width(wp, Columns);
+ wp->w_vsep_width = 0;
+ } else {
+ wp->w_wincol = oldwin->w_wincol;
+ win_new_width(wp, oldwin->w_width);
+ wp->w_vsep_width = oldwin->w_vsep_width;
+ }
+ frp->fr_width = curfrp->fr_width;
+
+ /* "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
+ win_new_height(oldwin, oldwin_height - (new_size + STATUS_HEIGHT));
+ if (before) { /* new window above current one */
+ wp->w_winrow = oldwin->w_winrow;
+ wp->w_status_height = STATUS_HEIGHT;
+ oldwin->w_winrow += wp->w_height + STATUS_HEIGHT;
+ } 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;
+ oldwin->w_status_height = STATUS_HEIGHT;
+ }
+ if (flags & WSP_BOT)
+ frame_add_statusline(curfrp);
+ frame_fix_height(wp);
+ frame_fix_height(oldwin);
+ }
+
+ if (flags & (WSP_TOP | WSP_BOT))
+ (void)win_comp_pos();
+
+ /*
+ * Both windows need redrawing
+ */
+ redraw_win_later(wp, NOT_VALID);
+ wp->w_redr_status = TRUE;
+ redraw_win_later(oldwin, NOT_VALID);
+ oldwin->w_redr_status = TRUE;
+
+ if (need_status) {
+ msg_row = Rows - 1;
+ msg_col = sc_col;
+ msg_clr_eos_force(); /* Old command/ruler may still be there */
+ comp_col();
+ msg_row = Rows - 1;
+ msg_col = 0; /* put position back at start of line */
+ }
+
+ /*
+ * equalize the window sizes.
+ */
+ if (do_equal || dir != 0)
+ win_equal(wp, TRUE,
+ (flags & WSP_VERT) ? (dir == 'v' ? 'b' : 'h')
+ : dir == 'h' ? 'b' :
+ 'v');
+
+ /* Don't change the window height/width to 'winheight' / 'winwidth' if a
+ * size was given. */
+ if (flags & WSP_VERT) {
+ i = p_wiw;
+ if (size != 0)
+ p_wiw = size;
+
+ } else {
+ i = p_wh;
+ if (size != 0)
+ p_wh = size;
+ }
+
+ /*
+ * make the new window the current window
+ */
+ win_enter(wp, FALSE);
+ if (flags & WSP_VERT)
+ p_wiw = i;
+ else
+ p_wh = i;
+
+ return OK;
+}
+
+
+/*
+ * Initialize window "newp" from window "oldp".
+ * Used when splitting a window and when creating a new tab page.
+ * 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)
+{
+ int i;
+
+ newp->w_buffer = oldp->w_buffer;
+ newp->w_s = &(oldp->w_buffer->b_s);
+ oldp->w_buffer->b_nwindows++;
+ newp->w_cursor = oldp->w_cursor;
+ newp->w_valid = 0;
+ newp->w_curswant = oldp->w_curswant;
+ newp->w_set_curswant = oldp->w_set_curswant;
+ newp->w_topline = oldp->w_topline;
+ newp->w_topfill = oldp->w_topfill;
+ newp->w_leftcol = oldp->w_leftcol;
+ newp->w_pcmark = oldp->w_pcmark;
+ newp->w_prev_pcmark = oldp->w_prev_pcmark;
+ newp->w_alt_fnum = oldp->w_alt_fnum;
+ newp->w_wrow = oldp->w_wrow;
+ newp->w_fraction = oldp->w_fraction;
+ newp->w_prev_fraction_row = oldp->w_prev_fraction_row;
+ copy_jumplist(oldp, newp);
+ if (flags & WSP_NEWLOC) {
+ /* Don't copy the location list. */
+ newp->w_llist = NULL;
+ newp->w_llist_ref = NULL;
+ } else
+ copy_loclist(oldp, newp);
+ newp->w_localdir = (oldp->w_localdir == NULL)
+ ? NULL : vim_strsave(oldp->w_localdir);
+
+ /* copy tagstack and folds */
+ for (i = 0; i < oldp->w_tagstacklen; i++) {
+ newp->w_tagstack[i] = oldp->w_tagstack[i];
+ if (newp->w_tagstack[i].tagname != NULL)
+ newp->w_tagstack[i].tagname =
+ vim_strsave(newp->w_tagstack[i].tagname);
+ }
+ newp->w_tagstackidx = oldp->w_tagstackidx;
+ newp->w_tagstacklen = oldp->w_tagstacklen;
+ copyFoldingState(oldp, newp);
+
+ win_init_some(newp, oldp);
+
+ check_colorcolumn(newp);
+}
+
+/*
+ * Initialize window "newp" from window"old".
+ * Only the essential things are copied.
+ */
+static void win_init_some(win_T *newp, win_T *oldp)
+{
+ /* Use the same argument list. */
+ newp->w_alist = oldp->w_alist;
+ ++newp->w_alist->al_refcount;
+ newp->w_arg_idx = oldp->w_arg_idx;
+
+ /* copy options from existing window */
+ win_copy_options(oldp, newp);
+}
+
+
+/*
+ * Check if "win" is a pointer to an existing window.
+ */
+int win_valid(win_T *win)
+{
+ win_T *wp;
+
+ if (win == NULL)
+ return FALSE;
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ if (wp == win)
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Return the number of windows.
+ */
+int win_count(void)
+{
+ win_T *wp;
+ int count = 0;
+
+ for (wp = firstwin; wp != NULL; wp = wp->w_next)
+ ++count;
+ return count;
+}
+
+/*
+ * Make "count" windows on the screen.
+ * Return actual number of windows on the screen.
+ * Must be called when there is just one window, filling the whole screen
+ * (excluding the command line).
+ */
+int
+make_windows (
+ int count,
+ int vertical /* split windows vertically if TRUE */
+)
+{
+ int maxcount;
+ int todo;
+
+ if (vertical) {
+ /* Each windows needs at least 'winminwidth' lines and a separator
+ * column. */
+ maxcount = (curwin->w_width + curwin->w_vsep_width
+ - (p_wiw - p_wmw)) / (p_wmw + 1);
+ } else {
+ /* Each window needs at least 'winminheight' lines and a status line. */
+ maxcount = (curwin->w_height + curwin->w_status_height
+ - (p_wh - p_wmh)) / (p_wmh + STATUS_HEIGHT);
+ }
+
+ if (maxcount < 2)
+ maxcount = 2;
+ if (count > maxcount)
+ count = maxcount;
+
+ /*
+ * add status line now, otherwise first window will be too big
+ */
+ if (count > 1)
+ last_status(TRUE);
+
+ /*
+ * Don't execute autocommands while creating the windows. Must do that
+ * when putting the buffers in the windows.
+ */
+ block_autocmds();
+
+ /* todo is number of windows left to create */
+ for (todo = count - 1; todo > 0; --todo)
+ if (vertical) {
+ if (win_split(curwin->w_width - (curwin->w_width - todo)
+ / (todo + 1) - 1, WSP_VERT | WSP_ABOVE) == FAIL)
+ break;
+ } else {
+ if (win_split(curwin->w_height - (curwin->w_height - todo
+ * STATUS_HEIGHT) / (todo + 1)
+ - STATUS_HEIGHT, WSP_ABOVE) == FAIL)
+ break;
+ }
+
+ unblock_autocmds();
+
+ /* return actual number of windows */
+ return count - todo;
+}
+
+/*
+ * Exchange current and next window
+ */
+static void win_exchange(long Prenum)
+{
+ frame_T *frp;
+ frame_T *frp2;
+ win_T *wp;
+ win_T *wp2;
+ int temp;
+
+ if (lastwin == firstwin) { /* just one window */
+ beep_flush();
+ return;
+ }
+
+
+ /*
+ * find window to exchange with
+ */
+ if (Prenum) {
+ frp = curwin->w_frame->fr_parent->fr_child;
+ while (frp != NULL && --Prenum > 0)
+ frp = frp->fr_next;
+ } else if (curwin->w_frame->fr_next != NULL) /* Swap with next */
+ frp = curwin->w_frame->fr_next;
+ else /* Swap last window in row/col with previous */
+ frp = curwin-&g