patch 8.2.2289: Vim9: 'cpo' can become empty

Problem:    Vim9: 'cpo' can become empty.
Solution:   Use empty_option instead of an empty string.  Update quickfix
            buffer after restoring 'cpo'.  (closes #7608)
diff --git a/src/eval.c b/src/eval.c
index ca1c55b..87da4f3 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1940,7 +1940,7 @@
 
     // avoid 'l' flag in 'cpoptions'
     save_cpo = p_cpo;
-    p_cpo = (char_u *)"";
+    p_cpo = empty_option;
     regmatch.regprog = vim_regcomp(pat, RE_MAGIC + RE_STRING);
     if (regmatch.regprog != NULL)
     {
@@ -6200,8 +6200,14 @@
     if (p_cpo == empty_option)
 	p_cpo = save_cpo;
     else
+    {
 	// Darn, evaluating {sub} expression or {expr} changed the value.
+	// If it's still empty it was changed and restored, need to restore in
+	// the complicated way.
+	if (*p_cpo == NUL)
+	    set_option_value((char_u *)"cpo", 0L, save_cpo, 0);
 	free_string_option(save_cpo);
+    }
 
     return ret;
 }
diff --git a/src/evalbuffer.c b/src/evalbuffer.c
index 376c1fe..3fed389 100644
--- a/src/evalbuffer.c
+++ b/src/evalbuffer.c
@@ -68,7 +68,7 @@
     save_magic = p_magic;
     p_magic = TRUE;
     save_cpo = p_cpo;
-    p_cpo = (char_u *)"";
+    p_cpo = empty_option;
 
     buf = buflist_findnr(buflist_findpat(name, name + STRLEN(name),
 						    TRUE, FALSE, curtab_only));
diff --git a/src/evalfunc.c b/src/evalfunc.c
index 9b3b5be..07f1da9 100644
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -6316,7 +6316,7 @@
 
     // Make 'cpoptions' empty, the 'l' flag should not be used here.
     save_cpo = p_cpo;
-    p_cpo = (char_u *)"";
+    p_cpo = empty_option;
 
     rettv->vval.v_number = -1;
     if (type == MATCH_LIST || type == MATCH_POS)
@@ -8024,8 +8024,14 @@
     if (p_cpo == empty_option)
 	p_cpo = save_cpo;
     else
+    {
 	// Darn, evaluating the {skip} expression changed the value.
+	// If it's still empty it was changed and restored, need to restore in
+	// the complicated way.
+	if (*p_cpo == NUL)
+	    set_option_value((char_u *)"cpo", 0L, save_cpo, 0);
 	free_string_option(save_cpo);
+    }
 
     return retval;
 }
@@ -8723,7 +8729,7 @@
 
     // Make 'cpoptions' empty, the 'l' flag should not be used here.
     save_cpo = p_cpo;
-    p_cpo = (char_u *)"";
+    p_cpo = empty_option;
 
     str = tv_get_string(&argvars[0]);
     if (argvars[1].v_type != VAR_UNKNOWN)
diff --git a/src/ex_eval.c b/src/ex_eval.c
index 2064cfa..94338fc 100644
--- a/src/ex_eval.c
+++ b/src/ex_eval.c
@@ -1695,7 +1695,7 @@
 		    *end = NUL;
 		}
 		save_cpo  = p_cpo;
-		p_cpo = (char_u *)"";
+		p_cpo = empty_option;
 		// Disable error messages, it will make current_exception
 		// invalid.
 		++emsg_off;
diff --git a/src/gui_motif.c b/src/gui_motif.c
index ed2cfec..1863fa6 100644
--- a/src/gui_motif.c
+++ b/src/gui_motif.c
@@ -3525,7 +3525,7 @@
 	char_u	*save_cpo = p_cpo;
 
 	// No need to be Vi compatible here.
-	p_cpo = (char_u *)"";
+	p_cpo = empty_options;
 	u_undo(1);
 	p_cpo = save_cpo;
 	gui_update_screen();
diff --git a/src/map.c b/src/map.c
index f3dd569..917f1a2 100644
--- a/src/map.c
+++ b/src/map.c
@@ -2475,7 +2475,7 @@
     char_u	*s;
     char_u	*cpo_save = p_cpo;
 
-    p_cpo = (char_u *)"";	// Allow <> notation
+    p_cpo = empty_option;	// Allow <> notation
     s = vim_strsave(map);
     if (s != NULL)
     {
diff --git a/src/quickfix.c b/src/quickfix.c
index 697a3d4..f22d2bf 100644
--- a/src/quickfix.c
+++ b/src/quickfix.c
@@ -8099,6 +8099,7 @@
     int		new_qi = FALSE;
     char_u	*au_name =  NULL;
     char_u	*lang = NULL;
+    int		updated = FALSE;
 
     switch (eap->cmdidx)
     {
@@ -8150,14 +8151,24 @@
 	qfl->qf_ptr = qfl->qf_start;
 	qfl->qf_index = 1;
 	qf_list_changed(qfl);
-	qf_update_buffer(qi, NULL);
+	updated = TRUE;
     }
 
     if (p_cpo == empty_option)
 	p_cpo = save_cpo;
     else
-	// Darn, some plugin changed the value.
+    {
+	// Darn, some plugin changed the value.  If it's still empty it was
+	// changed and restored, need to restore in the complicated way.
+	if (*p_cpo == NUL)
+	    set_option_value((char_u *)"cpo", 0L, save_cpo, 0);
 	free_string_option(save_cpo);
+    }
+
+    if (updated)
+	// This may open a window and source scripts, do this after 'cpo' was
+	// restored.
+	qf_update_buffer(qi, NULL);
 
     if (au_name != NULL)
     {
diff --git a/src/syntax.c b/src/syntax.c
index 890158c..d4ec0d3 100644
--- a/src/syntax.c
+++ b/src/syntax.c
@@ -5670,7 +5670,7 @@
 
     // Make 'cpoptions' empty, to avoid the 'l' flag
     cpo_save = p_cpo;
-    p_cpo = (char_u *)"";
+    p_cpo = empty_option;
     ci->sp_prog = vim_regcomp(ci->sp_pattern, RE_MAGIC);
     p_cpo = cpo_save;
 
@@ -5858,7 +5858,7 @@
 
 		// Make 'cpoptions' empty, to avoid the 'l' flag
 		cpo_save = p_cpo;
-		p_cpo = (char_u *)"";
+		p_cpo = empty_option;
 		curwin->w_s->b_syn_linecont_prog =
 		       vim_regcomp(curwin->w_s->b_syn_linecont_pat, RE_MAGIC);
 		p_cpo = cpo_save;
diff --git a/src/testdir/test_quickfix.vim b/src/testdir/test_quickfix.vim
index 888c2f4..8667631 100644
--- a/src/testdir/test_quickfix.vim
+++ b/src/testdir/test_quickfix.vim
@@ -702,6 +702,26 @@
   call s:test_xhelpgrep('l')
 endfunc
 
+def Test_helpgrep_vim9_restore_cpo()
+  assert_equal('aABceFs', &cpo)
+
+  var rtp_save = &rtp
+  var dir = 'Xruntime/after'
+  &rtp ..= ',' .. dir
+  mkdir(dir .. '/ftplugin', 'p')
+  writefile(['vim9'], dir .. '/ftplugin/qf.vim')
+  filetype plugin on
+  silent helpgrep grail
+  cwindow
+  silent helpgrep grail
+
+  assert_equal('aABceFs', &cpo)
+  delete(dir, 'rf')
+  &rtp = rtp_save
+  cclose
+  helpclose
+enddef
+
 func Test_errortitle()
   augroup QfBufWinEnter
     au!
diff --git a/src/version.c b/src/version.c
index 557d165..c5017a4 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    2289,
+/**/
     2288,
 /**/
     2287,