To: vim_dev@googlegroups.com Subject: Patch 8.2.1308 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.1308 Problem: Vim9: accidentally using "x" causes Vim to exit. Solution: Disallow using ":x" or "xit" in Vim9 script. (closes #6399) Files: runtime/doc/vim9.txt, src/vim9compile.c, src/vim9script.c, src/proto/vim9script.pro, src/ex_docmd.c, src/ex_cmds.c, src/testdir/test_vim9_script.vim *** ../vim-8.2.1307/runtime/doc/vim9.txt 2020-07-17 20:35:00.853574362 +0200 --- runtime/doc/vim9.txt 2020-07-28 19:42:16.402015148 +0200 *************** *** 71,86 **** 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. In Vi # is a command to list text with numbers. In Vim9 script you can use `:number` for that. > ! 101number 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. Vim9 functions ~ --- 71,87 ---- 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, 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. > ! 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 does not start a comment. Vim9 functions ~ *************** *** 93,99 **** Compilation is done when the function is first called, or when the `:defcompile` command is encountered in the script where the function was ! defined. `: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 --- 94,100 ---- Compilation is done when the function is first called, or when the `:defcompile` command is encountered in the script where the function was ! 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,110 **** functions. Arguments are accessed by name, without "a:". There is no "a:" dictionary or ! "a:000" list. 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: > --- 105,111 ---- functions. Arguments are accessed by name, without "a:". There is no "a:" dictionary or ! "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: > *************** *** 184,191 **** echo temp " Error! An existing variable cannot be assigned to with `:let`, since that implies a ! declaration. An exception is global variables: these can be both used with ! and without `:let`, because there is no rule about where they are declared. Variables cannot shadow previously defined variables. Variables may shadow Ex commands, rename the variable if needed. --- 185,193 ---- echo temp " Error! An existing variable cannot be assigned to with `:let`, since that implies a ! declaration. Global, window, tab, buffer and Vim variables can only be used ! without `:let`, because they are are not really declared, they can also be ! deleted with `:unlet`. Variables cannot shadow previously defined variables. Variables may shadow Ex commands, rename the variable if needed. *************** *** 194,205 **** at the script level. > vim9script let script_local = 'text' ! let g:global = 'value' let Funcref = g:ThatFunction Since "&opt = value" is now assigning a value to option "opt", ":&" cannot be used to repeat a `:substitute` command. Omitting :call and :eval ~ --- 196,214 ---- at the script level. > vim9script let script_local = 'text' ! g:global = 'value' let Funcref = g:ThatFunction Since "&opt = value" is now assigning a value to option "opt", ":&" cannot be used to repeat a `:substitute` command. + *E1092* + Declaring more than one variable at a time, using the unpack notation, is + currently not supported: > + let [v1, v2] = GetValues() # Error! + That is because the type needs to be inferred from the list item type, which + isn't that easy. + Omitting :call and :eval ~ *************** *** 208,236 **** 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: > :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. Omitting function() ~ --- 217,245 ---- 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. 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 function is called. Omitting function() ~ *************** *** 277,286 **** --- 286,299 ---- ? PosFunc(arg) : NegFunc(arg) + For a method call using "->" and a member using a dot, a line break is allowed + before it: > let result = GetBuilder() ->BuilderSetWidth(333) ->BuilderSetHeight(777) ->BuilderBuild() + let result = MyDict + .member < *E1050* To make it possible for the operator at the start of the line to be *************** *** 335,343 **** |curly-braces-names| cannot be used. ! No :append, :change or :insert ~ ! These commands are too quickly confused with local variable names. Comparators ~ --- 348,357 ---- |curly-braces-names| cannot be used. ! No :xit, :append, :change or :insert ~ ! These commands are too easily confused with local variable names. Instead of ! `:x` or `:xit` you can use `:exit`. Comparators ~ *************** *** 486,504 **** When the caller omits an argument the {value} is used. The function will be compiled into instructions when ! called, or when `:defcompile` is used. Syntax and ! type errors will be produced at that time. ! NOTE: It is possible to nest `:def` inside another ! `:def`, but it is not possible to nest `:def` inside ! `:function`, for backwards compatibility. [!] is used as with `:function`. Note that in Vim9 script script-local functions cannot be deleted or redefined later in the same script. *:enddef* ! :enddef End of a function defined with `:def`. If the script the function is defined in is Vim9 script, then script-local --- 500,519 ---- When the caller omits an argument the {value} is used. The function will be compiled into instructions when ! called, or when `:disassemble` or `:defcompile` is ! used. Syntax and type errors will be produced at that ! time. ! It is possible to nest `:def` inside another `:def` or ! `:function` up to about 50 levels deep. [!] is used as with `:function`. Note that in Vim9 script script-local functions cannot be deleted or redefined later in the same script. *:enddef* ! :enddef End of a function defined with `:def`. It should be on ! a line by its own. If the script the function is defined in is Vim9 script, then script-local *************** *** 559,565 **** tuple These types can be used in declarations, but no value will have this type: ! {type}|{type} void any --- 574,580 ---- tuple These types can be used in declarations, but no value will have this type: ! {type}|{type} {not implemented yet} void any *************** *** 661,679 **** Export ~ *:export* *:exp* ! Exporting one item can be written as: > export const EXPORTED_CONST = 1234 export let someValue = ... export def MyFunc() ... export class MyClass ... As this suggests, only constants, variables, `:def` functions and classes can ! be exported. - Alternatively, an export statement can be used to export several already - defined (otherwise script-local) items: > - export {EXPORTED_CONST, someValue, MyFunc, MyClass} - < *E1042* `:export` can only be used in Vim9 script, at the script level. --- 676,690 ---- Export ~ *:export* *:exp* ! Exporting an item can be written as: > export const EXPORTED_CONST = 1234 export let someValue = ... export def MyFunc() ... export class MyClass ... As this suggests, only constants, variables, `:def` functions and classes can ! be exported. {classes are not implemented yet} *E1042* `:export` can only be used in Vim9 script, at the script level. *** ../vim-8.2.1307/src/vim9compile.c 2020-07-27 21:43:24.137946109 +0200 --- src/vim9compile.c 2020-07-28 19:54:13.594680696 +0200 *************** *** 7409,7414 **** --- 7409,7421 ---- // 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; *** ../vim-8.2.1307/src/vim9script.c 2020-07-23 22:41:39.779937421 +0200 --- src/vim9script.c 2020-07-28 19:56:37.126053261 +0200 *************** *** 59,70 **** } /* * ":export let Name: type" * ":export const Name: type" * ":export def Name(..." * ":export class Name ..." - * - * ":export {Name, ...}" */ void ex_export(exarg_T *eap) --- 59,87 ---- } /* + * 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 ..." */ void ex_export(exarg_T *eap) *** ../vim-8.2.1307/src/proto/vim9script.pro 2020-07-04 13:15:26.506990170 +0200 --- src/proto/vim9script.pro 2020-07-28 19:55:18.378396572 +0200 *************** *** 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); *** ../vim-8.2.1307/src/ex_docmd.c 2020-07-26 15:36:12.963172976 +0200 --- src/ex_docmd.c 2020-07-28 19:53:52.598773145 +0200 *************** *** 5686,5691 **** --- 5686,5693 ---- static void ex_exit(exarg_T *eap) { + if (not_in_vim9(eap) == FAIL) + return; #ifdef FEAT_CMDWIN if (cmdwin_type != 0) { *** ../vim-8.2.1307/src/ex_cmds.c 2020-07-23 17:16:15.046100627 +0200 --- src/ex_cmds.c 2020-07-28 19:56:19.026131985 +0200 *************** *** 3176,3181 **** --- 3176,3184 ---- 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,3322 **** --- 3320,3328 ---- { 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; *** ../vim-8.2.1307/src/testdir/test_vim9_script.vim 2020-07-25 19:30:55.111813216 +0200 --- src/testdir/test_vim9_script.vim 2020-07-28 20:04:58.955764836 +0200 *************** *** 1515,1520 **** --- 1515,1535 ---- 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 *** ../vim-8.2.1307/src/version.c 2020-07-27 22:40:20.694572378 +0200 --- src/version.c 2020-07-28 19:41:02.950390468 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 1308, /**/ -- From "know your smileys": ...---... SOS /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///