To: vim_dev@googlegroups.com Subject: Patch 8.2.4607 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.4607 Problem: Sourcing buffer lines may lead to errors for conflicts. Solution: Add the ++clear argument. (Yegappan Lakshmanan, closes #9991) Files: runtime/doc/repeat.txt, src/scriptfile.c, src/vim9script.c, src/proto/vim9script.pro, src/testdir/test_source.vim *** ../vim-8.2.4606/runtime/doc/repeat.txt 2022-03-21 19:45:13.200420997 +0000 --- runtime/doc/repeat.txt 2022-03-22 12:09:01.580053334 +0000 *************** *** 189,204 **** 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. *:source!* :so[urce]! {file} Read Vim commands from {file}. These are commands --- 198,232 ---- start with a ":". Triggers the |SourcePre| autocommand. ! :[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 *** ../vim-8.2.4606/src/scriptfile.c 2022-03-21 19:45:13.200420997 +0000 --- src/scriptfile.c 2022-03-22 12:09:01.584053325 +0000 *************** *** 23,29 **** 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); /* * Initialize the execution stack. --- 23,29 ---- 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, int clearvars); /* * Initialize the execution stack. *************** *** 1084,1089 **** --- 1084,1103 ---- 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,1104 **** emsg(_(e_argument_required)); else // source ex commands from the current buffer ! do_source_ext(NULL, FALSE, FALSE, NULL, eap); } else if (eap != NULL && eap->forceit) // ":source!": read Normal mode commands --- 1112,1118 ---- emsg(_(e_argument_required)); else // source ex commands from the current buffer ! do_source_ext(NULL, FALSE, FALSE, NULL, eap, clearvars); } else if (eap != NULL && eap->forceit) // ":source!": read Normal mode commands *************** *** 1292,1297 **** --- 1306,1315 ---- * 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,1309 **** int check_other, // check for .vimrc and _vimrc int is_vimrc, // DOSO_ value int *ret_sid UNUSED, ! exarg_T *eap) { source_cookie_T cookie; char_u *p; --- 1321,1328 ---- int check_other, // check for .vimrc and _vimrc int is_vimrc, // DOSO_ value int *ret_sid UNUSED, ! exarg_T *eap, ! int clearvars UNUSED) { source_cookie_T cookie; char_u *p; *************** *** 1527,1546 **** { 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); // reset version, "vim9script" may have been added or removed. si->sn_version = 1; --- 1546,1570 ---- { si->sn_state = SN_STATE_RELOAD; ! 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,1737 **** int is_vimrc, // DOSO_ value int *ret_sid UNUSED) { ! return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL); } --- 1755,1761 ---- int is_vimrc, // DOSO_ value int *ret_sid UNUSED) { ! return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL, FALSE); } *** ../vim-8.2.4606/src/vim9script.c 2022-03-19 12:56:42.533503825 +0000 --- src/vim9script.c 2022-03-22 12:09:01.584053325 +0000 *************** *** 59,64 **** --- 59,82 ---- } #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,120 **** } 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); ! } si->sn_state = SN_STATE_HAD_COMMAND; // Store the prefix with the script, it is used to find exported functions. --- 121,129 ---- } if (si->sn_state == SN_STATE_RELOAD && !found_noclear) // Reloading a script without the "noclear" argument: clear // script-local variables and functions. ! 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. *** ../vim-8.2.4606/src/proto/vim9script.pro 2022-02-08 21:17:18.881463910 +0000 --- src/proto/vim9script.pro 2022-03-22 12:09:01.584053325 +0000 *************** *** 2,7 **** --- 2,8 ---- 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); *** ../vim-8.2.4606/src/testdir/test_source.vim 2022-03-21 19:45:13.200420997 +0000 --- src/testdir/test_source.vim 2022-03-22 12:09:01.584053325 +0000 *************** *** 608,613 **** --- 608,641 ---- 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 *** ../vim-8.2.4606/src/version.c 2022-03-21 20:40:32.408367357 +0000 --- src/version.c 2022-03-22 12:12:54.155508636 +0000 *************** *** 752,753 **** --- 752,755 ---- { /* Add new patch number below this line */ + /**/ + 4607, /**/ -- LAUNCELOT: At last! A call! A cry of distress ... (he draws his sword, and turns to CONCORDE) Concorde! Brave, Concorde ... you shall not have died in vain! CONCORDE: I'm not quite dead, sir ... "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// \\\ \\\ sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///