patch 8.2.2672: Vim9: cannot use :lockvar and :unlockvar in compiled script
Problem: Vim9: cannot use :lockvar and :unlockvar in compiled script.
Solution: Implement locking support.
diff --git a/src/errors.h b/src/errors.h
index cb0f846..e43cddb 100644
--- a/src/errors.h
+++ b/src/errors.h
@@ -391,3 +391,5 @@
INIT(= N_("E1176: Misplaced command modifier"));
EXTERN char e_for_loop_on_str_not_supported[]
INIT(= N_("E1177: For loop on %s not supported"));
+EXTERN char e_cannot_lock_unlock_local_variable[]
+ INIT(= N_("E1178: Cannot lock or unlock a local variable"));
diff --git a/src/testdir/test_vim9_cmd.vim b/src/testdir/test_vim9_cmd.vim
index bc4cfa6..3b479f3 100644
--- a/src/testdir/test_vim9_cmd.vim
+++ b/src/testdir/test_vim9_cmd.vim
@@ -1135,4 +1135,42 @@
CheckDefExecFailure(lines, 'E171:', 1)
enddef
+let s:theList = [1, 2, 3]
+
+def Test_lockvar()
+ s:theList[1] = 22
+ assert_equal([1, 22, 3], s:theList)
+ lockvar s:theList
+ assert_fails('theList[1] = 77', 'E741:')
+ unlockvar s:theList
+ s:theList[1] = 44
+ assert_equal([1, 44, 3], s:theList)
+
+ var lines =<< trim END
+ vim9script
+ var theList = [1, 2, 3]
+ def SetList()
+ theList[1] = 22
+ assert_equal([1, 22, 3], theList)
+ lockvar theList
+ theList[1] = 77
+ enddef
+ SetList()
+ END
+ CheckScriptFailure(lines, 'E1119', 4)
+
+ lines =<< trim END
+ var theList = [1, 2, 3]
+ lockvar theList
+ END
+ CheckDefFailure(lines, 'E1178', 2)
+
+ lines =<< trim END
+ var theList = [1, 2, 3]
+ unlockvar theList
+ END
+ CheckDefFailure(lines, 'E1178', 2)
+enddef
+
+
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
diff --git a/src/version.c b/src/version.c
index 1937de1..602fdb6 100644
--- a/src/version.c
+++ b/src/version.c
@@ -751,6 +751,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 2672,
+/**/
2671,
/**/
2670,
diff --git a/src/vim9compile.c b/src/vim9compile.c
index c1adb36..26d346f 100644
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -6708,22 +6708,67 @@
}
/*
+ * Callback passed to ex_unletlock().
+ */
+ static int
+compile_lock_unlock(
+ lval_T *lvp,
+ char_u *name_end,
+ exarg_T *eap,
+ int deep UNUSED,
+ void *coookie)
+{
+ cctx_T *cctx = coookie;
+ int cc = *name_end;
+ char_u *p = lvp->ll_name;
+ int ret = OK;
+ size_t len;
+ char_u *buf;
+
+ if (cctx->ctx_skip == SKIP_YES)
+ return OK;
+
+ // Cannot use :lockvar and :unlockvar on local variables.
+ if (p[1] != ':')
+ {
+ char_u *end = skip_var_one(p, FALSE);
+
+ if (lookup_local(p, end - p, NULL, cctx) == OK)
+ {
+ emsg(_(e_cannot_lock_unlock_local_variable));
+ return FAIL;
+ }
+ }
+
+ // Checking is done at runtime.
+ *name_end = NUL;
+ len = name_end - p + 20;
+ buf = alloc(len);
+ if (buf == NULL)
+ ret = FAIL;
+ else
+ {
+ vim_snprintf((char *)buf, len, "%s %s",
+ eap->cmdidx == CMD_lockvar ? "lockvar" : "unlockvar",
+ p);
+ ret = generate_EXEC(cctx, buf);
+
+ vim_free(buf);
+ *name_end = cc;
+ }
+ return ret;
+}
+
+/*
* compile "unlet var", "lock var" and "unlock var"
* "arg" points to "var".
*/
static char_u *
compile_unletlock(char_u *arg, exarg_T *eap, cctx_T *cctx)
{
- char_u *p = arg;
-
- if (eap->cmdidx != CMD_unlet)
- {
- emsg("Sorry, :lock and unlock not implemented yet");
- return NULL;
- }
-
- ex_unletlock(eap, p, 0, GLV_NO_AUTOLOAD | GLV_COMPILING,
- compile_unlet, cctx);
+ ex_unletlock(eap, arg, 0, GLV_NO_AUTOLOAD | GLV_COMPILING,
+ eap->cmdidx == CMD_unlet ? compile_unlet : compile_lock_unlock,
+ cctx);
return eap->nextcmd == NULL ? (char_u *)"" : eap->nextcmd;
}