changeset 15008:67e3103d6e18 v8.1.0515

patch 8.1.0515: reloading a script gives errors for existing functions commit https://github.com/vim/vim/commit/ded5f1bed7ff2d138b3ee0f9610d17290b62692d Author: Bram Moolenaar <Bram@vim.org> Date: Sat Nov 10 17:33:29 2018 +0100 patch 8.1.0515: reloading a script gives errors for existing functions Problem: Reloading a script gives errors for existing functions. Solution: Allow redefining a function once when reloading a script.
author Bram Moolenaar <Bram@vim.org>
date Sat, 10 Nov 2018 17:45:06 +0100
parents d04499070024
children 29667f9e57b5
files runtime/doc/eval.txt src/buffer.c src/ex_cmds2.c src/globals.h src/main.c src/option.c src/structs.h src/testdir/test_functions.vim src/userfunc.c src/version.c
diffstat 10 files changed, 55 insertions(+), 11 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -9673,9 +9673,13 @@ See |:verbose-cmd| for more information.
 			deleted if there are no more references to it.
 								*E127* *E122*
 			When a function by this name already exists and [!] is
-			not used an error message is given.  When [!] is used,
-			an existing function is silently replaced.  Unless it
-			is currently being executed, that is an error.
+			not used an error message is given.  There is one
+			exception: When sourcing a script again, a function
+			that was previously defined in that script will be
+			silently replaced.
+			When [!] is used, an existing function is silently
+			replaced.  Unless it is currently being executed, that
+			is an error.
 			NOTE: Use ! wisely.  If used without care it can cause
 			an existing function to be replaced unexpectedly,
 			which is hard to debug.
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -5519,6 +5519,7 @@ chk_modeline(
 #ifdef FEAT_EVAL
 		save_current_sctx = current_sctx;
 		current_sctx.sc_sid = SID_MODELINE;
+		current_sctx.sc_seq = 0;
 		current_sctx.sc_lnum = 0;
 #endif
 		retval = do_set(s, OPT_MODELINE | OPT_LOCAL | flags);
--- a/src/ex_cmds2.c
+++ b/src/ex_cmds2.c
@@ -4344,6 +4344,7 @@ do_source(
 #ifdef FEAT_EVAL
     sctx_T		    save_current_sctx;
     static scid_T	    last_current_SID = 0;
+    static int		    last_current_SID_seq = 0;
     funccal_entry_T	    funccalp_entry;
     int			    save_debug_break_level = debug_break_level;
     scriptitem_T	    *si = NULL;
@@ -4508,11 +4509,11 @@ do_source(
      * Also starts profiling timer for nested script. */
     save_funccal(&funccalp_entry);
 
-    /*
-     * Check if this script was sourced before to finds its SID.
-     * If it's new, generate a new SID.
-     */
+    // Check if this script was sourced before to finds its SID.
+    // If it's new, generate a new SID.
+    // Always use a new sequence number.
     save_current_sctx = current_sctx;
+    current_sctx.sc_seq = ++last_current_SID_seq;
     current_sctx.sc_lnum = 0;
 # ifdef UNIX
     stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
--- a/src/globals.h
+++ b/src/globals.h
@@ -326,7 +326,7 @@ EXTERN int	want_garbage_collect INIT(= F
 EXTERN int	garbage_collect_at_exit INIT(= FALSE);
 
 // Script CTX being sourced or was sourced to define the current function.
-EXTERN sctx_T	current_sctx INIT(= {0 COMMA 0});
+EXTERN sctx_T	current_sctx INIT(= {0 COMMA 0 COMMA 0});
 #endif
 
 EXTERN int	did_source_packages INIT(= FALSE);
--- a/src/main.c
+++ b/src/main.c
@@ -2953,6 +2953,7 @@ exe_commands(mparm_T *parmp)
     sourcing_name = (char_u *)"command line";
 #ifdef FEAT_EVAL
     current_sctx.sc_sid = SID_CARG;
+    current_sctx.sc_seq = 0;
 #endif
     for (i = 0; i < parmp->n_commands; ++i)
     {
@@ -3183,6 +3184,7 @@ process_env(
 #ifdef FEAT_EVAL
 	save_current_sctx = current_sctx;
 	current_sctx.sc_sid = SID_ENV;
+	current_sctx.sc_seq = 0;
 	current_sctx.sc_lnum = 0;
 #endif
 	do_cmdline_cmd(initstr);
--- a/src/option.c
+++ b/src/option.c
@@ -415,7 +415,7 @@ struct vimoption
     char_u	*def_val[2];	// default values for variable (vi and vim)
 #ifdef FEAT_EVAL
     sctx_T	script_ctx;	// script context where the option was last set
-# define SCTX_INIT , {0, 0}
+# define SCTX_INIT , {0, 0, 0}
 #else
 # define SCTX_INIT
 #endif
@@ -5959,6 +5959,7 @@ set_string_option_direct(
 	    else
 	    {
 		script_ctx.sc_sid = set_sid;
+		script_ctx.sc_seq = 0;
 		script_ctx.sc_lnum = 0;
 	    }
 	    set_option_sctx_idx(idx, opt_flags, script_ctx);
--- a/src/structs.h
+++ b/src/structs.h
@@ -84,6 +84,7 @@ typedef struct VimMenu vimmenu_T;
  */
 typedef struct {
     scid_T	sc_sid;		// script ID
+    int		sc_seq;		// sourcing sequence number
     linenr_T	sc_lnum;	// line number
 } sctx_T;
 
--- a/src/testdir/test_functions.vim
+++ b/src/testdir/test_functions.vim
@@ -1138,3 +1138,30 @@ func Test_func_range_with_edit()
   call delete('Xfuncrange2')
   bwipe!
 endfunc
+
+func Test_func_exists_on_reload()
+  call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists')
+  call assert_equal(0, exists('*ExistingFunction'))
+  source Xfuncexists
+  call assert_equal(1, exists('*ExistingFunction'))
+  " Redefining a function when reloading a script is OK.
+  source Xfuncexists
+  call assert_equal(1, exists('*ExistingFunction'))
+
+  " But redefining in another script is not OK.
+  call writefile(['func ExistingFunction()', 'echo "yes"', 'endfunc'], 'Xfuncexists2')
+  call assert_fails('source Xfuncexists2', 'E122:')
+
+  delfunc ExistingFunction
+  call assert_equal(0, exists('*ExistingFunction'))
+  call writefile([
+	\ 'func ExistingFunction()', 'echo "yes"', 'endfunc',
+	\ 'func ExistingFunction()', 'echo "no"', 'endfunc',
+	\ ], 'Xfuncexists')
+  call assert_fails('source Xfuncexists', 'E122:')
+  call assert_equal(1, exists('*ExistingFunction'))
+
+  call delete('Xfuncexists2')
+  call delete('Xfuncexists')
+  delfunc ExistingFunction
+endfunc
--- a/src/userfunc.c
+++ b/src/userfunc.c
@@ -2330,14 +2330,19 @@ ex_function(exarg_T *eap)
 	fp = find_func(name);
 	if (fp != NULL)
 	{
-	    if (!eap->forceit)
+	    // Function can be replaced with "function!" and when sourcing the
+	    // same script again, but only once.
+	    if (!eap->forceit
+			&& (fp->uf_script_ctx.sc_sid != current_sctx.sc_sid
+			    || fp->uf_script_ctx.sc_seq == current_sctx.sc_seq))
 	    {
 		emsg_funcname(e_funcexts, name);
 		goto erret;
 	    }
 	    if (fp->uf_calls > 0)
 	    {
-		emsg_funcname(N_("E127: Cannot redefine function %s: It is in use"),
+		emsg_funcname(
+			N_("E127: Cannot redefine function %s: It is in use"),
 									name);
 		goto erret;
 	    }
--- a/src/version.c
+++ b/src/version.c
@@ -793,6 +793,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    515,
+/**/
     514,
 /**/
     513,