# HG changeset patch # User Bram Moolenaar # Date 1647951308 -3600 # Node ID b0b712d48225feb174b49462fc17f9f83a2c545a # Parent 8a6e4f9e45e3c556f407659603c484a2766481df patch 8.2.4607: sourcing buffer lines may lead to errors for conflicts Commit: https://github.com/vim/vim/commit/35dc17634dd6da5b90bd1b0160c4ed9e394f4b87 Author: Yegappan Lakshmanan 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) diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt --- 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 || 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 || 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 diff --git a/src/proto/vim9script.pro b/src/proto/vim9script.pro --- 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); diff --git a/src/scriptfile.c b/src/scriptfile.c --- 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); } diff --git a/src/testdir/test_source.vim b/src/testdir/test_source.vim --- 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 diff --git a/src/version.c b/src/version.c --- 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, diff --git a/src/vim9script.c b/src/vim9script.c --- 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.