]> granicus.if.org Git - vim/commitdiff
patch 8.1.1116: cannot enforce a Vim script style v8.1.1116
authorBram Moolenaar <Bram@vim.org>
Thu, 4 Apr 2019 16:15:38 +0000 (18:15 +0200)
committerBram Moolenaar <Bram@vim.org>
Thu, 4 Apr 2019 16:15:38 +0000 (18:15 +0200)
Problem:    Cannot enforce a Vim script style.
Solution:   Add the :scriptversion command. (closes #3857)

14 files changed:
runtime/doc/eval.txt
runtime/doc/repeat.txt
src/buffer.c
src/eval.c
src/evalfunc.c
src/ex_cmdidxs.h
src/ex_cmds.h
src/ex_cmds2.c
src/main.c
src/option.c
src/proto/ex_cmds2.pro
src/structs.h
src/testdir/test_eval_stuff.vim
src/version.c

index 0e301a3b45aa07e3ea885cf8e74d4aef25702d1d..30fd05c0bf6476bf9f5ea3d384a272524c42b6a3 100644 (file)
@@ -27,10 +27,11 @@ done, the features in this document are not available.  See |+eval| and
 7.  Commands                   |expression-commands|
 8.  Exception handling         |exception-handling|
 9.  Examples                   |eval-examples|
-10. No +eval feature           |no-eval-feature|
-11. The sandbox                        |eval-sandbox|
-12. Textlock                   |textlock|
-13. Testing                    |testing|
+10. Vim script version         |vimscript-version|
+11. No +eval feature           |no-eval-feature|
+12. The sandbox                        |eval-sandbox|
+13. Textlock                   |textlock|
+14. Testing                    |testing|
 
 {Vi does not have any of these commands}
 
@@ -1037,6 +1038,7 @@ result is a new list with the two lists Concatenated.
 
 For String concatenation ".." is preferred, since "." is ambiguous, it is also
 used for |Dict| member access and floating point numbers.
+When |vimscript-version| is 2 or higher, using "." is not allowed.
 
 expr7 * expr7  Number multiplication                           *expr-star*
 expr7 / expr7  Number division                                 *expr-/*
@@ -10476,6 +10478,8 @@ vertsplit               Compiled with vertically split windows |:vsplit|.
 vim_starting           True while initial source'ing takes place. |startup|
                        *vim_starting*
 viminfo                        Compiled with viminfo support.
+vimscript-1            Compiled Vim script version 1 support
+vimscript-2            Compiled Vim script version 2 support
 virtualedit            Compiled with 'virtualedit' option. (always true)
 visual                 Compiled with Visual mode. (always true)
 visualextra            Compiled with extra Visual mode commands. (always
@@ -10966,16 +10970,19 @@ This does NOT work: >
                        When the selected range of items is partly past the
                        end of the list, items will be added.
 
-                                            *:let+=* *:let-=* *:letstar=*
-                                            *:let/=* *:let%=* *:let.=* *E734*
+                       *:let+=* *:let-=* *:letstar=*
+                       *:let/=* *:let%=* *:let.=* *:let..=* *E734* *E985*
 :let {var} += {expr1}  Like ":let {var} = {var} + {expr1}".
 :let {var} -= {expr1}  Like ":let {var} = {var} - {expr1}".
 :let {var} *= {expr1}  Like ":let {var} = {var} * {expr1}".
 :let {var} /= {expr1}  Like ":let {var} = {var} / {expr1}".
 :let {var} %= {expr1}  Like ":let {var} = {var} % {expr1}".
 :let {var} .= {expr1}  Like ":let {var} = {var} . {expr1}".
+:let {var} ..= {expr1} Like ":let {var} = {var} .. {expr1}".
                        These fail if {var} was not set yet and when the type
                        of {var} and {expr1} don't fit the operator.
+                       `.=` is not supported with Vim script version 2 and
+                       later, see |vimscript-version|.
 
 
 :let ${env-name} = {expr1}                     *:let-environment* *:let-$*
@@ -12609,7 +12616,34 @@ code can be used: >
     unlet scriptnames_output
 
 ==============================================================================
-10. No +eval feature                           *no-eval-feature*
+10. Vim script versions                *vimscript-version* *vimscript-versions*
+
+Over time many features have been added to Vim script.  This includes Ex
+commands, functions, variable types, etc.  Each individual feature can be
+checked with the |has()| and |exists()| functions.
+
+Sometimes old syntax of functionality gets in the way of making Vim better.
+When support is taken away this will break older Vim scripts.  To make this
+explicit the |:scriptversion| command can be used.  When a Vim script is not
+compatible with older versions of Vim this will give an explicit error,
+instead of failing in mysterious ways. >
+
+ :scriptversion 1
+<      This is the original Vim script, same as not using a |:scriptversion|
+       command.  Can be used to go back to old syntax for a range of lines.
+       Test for support with: >
+               has('vimscript-1')
+
+ :scriptversion 2
+<      String concatenation with "." is not supported, use ".." instead.
+       This avoids the ambiguity using "." for Dict member access and
+       floating point numbers.  Now ".5" means the number 0.5.
+       Test for support with: >
+               has('vimscript-2')
+
+
+==============================================================================
+11. No +eval feature                           *no-eval-feature*
 
 When the |+eval| feature was disabled at compile time, none of the expression
 evaluation commands are available.  To prevent this from causing Vim scripts
@@ -12640,7 +12674,7 @@ When the |+eval| feature is available the command is skipped because of the
 silently ignored, and the command is executed.
 
 ==============================================================================
-11. The sandbox                                        *eval-sandbox* *sandbox* *E48*
+12. The sandbox                                        *eval-sandbox* *sandbox* *E48*
 
 The 'foldexpr', 'formatexpr', 'includeexpr', 'indentexpr', 'statusline' and
 'foldtext' options may be evaluated in a sandbox.  This means that you are
@@ -12679,7 +12713,7 @@ Note that when in the sandbox and saving an option value and restoring it, the
 option will still be marked as it was set in the sandbox.
 
 ==============================================================================
-12. Textlock                                                   *textlock*
+13. Textlock                                                   *textlock*
 
 In a few situations it is not allowed to change the text in the buffer, jump
 to another window and some other things that might confuse or break what Vim
@@ -12695,7 +12729,7 @@ This is not allowed when the textlock is active:
        - etc.
 
 ==============================================================================
-13. Testing                                                    *testing*
+14. Testing                                                    *testing*
 
 Vim can be tested after building it, usually with "make test".
 The tests are located in the directory "src/testdir".
index eed064ac8f6f3c64d0e3798a1337986553ecbb6b..dc441d37ed688696ddcec3d3659a02ed3ee42e69 100644 (file)
@@ -1,4 +1,4 @@
-*repeat.txt*    For Vim version 8.1.  Last change: 2018 Dec 18
+*repeat.txt*    For Vim version 8.1.  Last change: 2019 Apr 04
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -325,6 +325,17 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
 <
                        {not in Vi}
 
+:scriptv[ersion] {version}             *:scriptv* *:scriptversion*
+                                                               *E999* *E984*
+                       Specify the version of Vim for the lines that follow.
+                       Does not apply to sourced scripts.
+
+                       If {version} is higher than what the current Vim
+                       version supports E999 will be given.  You either need
+                       to rewrite the script to make it work with an older
+                       Vim version, or update Vim to a newer version.  See
+                       |vimscript-version| for what changed between versions.
+
                                                *:scr* *:scriptnames*
 :scr[iptnames]         List all sourced script names, in the order they were
                        first sourced.  The number is used for the script ID
index 33a7c66159cfeb2062fded3aa3bc05acab43ec2e..b9b44886c4f7195b541a6f09b305fbd5dc83f180 100644 (file)
@@ -5493,6 +5493,7 @@ chk_modeline(
                current_sctx.sc_sid = SID_MODELINE;
                current_sctx.sc_seq = 0;
                current_sctx.sc_lnum = 0;
+               current_sctx.sc_version = 1;
 #endif
                // Make sure no risky things are executed as a side effect.
                secure = 1;
index 78e2c60adde18ab9473cee9752ca5f5a68f126e6..9e93dcf2701880ebb107791e1de63822c8f945dc 100644 (file)
@@ -1249,6 +1249,7 @@ ex_let(exarg_T *eap)
     char_u     op[2];
     char_u     *argend;
     int                first = TRUE;
+    int                concat;
 
     argend = skip_var_list(arg, &var_count, &semicolon);
     if (argend == NULL)
@@ -1256,14 +1257,19 @@ ex_let(exarg_T *eap)
     if (argend > arg && argend[-1] == '.')  // for var.='str'
        --argend;
     expr = skipwhite(argend);
-    if (*expr != '=' && !((vim_strchr((char_u *)"+-*/%.", *expr) != NULL
-                          && expr[1] == '=') || STRNCMP(expr, "..=", 3) == 0))
+    concat = expr[0] == '.'
+       && ((expr[1] == '=' && current_sctx.sc_version < 2)
+               || (expr[1] == '.' && expr[2] == '='));
+    if (*expr != '=' && !((vim_strchr((char_u *)"+-*/%", *expr) != NULL
+                                                && expr[1] == '=') || concat))
     {
        /*
         * ":let" without "=": list variables
         */
        if (*arg == '[')
            emsg(_(e_invarg));
+       else if (expr[0] == '.')
+           emsg(_("E985: .= is not supported with script version 2"));
        else if (!ends_excmd(*arg))
            /* ":let var1 var2" */
            arg = list_arg_vars(eap, arg, &first);
@@ -3817,7 +3823,7 @@ eval4(char_u **arg, typval_T *rettv, int evaluate)
  * Handle fourth level expression:
  *     +       number addition
  *     -       number subtraction
- *     .       string concatenation
+ *     .       string concatenation (if script version is 1)
  *     ..      string concatenation
  *
  * "arg" must point to the first non-white of the expression.
@@ -3838,6 +3844,7 @@ eval5(char_u **arg, typval_T *rettv, int evaluate)
     char_u     *s1, *s2;
     char_u     buf1[NUMBUFLEN], buf2[NUMBUFLEN];
     char_u     *p;
+    int                concat;
 
     /*
      * Get the first variable.
@@ -3850,8 +3857,11 @@ eval5(char_u **arg, typval_T *rettv, int evaluate)
      */
     for (;;)
     {
+       // "." is only string concatenation when scriptversion is 1
        op = **arg;
-       if (op != '+' && op != '-' && op != '.')
+       concat = op == '.'
+                       && (*(*arg + 1) == '.' || current_sctx.sc_version < 2);
+       if (op != '+' && op != '-' && !concat)
            break;
 
        if ((op != '+' || (rettv->v_type != VAR_LIST
@@ -4224,6 +4234,17 @@ eval7(
        *arg = skipwhite(*arg + 1);
     end_leader = *arg;
 
+    if (**arg == '.' && (!isdigit(*(*arg + 1))
+#ifdef FEAT_FLOAT
+           || current_sctx.sc_version < 2
+#endif
+           ))
+    {
+       semsg(_(e_invexpr2), *arg);
+       ++*arg;
+       return FAIL;
+    }
+
     switch (**arg)
     {
     /*
@@ -4239,16 +4260,23 @@ eval7(
     case '7':
     case '8':
     case '9':
+    case '.':
        {
 #ifdef FEAT_FLOAT
-               char_u *p = skipdigits(*arg + 1);
+               char_u *p;
                int    get_float = FALSE;
 
                /* We accept a float when the format matches
                 * "[0-9]\+\.[0-9]\+\([eE][+-]\?[0-9]\+\)\?".  This is very
                 * strict to avoid backwards compatibility problems.
+                * With script version 2 and later the leading digit can be
+                * omitted.
                 * Don't look for a float after the "." operator, so that
                 * ":let vers = 1.2.3" doesn't fail. */
+               if (**arg == '.')
+                   p = *arg;
+               else
+                   p = skipdigits(*arg + 1);
                if (!want_string && p[0] == '.' && vim_isdigit(p[1]))
                {
                    get_float = TRUE;
index 4a54d6dc9de74f1ff52abc27bc31543d55c42e4f..ea2ec3c9fc5663eac2d50be0033220eadbe977eb 100644 (file)
@@ -6631,10 +6631,12 @@ f_has(typval_T *argvars, typval_T *rettv)
 #ifdef FEAT_VARTABS
        "vartabs",
 #endif
+       "vertsplit",
 #ifdef FEAT_VIMINFO
        "viminfo",
 #endif
-       "vertsplit",
+       "vimscript-1",
+       "vimscript-2",
        "virtualedit",
        "visual",
        "visualextra",
index 867047f67a4bcde424b84c153d7a83fa58dc9ba2..feea5b2bae13e59c4b110b15a8049629e70c2584 100644 (file)
@@ -24,13 +24,13 @@ static const unsigned short cmdidxs1[26] =
   /* q */ 348,
   /* r */ 351,
   /* s */ 371,
-  /* t */ 438,
-  /* u */ 481,
-  /* v */ 492,
-  /* w */ 510,
-  /* x */ 524,
-  /* y */ 533,
-  /* z */ 534
+  /* t */ 439,
+  /* u */ 482,
+  /* v */ 493,
+  /* w */ 511,
+  /* x */ 525,
+  /* y */ 534,
+  /* z */ 535
 };
 
 /*
@@ -59,7 +59,7 @@ static const unsigned char cmdidxs2[26][26] =
   /* p */ {  1,  0,  3,  0,  4,  0,  0,  0,  0,  0,  0,  0,  0,  0,  7,  9,  0,  0, 16, 17, 26,  0, 27,  0, 28,  0 },
   /* q */ {  2,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* r */ {  0,  0,  0,  0,  0,  0,  0,  0, 12,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 14, 19,  0,  0,  0,  0 },
-  /* s */ {  2,  6, 15,  0, 18, 22,  0, 24, 25,  0,  0, 28, 30, 34, 38, 40,  0, 48,  0, 49,  0, 61, 62,  0, 63,  0 },
+  /* s */ {  2,  6, 15,  0, 19, 23,  0, 25, 26,  0,  0, 29, 31, 35, 39, 41,  0, 49,  0, 50,  0, 62, 63,  0, 64,  0 },
   /* t */ {  2,  0, 19,  0, 22, 24,  0, 25,  0, 26,  0, 27, 31, 34, 36, 37,  0, 38, 40,  0, 41,  0,  0,  0,  0,  0 },
   /* u */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 10,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 },
   /* v */ {  0,  0,  0,  0,  1,  0,  0,  0,  4,  0,  0,  0,  9, 12,  0,  0,  0,  0, 15,  0, 16,  0,  0,  0,  0,  0 },
@@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] =
   /* z */ {  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0 }
 };
 
-static const int command_count = 547;
+static const int command_count = 548;
index eed4ce23823ae4ecd0d95747e67f0601726762ec..c080cbef0fec5d55a3347efd6ceb5aaa0176f73f 100644 (file)
@@ -1269,6 +1269,9 @@ EX(CMD_scriptnames,       "scriptnames",  ex_scriptnames,
 EX(CMD_scriptencoding, "scriptencoding", ex_scriptencoding,
                        WORD1|TRLBAR|CMDWIN,
                        ADDR_LINES),
+EX(CMD_scriptversion,  "scriptversion", ex_scriptversion,
+                       WORD1|TRLBAR|CMDWIN,
+                       ADDR_LINES),
 EX(CMD_scscope,                "scscope",      ex_scscope,
                        EXTRA|NOTRLCOM,
                        ADDR_LINES),
index a8040a71b4bd2ee60947cd7ca0c9175d89983457..97bf14358c0521f08cc20410ed19899e2cb61919 100644 (file)
@@ -2321,7 +2321,7 @@ check_changed_any(
            else
 #endif
            /* Try auto-writing the buffer.  If this fails but the buffer no
-           * longer exists it's not changed, that's OK. */
+            * longer exists it's not changed, that's OK. */
            if (check_changed(buf, (p_awa ? CCGD_AW : 0)
                                 | CCGD_MULTWIN
                                 | CCGD_ALLBUF) && bufref_valid(&bufref))
@@ -4501,12 +4501,14 @@ do_source(
      * Also starts profiling timer for nested script. */
     save_funccal(&funccalp_entry);
 
+    save_current_sctx = current_sctx;
+    current_sctx.sc_lnum = 0;
+    current_sctx.sc_version = 1;
+
     // Check if this script was sourced before to finds its SID.
     // If it's new, generate a new SID.
     // Always use a new sequence number.
-    save_current_sctx = current_sctx;
     current_sctx.sc_seq = ++last_current_SID_seq;
-    current_sctx.sc_lnum = 0;
 # ifdef UNIX
     stat_ok = (mch_stat((char *)fname_exp, &st) >= 0);
 # endif
@@ -5077,10 +5079,9 @@ script_line_end(void)
 
 /*
  * ":scriptencoding": Set encoding conversion for a sourced script.
- * Without the multi-byte feature it's simply ignored.
  */
     void
-ex_scriptencoding(exarg_T *eap UNUSED)
+ex_scriptencoding(exarg_T *eap)
 {
     struct source_cookie       *sp;
     char_u                     *name;
@@ -5108,6 +5109,29 @@ ex_scriptencoding(exarg_T *eap UNUSED)
        vim_free(name);
 }
 
+/*
+ * ":scriptversion": Set Vim script version for a sourced script.
+ */
+    void
+ex_scriptversion(exarg_T *eap UNUSED)
+{
+    int                nr;
+
+    if (!getline_equal(eap->getline, eap->cookie, getsourceline))
+    {
+       emsg(_("E984: :scriptversion used outside of a sourced file"));
+       return;
+    }
+
+    nr = getdigits(&eap->arg);
+    if (nr == 0 || *eap->arg != NUL)
+       emsg(_(e_invarg));
+    else if (nr > 2)
+       semsg(_("E999: scriptversion not supported: %d"), nr);
+    else
+       current_sctx.sc_version = nr;
+}
+
 #if defined(FEAT_EVAL) || defined(PROTO)
 /*
  * ":finish": Mark a sourced file as finished.
index a07ce1b830159ee09e25144c7f6a791757090aa6..5edcbdf8f69a48dc012629983f5d57fa05b9e0a0 100644 (file)
@@ -3166,6 +3166,7 @@ process_env(
        current_sctx.sc_sid = SID_ENV;
        current_sctx.sc_seq = 0;
        current_sctx.sc_lnum = 0;
+       current_sctx.sc_version = 1;
 #endif
        do_cmdline_cmd(initstr);
        sourcing_name = save_sourcing_name;
index d1fdcc121eb1cd9ec91d1f51eef58d574dfba215..456439581fef5d8444676280aeaa5fdb2027fda2 100644 (file)
@@ -407,7 +407,7 @@ struct vimoption
     char_u     *def_val[2];    // default values for variable (vi and vim)
 #ifdef FEAT_EVAL
     sctx_T     script_ctx;     // script context where the option was last set
-# define SCTX_INIT , {0, 0, 0}
+# define SCTX_INIT , {0, 0, 0, 1}
 #else
 # define SCTX_INIT
 #endif
@@ -5911,6 +5911,7 @@ set_string_option_direct(
                script_ctx.sc_sid = set_sid;
                script_ctx.sc_seq = 0;
                script_ctx.sc_lnum = 0;
+               script_ctx.sc_version = 1;
            }
            set_option_sctx_idx(idx, opt_flags, script_ctx);
        }
index 01d20db6341ddb6c409c0a8c9bd72cdf0b96eb56..29bdf6a88dbf04066ae5f8db1b951283d0774cdf 100644 (file)
@@ -98,6 +98,7 @@ void script_line_start(void);
 void script_line_exec(void);
 void script_line_end(void);
 void ex_scriptencoding(exarg_T *eap);
+void ex_scriptversion(exarg_T *eap);
 void ex_finish(exarg_T *eap);
 void do_finish(exarg_T *eap, int reanimate);
 int source_finished(char_u *(*fgetline)(int, void *, int), void *cookie);
index c81960b1d7fcc457a873eeb52b38236d67b87fbc..f2f2cc923140597dc9ce66c4db1054085ae1835c 100644 (file)
@@ -79,6 +79,7 @@ typedef struct {
     scid_T     sc_sid;         // script ID
     int                sc_seq;         // sourcing sequence number
     linenr_T   sc_lnum;        // line number
+    int                sc_version;     // :scriptversion
 } sctx_T;
 
 /*
index 6d61ce9244b22f8f2ac9f00a3de5c0abe6411751..d6aeb6f93923f933548d41a607a65e23a4dcac1d 100644 (file)
@@ -123,3 +123,39 @@ func Test_string_concatenation()
   let a..=b
   call assert_equal('ab', a)
 endfunc
+
+scriptversion 2
+func Test_string_concat_scriptversion2()
+  let a = 'a'
+  let b = 'b'
+
+  call assert_fails('echo a . b', 'E15:')
+  call assert_fails('let a .= b', 'E985:')
+  call assert_fails('let vers = 1.2.3', 'E15:')
+
+  if has('float')
+    let f = .5
+    call assert_equal(0.5, f)
+  endif
+endfunc
+
+scriptversion 1
+func Test_string_concat_scriptversion1()
+  let a = 'a'
+  let b = 'b'
+
+  echo a . b
+  let a .= b
+  let vers = 1.2.3
+  call assert_equal('123', vers)
+
+  if has('float')
+    call assert_fails('let f = .5', 'E15:')
+  endif
+endfunc
+
+func Test_scriptversion()
+  call writefile(['scriptversion 9'], 'Xversionscript')
+  call assert_fails('source Xversionscript', 'E999:')
+  call delete('Xversionscript')
+endfunc
index 9f65ca624dd5b38150aefb8bbd5d4d66f7a06175..aa069d68ee1d384f16015cef55aa7e3f1a758cdd 100644 (file)
@@ -771,6 +771,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1116,
 /**/
     1115,
 /**/