changeset 18049:a9f1656f13c9 v8.1.2020

patch 8.1.2020: it is not easy to change the window layout Commit: https://github.com/vim/vim/commit/d20dcb3d011da6111153109f6e46fbd5c7fe9fb6 Author: Bram Moolenaar <Bram@vim.org> Date: Tue Sep 10 21:22:58 2019 +0200 patch 8.1.2020: it is not easy to change the window layout Problem: It is not easy to change the window layout. Solution: Add win_splitmove(). (Andy Massimino, closes https://github.com/vim/vim/issues/4561)
author Bram Moolenaar <Bram@vim.org>
date Tue, 10 Sep 2019 21:30:05 +0200
parents ceafe0986ad7
children dd9de94583a5
files runtime/doc/eval.txt src/evalfunc.c src/evalwindow.c src/proto/evalwindow.pro src/testdir/test_window_cmd.vim src/version.c
diffstat 6 files changed, 147 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -2875,6 +2875,8 @@ win_gotoid({expr})		Number	go to window 
 win_id2tabwin({expr})		List	get tab and window nr from window ID
 win_id2win({expr})		Number	get window nr from window ID
 win_screenpos({nr})		List	get screen position of window {nr}
+win_splitmove({nr}, {target} [, {options}])
+				none	move window {nr} to split of {target}
 winbufnr({nr})			Number	buffer number of window {nr}
 wincol()			Number	window column of the cursor
 winheight({nr})			Number	height of window {nr}
@@ -10144,6 +10146,28 @@ win_screenpos({nr})					*win_screenpos()
 		Can also be used as a |method|: >
 			GetWinid()->win_screenpos()
 <
+win_splitmove({nr}, {target} [, {options}])		*win_splitmove()*
+	        Move the window {nr} to a new split of the window {target}.
+		This is similar to moving to {target}, creating a new window
+		using |:split| but having the same contents as window {nr}, and
+		then closing {nr}.
+
+		Both {nr} and {target} can be window numbers or |window-ID|s.
+
+		Returns zero for success, non-zero for failure.
+
+		{options} is a Dictionary with the following optional entries:
+		  "vertical"	When TRUE, the split is created vertically,
+				like with |:vsplit|.
+		  "rightbelow"	When TRUE, the split is made below or to the
+				right (if vertical).  When FALSE, it is done
+				above or to the left (if vertical).  When not
+				present, the values of 'splitbelow' and
+				'splitright' are used.
+
+		Can also be used as a |method|: >
+			GetWinid()->win_splitmove(target)
+<
 							*winbufnr()*
 winbufnr({nr})	The result is a Number, which is the number of the buffer
 		associated with window {nr}.  {nr} can be the window number or
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -850,6 +850,7 @@ static funcentry_T global_functions[] =
     {"win_id2tabwin",	1, 1, FEARG_1,	  f_win_id2tabwin},
     {"win_id2win",	1, 1, FEARG_1,	  f_win_id2win},
     {"win_screenpos",	1, 1, FEARG_1,	  f_win_screenpos},
+    {"win_splitmove",   2, 3, FEARG_1,    f_win_splitmove},
     {"winbufnr",	1, 1, FEARG_1,	  f_winbufnr},
     {"wincol",		0, 0, 0,	  f_wincol},
     {"winheight",	1, 1, FEARG_1,	  f_winheight},
--- a/src/evalwindow.c
+++ b/src/evalwindow.c
@@ -744,6 +744,92 @@ f_win_screenpos(typval_T *argvars, typva
 }
 
 /*
+ * Move the window wp into a new split of targetwin in a given direction
+ */
+    static void
+win_move_into_split(win_T *wp, win_T *targetwin, int size, int flags)
+{
+    int	    dir;
+    int	    height = wp->w_height;
+    win_T   *oldwin = curwin;
+
+    if (wp == targetwin)
+	return;
+
+    // Jump to the target window
+    if (curwin != targetwin)
+	win_goto(targetwin);
+
+    // Remove the old window and frame from the tree of frames
+    (void)winframe_remove(wp, &dir, NULL);
+    win_remove(wp, NULL);
+    last_status(FALSE);	    // may need to remove last status line
+    (void)win_comp_pos();   // recompute window positions
+
+    // Split a window on the desired side and put the old window there
+    (void)win_split_ins(size, flags, wp, dir);
+
+    // If splitting horizontally, try to preserve height
+    if (size == 0 && !(flags & WSP_VERT))
+    {
+	win_setheight_win(height, wp);
+	if (p_ea)
+	    win_equal(wp, TRUE, 'v');
+    }
+
+#if defined(FEAT_GUI)
+    // When 'guioptions' includes 'L' or 'R' may have to remove or add
+    // scrollbars.  Have to update them anyway.
+    gui_may_update_scrollbars();
+#endif
+
+    if (oldwin != curwin)
+	win_goto(oldwin);
+}
+
+/*
+ * "win_splitmove()" function
+ */
+    void
+f_win_splitmove(typval_T *argvars, typval_T *rettv)
+{
+    win_T   *wp;
+    win_T   *targetwin;
+    int     flags = 0, size = 0;
+
+    wp = find_win_by_nr_or_id(&argvars[0]);
+    targetwin = find_win_by_nr_or_id(&argvars[1]);
+
+    if (wp == NULL || targetwin == NULL || wp == targetwin)
+    {
+        emsg(_(e_invalwindow));
+	rettv->vval.v_number = -1;
+	return;
+    }
+
+    if (argvars[2].v_type != VAR_UNKNOWN)
+    {
+        dict_T      *d;
+        dictitem_T  *di;
+
+        if (argvars[2].v_type != VAR_DICT || argvars[2].vval.v_dict == NULL)
+        {
+            emsg(_(e_invarg));
+            return;
+        }
+
+        d = argvars[2].vval.v_dict;
+        if (dict_get_number(d, (char_u *)"vertical"))
+            flags |= WSP_VERT;
+        if ((di = dict_find(d, (char_u *)"rightbelow", -1)) != NULL)
+            flags |= tv_get_number(&di->di_tv) ? WSP_BELOW : WSP_ABOVE;
+        size = (int)dict_get_number(d, (char_u *)"size");
+    }
+
+    win_move_into_split(wp, targetwin, size, flags);
+}
+
+/*
  * "winbufnr(nr)" function
  */
     void
--- a/src/proto/evalwindow.pro
+++ b/src/proto/evalwindow.pro
@@ -19,6 +19,7 @@ void f_win_gotoid(typval_T *argvars, typ
 void f_win_id2tabwin(typval_T *argvars, typval_T *rettv);
 void f_win_id2win(typval_T *argvars, typval_T *rettv);
 void f_win_screenpos(typval_T *argvars, typval_T *rettv);
+void f_win_splitmove(typval_T *argvars, typval_T *rettv);
 void f_winbufnr(typval_T *argvars, typval_T *rettv);
 void f_wincol(typval_T *argvars, typval_T *rettv);
 void f_winheight(typval_T *argvars, typval_T *rettv);
--- a/src/testdir/test_window_cmd.vim
+++ b/src/testdir/test_window_cmd.vim
@@ -857,4 +857,37 @@ func Test_winrestview()
   bwipe!
 endfunc
 
+func Test_win_splitmove()
+  edit a
+  leftabove split b
+  leftabove vsplit c
+  leftabove split d
+  call assert_equal(0, win_splitmove(winnr(), winnr('l')))
+  call assert_equal(bufname(winbufnr(1)), 'c')
+  call assert_equal(bufname(winbufnr(2)), 'd')
+  call assert_equal(bufname(winbufnr(3)), 'b')
+  call assert_equal(bufname(winbufnr(4)), 'a')
+  call assert_equal(0, win_splitmove(winnr(), winnr('j'), {'vertical': 1}))
+  call assert_equal(0, win_splitmove(winnr(), winnr('j'), {'vertical': 1}))
+  call assert_equal(bufname(winbufnr(1)), 'c')
+  call assert_equal(bufname(winbufnr(2)), 'b')
+  call assert_equal(bufname(winbufnr(3)), 'd')
+  call assert_equal(bufname(winbufnr(4)), 'a')
+  call assert_equal(0, win_splitmove(winnr(), winnr('k'), {'vertical': 1}))
+  call assert_equal(bufname(winbufnr(1)), 'd')
+  call assert_equal(bufname(winbufnr(2)), 'c')
+  call assert_equal(bufname(winbufnr(3)), 'b')
+  call assert_equal(bufname(winbufnr(4)), 'a')
+  call assert_equal(0, win_splitmove(winnr(), winnr('j'), {'rightbelow': v:true}))
+  call assert_equal(bufname(winbufnr(1)), 'c')
+  call assert_equal(bufname(winbufnr(2)), 'b')
+  call assert_equal(bufname(winbufnr(3)), 'a')
+  call assert_equal(bufname(winbufnr(4)), 'd')
+  only | bd
+
+  call assert_fails('call win_splitmove(winnr(), 123)', 'E957:')
+  call assert_fails('call win_splitmove(123, winnr())', 'E957:')
+  call assert_fails('call win_splitmove(winnr(), winnr())', 'E957:')
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2020,
+/**/
     2019,
 /**/
     2018,