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 #4561)
diff --git a/src/evalfunc.c b/src/evalfunc.c
index dafd3b6..438aac8 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -850,6 +850,7 @@
     {"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},
diff --git a/src/evalwindow.c b/src/evalwindow.c
index 351492c..08bdb95 100644
--- a/src/evalwindow.c
+++ b/src/evalwindow.c
@@ -744,6 +744,92 @@
 }
 
 /*
+ * 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
diff --git a/src/proto/evalwindow.pro b/src/proto/evalwindow.pro
index c201a2e..e3faa96 100644
--- a/src/proto/evalwindow.pro
+++ b/src/proto/evalwindow.pro
@@ -19,6 +19,7 @@
 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);
diff --git a/src/testdir/test_window_cmd.vim b/src/testdir/test_window_cmd.vim
index 9bdae2c..ffe8f43 100644
--- a/src/testdir/test_window_cmd.vim
+++ b/src/testdir/test_window_cmd.vim
@@ -857,4 +857,37 @@
   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
diff --git a/src/version.c b/src/version.c
index afbf0bc..44b6a51 100644
--- a/src/version.c
+++ b/src/version.c
@@ -758,6 +758,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2020,
+/**/
     2019,
 /**/
     2018,