changeset 20089:7fc5d62fe2a5 v8.2.0600

patch 8.2.0600: Vim9: cannot read or write w:, t: and b: variables Commit: https://github.com/vim/vim/commit/d3aac2917db38f8590648ee76eebfa178fc4c069 Author: Bram Moolenaar <Bram@vim.org> Date: Sun Apr 19 14:32:17 2020 +0200 patch 8.2.0600: Vim9: cannot read or write w:, t: and b: variables Problem: Vim9: cannot read or write w:, t: and b: variables. Solution: Implement load and store for w:, t: and b: variables. (closes #5950)
author Bram Moolenaar <Bram@vim.org>
date Sun, 19 Apr 2020 14:45:03 +0200
parents 004788f1261e
children b2225a10b777
files src/testdir/test_vim9_disassemble.vim src/testdir/test_vim9_expr.vim src/testdir/test_vim9_script.vim src/version.c src/vim9.h src/vim9compile.c src/vim9execute.c
diffstat 7 files changed, 203 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -8,6 +8,9 @@ endfunc
 
 let s:scriptvar = 4
 let g:globalvar = 'g'
+let b:buffervar = 'b'
+let w:windowvar = 'w'
+let t:tabpagevar = 't'
 
 def s:ScriptFuncLoad(arg: string)
   let local = 1
@@ -17,6 +20,9 @@ def s:ScriptFuncLoad(arg: string)
   echo v:version
   echo s:scriptvar
   echo g:globalvar
+  echo b:buffervar
+  echo w:windowvar
+  echo t:tabpagevar
   echo &tabstop
   echo $ENVVAR
   echo @z
@@ -39,6 +45,9 @@ def Test_disassemble_load()
         ' LOADV v:version.*' ..
         ' LOADS s:scriptvar from .*test_vim9_disassemble.vim.*' ..
         ' LOADG g:globalvar.*' ..
+        ' LOADB b:buffervar.*' ..
+        ' LOADW w:windowvar.*' ..
+        ' LOADT t:tabpagevar.*' ..
         ' LOADENV $ENVVAR.*' ..
         ' LOADREG @z.*',
         res)
@@ -79,6 +88,9 @@ def s:ScriptFuncStore()
   v:char = 'abc'
   s:scriptvar = 'sv'
   g:globalvar = 'gv'
+  b:buffervar = 'bv'
+  w:windowvar = 'wv'
+  t:tabpagevar = 'tv'
   &tabstop = 8
   $ENVVAR = 'ev'
   @z = 'rv'
@@ -99,6 +111,12 @@ def Test_disassemble_store()
         ' STORES s:scriptvar in .*test_vim9_disassemble.vim.*' ..
         'g:globalvar = ''gv''.*' ..
         ' STOREG g:globalvar.*' ..
+        'b:buffervar = ''bv''.*' ..
+        ' STOREB b:buffervar.*' ..
+        'w:windowvar = ''wv''.*' ..
+        ' STOREW w:windowvar.*' ..
+        't:tabpagevar = ''tv''.*' ..
+        ' STORET t:tabpagevar.*' ..
         '&tabstop = 8.*' ..
         ' STOREOPT &tabstop.*' ..
         '$ENVVAR = ''ev''.*' ..
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -931,11 +931,6 @@ func Test_expr7_fails()
   call CheckDefFailure("echo l:somevar", 'E1075:')
   call CheckDefFailure("echo x:somevar", 'E1075:')
 
-  " TODO
-  call CheckDefFailure("echo b:somevar", 'not supported yet')
-  call CheckDefFailure("echo w:somevar", 'not supported yet')
-  call CheckDefFailure("echo t:somevar", 'not supported yet')
-
   call CheckDefExecFailure("let x = +g:astring", 'E1030:')
   call CheckDefExecFailure("let x = +g:ablob", 'E974:')
   call CheckDefExecFailure("let x = +g:alist", 'E745:')
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -135,6 +135,38 @@ def Test_assignment()
   call CheckDefFailure(['v:errmsg += 123'], 'E1013:')
 enddef
 
+def Test_assignment_local()
+  " Test in a separated file in order not to the current buffer/window/tab is
+  " changed.
+  let script_lines: list<string> =<< trim END
+    let b:existing = 'yes'
+    let w:existing = 'yes'
+    let t:existing = 'yes'
+
+    def Test_assignment_local_internal()
+      b:newvar = 'new'
+      assert_equal('new', b:newvar)
+      assert_equal('yes', b:existing)
+      b:existing = 'no'
+      assert_equal('no', b:existing)
+
+      w:newvar = 'new'
+      assert_equal('new', w:newvar)
+      assert_equal('yes', w:existing)
+      w:existing = 'no'
+      assert_equal('no', w:existing)
+
+      t:newvar = 'new'
+      assert_equal('new', t:newvar)
+      assert_equal('yes', t:existing)
+      t:existing = 'no'
+      assert_equal('no', t:existing)
+    enddef
+    call Test_assignment_local_internal()
+  END
+  call CheckScriptSuccess(script_lines)
+enddef
+
 def Test_assignment_default()
 
   # Test default values.
@@ -201,11 +233,17 @@ func Test_assignment_failure()
   call CheckDefFailure(['let @a = 5'], 'E1066:')
 
   call CheckDefFailure(['let g:var = 5'], 'E1016:')
+  call CheckDefFailure(['let w:var = 5'], 'E1079:')
+  call CheckDefFailure(['let b:var = 5'], 'E1078:')
+  call CheckDefFailure(['let t:var = 5'], 'E1080:')
 
   call CheckDefFailure(['let anr = 4', 'anr ..= "text"'], 'E1019:')
   call CheckDefFailure(['let xnr += 4'], 'E1020:')
 
   call CheckScriptFailure(['vim9script', 'def Func()', 'let dummy = s:notfound', 'enddef'], 'E1050:')
+  " TODO: implement this error
+  "call CheckScriptFailure(['vim9script', 'let svar = 123', 'unlet svar'], 'E1050:')
+  "call CheckScriptFailure(['vim9script', 'let svar = 123', 'unlet s:svar'], 'E1050:')
 
   call CheckDefFailure(['let var: list<string> = [123]'], 'expected list<string> but got list<number>')
   call CheckDefFailure(['let var: list<number> = ["xx"]'], 'expected list<number> but got list<string>')
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    600,
+/**/
     599,
 /**/
     598,
--- a/src/vim9.h
+++ b/src/vim9.h
@@ -20,6 +20,9 @@ typedef enum {
     ISN_LOAD,	    // push local variable isn_arg.number
     ISN_LOADV,	    // push v: variable isn_arg.number
     ISN_LOADG,	    // push g: variable isn_arg.string
+    ISN_LOADB,	    // push b: variable isn_arg.string
+    ISN_LOADW,	    // push w: variable isn_arg.string
+    ISN_LOADT,	    // push t: variable isn_arg.string
     ISN_LOADS,	    // push s: variable isn_arg.loadstore
     ISN_LOADSCRIPT, // push script-local variable isn_arg.script.
     ISN_LOADOPT,    // push option isn_arg.string
@@ -29,6 +32,9 @@ typedef enum {
     ISN_STORE,	    // pop into local variable isn_arg.number
     ISN_STOREV,	    // pop into v: variable isn_arg.number
     ISN_STOREG,	    // pop into global variable isn_arg.string
+    ISN_STOREB,	    // pop into buffer-local variable isn_arg.string
+    ISN_STOREW,	    // pop into window-local variable isn_arg.string
+    ISN_STORET,	    // pop into tab-local variable isn_arg.string
     ISN_STORES,	    // pop into script variable isn_arg.loadstore
     ISN_STORESCRIPT, // pop into script variable isn_arg.script
     ISN_STOREOPT,   // pop into option isn_arg.string
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2235,18 +2235,21 @@ compile_load(char_u **arg, char_u *end_a
 	}
 	else if (**arg == 'b')
 	{
-	    semsg("Namespace b: not supported yet: %s", *arg);
-	    goto theend;
+	    // Buffer-local variables can be defined later, thus we don't check
+	    // if it exists, give error at runtime.
+	    res = generate_LOAD(cctx, ISN_LOADB, 0, name, &t_any);
 	}
 	else if (**arg == 'w')
 	{
-	    semsg("Namespace w: not supported yet: %s", *arg);
-	    goto theend;
+	    // Window-local variables can be defined later, thus we don't check
+	    // if it exists, give error at runtime.
+	    res = generate_LOAD(cctx, ISN_LOADW, 0, name, &t_any);
 	}
 	else if (**arg == 't')
 	{
-	    semsg("Namespace t: not supported yet: %s", *arg);
-	    goto theend;
+	    // Tabpage-local variables can be defined later, thus we don't
+	    // check if it exists, give error at runtime.
+	    res = generate_LOAD(cctx, ISN_LOADT, 0, name, &t_any);
 	}
 	else
 	{
@@ -3958,6 +3961,9 @@ typedef enum {
     dest_option,
     dest_env,
     dest_global,
+    dest_buffer,
+    dest_window,
+    dest_tab,
     dest_vimvar,
     dest_script,
     dest_reg,
@@ -4087,6 +4093,33 @@ compile_assignment(char_u *arg, exarg_T 
 		goto theend;
 	    }
 	}
+	else if (STRNCMP(arg, "b:", 2) == 0)
+	{
+	    dest = dest_buffer;
+	    if (is_decl)
+	    {
+		semsg(_("E1078: Cannot declare a buffer variable: %s"), name);
+		goto theend;
+	    }
+	}
+	else if (STRNCMP(arg, "w:", 2) == 0)
+	{
+	    dest = dest_window;
+	    if (is_decl)
+	    {
+		semsg(_("E1079: Cannot declare a window variable: %s"), name);
+		goto theend;
+	    }
+	}
+	else if (STRNCMP(arg, "t:", 2) == 0)
+	{
+	    dest = dest_tab;
+	    if (is_decl)
+	    {
+		semsg(_("E1080: Cannot declare a tab variable: %s"), name);
+		goto theend;
+	    }
+	}
 	else if (STRNCMP(arg, "v:", 2) == 0)
 	{
 	    typval_T	*vtv;
@@ -4245,6 +4278,15 @@ compile_assignment(char_u *arg, exarg_T 
 		case dest_global:
 		    generate_LOAD(cctx, ISN_LOADG, 0, name + 2, type);
 		    break;
+		case dest_buffer:
+		    generate_LOAD(cctx, ISN_LOADB, 0, name + 2, type);
+		    break;
+		case dest_window:
+		    generate_LOAD(cctx, ISN_LOADW, 0, name + 2, type);
+		    break;
+		case dest_tab:
+		    generate_LOAD(cctx, ISN_LOADT, 0, name + 2, type);
+		    break;
 		case dest_script:
 		    compile_load_scriptvar(cctx,
 			    name + (name[1] == ':' ? 2 : 0), NULL, NULL, TRUE);
@@ -4410,6 +4452,18 @@ compile_assignment(char_u *arg, exarg_T 
 	    // include g: with the name, easier to execute that way
 	    generate_STORE(cctx, ISN_STOREG, 0, name);
 	    break;
+	case dest_buffer:
+	    // include b: with the name, easier to execute that way
+	    generate_STORE(cctx, ISN_STOREB, 0, name);
+	    break;
+	case dest_window:
+	    // include w: with the name, easier to execute that way
+	    generate_STORE(cctx, ISN_STOREW, 0, name);
+	    break;
+	case dest_tab:
+	    // include t: with the name, easier to execute that way
+	    generate_STORE(cctx, ISN_STORET, 0, name);
+	    break;
 	case dest_env:
 	    generate_STORE(cctx, ISN_STOREENV, 0, name + 1);
 	    break;
@@ -6189,12 +6243,18 @@ delete_instr(isn_T *isn)
 	case ISN_EXEC:
 	case ISN_LOADENV:
 	case ISN_LOADG:
+	case ISN_LOADB:
+	case ISN_LOADW:
+	case ISN_LOADT:
 	case ISN_LOADOPT:
 	case ISN_MEMBER:
 	case ISN_PUSHEXC:
 	case ISN_PUSHS:
 	case ISN_STOREENV:
 	case ISN_STOREG:
+	case ISN_STOREB:
+	case ISN_STOREW:
+	case ISN_STORET:
 	case ISN_PUSHFUNC:
 	    vim_free(isn->isn_arg.string);
 	    break;
--- a/src/vim9execute.c
+++ b/src/vim9execute.c
@@ -446,6 +446,7 @@ store_var(char_u *name, typval_T *tv)
     restore_funccal();
 }
 
+
 /*
  * Execute a function by "name".
  * This can be a builtin function, user function or a funcref.
@@ -757,16 +758,42 @@ call_def_function(
 		}
 		break;
 
-	    // load g: variable
+	    // load g:/b:/w:/t: variable
 	    case ISN_LOADG:
+	    case ISN_LOADB:
+	    case ISN_LOADW:
+	    case ISN_LOADT:
 		{
-		    dictitem_T *di = find_var_in_ht(get_globvar_ht(), 0,
-						   iptr->isn_arg.string, TRUE);
+		    dictitem_T *di = NULL;
+		    hashtab_T *ht = NULL;
+		    char namespace;
+		    switch (iptr->isn_type)
+		    {
+			case ISN_LOADG:
+			    ht = get_globvar_ht();
+			    namespace = 'g';
+			    break;
+			case ISN_LOADB:
+			    ht = &curbuf->b_vars->dv_hashtab;
+			    namespace = 'b';
+			    break;
+			case ISN_LOADW:
+			    ht = &curwin->w_vars->dv_hashtab;
+			    namespace = 'w';
+			    break;
+			case ISN_LOADT:
+			    ht = &curtab->tp_vars->dv_hashtab;
+			    namespace = 't';
+			    break;
+			default:  // Cannot reach here
+			    goto failed;
+		    }
+		    di = find_var_in_ht(ht, 0, iptr->isn_arg.string, TRUE);
 
 		    if (di == NULL)
 		    {
-			semsg(_("E121: Undefined variable: g:%s"),
-							 iptr->isn_arg.string);
+			semsg(_("E121: Undefined variable: %c:%s"),
+					     namespace, iptr->isn_arg.string);
 			goto failed;
 		    }
 		    else
@@ -925,13 +952,34 @@ call_def_function(
 		    goto failed;
 		break;
 
-	    // store g: variable
+	    // store g:/b:/w:/t: variable
 	    case ISN_STOREG:
+	    case ISN_STOREB:
+	    case ISN_STOREW:
+	    case ISN_STORET:
 		{
 		    dictitem_T *di;
+		    hashtab_T *ht;
+		    switch (iptr->isn_type)
+		    {
+			case ISN_STOREG:
+			    ht = get_globvar_ht();
+			    break;
+			case ISN_STOREB:
+			    ht = &curbuf->b_vars->dv_hashtab;
+			    break;
+			case ISN_STOREW:
+			    ht = &curwin->w_vars->dv_hashtab;
+			    break;
+			case ISN_STORET:
+			    ht = &curtab->tp_vars->dv_hashtab;
+			    break;
+			default:  // Cannot reach here
+			    goto failed;
+		    }
 
 		    --ectx.ec_stack.ga_len;
-		    di = find_var_in_ht(get_globvar_ht(), 0,
+		    di = find_var_in_ht(ht, 0,
 					       iptr->isn_arg.string + 2, TRUE);
 		    if (di == NULL)
 			store_var(iptr->isn_arg.string, STACK_TV_BOT(0));
@@ -1918,6 +1966,15 @@ ex_disassemble(exarg_T *eap)
 	    case ISN_LOADG:
 		smsg("%4d LOADG g:%s", current, iptr->isn_arg.string);
 		break;
+	    case ISN_LOADB:
+		smsg("%4d LOADB b:%s", current, iptr->isn_arg.string);
+		break;
+	    case ISN_LOADW:
+		smsg("%4d LOADW w:%s", current, iptr->isn_arg.string);
+		break;
+	    case ISN_LOADT:
+		smsg("%4d LOADT t:%s", current, iptr->isn_arg.string);
+		break;
 	    case ISN_LOADOPT:
 		smsg("%4d LOADOPT %s", current, iptr->isn_arg.string);
 		break;
@@ -1943,6 +2000,15 @@ ex_disassemble(exarg_T *eap)
 	    case ISN_STOREG:
 		smsg("%4d STOREG %s", current, iptr->isn_arg.string);
 		break;
+	    case ISN_STOREB:
+		smsg("%4d STOREB %s", current, iptr->isn_arg.string);
+		break;
+	    case ISN_STOREW:
+		smsg("%4d STOREW %s", current, iptr->isn_arg.string);
+		break;
+	    case ISN_STORET:
+		smsg("%4d STORET %s", current, iptr->isn_arg.string);
+		break;
 	    case ISN_STORES:
 		{
 		    scriptitem_T *si = SCRIPT_ITEM(