changeset 28164:b0b712d48225 v8.2.4607

patch 8.2.4607: sourcing buffer lines may lead to errors for conflicts Commit: https://github.com/vim/vim/commit/35dc17634dd6da5b90bd1b0160c4ed9e394f4b87 Author: Yegappan Lakshmanan <yegappan@yahoo.com> Date: Tue Mar 22 12:13:54 2022 +0000 patch 8.2.4607: sourcing buffer lines may lead to errors for conflicts Problem: Sourcing buffer lines may lead to errors for conflicts. Solution: Add the ++clear argument. (Yegappan Lakshmanan, closes https://github.com/vim/vim/issues/9991)
author Bram Moolenaar <Bram@vim.org>
date Tue, 22 Mar 2022 13:15:08 +0100
parents 8a6e4f9e45e3
children da0e04fbb0a3
files runtime/doc/repeat.txt src/proto/vim9script.pro src/scriptfile.c src/testdir/test_source.vim src/version.c src/vim9script.c
diffstat 6 files changed, 121 insertions(+), 38 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/repeat.txt
+++ b/runtime/doc/repeat.txt
@@ -198,16 +198,35 @@ For writing a Vim script, see chapter 41
 			start with a ":".
 			Triggers the |SourcePre| autocommand.
 
-:[range]so[urce] 	Read Ex commands from the [range] of lines in the
-			current buffer.  When sourcing commands from the
-			current buffer, the same script-ID |<SID>| is used
-			even if the buffer is sourced multiple times. If a
-			buffer is sourced more than once, then the functions
-			in the buffer are redefined again.
-			Sourcing a buffer with a Vim9 script more than once
-			works like |vim9-reload|.
-			To source a script in the Vim9 context, the |:vim9cmd|
-			modifier can be used.
+:[range]so[urce] [++clear]
+			Read Ex commands from the [range] of lines in the
+			current buffer.
+
+			When sourcing commands from the current buffer, the
+			same script-ID |<SID>| is used even if the buffer is
+			sourced multiple times. If a buffer is sourced more
+			than once, then the functions in the buffer are
+			defined again.
+
+			To source a range of lines that doesn't start with the
+			|:vim9script| command in Vim9 script context, the
+			|:vim9cmd| modifier can be used.
+
+			When a range of lines in a buffer is sourced in the
+			Vim9 script context, the previously defined
+			script-local variables and functions are not cleared.
+			This works like the range started with the
+			":vim9script noclear" command.  The "++clear" argument
+			can be used to clear the script-local variables and
+			functions before sourcing the script. This works like
+			the range started with the |:vimscript| command
+			without the "noclear" argument. See |vim9-reload| for
+			more information.
+			Examples: >
+
+				:4,5source
+				:vim9cmd :'<,'>source
+				:10,18source ++clear
 
 							*:source!*
 :so[urce]! {file}	Read Vim commands from {file}.  These are commands
--- a/src/proto/vim9script.pro
+++ b/src/proto/vim9script.pro
@@ -2,6 +2,7 @@
 int in_vim9script(void);
 int in_old_script(int max_version);
 int current_script_is_vim9(void);
+void clear_vim9_scriptlocal_vars(int sid);
 void ex_vim9script(exarg_T *eap);
 int not_in_vim9(exarg_T *eap);
 int vim9_bad_comment(char_u *p);
--- a/src/scriptfile.c
+++ b/src/scriptfile.c
@@ -23,7 +23,7 @@ static garray_T		ga_loaded = {0, 0, size
 static int		last_current_SID_seq = 0;
 #endif
 
-static int do_source_ext(char_u *fname, int check_other, int is_vimrc, int *ret_sid, exarg_T *eap);
+static int do_source_ext(char_u *fname, int check_other, int is_vimrc, int *ret_sid, exarg_T *eap, int clearvars);
 
 /*
  * Initialize the execution stack.
@@ -1084,6 +1084,20 @@ ExpandPackAddDir(
     static void
 cmd_source(char_u *fname, exarg_T *eap)
 {
+    int clearvars = FALSE;
+
+    if (*fname != NUL && STRNCMP(fname, "++clear", 7) == 0)
+    {
+	// ++clear argument is supplied
+	clearvars = TRUE;
+	fname = fname + 7;
+	if (*fname != NUL)
+	{
+	    semsg(_(e_invalid_argument_str), eap->arg);
+	    return;
+	}
+    }
+
     if (*fname != NUL && eap != NULL && eap->addr_count > 0)
     {
 	// if a filename is specified to :source, then a range is not allowed
@@ -1098,7 +1112,7 @@ cmd_source(char_u *fname, exarg_T *eap)
 	    emsg(_(e_argument_required));
 	else
 	    // source ex commands from the current buffer
-	    do_source_ext(NULL, FALSE, FALSE, NULL, eap);
+	    do_source_ext(NULL, FALSE, FALSE, NULL, eap, clearvars);
     }
     else if (eap != NULL && eap->forceit)
 	// ":source!": read Normal mode commands
@@ -1292,6 +1306,10 @@ errret:
  * The 'eap' argument is used when sourcing lines from a buffer instead of a
  * file.
  *
+ * If 'clearvars' is TRUE, then for scripts which are loaded more than
+ * once, clear all the functions and variables previously defined in that
+ * script.
+ *
  * This function may be called recursively!
  *
  * Return FAIL if file could not be opened, OK otherwise.
@@ -1303,7 +1321,8 @@ do_source_ext(
     int		check_other,	    // check for .vimrc and _vimrc
     int		is_vimrc,	    // DOSO_ value
     int		*ret_sid UNUSED,
-    exarg_T	*eap)
+    exarg_T	*eap,
+    int		clearvars UNUSED)
 {
     source_cookie_T	    cookie;
     char_u		    *p;
@@ -1527,20 +1546,25 @@ do_source_ext(
 	{
 	    si->sn_state = SN_STATE_RELOAD;
 
-	    // Script-local variables remain but "const" can be set again.
-	    // In Vim9 script variables will be cleared when "vim9script" is
-	    // encountered without the "noclear" argument.
-	    ht = &SCRIPT_VARS(sid);
-	    todo = (int)ht->ht_used;
-	    for (hi = ht->ht_array; todo > 0; ++hi)
-		if (!HASHITEM_EMPTY(hi))
-		{
-		    --todo;
-		    di = HI2DI(hi);
-		    di->di_flags |= DI_FLAGS_RELOAD;
-		}
-	    // imports can be redefined once
-	    mark_imports_for_reload(sid);
+	    if (!clearvars)
+	    {
+		// Script-local variables remain but "const" can be set again.
+		// In Vim9 script variables will be cleared when "vim9script"
+		// is encountered without the "noclear" argument.
+		ht = &SCRIPT_VARS(sid);
+		todo = (int)ht->ht_used;
+		for (hi = ht->ht_array; todo > 0; ++hi)
+		    if (!HASHITEM_EMPTY(hi))
+		    {
+			--todo;
+			di = HI2DI(hi);
+			di->di_flags |= DI_FLAGS_RELOAD;
+		    }
+		// imports can be redefined once
+		mark_imports_for_reload(sid);
+	    }
+	    else
+		clear_vim9_scriptlocal_vars(sid);
 
 	    // reset version, "vim9script" may have been added or removed.
 	    si->sn_version = 1;
@@ -1731,7 +1755,7 @@ do_source(
     int		is_vimrc,	    // DOSO_ value
     int		*ret_sid UNUSED)
 {
-    return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL);
+    return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL, FALSE);
 }
 
 
--- a/src/testdir/test_source.vim
+++ b/src/testdir/test_source.vim
@@ -608,6 +608,34 @@ func Test_source_buffer_vim9()
   source
   call assert_equal('red', g:Color)
 
+  " test for ++clear argument to clear all the functions/variables
+  %d _
+  let lines =<< trim END
+     g:ScriptVarFound = exists("color")
+     g:MyFuncFound = exists('*Myfunc')
+     if g:MyFuncFound
+       finish
+     endif
+     var color = 'blue'
+     def Myfunc()
+     enddef
+  END
+  call setline(1, lines)
+  vim9cmd source
+  call assert_false(g:MyFuncFound)
+  call assert_false(g:ScriptVarFound)
+  vim9cmd source
+  call assert_true(g:MyFuncFound)
+  call assert_true(g:ScriptVarFound)
+  vim9cmd source ++clear
+  call assert_false(g:MyFuncFound)
+  call assert_false(g:ScriptVarFound)
+  vim9cmd source ++clear
+  call assert_false(g:MyFuncFound)
+  call assert_false(g:ScriptVarFound)
+  call assert_fails('vim9cmd source ++clearx', 'E475:')
+  call assert_fails('vim9cmd source ++abcde', 'E484:')
+
   %bw!
 endfunc
 
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4607,
+/**/
     4606,
 /**/
     4605,
--- a/src/vim9script.c
+++ b/src/vim9script.c
@@ -59,6 +59,24 @@ current_script_is_vim9(void)
 }
 #endif
 
+#ifdef FEAT_EVAL
+/*
+ * Clear Vim9 script-local variables and functions.
+ */
+    void
+clear_vim9_scriptlocal_vars(int sid)
+{
+    hashtab_T	*ht = &SCRIPT_VARS(sid);
+
+    hashtab_free_contents(ht);
+    hash_init(ht);
+    delete_script_functions(sid);
+
+    // old imports and script variables are no longer valid
+    free_imports_and_script_vars(sid);
+}
+#endif
+
 /*
  * ":vim9script".
  */
@@ -103,18 +121,9 @@ ex_vim9script(exarg_T *eap UNUSED)
     }
 
     if (si->sn_state == SN_STATE_RELOAD && !found_noclear)
-    {
-	hashtab_T	*ht = &SCRIPT_VARS(sid);
-
 	// Reloading a script without the "noclear" argument: clear
 	// script-local variables and functions.
-	hashtab_free_contents(ht);
-	hash_init(ht);
-	delete_script_functions(sid);
-
-	// old imports and script variables are no longer valid
-	free_imports_and_script_vars(sid);
-    }
+	clear_vim9_scriptlocal_vars(sid);
     si->sn_state = SN_STATE_HAD_COMMAND;
 
     // Store the prefix with the script, it is used to find exported functions.