]> granicus.if.org Git - vim/commitdiff
patch 8.2.1308: Vim9: accidentally using "x" causes Vim to exit v8.2.1308
authorBram Moolenaar <Bram@vim.org>
Tue, 28 Jul 2020 18:07:27 +0000 (20:07 +0200)
committerBram Moolenaar <Bram@vim.org>
Tue, 28 Jul 2020 18:07:27 +0000 (20:07 +0200)
Problem:    Vim9: accidentally using "x" causes Vim to exit.
Solution:   Disallow using ":x" or "xit" in Vim9 script. (closes #6399)

runtime/doc/vim9.txt
src/ex_cmds.c
src/ex_docmd.c
src/proto/vim9script.pro
src/testdir/test_vim9_script.vim
src/version.c
src/vim9compile.c
src/vim9script.c

index 2ba2275274e176dc6df68195da5a3ede084d3357..7b2597dc6e445e41aeb49a311e7da1f31e70e25a 100644 (file)
@@ -71,16 +71,17 @@ comments start with #. >
 
 The reason is that a double quote can also be the start of a string. In many
 places, especially halfway an expression with a line break, it's hard to tell
-what the meaning is.  To avoid confusion only # comments are recognized.
-This is the same as in shell scripts and Python programs.
+what the meaning is, since both a string and a comment can be followed by
+arbitrary text.  To avoid confusion only # comments are recognized.  This is
+the same as in shell scripts and Python programs.
 
 In Vi # is a command to list text with numbers.  In Vim9 script you can use
 `:number` for that. >
-       101number
+       101 number
 
 To improve readability there must be a space between a command and the #
 that starts a comment.  Note that #{ is the start of a dictionary, therefore
-it cannot start a comment.
+it does not start a comment.
 
 
 Vim9 functions ~
@@ -93,7 +94,7 @@ The syntax is strict, to enforce code that is easy to read and understand.
 
 Compilation is done when the function is first called, or when the
 `:defcompile` command is encountered in the script where the function was
-defined.
+defined. `:disassemble` also compiles the function.
 
 `:def` has no options like `:function` does: "range", "abort", "dict" or
 "closure".  A `:def` function always aborts on an error, does not get a range
@@ -104,7 +105,7 @@ be used, type checking will then be done at runtime, like with legacy
 functions.
 
 Arguments are accessed by name, without "a:".  There is no "a:" dictionary or
-"a:000" list.
+"a:000" list.  Just like any other language.
 
 Variable arguments are defined as the last argument, with a name and have a
 list type, similar to Typescript.  For example, a list of numbers: >
@@ -216,29 +217,29 @@ Functions can be called without `:call`: >
 Using `:call` is still possible, but this is discouraged.
 
 A method call without `eval` is possible, so long as the start is an
-identifier or can't be an Ex command.  It does NOT work for string constants: >
-       myList->add(123)                # works
-       g:myList->add(123)              # works
-       [1, 2, 3]->Process()            # works
-       #{a: 1, b: 2}->Process()        # works
-       {'a': 1, 'b': 2}->Process()     # works
-       "foobar"->Process()             # does NOT work
-       ("foobar")->Process()           # works
-       'foobar'->Process()             # does NOT work
-       ('foobar')->Process()           # works
-
-In case there is ambiguity between a function name and an Ex command, use ":"
-to make clear you want to use the Ex command.  For example, there is both the
-`:substitute` command and the `substitute()` function.  When the line starts
-with `substitute(` this will use the function, prepend a colon to use the
-command instead: >
+identifier or can't be an Ex command.  Examples: >
+       myList->add(123)
+       g:myList->add(123)
+       [1, 2, 3]->Process()
+       #{a: 1, b: 2}->Process()
+       {'a': 1, 'b': 2}->Process()
+       "foobar"->Process()
+       ("foobar")->Process()
+       'foobar'->Process()
+       ('foobar')->Process()
+
+In rare case there is ambiguity between a function name and an Ex command, use
+":" to make clear you want to use the Ex command.  For example, there is both
+the `:substitute` command and the `substitute()` function.  When the line
+starts with `substitute(` this will use the function. Prepend a colon to use
+the command instead: >
        :substitute(pattern (replacement (
 
 Note that while variables need to be defined before they can be used,
 functions can be called before being defined.  This is required to be able
 have cyclic dependencies between functions.  It is slightly less efficient,
 since the function has to be looked up by name.  And a typo in the function
-name will only be found when the call is executed.
+name will only be found when the function is called.
 
 
 Omitting function() ~
@@ -347,9 +348,10 @@ No curly braces expansion ~
 |curly-braces-names| cannot be used.
 
 
-No :append, :change or :insert ~
+No :xit, :append, :change or :insert ~
 
-These commands are too quickly confused with local variable names.
+These commands are too easily confused with local variable names.  Instead of
+`:x` or `:xit` you can use `:exit`.
 
 
 Comparators ~
index e8e38aa97a300b6309502be0508e7cdbd22097e1..895912f4197ec5b93e4089a6a51df5511dde0a03 100644 (file)
@@ -3176,6 +3176,9 @@ ex_append(exarg_T *eap)
     int                vcol;
     int                empty = (curbuf->b_ml.ml_flags & ML_EMPTY);
 
+    if (not_in_vim9(eap) == FAIL)
+       return;
+
     // the ! flag toggles autoindent
     if (eap->forceit)
        curbuf->b_p_ai = !curbuf->b_p_ai;
@@ -3317,6 +3320,9 @@ ex_change(exarg_T *eap)
 {
     linenr_T   lnum;
 
+    if (not_in_vim9(eap) == FAIL)
+       return;
+
     if (eap->line2 >= eap->line1
            && u_save(eap->line1 - 1, eap->line2 + 1) == FAIL)
        return;
index 987e92dea9c85d9bcf83858108945ae907cc7f27..fe654104daf6cf2375bfd507a7c03249124824ca 100644 (file)
@@ -5686,6 +5686,8 @@ ex_stop(exarg_T *eap)
     static void
 ex_exit(exarg_T *eap)
 {
+    if (not_in_vim9(eap) == FAIL)
+       return;
 #ifdef FEAT_CMDWIN
     if (cmdwin_type != 0)
     {
index 6a32abdcacbc050e59ea88968213b86198a246e6..f8b12ee5de73d23c9a607f327dee9ab0d90db011 100644 (file)
@@ -1,6 +1,7 @@
 /* vim9script.c */
 int in_vim9script(void);
 void ex_vim9script(exarg_T *eap);
+int not_in_vim9(exarg_T *eap);
 void ex_export(exarg_T *eap);
 void free_imports(int sid);
 void ex_import(exarg_T *eap);
index ac76e8d5bce04769212f4ee559b73c8ace6ae258..379245f397f229e3fa77c34d69b7cdf7094ffa85 100644 (file)
@@ -1515,6 +1515,21 @@ def Test_fixed_size_list()
   assert_equal([2, 99, 3, 4, 5], l)
 enddef
 
+def Test_no_insert_xit()
+  call CheckDefExecFailure(['x = 1'], 'E1100:')
+  call CheckDefExecFailure(['a = 1'], 'E1100:')
+  call CheckDefExecFailure(['i = 1'], 'E1100:')
+  call CheckDefExecFailure(['c = 1'], 'E1100:')
+
+  CheckScriptFailure(['vim9script', 'x = 1'], 'E1100:')
+  CheckScriptFailure(['vim9script', 'a = 1'], 'E488:')
+  CheckScriptFailure(['vim9script', 'a'], 'E1100:')
+  CheckScriptFailure(['vim9script', 'i = 1'], 'E488:')
+  CheckScriptFailure(['vim9script', 'i'], 'E1100:')
+  CheckScriptFailure(['vim9script', 'c = 1'], 'E488:')
+  CheckScriptFailure(['vim9script', 'c'], 'E1100:')
+enddef
+
 def IfElse(what: number): string
   let res = ''
   if what == 1
index 78aafb8e3ee234c2c2714bd95d8588095d00dbf8..710c570604c5106d1a338d106192805fe1e1fca1 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1308,
 /**/
     1307,
 /**/
index 1ed3211463a0975d5f1438f0005fb3730335fd18..cbb9231d8cf0049e45ea23bf90afa31bf083cd9d 100644 (file)
@@ -7409,6 +7409,13 @@ compile_def_function(ufunc_T *ufunc, int set_return_type, cctx_T *outer_cctx)
 
            // TODO: other commands with an expression argument
 
+           case CMD_append:
+           case CMD_change:
+           case CMD_insert:
+           case CMD_xit:
+                   not_in_vim9(&ea);
+                   goto erret;
+
            case CMD_SIZE:
                    semsg(_("E476: Invalid command: %s"), ea.cmd);
                    goto erret;
index 784283ef9762e467ae53c92fac4fe2e3842fd847..7d783ea9342efbbc1fb97a00daad1b2317964ccc 100644 (file)
@@ -58,13 +58,30 @@ ex_vim9script(exarg_T *eap)
     }
 }
 
+/*
+ * When in Vim9 script give an error and return FAIL.
+ */
+    int
+not_in_vim9(exarg_T *eap)
+{
+    switch (eap->cmdidx)
+    {
+       case CMD_insert:
+       case CMD_append:
+       case CMD_change:
+       case CMD_xit:
+           semsg(_("E1100: Missing :let: %s"), eap->cmd);
+           return FAIL;
+       default: break;
+    }
+    return OK;
+}
+
 /*
  * ":export let Name: type"
  * ":export const Name: type"
  * ":export def Name(..."
  * ":export class Name ..."
- *
- * ":export {Name, ...}"
  */
     void
 ex_export(exarg_T *eap)