patch 8.1.0864: cannot have a local value for 'scrolloff' and 'sidescrolloff'

Problem:    Cannot have a local value for 'scrolloff' and 'sidescrolloff'.
            (Gary Holloway)
Solution:   Make 'scrolloff' and 'sidescrolloff' global-local. (mostly by
            Aron Widforss, closes #3539)
diff --git a/src/edit.c b/src/edit.c
index de1666c..eac4803 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -728,7 +728,7 @@
 		(int)curwin->w_wcol < mincol - curbuf->b_p_ts
 #endif
 		    && curwin->w_wrow == W_WINROW(curwin)
-						 + curwin->w_height - 1 - p_so
+				 + curwin->w_height - 1 - get_scrolloff_value()
 		    && (curwin->w_cursor.lnum != curwin->w_topline
 #ifdef FEAT_DIFF
 			|| curwin->w_topfill > 0
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 6d03d88..a3974c1 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -3784,6 +3784,7 @@
 #endif
     int		readfile_flags = 0;
     int		did_inc_redrawing_disabled = FALSE;
+    long        *so_ptr = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so;
 
     if (eap != NULL)
 	command = eap->do_ecmd_cmd;
@@ -4389,12 +4390,12 @@
     did_inc_redrawing_disabled = FALSE;
     if (!skip_redraw)
     {
-	n = p_so;
+	n = *so_ptr;
 	if (topline == 0 && command == NULL)
-	    p_so = 999;			/* force cursor halfway the window */
+	    *so_ptr = 9999;		// force cursor halfway the window
 	update_topline();
 	curwin->w_scbind_pos = curwin->w_topline;
-	p_so = n;
+	*so_ptr = n;
 	redraw_curbuf_later(NOT_VALID);	/* redraw this buffer later */
     }
 
diff --git a/src/ex_docmd.c b/src/ex_docmd.c
index 1646a22..b90ea7b 100644
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -8923,7 +8923,7 @@
 	{
 	    if (wp->w_p_scb && wp->w_buffer)
 	    {
-		y = wp->w_buffer->b_ml.ml_line_count - p_so;
+		y = wp->w_buffer->b_ml.ml_line_count - get_scrolloff_value();
 		if (topline > y)
 		    topline = y;
 	    }
diff --git a/src/gui.c b/src/gui.c
index 3996895..d993bc6 100644
--- a/src/gui.c
+++ b/src/gui.c
@@ -4405,7 +4405,7 @@
 #endif
 	    )
     {
-	if (p_so != 0)
+	if (get_scrolloff_value() != 0)
 	{
 	    cursor_correct();		/* fix window for 'so' */
 	    update_topline();		/* avoid up/down jump */
diff --git a/src/misc2.c b/src/misc2.c
index 657e164..278cc33 100644
--- a/src/misc2.c
+++ b/src/misc2.c
@@ -643,6 +643,7 @@
     long	lastcol;
     colnr_T	s, e;
     int		retval = FALSE;
+    long        siso = get_sidescrolloff_value();
 
     changed_cline_bef_curs();
     lastcol = curwin->w_leftcol + curwin->w_width - curwin_col_off() - 1;
@@ -652,15 +653,15 @@
      * If the cursor is right or left of the screen, move it to last or first
      * character.
      */
-    if (curwin->w_virtcol > (colnr_T)(lastcol - p_siso))
+    if (curwin->w_virtcol > (colnr_T)(lastcol - siso))
     {
 	retval = TRUE;
-	coladvance((colnr_T)(lastcol - p_siso));
+	coladvance((colnr_T)(lastcol - siso));
     }
-    else if (curwin->w_virtcol < curwin->w_leftcol + p_siso)
+    else if (curwin->w_virtcol < curwin->w_leftcol + siso)
     {
 	retval = TRUE;
-	(void)coladvance((colnr_T)(curwin->w_leftcol + p_siso));
+	(void)coladvance((colnr_T)(curwin->w_leftcol + siso));
     }
 
     /*
diff --git a/src/move.c b/src/move.c
index 14b7a6e..da29b36 100644
--- a/src/move.c
+++ b/src/move.c
@@ -192,8 +192,9 @@
 #endif
     int		check_topline = FALSE;
     int		check_botline = FALSE;
+    long        *so_ptr = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so;
 #ifdef FEAT_MOUSE
-    int		save_so = p_so;
+    int		save_so = *so_ptr;
 #endif
 
     /* If there is no valid screen and when the window height is zero just use
@@ -214,7 +215,7 @@
 #ifdef FEAT_MOUSE
     /* When dragging with the mouse, don't scroll that quickly */
     if (mouse_dragging > 0)
-	p_so = mouse_dragging - 1;
+	*so_ptr = mouse_dragging - 1;
 #endif
 
     old_topline = curwin->w_topline;
@@ -268,11 +269,11 @@
 	    if (hasAnyFolding(curwin))
 	    {
 		/* Count the number of logical lines between the cursor and
-		 * topline + p_so (approximation of how much will be
+		 * topline + scrolloff (approximation of how much will be
 		 * scrolled). */
 		n = 0;
 		for (lnum = curwin->w_cursor.lnum;
-				      lnum < curwin->w_topline + p_so; ++lnum)
+				      lnum < curwin->w_topline + *so_ptr; ++lnum)
 		{
 		    ++n;
 		    /* stop at end of file or when we know we are far off */
@@ -283,7 +284,7 @@
 	    }
 	    else
 #endif
-		n = curwin->w_topline + p_so - curwin->w_cursor.lnum;
+		n = curwin->w_topline + *so_ptr - curwin->w_cursor.lnum;
 
 	    /* If we weren't very close to begin with, we scroll to put the
 	     * cursor in the middle of the window.  Otherwise put the cursor
@@ -325,7 +326,7 @@
 	    if (curwin->w_cursor.lnum < curwin->w_botline)
 	    {
 	      if (((long)curwin->w_cursor.lnum
-					     >= (long)curwin->w_botline - p_so
+					     >= (long)curwin->w_botline - *so_ptr
 #ifdef FEAT_FOLDING
 			|| hasAnyFolding(curwin)
 #endif
@@ -354,11 +355,11 @@
 			)
 		{
 		    n += loff.height;
-		    if (n >= p_so)
+		    if (n >= *so_ptr)
 			break;
 		    botline_forw(&loff);
 		}
-		if (n >= p_so)
+		if (n >= *so_ptr)
 		    /* sufficient context, no need to scroll */
 		    check_botline = FALSE;
 	      }
@@ -372,11 +373,11 @@
 		if (hasAnyFolding(curwin))
 		{
 		    /* Count the number of logical lines between the cursor and
-		     * botline - p_so (approximation of how much will be
+		     * botline - scrolloff (approximation of how much will be
 		     * scrolled). */
 		    line_count = 0;
 		    for (lnum = curwin->w_cursor.lnum;
-				     lnum >= curwin->w_botline - p_so; --lnum)
+				     lnum >= curwin->w_botline - *so_ptr; --lnum)
 		    {
 			++line_count;
 			/* stop at end of file or when we know we are far off */
@@ -388,7 +389,7 @@
 		else
 #endif
 		    line_count = curwin->w_cursor.lnum - curwin->w_botline
-								   + 1 + p_so;
+								   + 1 + *so_ptr;
 		if (line_count <= curwin->w_height + 1)
 		    scroll_cursor_bot(scrolljump_value(), FALSE);
 		else
@@ -421,7 +422,7 @@
     }
 
 #ifdef FEAT_MOUSE
-    p_so = save_so;
+    *so_ptr = save_so;
 #endif
 }
 
@@ -447,8 +448,9 @@
 {
     lineoff_T	loff;
     int		n;
+    long        so = get_scrolloff_value();
 
-    if (curwin->w_cursor.lnum < curwin->w_topline + p_so
+    if (curwin->w_cursor.lnum < curwin->w_topline + so
 #ifdef FEAT_FOLDING
 		    || hasAnyFolding(curwin)
 #endif
@@ -462,7 +464,7 @@
 	n = 0;
 #endif
 	/* Count the visible screen lines above the cursor line. */
-	while (n < p_so)
+	while (n < so)
 	{
 	    topline_back(&loff);
 	    /* Stop when included a line above the window. */
@@ -474,7 +476,7 @@
 		break;
 	    n += loff.height;
 	}
-	if (n < p_so)
+	if (n < so)
 	    return TRUE;
     }
     return FALSE;
@@ -946,6 +948,8 @@
     colnr_T	startcol;
     colnr_T	endcol;
     colnr_T	prev_skipcol;
+    long        so = get_scrolloff_value();
+    long        siso = get_sidescrolloff_value();
 
     /*
      * First make sure that w_topline is valid (after moving the cursor).
@@ -1028,9 +1032,9 @@
 	 * If we get closer to the edge than 'sidescrolloff', scroll a little
 	 * extra
 	 */
-	off_left = (int)startcol - (int)curwin->w_leftcol - p_siso;
+	off_left = (int)startcol - (int)curwin->w_leftcol - siso;
 	off_right = (int)endcol - (int)(curwin->w_leftcol + curwin->w_width
-								- p_siso) + 1;
+								- siso) + 1;
 	if (off_left < 0 || off_right > 0)
 	{
 	    if (off_left < 0)
@@ -1079,9 +1083,10 @@
     prev_skipcol = curwin->w_skipcol;
 
     p_lines = 0;
+
     if ((curwin->w_wrow >= curwin->w_height
 		|| ((prev_skipcol > 0
-			|| curwin->w_wrow + p_so >= curwin->w_height)
+			|| curwin->w_wrow + so >= curwin->w_height)
 		    && (p_lines =
 #ifdef FEAT_DIFF
 			plines_win_nofill
@@ -1098,25 +1103,25 @@
 	/* Cursor past end of screen.  Happens with a single line that does
 	 * not fit on screen.  Find a skipcol to show the text around the
 	 * cursor.  Avoid scrolling all the time. compute value of "extra":
-	 * 1: Less than "p_so" lines above
-	 * 2: Less than "p_so" lines below
+	 * 1: Less than 'scrolloff' lines above
+	 * 2: Less than 'scrolloff' lines below
 	 * 3: both of them */
 	extra = 0;
-	if (curwin->w_skipcol + p_so * width > curwin->w_virtcol)
+	if (curwin->w_skipcol + so * width > curwin->w_virtcol)
 	    extra = 1;
 	/* Compute last display line of the buffer line that we want at the
 	 * bottom of the window. */
 	if (p_lines == 0)
 	    p_lines = plines_win(curwin, curwin->w_cursor.lnum, FALSE);
 	--p_lines;
-	if (p_lines > curwin->w_wrow + p_so)
-	    n = curwin->w_wrow + p_so;
+	if (p_lines > curwin->w_wrow + so)
+	    n = curwin->w_wrow + so;
 	else
 	    n = p_lines;
 	if ((colnr_T)n >= curwin->w_height + curwin->w_skipcol / width)
 	    extra += 2;
 
-	if (extra == 3 || p_lines < p_so * 2)
+	if (extra == 3 || p_lines < so * 2)
 	{
 	    /* not enough room for 'scrolloff', put cursor in the middle */
 	    n = curwin->w_virtcol / width;
@@ -1132,7 +1137,7 @@
 	else if (extra == 1)
 	{
 	    /* less then 'scrolloff' lines above, decrease skipcol */
-	    extra = (curwin->w_skipcol + p_so * width - curwin->w_virtcol
+	    extra = (curwin->w_skipcol + so * width - curwin->w_virtcol
 				     + width - 1) / width;
 	    if (extra > 0)
 	    {
@@ -1464,7 +1469,7 @@
 	end_row += curwin->w_cline_height - 1 -
 	    curwin->w_virtcol / curwin->w_width;
     }
-    if (end_row < curwin->w_height - p_so)
+    if (end_row < curwin->w_height - get_scrolloff_value())
     {
 #ifdef FEAT_DIFF
 	if (can_fill)
@@ -1522,7 +1527,7 @@
 	validate_virtcol();
 	start_row -= curwin->w_virtcol / curwin->w_width;
     }
-    if (start_row >= p_so)
+    if (start_row >= get_scrolloff_value())
     {
 #ifdef FEAT_DIFF
 	if (curwin->w_topfill > 0)
@@ -1666,7 +1671,7 @@
     linenr_T	old_topfill = curwin->w_topfill;
 #endif
     linenr_T	new_topline;
-    int		off = p_so;
+    int		off = get_scrolloff_value();
 
 #ifdef FEAT_MOUSE
     if (mouse_dragging > 0)
@@ -1842,6 +1847,7 @@
     linenr_T	old_valid = curwin->w_valid;
     int		old_empty_rows = curwin->w_empty_rows;
     linenr_T	cln;		    /* Cursor Line Number */
+    long        so = get_scrolloff_value();
 
     cln = curwin->w_cursor.lnum;
     if (set_topbot)
@@ -1898,7 +1904,7 @@
      * Stop counting lines to scroll when
      * - hitting start of the file
      * - scrolled nothing or at least 'sj' lines
-     * - at least 'so' lines below the cursor
+     * - at least 'scrolloff' lines below the cursor
      * - lines between botline and cursor have been counted
      */
 #ifdef FEAT_FOLDING
@@ -1924,7 +1930,7 @@
 #ifdef FEAT_MOUSE
 			    mouse_dragging > 0 ? mouse_dragging - 1 :
 #endif
-			    p_so))
+			    so))
 		    || boff.lnum + 1 > curbuf->b_ml.ml_line_count)
 		&& loff.lnum <= curwin->w_botline
 #ifdef FEAT_DIFF
@@ -1970,7 +1976,7 @@
 #ifdef FEAT_MOUSE
 			mouse_dragging > 0 ? mouse_dragging - 1 :
 #endif
-			p_so) || scrolled < min_scroll)
+			so) || scrolled < min_scroll)
 	    {
 		extra += boff.height;
 		if (boff.lnum >= curwin->w_botline
@@ -2124,7 +2130,7 @@
 
 /*
  * Correct the cursor position so that it is in a part of the screen at least
- * 'so' lines from the top and bottom, if possible.
+ * 'scrolloff' lines from the top and bottom, if possible.
  * If not possible, put it at the same position as scroll_cursor_halfway().
  * When called topline must be valid!
  */
@@ -2138,13 +2144,14 @@
     int		above_wanted, below_wanted;
     linenr_T	cln;		    /* Cursor Line Number */
     int		max_off;
+    long        so = get_scrolloff_value();
 
     /*
      * How many lines we would like to have above/below the cursor depends on
      * whether the first/last line of the file is on screen.
      */
-    above_wanted = p_so;
-    below_wanted = p_so;
+    above_wanted = so;
+    below_wanted = so;
 #ifdef FEAT_MOUSE
     if (mouse_dragging > 0)
     {
@@ -2262,6 +2269,7 @@
     int		retval = OK;
     lineoff_T	loff;
     linenr_T	old_topline = curwin->w_topline;
+    long        so = get_scrolloff_value();
 
     if (curbuf->b_ml.ml_line_count == 1)    /* nothing to do */
     {
@@ -2279,7 +2287,7 @@
 	 * last line.
 	 */
 	if (dir == FORWARD
-		? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - p_so)
+		? ((curwin->w_topline >= curbuf->b_ml.ml_line_count - so)
 		    && curwin->w_botline > curbuf->b_ml.ml_line_count)
 		: (curwin->w_topline == 1
 #ifdef FEAT_DIFF
diff --git a/src/normal.c b/src/normal.c
index b512b55..41af966 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -2814,7 +2814,7 @@
 
     /* Set global flag that we are extending the Visual area with mouse
      * dragging; temporarily minimize 'scrolloff'. */
-    if (VIsual_active && is_drag && p_so)
+    if (VIsual_active && is_drag && get_scrolloff_value())
     {
 	/* In the very first line, allow scrolling one line */
 	if (mouse_row == 0)
@@ -4635,7 +4635,7 @@
 	scrollup(count, TRUE);
     else
 	scrolldown(count, TRUE);
-    if (p_so)
+    if (get_scrolloff_value())
     {
 	/* Adjust the cursor position for 'scrolloff'.  Mark w_topline as
 	 * valid, otherwise the screen jumps back at the end of the file. */
@@ -4692,6 +4692,7 @@
 #ifdef FEAT_SPELL
     int		undo = FALSE;
 #endif
+    long        siso = get_sidescrolloff_value();
 
     if (VIM_ISDIGIT(nchar))
     {
@@ -4874,8 +4875,8 @@
 		    else
 #endif
 		    getvcol(curwin, &curwin->w_cursor, &col, NULL, NULL);
-		    if ((long)col > p_siso)
-			col -= p_siso;
+		    if ((long)col > siso)
+			col -= siso;
 		    else
 			col = 0;
 		    if (curwin->w_leftcol != col)
@@ -4896,10 +4897,10 @@
 #endif
 		    getvcol(curwin, &curwin->w_cursor, NULL, NULL, &col);
 		    n = curwin->w_width - curwin_col_off();
-		    if ((long)col + p_siso < n)
+		    if ((long)col + siso < n)
 			col = 0;
 		    else
-			col = col + p_siso - n + 1;
+			col = col + siso - n + 1;
 		    if (curwin->w_leftcol != col)
 		    {
 			curwin->w_leftcol = col;
diff --git a/src/option.c b/src/option.c
index abbbaa5..6d2bab1 100644
--- a/src/option.c
+++ b/src/option.c
@@ -227,6 +227,8 @@
 #endif
 #define PV_SCBIND	OPT_WIN(WV_SCBIND)
 #define PV_SCROLL	OPT_WIN(WV_SCROLL)
+#define PV_SISO		OPT_BOTH(OPT_WIN(WV_SISO))
+#define PV_SO		OPT_BOTH(OPT_WIN(WV_SO))
 #ifdef FEAT_SPELL
 # define PV_SPELL	OPT_WIN(WV_SPELL)
 #endif
@@ -2333,7 +2335,7 @@
 			    (char_u *)&p_sj, PV_NONE,
 			    {(char_u *)1L, (char_u *)0L} SCTX_INIT},
     {"scrolloff",   "so",   P_NUM|P_VI_DEF|P_VIM|P_RALL,
-			    (char_u *)&p_so, PV_NONE,
+			    (char_u *)&p_so, PV_SO,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"scrollopt",   "sbo",  P_STRING|P_VI_DEF|P_ONECOMMA|P_NODUP,
 			    (char_u *)&p_sbo, PV_NONE,
@@ -2490,7 +2492,7 @@
 			    (char_u *)&p_ss, PV_NONE,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"sidescrolloff", "siso", P_NUM|P_VI_DEF|P_VIM|P_RBUF,
-			    (char_u *)&p_siso, PV_NONE,
+			    (char_u *)&p_siso, PV_SISO,
 			    {(char_u *)0L, (char_u *)0L} SCTX_INIT},
     {"signcolumn",   "scl",  P_STRING|P_ALLOCED|P_VI_DEF|P_RWIN,
 #ifdef FEAT_SIGNS
@@ -3736,11 +3738,19 @@
 		win_comp_scroll(curwin);
 	    else
 	    {
-		*(long *)varp = (long)(long_i)options[opt_idx].def_val[dvi];
+		long def_val = (long)(long_i)options[opt_idx].def_val[dvi];
+
+		if ((long *)varp == &curwin->w_p_so
+			|| (long *)varp == &curwin->w_p_siso)
+		    // 'scrolloff' and 'sidescrolloff' local values have a
+		    // different default value than the global default.
+		    *(long *)varp = -1;
+		else
+		    *(long *)varp = def_val;
 		/* May also set global value for local option. */
 		if (both)
 		    *(long *)get_varp_scope(&(options[opt_idx]), OPT_GLOBAL) =
-								*(long *)varp;
+								def_val;
 	    }
 	}
 	else	/* P_BOOL */
@@ -9382,7 +9392,7 @@
     }
     if (p_so < 0 && full_screen)
     {
-	errmsg = e_scroll;
+	errmsg = e_positive;
 	p_so = 0;
     }
     if (p_siso < 0 && full_screen)
@@ -10657,6 +10667,12 @@
 	    clear_string_option(&buf->b_p_tc);
 	    buf->b_tc_flags = 0;
 	    break;
+        case PV_SISO:
+            curwin->w_p_siso = -1;
+            break;
+        case PV_SO:
+            curwin->w_p_so = -1;
+            break;
 #ifdef FEAT_FIND_ID
 	case PV_DEF:
 	    clear_string_option(&buf->b_p_def);
@@ -10745,6 +10761,8 @@
 	    case PV_AR:   return (char_u *)&(curbuf->b_p_ar);
 	    case PV_TAGS: return (char_u *)&(curbuf->b_p_tags);
 	    case PV_TC:   return (char_u *)&(curbuf->b_p_tc);
+            case PV_SISO: return (char_u *)&(curwin->w_p_siso);
+            case PV_SO:   return (char_u *)&(curwin->w_p_so);
 #ifdef FEAT_FIND_ID
 	    case PV_DEF:  return (char_u *)&(curbuf->b_p_def);
 	    case PV_INC:  return (char_u *)&(curbuf->b_p_inc);
@@ -10803,6 +10821,10 @@
 				    ? (char_u *)&(curbuf->b_p_tc) : p->var;
 	case PV_BKC:	return *curbuf->b_p_bkc != NUL
 				    ? (char_u *)&(curbuf->b_p_bkc) : p->var;
+	case PV_SISO:	return curwin->w_p_siso >= 0
+				    ? (char_u *)&(curwin->w_p_siso) : p->var;
+	case PV_SO:	return curwin->w_p_so >= 0
+				    ? (char_u *)&(curwin->w_p_so) : p->var;
 #ifdef FEAT_FIND_ID
 	case PV_DEF:	return *curbuf->b_p_def != NUL
 				    ? (char_u *)&(curbuf->b_p_def) : p->var;
@@ -13099,6 +13121,26 @@
 }
 
 /*
+ * Return the effective 'scrolloff' value for the current window, using the
+ * global value when appropriate.
+ */
+    long
+get_scrolloff_value(void)
+{
+    return curwin->w_p_so < 0 ? p_so : curwin->w_p_so;
+}
+
+/*
+ * Return the effective 'sidescrolloff' value for the current window, using the
+ * global value when appropriate.
+ */
+    long
+get_sidescrolloff_value(void)
+{
+    return curwin->w_p_siso < 0 ? p_siso : curwin->w_p_siso;
+}
+
+/*
  * Check matchpairs option for "*initc".
  * If there is a match set "*initc" to the matching character and "*findc" to
  * the opposite character.  Set "*backwards" to the direction.
diff --git a/src/option.h b/src/option.h
index c282da3..90c0508 100644
--- a/src/option.h
+++ b/src/option.h
@@ -1152,6 +1152,8 @@
 #endif
     , WV_SCBIND
     , WV_SCROLL
+    , WV_SISO
+    , WV_SO
 #ifdef FEAT_SPELL
     , WV_SPELL
 #endif
diff --git a/src/proto/option.pro b/src/proto/option.pro
index 3da51c6..1027edf 100644
--- a/src/proto/option.pro
+++ b/src/proto/option.pro
@@ -76,6 +76,8 @@
 long get_sw_value_pos(buf_T *buf, pos_T *pos);
 long get_sw_value_col(buf_T *buf, colnr_T col);
 long get_sts_value(void);
+long get_scrolloff_value(void);
+long get_sidescrolloff_value(void);
 void find_mps_values(int *initc, int *findc, int *backwards, int switchit);
 unsigned int get_bkc_value(buf_T *buf);
 int signcolumn_on(win_T *wp);
diff --git a/src/search.c b/src/search.c
index a4b4c41..4b3f853 100644
--- a/src/search.c
+++ b/src/search.c
@@ -2601,6 +2601,8 @@
 #endif
     colnr_T	save_dollar_vcol;
     char_u	*p;
+    long        *so = curwin->w_p_so >= 0 ? &curwin->w_p_so : &p_so;
+    long        *siso = curwin->w_p_siso >= 0 ? &curwin->w_p_siso : &p_siso;
 
     /*
      * Only show match for chars in the 'matchpairs' option.
@@ -2635,8 +2637,8 @@
 	{
 	    mpos = *lpos;    /* save the pos, update_screen() may change it */
 	    save_cursor = curwin->w_cursor;
-	    save_so = p_so;
-	    save_siso = p_siso;
+	    save_so = *so;
+	    save_siso = *siso;
 	    /* Handle "$" in 'cpo': If the ')' is typed on top of the "$",
 	     * stop displaying the "$". */
 	    if (dollar_vcol >= 0 && dollar_vcol == curwin->w_virtcol)
@@ -2651,8 +2653,8 @@
 	    ui_cursor_shape();		/* may show different cursor shape */
 #endif
 	    curwin->w_cursor = mpos;	/* move to matching char */
-	    p_so = 0;			/* don't use 'scrolloff' here */
-	    p_siso = 0;			/* don't use 'sidescrolloff' here */
+	    *so = 0;			/* don't use 'scrolloff' here */
+	    *siso = 0;			/* don't use 'sidescrolloff' here */
 	    showruler(FALSE);
 	    setcursor();
 	    cursor_on();		/* make sure that the cursor is shown */
@@ -2672,8 +2674,8 @@
 	    else if (!char_avail())
 		ui_delay(p_mat * 100L, FALSE);
 	    curwin->w_cursor = save_cursor;	/* restore cursor position */
-	    p_so = save_so;
-	    p_siso = save_siso;
+	    *so = save_so;
+	    *siso = save_siso;
 #ifdef CURSOR_SHAPE
 	    State = save_state;
 	    ui_cursor_shape();		/* may show different cursor shape */
diff --git a/src/structs.h b/src/structs.h
index ddc56bc..e6cc829 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -2932,6 +2932,8 @@
     int		w_p_brishift;	    /* additional shift for breakindent */
     int		w_p_brisbr;	    /* sbr in 'briopt' */
 #endif
+    long        w_p_siso;           /* 'sidescrolloff' local value */
+    long        w_p_so;             /* 'scrolloff' local value */
 
     /* transform a pointer to a "onebuf" option into a "allbuf" option */
 #define GLOBAL_WO(p)	((char *)p + sizeof(winopt_T))
diff --git a/src/testdir/test_options.vim b/src/testdir/test_options.vim
index 740863e..83b315d 100644
--- a/src/testdir/test_options.vim
+++ b/src/testdir/test_options.vim
@@ -483,3 +483,38 @@
   bwipe
   bwipe
 endfunc
+
+func Test_local_scrolloff()
+  set so=5
+  set siso=7
+  split
+  call assert_equal(5, &so)
+  setlocal so=3
+  call assert_equal(3, &so)
+  wincmd w
+  call assert_equal(5, &so)
+  wincmd w
+  setlocal so<
+  call assert_equal(5, &so)
+  setlocal so=0
+  call assert_equal(0, &so)
+  setlocal so=-1
+  call assert_equal(5, &so)
+
+  call assert_equal(7, &siso)
+  setlocal siso=3
+  call assert_equal(3, &siso)
+  wincmd w
+  call assert_equal(7, &siso)
+  wincmd w
+  setlocal siso<
+  call assert_equal(7, &siso)
+  setlocal siso=0
+  call assert_equal(0, &siso)
+  setlocal siso=-1
+  call assert_equal(7, &siso)
+
+  close
+  set so&
+  set siso&
+endfunc
diff --git a/src/version.c b/src/version.c
index 05b18ed..7a02fbd 100644
--- a/src/version.c
+++ b/src/version.c
@@ -784,6 +784,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    864,
+/**/
     863,
 /**/
     862,
diff --git a/src/window.c b/src/window.c
index d89f3a8..f78fcca 100644
--- a/src/window.c
+++ b/src/window.c
@@ -4594,6 +4594,10 @@
     new_wp->w_cursor.lnum = 1;
     new_wp->w_scbind_pos = 1;
 
+    // use global option value for global-local options
+    new_wp->w_p_so = -1;
+    new_wp->w_p_siso = -1;
+
     /* We won't calculate w_fraction until resizing the window */
     new_wp->w_fraction = 0;
     new_wp->w_prev_fraction_row = -1;
@@ -5871,7 +5875,7 @@
 
     if (wp == curwin)
     {
-	if (p_so)
+	if (get_scrolloff_value())
 	    update_topline();
 	curs_columns(FALSE);	/* validate w_wrow */
     }