]> granicus.if.org Git - vim/commitdiff
patch 8.2.0600: Vim9: cannot read or write w:, t: and b: variables v8.2.0600
authorBram Moolenaar <Bram@vim.org>
Sun, 19 Apr 2020 12:32:17 +0000 (14:32 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 19 Apr 2020 12:32:17 +0000 (14:32 +0200)
Problem:    Vim9: cannot read or write w:, t: and b: variables.
Solution:   Implement load and store for w:, t: and b: variables.
            (closes #5950)

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

index 985c8ca4ebe02b5f6de37c84169e179ec08195eb..13d9542622835a9de7e072a1e08278992652866c 100644 (file)
@@ -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''.*' ..
index dad4dee7d8d2b5246d0963663409ba876b66e7bc..fe3752c8cfaadd3c2be386c08a421799030c3c05 100644 (file)
@@ -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:')
index 1963c2e4cf609d13a70b34a3e4b6a61377b143be..15ac0fc9d27be63069da7369d05b9941a53e8840 100644 (file)
@@ -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>')
index ebe3b2c324fc2ed4169232b0b01381224d9f372a..d4a1f4fb0f254a2533253129f07e73d97b522e8f 100644 (file)
@@ -746,6 +746,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    600,
 /**/
     599,
 /**/
index 57cbfead331e268dbe6f83cb307789702dcd4408..d5d4fd9be4cf345bc0607500026ca1bc15e1b1b4 100644 (file)
@@ -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
index 64f7dd59b1ddc0eb7d18bf05977b390ae5960169..c99d39602ed8bd091e509c55f980df41aeb8f8cf 100644 (file)
@@ -2235,18 +2235,21 @@ compile_load(char_u **arg, char_u *end_arg, cctx_T *cctx, int error)
        }
        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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
                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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
                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 *eap, cmdidx_T cmdidx, cctx_T *cctx)
            // 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;
index dbc5d22bae4bfb91e9e6f03367fe076aa8ea9381..b0e35b6f36d3147d2f0dab4a79b71ae390006f1d 100644 (file)
@@ -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(