aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorerw7 <erw7.github@gmail.com>2019-08-29 13:07:03 +0900
committererw7 <erw7.github@gmail.com>2019-09-04 13:40:05 +0900
commit9db60b06a1d9b50b3ba6beb858eb0fd2c58571c4 (patch)
treebd7d55a5af91afae726345b7fc40324625241879
parente4a47862415ee6e0c4904f9c5cc8c3453be6bf17 (diff)
downloadrneovim-9db60b06a1d9b50b3ba6beb858eb0fd2c58571c4.tar.gz
rneovim-9db60b06a1d9b50b3ba6beb858eb0fd2c58571c4.tar.bz2
rneovim-9db60b06a1d9b50b3ba6beb858eb0fd2c58571c4.zip
vim-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. https://github.com/vim/vim/commit/ded5f1bed7ff2d138b3ee0f9610d17290b62692d
-rw-r--r--runtime/doc/eval.txt10
-rw-r--r--src/nvim/buffer.c1
-rw-r--r--src/nvim/eval.c6
-rw-r--r--src/nvim/eval/typval.h1
-rw-r--r--src/nvim/ex_cmds2.c3
-rw-r--r--src/nvim/globals.h2
-rw-r--r--src/nvim/main.c2
-rw-r--r--src/nvim/option.c6
-rw-r--r--src/nvim/testdir/test_functions.vim27
9 files changed, 51 insertions, 7 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 97ca804c9a..d8c1132906 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -9238,9 +9238,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.
diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c
index f15205349b..f8e07a471f 100644
--- a/src/nvim/buffer.c
+++ b/src/nvim/buffer.c
@@ -5155,6 +5155,7 @@ chk_modeline(
const int secure_save = secure;
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_MODELINE;
+ current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
// Make sure no risky things are executed as a side effect.
secure = 1;
diff --git a/src/nvim/eval.c b/src/nvim/eval.c
index 8fa4e23df6..6f7d5ed6e4 100644
--- a/src/nvim/eval.c
+++ b/src/nvim/eval.c
@@ -21504,7 +21504,11 @@ void 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;
}
diff --git a/src/nvim/eval/typval.h b/src/nvim/eval/typval.h
index c40585443e..823367560a 100644
--- a/src/nvim/eval/typval.h
+++ b/src/nvim/eval/typval.h
@@ -256,6 +256,7 @@ typedef int scid_T;
// the line number in the script "sc_sid".
typedef struct {
scid_T sc_sid; // script ID
+ int sc_seq; // sourcing sequence number
linenr_T sc_lnum; // line number
} sctx_T;
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c
index 96f758805b..964b884460 100644
--- a/src/nvim/ex_cmds2.c
+++ b/src/nvim/ex_cmds2.c
@@ -3034,6 +3034,7 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
char_u *firstline = NULL;
int retval = FAIL;
static scid_T last_current_SID = 0;
+ static int last_current_SID_seq = 0;
void *save_funccalp;
int save_debug_break_level = debug_break_level;
scriptitem_T *si = NULL;
@@ -3160,7 +3161,9 @@ int do_source(char_u *fname, int check_other, int is_vimrc)
// 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.
const sctx_T save_current_sctx = current_sctx;
+ current_sctx.sc_seq = ++last_current_SID_seq;
current_sctx.sc_lnum = 0;
FileID file_id;
bool file_id_ok = os_fileid((char *)fname_exp, &file_id);
diff --git a/src/nvim/globals.h b/src/nvim/globals.h
index 3eae415719..5237c621f9 100644
--- a/src/nvim/globals.h
+++ b/src/nvim/globals.h
@@ -333,7 +333,7 @@ EXTERN int garbage_collect_at_exit INIT(= false);
#define SID_API_CLIENT -8 // for API clients
// 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 });
// ID of the current channel making a client API call
EXTERN uint64_t current_channel_id INIT(= 0);
diff --git a/src/nvim/main.c b/src/nvim/main.c
index 9ea856a0c4..50e495c1e6 100644
--- a/src/nvim/main.c
+++ b/src/nvim/main.c
@@ -1665,6 +1665,7 @@ static void exe_commands(mparm_T *parmp)
curwin->w_cursor.lnum = 0;
sourcing_name = (char_u *)"command line";
current_sctx.sc_sid = SID_CARG;
+ current_sctx.sc_seq = 0;
for (i = 0; i < parmp->n_commands; i++) {
do_cmdline_cmd(parmp->commands[i]);
if (parmp->cmds_tofree[i])
@@ -1862,6 +1863,7 @@ static int execute_env(char *env)
sourcing_lnum = 0;
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_ENV;
+ current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
do_cmdline_cmd((char *)initstr);
sourcing_name = save_sourcing_name;
diff --git a/src/nvim/option.c b/src/nvim/option.c
index c7d2dcb602..ae75644794 100644
--- a/src/nvim/option.c
+++ b/src/nvim/option.c
@@ -200,7 +200,7 @@ typedef struct vimoption {
// local option: indirect option index
char_u *def_val[2]; // default values for variable (vi and vim)
LastSet last_set; // script in which the option was last set
-# define SCTX_INIT , { 0, 0 }
+# define SCTX_INIT , { 0, 0, 0 }
} vimoption_T;
#define VI_DEFAULT 0 // def_val[VI_DEFAULT] is Vi default value
@@ -2423,6 +2423,7 @@ set_string_option_direct(
script_ctx = current_sctx;
} 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);
@@ -3798,7 +3799,8 @@ static void set_option_sctx_idx(int opt_idx, int opt_flags, sctx_T script_ctx)
int both = (opt_flags & (OPT_LOCAL | OPT_GLOBAL)) == 0;
int indir = (int)options[opt_idx].indir;
const LastSet last_set = { .script_ctx =
- { script_ctx.sc_sid, script_ctx.sc_lnum + sourcing_lnum },
+ { script_ctx.sc_sid, script_ctx.sc_seq,
+ script_ctx.sc_lnum + sourcing_lnum },
current_channel_id };
// Remember where the option was set. For local options need to do that
diff --git a/src/nvim/testdir/test_functions.vim b/src/nvim/testdir/test_functions.vim
index 6c3d944ad5..a36c51f56f 100644
--- a/src/nvim/testdir/test_functions.vim
+++ b/src/nvim/testdir/test_functions.vim
@@ -1067,6 +1067,33 @@ func Test_func_range_with_edit()
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
+
sandbox function Fsandbox()
normal ix
endfunc