Patch 8.2.2222

4 views
Skip to first unread message

Bram Moolenaar

unread,
Dec 26, 2020, 9:41:15 AM12/26/20
to vim...@googlegroups.com

Patch 8.2.2222
Problem: Vim9: cannot keep script variables when reloading.
Solution: Add the "noclear" argument to :vim9script.
Files: runtime/doc/vim9.txt, src/structs.h, src/scriptfile.c,
src/vim9script.c, src/ex_cmds.h, src/ex_docmd.c,
src/testdir/test_vim9_script.vim


*** ../vim-8.2.2221/runtime/doc/vim9.txt 2020-12-24 15:13:35.850860411 +0100
--- runtime/doc/vim9.txt 2020-12-26 15:27:08.281901673 +0100
***************
*** 25,31 ****

==============================================================================

! 1. What is Vim9 script? *vim9-script*

THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE

--- 25,31 ----

==============================================================================

! 1. What is Vim9 script? *Vim9-script*

THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE

***************
*** 112,118 ****
101 number

To improve readability there must be a space between a command and the #
! that starts a comment.


Vim9 functions ~
--- 112,123 ----
101 number

To improve readability there must be a space between a command and the #
! that starts a comment: >
! var = value # comment
! var = value# error!
!
! In legacy script # is also used for the alternate file name. In Vim9 script
! you need to use %% instead. Instead of ## use %%% (stands for all arguments).


Vim9 functions ~
***************
*** 193,198 ****
--- 198,242 ----
|FuncUndefined| triggered there.


+ Reloading a Vim9 script clears functions and variables by default ~
+ *vim9-reload*
+ When loading a legacy Vim script a second time nothing is removed, the
+ commands will replace existing variables and functions and create new ones.
+
+ When loading a Vim9 script a second time all existing script-local functions
+ and variables are deleted, thus you start with a clean slate. This is useful
+ if you are developing a plugin and want to try a new version. If you renamed
+ something you don't have to worry about the old name still hanging around.
+
+ If you do want to keep items, use: >
+ vimscript noclear
+
+ You want to use this in scripts that use a `finish` command to bail out at
+ some point when loaded again. E.g. when a buffer local option is set: >
+ vimscript noclear
+ setlocal completefunc=SomeFunc
+ if exists('*SomeFunc') | finish | endif
+ def g:SomeFunc()
+ ....
+
+ There is one gotcha: If a compiled function is replaced and it is called from
+ another compiled function that is not replaced, it will try to call the
+ function from before it was replaced, which no longer exists. This doesn't
+ work: >
+ vimscript noclear
+
+ def ReplaceMe()
+ echo 'function redefined every time'
+ enddef
+
+ if exists('s:loaded') | finish | endif
+ var s:loaded = true
+
+ def NotReplaced()
+ ReplaceMe() # Error if ReplaceMe() was redefined
+ enddef
+
+
Variable declarations with :var, :final and :const ~
*vim9-declaration* *:var*
Local variables need to be declared with `:var`. Local constants need to be
***************
*** 340,346 ****
number of arguments and any return type. The function can be defined later.


! Lamba using => instead of -> ~

In legacy script there can be confusion between using "->" for a method call
and for a lambda. Also, when a "{" is found the parser needs to figure out if
--- 384,390 ----
number of arguments and any return type. The function can be defined later.


! Lambda using => instead of -> ~

In legacy script there can be confusion between using "->" for a method call
and for a lambda. Also, when a "{" is found the parser needs to figure out if
***************
*** 351,357 ****
which is similar to Javascript: >
var Lambda = (arg) => expression

! No line break is allowed in the arguments of a lambda up to and includeing the
"=>". This is OK: >
filter(list, (k, v) =>
v > 0)
--- 395,401 ----
which is similar to Javascript: >
var Lambda = (arg) => expression

! No line break is allowed in the arguments of a lambda up to and including the
"=>". This is OK: >
filter(list, (k, v) =>
v > 0)
***************
*** 369,377 ****
}
NOT IMPLEMENTED YET

! Note that the "{" must be followed by white space, otherwise it is assumed to
! be the start of a dictionary: >
! var Lambda = (arg) => {key: 42}


Automatic line continuation ~
--- 413,421 ----
}
NOT IMPLEMENTED YET

! To avoid the "{" of a dictionary literal to be recognized as a statement block
! wrap it in parenthesis: >
! var Lambda = (arg) => ({key: 42})


Automatic line continuation ~
***************
*** 737,754 ****
Limitations ~

Local variables will not be visible to string evaluation. For example: >
! def EvalString(): list<string>
var list = ['aa', 'bb', 'cc', 'dd']
return range(1, 2)->map('list[v:val]')
enddef

The map argument is a string expression, which is evaluated without the
function scope. Instead, use a lambda: >
! def EvalString(): list<string>
var list = ['aa', 'bb', 'cc', 'dd']
! return range(1, 2)->map({ _, v -> list[v] })
enddef


==============================================================================

--- 781,804 ----
Limitations ~

Local variables will not be visible to string evaluation. For example: >
! def MapList(): list<string>
var list = ['aa', 'bb', 'cc', 'dd']
return range(1, 2)->map('list[v:val]')
enddef

The map argument is a string expression, which is evaluated without the
function scope. Instead, use a lambda: >
! def MapList(): list<string>
var list = ['aa', 'bb', 'cc', 'dd']
! return range(1, 2)->map(( _, v) => list[v])
enddef

+ The same is true for commands that are not compiled, such as `:global`.
+ For these the backtick expansion can be used. Example: >
+ def Replace()
+ var newText = 'blah'
+ g/pattern/s/^/`=newText`/
+ enddef

==============================================================================

*** ../vim-8.2.2221/src/structs.h 2020-12-24 21:56:37.643479575 +0100
--- src/structs.h 2020-12-26 14:18:58.239440119 +0100
***************
*** 1821,1827 ****
int sn_last_block_id; // Unique ID for each script block

int sn_version; // :scriptversion
! int sn_had_command; // TRUE if any command was executed
char_u *sn_save_cpo; // 'cpo' value when :vim9script found

# ifdef FEAT_PROFILE
--- 1821,1827 ----
int sn_last_block_id; // Unique ID for each script block

int sn_version; // :scriptversion
! int sn_state; // SN_STATE_ values
char_u *sn_save_cpo; // 'cpo' value when :vim9script found

# ifdef FEAT_PROFILE
***************
*** 1845,1850 ****
--- 1845,1854 ----
# endif
} scriptitem_T;

+ #define SN_STATE_NEW 0 // newly loaded script, nothing done
+ #define SN_STATE_RELOAD 1 // script loaded before, nothing done
+ #define SN_STATE_HAD_COMMAND 9 // a command was executed
+
// Struct passed through eval() functions.
// See EVALARG_EVALUATE for a fixed value with eval_flags set to EVAL_EVALUATE.
typedef struct {
*** ../vim-8.2.2221/src/scriptfile.c 2020-12-24 21:56:37.647479568 +0100
--- src/scriptfile.c 2020-12-26 14:52:08.044775762 +0100
***************
*** 1320,1362 ****
if (sid > 0)
{
hashtab_T *ht;
! int is_vim9 = si->sn_version == SCRIPT_VERSION_VIM9;

// loading the same script again
! si->sn_had_command = FALSE;
si->sn_version = 1;
current_sctx.sc_sid = sid;

! // In Vim9 script all script-local variables are removed when reloading
! // the same script. In legacy script they remain but "const" can be
! // set again.
ht = &SCRIPT_VARS(sid);
! if (is_vim9)
! {
! hashtab_free_contents(ht);
! hash_init(ht);
! }
! else
! {
! int todo = (int)ht->ht_used;
! hashitem_T *hi;
! dictitem_T *di;
!
! for (hi = ht->ht_array; todo > 0; ++hi)
! if (!HASHITEM_EMPTY(hi))
! {
! --todo;
! di = HI2DI(hi);
! di->di_flags |= DI_FLAGS_RELOAD;
! }
! }
!
! // old imports and script variables are no longer valid
! free_imports_and_script_vars(sid);
!
! // in Vim9 script functions are marked deleted
! if (is_vim9)
! delete_script_functions(sid);
}
else
{
--- 1320,1346 ----
if (sid > 0)
{
hashtab_T *ht;
! int todo;
! hashitem_T *hi;
! dictitem_T *di;

// loading the same script again
! si->sn_state = SN_STATE_RELOAD;
si->sn_version = 1;
current_sctx.sc_sid = sid;

! // 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;
! }
}
else
{
***************
*** 1390,1397 ****
fname_exp = vim_strsave(si->sn_name); // used for autocmd
if (ret_sid != NULL)
*ret_sid = current_sctx.sc_sid;
}
- si->sn_script_seq = current_sctx.sc_seq;

# ifdef FEAT_PROFILE
if (do_profiling == PROF_YES)
--- 1374,1383 ----
fname_exp = vim_strsave(si->sn_name); // used for autocmd
if (ret_sid != NULL)
*ret_sid = current_sctx.sc_sid;
+
+ // Used to check script variable index is still valid.
+ si->sn_script_seq = current_sctx.sc_seq;
}

# ifdef FEAT_PROFILE
if (do_profiling == PROF_YES)
*** ../vim-8.2.2221/src/vim9script.c 2020-12-25 12:37:59.487073428 +0100
--- src/vim9script.c 2020-12-26 14:53:45.184435701 +0100
***************
*** 32,37 ****
--- 32,38 ----
void
ex_vim9script(exarg_T *eap)
{
+ int sid = current_sctx.sc_sid;
scriptitem_T *si;

if (!getline_equal(eap->getline, eap->cookie, getsourceline))
***************
*** 39,53 ****
emsg(_(e_vim9script_can_only_be_used_in_script));
return;
}
! si = SCRIPT_ITEM(current_sctx.sc_sid);
! if (si->sn_had_command)
{
emsg(_(e_vim9script_must_be_first_command_in_script));
return;
}
current_sctx.sc_version = SCRIPT_VERSION_VIM9;
si->sn_version = SCRIPT_VERSION_VIM9;
- si->sn_had_command = TRUE;

if (STRCMP(p_cpo, CPO_VIM) != 0)
{
--- 40,74 ----
emsg(_(e_vim9script_can_only_be_used_in_script));
return;
}
!
! si = SCRIPT_ITEM(sid);
! if (si->sn_state == SN_STATE_HAD_COMMAND)
{
emsg(_(e_vim9script_must_be_first_command_in_script));
return;
}
+ if (!IS_WHITE_OR_NUL(*eap->arg) && STRCMP(eap->arg, "noclear") != 0)
+ {
+ semsg(_(e_invarg2), eap->arg);
+ return;
+ }
+ if (si->sn_state == SN_STATE_RELOAD && IS_WHITE_OR_NUL(*eap->arg))
+ {
+ 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;
+
current_sctx.sc_version = SCRIPT_VERSION_VIM9;
si->sn_version = SCRIPT_VERSION_VIM9;

if (STRCMP(p_cpo, CPO_VIM) != 0)
{
***************
*** 719,724 ****
--- 740,748 ----
hash_init(ht);

ga_clear(&si->sn_var_vals);
+
+ // existing commands using script variable indexes are no longer valid
+ si->sn_script_seq = current_sctx.sc_seq;
}

/*
*** ../vim-8.2.2221/src/ex_cmds.h 2020-12-21 19:59:04.573197707 +0100
--- src/ex_cmds.h 2020-12-26 14:30:06.909262005 +0100
***************
*** 1680,1686 ****
EX_RANGE|EX_BANG|EX_NEEDARG|EX_EXTRA|EX_NOTRLCOM|EX_TRLBAR|EX_XFILE|EX_LOCK_OK,
ADDR_OTHER),
EXCMD(CMD_vim9script, "vim9script", ex_vim9script,
! EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_viusage, "viusage", ex_viusage,
EX_TRLBAR,
--- 1680,1686 ----
EX_RANGE|EX_BANG|EX_NEEDARG|EX_EXTRA|EX_NOTRLCOM|EX_TRLBAR|EX_XFILE|EX_LOCK_OK,
ADDR_OTHER),
EXCMD(CMD_vim9script, "vim9script", ex_vim9script,
! EX_WORD1|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_viusage, "viusage", ex_viusage,
EX_TRLBAR,
*** ../vim-8.2.2221/src/ex_docmd.c 2020-12-25 19:25:41.742706213 +0100
--- src/ex_docmd.c 2020-12-26 14:19:21.735361343 +0100
***************
*** 2594,2600 ****
// Set flag that any command was executed, used by ex_vim9script().
if (getline_equal(ea.getline, ea.cookie, getsourceline)
&& current_sctx.sc_sid > 0)
! SCRIPT_ITEM(current_sctx.sc_sid)->sn_had_command = TRUE;

/*
* If the command just executed called do_cmdline(), any throw or ":return"
--- 2594,2600 ----
// Set flag that any command was executed, used by ex_vim9script().
if (getline_equal(ea.getline, ea.cookie, getsourceline)
&& current_sctx.sc_sid > 0)
! SCRIPT_ITEM(current_sctx.sc_sid)->sn_state = SN_STATE_HAD_COMMAND;

/*
* If the command just executed called do_cmdline(), any throw or ":return"
*** ../vim-8.2.2221/src/testdir/test_vim9_script.vim 2020-12-25 17:36:23.710969861 +0100
--- src/testdir/test_vim9_script.vim 2020-12-26 15:11:24.260878931 +0100
***************
*** 1158,1163 ****
--- 1158,1210 ----
StopVimInTerminal(buf)
enddef

+ def Test_vim9script_reload_noclear()
+ var lines =<< trim END
+ vim9script noclear
+ g:loadCount += 1
+ var s:reloaded = 'init'
+
+ def Again(): string
+ return 'again'
+ enddef
+
+ if exists('s:loaded') | finish | endif
+ var s:loaded = true
+
+ var s:notReloaded = 'yes'
+ s:reloaded = 'first'
+ def g:Values(): list<string>
+ return [s:reloaded, s:notReloaded, Once()]
+ enddef
+ def g:CallAgain(): string
+ return Again()
+ enddef
+
+ def Once(): string
+ return 'once'
+ enddef
+ END
+ writefile(lines, 'XReloaded')
+ g:loadCount = 0
+ source XReloaded
+ assert_equal(1, g:loadCount)
+ assert_equal(['first', 'yes', 'once'], g:Values())
+ assert_equal('again', g:CallAgain())
+ source XReloaded
+ assert_equal(2, g:loadCount)
+ assert_equal(['init', 'yes', 'once'], g:Values())
+ assert_fails('call g:CallAgain()', 'E933:')
+ source XReloaded
+ assert_equal(3, g:loadCount)
+ assert_equal(['init', 'yes', 'once'], g:Values())
+ assert_fails('call g:CallAgain()', 'E933:')
+
+ delete('Xreloaded')
+ delfunc g:Values
+ delfunc g:CallAgain
+ unlet g:loadCount
+ enddef
+
def Test_vim9script_reload_import()
var lines =<< trim END
vim9script
*** ../vim-8.2.2221/src/version.c 2020-12-26 12:06:50.584655332 +0100
--- src/version.c 2020-12-26 14:20:01.147229726 +0100
***************
*** 752,753 ****
--- 752,755 ----
{ /* Add new patch number below this line */
+ /**/
+ 2222,
/**/

--
"Space is big. Really big. You just won't believe how vastly hugely mind-
bogglingly big it is. I mean, you may think it's a long way down the
road to the chemist, but that's just peanuts to space."
-- Douglas Adams, "The Hitchhiker's Guide to the Galaxy"

/// Bram Moolenaar -- Br...@Moolenaar.net -- http://www.Moolenaar.net \\\
/// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\ an exciting new programming language -- http://www.Zimbu.org ///
\\\ help me help AIDS victims -- http://ICCF-Holland.org ///
Reply all
Reply to author
Forward
0 new messages