changeset 21353:fb8c8fcb7b60 v8.2.1227

patch 8.2.1227: Vim9: allowing both quoted and # comments is confusing Commit: https://github.com/vim/vim/commit/f5be8cdb77786f93c23237d7d8162feca92067e2 Author: Bram Moolenaar <Bram@vim.org> Date: Fri Jul 17 20:36:00 2020 +0200 patch 8.2.1227: Vim9: allowing both quoted and # comments is confusing Problem: Vim9: allowing both quoted and # comments is confusing. Solution: Only support # comments in Vim9 script.
author Bram Moolenaar <Bram@vim.org>
date Fri, 17 Jul 2020 20:45:06 +0200
parents 92c30752e9d2
children 428b1c6355eb
files runtime/doc/vim9.txt src/ex_docmd.c src/proto/ex_docmd.pro src/testdir/test_vim9_disassemble.vim src/testdir/test_vim9_expr.vim src/testdir/test_vim9_func.vim src/testdir/test_vim9_script.vim src/version.c src/vim9compile.c
diffstat 9 files changed, 169 insertions(+), 128 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/vim9.txt
+++ b/runtime/doc/vim9.txt
@@ -1,4 +1,4 @@
-*vim9.txt*	For Vim version 8.2.  Last change: 2020 Jul 10
+*vim9.txt*	For Vim version 8.2.  Last change: 2020 Jul 17
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -64,20 +64,24 @@ THIS IS STILL UNDER DEVELOPMENT - ANYTHI
 
 Comments starting with # ~
 
-In Vim script comments start with double quote.  That can also be the start of
-a string, thus in many places it cannot be used.  In Vim9 script a comment
-normally starts with #.  In Vi this is a command to list text with numbers,
-but you can also use `:number` for that. >
+In legacy Vim script comments start with double quote.  In Vim9 script
+comments start with #. >
+	# declarations
 	let count = 0  # number of occurrences
 
-To improve readability there must be a space between the command and the #
+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.
 
-Since Vim9 script allows for line breaks in many places, the double quoted
-comment also cannot be used at the start of a line after an expression.  To
-avoid confusion it is best to only use # comments.
-
 
 Vim9 functions ~
 
@@ -400,6 +404,7 @@ The boolean operators "||" and "&&" do n
 	0 || ''  == ''
 	8 && 2   == 2
 	0 && 2   == 0
+	2 && 0   == 0
 	[] && 2  == []
 
 When using `..` for string concatenation the arguments are always converted to
@@ -418,13 +423,15 @@ be made.  Here is a summary of what migh
 
 Ex command ranges need to be prefixed with a colon. >
   	->		" legacy Vim: shifts the previous line to the right
-	->func()	" Vim9: method call
+	->func()	" Vim9: method call in continuation line
 	:->		" Vim9: shifts the previous line to the right
 
 	%s/a/b		" legacy Vim: substitute on all lines
 	x = alongname
 	     % another	" Vim9: line continuation without a backslash
 	:%s/a/b		" Vim9: substitute on all lines
+	'text'->func()	" Vim9: method call
+	:'t		" legacy Vim: jump to mark m
 
 Functions defined with `:def` compile the whole function.  Legacy functions
 can bail out, and the following lines are not parsed: >
--- a/src/ex_docmd.c
+++ b/src/ex_docmd.c
@@ -1649,6 +1649,16 @@ current_tab_nr(tabpage_T *tab)
     return nr;
 }
 
+    int
+comment_start(char_u *p, int starts_with_colon UNUSED)
+{
+#ifdef FEAT_EVAL
+    if (in_vim9script())
+	return p[0] == '#' && p[1] != '{' && !starts_with_colon;
+#endif
+    return *p == '"';
+}
+
 # define CURRENT_WIN_NR current_win_nr(curwin)
 # define LAST_WIN_NR current_win_nr(NULL)
 # define CURRENT_TAB_NR current_tab_nr(curtab)
@@ -1886,12 +1896,8 @@ do_one_cmd(
      * If we got a line, but no command, then go to the line.
      * If we find a '|' or '\n' we set ea.nextcmd.
      */
-    if (*ea.cmd == NUL || *ea.cmd == '"'
-#ifdef FEAT_EVAL
-		|| (*ea.cmd == '#' && ea.cmd[1] != '{'
-					   && !starts_with_colon && vim9script)
-#endif
-		|| (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL)
+    if (*ea.cmd == NUL || comment_start(ea.cmd, starts_with_colon)
+			       || (ea.nextcmd = check_nextcmd(ea.cmd)) != NULL)
     {
 	/*
 	 * strange vi behaviour:
@@ -2225,7 +2231,7 @@ do_one_cmd(
 	ea.do_ecmd_cmd = getargcmd(&ea.arg);
 
     /*
-     * Check for '|' to separate commands and '"' to start comments.
+     * Check for '|' to separate commands and '"' or '#' to start comments.
      * Don't do this for ":read !cmd" and ":write !cmd".
      */
     if ((ea.argt & EX_TRLBAR) && !ea.usefilter)
@@ -2659,7 +2665,8 @@ doend:
     int
 parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only)
 {
-    char_u *p;
+    char_u  *p;
+    int	    starts_with_colon = FALSE;
 
     CLEAR_FIELD(cmdmod);
     eap->verbose_save = -1;
@@ -2669,7 +2676,11 @@ parse_command_modifiers(exarg_T *eap, ch
     for (;;)
     {
 	while (*eap->cmd == ' ' || *eap->cmd == '\t' || *eap->cmd == ':')
+	{
+	    if (*eap->cmd == ':')
+		starts_with_colon = TRUE;
 	    ++eap->cmd;
+	}
 
 	// in ex mode, an empty line works like :+
 	if (*eap->cmd == NUL && exmode_active
@@ -2683,7 +2694,7 @@ parse_command_modifiers(exarg_T *eap, ch
 	}
 
 	// ignore comment and empty lines
-	if (*eap->cmd == '"')
+	if (comment_start(eap->cmd, starts_with_colon))
 	    return FAIL;
 	if (*eap->cmd == NUL)
 	{
@@ -4521,14 +4532,20 @@ separate_nextcmd(exarg_T *eap)
 	// Check for '"': start of comment or '|': next command
 	// :@" and :*" do not start a comment!
 	// :redir @" doesn't either.
-	else if ((*p == '"' && !(eap->argt & EX_NOTRLCOM)
+	else if ((*p == '"'
+#ifdef FEAT_EVAL
+		    && !in_vim9script()
+#endif
+		    && !(eap->argt & EX_NOTRLCOM)
 		    && ((eap->cmdidx != CMD_at && eap->cmdidx != CMD_star)
-			|| p != eap->arg)
+							      || p != eap->arg)
 		    && (eap->cmdidx != CMD_redir
-			|| p != eap->arg + 1 || p[-1] != '@'))
+					 || p != eap->arg + 1 || p[-1] != '@'))
 #ifdef FEAT_EVAL
-		|| (*p == '#' && in_vim9script()
-			  && p[1] != '{' && p > eap->cmd && VIM_ISWHITE(p[-1]))
+		|| (*p == '#'
+		    && in_vim9script()
+		    && p[1] != '{'
+		    && p > eap->cmd && VIM_ISWHITE(p[-1]))
 #endif
 		|| *p == '|' || *p == '\n')
 	{
@@ -4867,11 +4884,13 @@ ex_blast(exarg_T *eap)
     int
 ends_excmd(int c)
 {
+    int comment_char = '"';
+
 #ifdef FEAT_EVAL
-    if (c == '#')
-	return in_vim9script();
-#endif
-    return (c == NUL || c == '|' || c == '"' || c == '\n');
+    if (in_vim9script())
+	comment_char = '#';
+#endif
+    return (c == NUL || c == '|' || c == comment_char || c == '\n');
 }
 
 /*
@@ -4883,11 +4902,14 @@ ends_excmd2(char_u *cmd_start UNUSED, ch
 {
     int c = *cmd;
 
+    if (c == NUL || c == '|' || c == '\n')
+	return TRUE;
 #ifdef FEAT_EVAL
-    if (c == '#' && cmd[1] != '{' && (cmd == cmd_start || VIM_ISWHITE(cmd[-1])))
-	return in_vim9script();
-#endif
-    return (c == NUL || c == '|' || c == '"' || c == '\n');
+    if (in_vim9script())
+	return c == '#' && cmd[1] != '{'
+				 && (cmd == cmd_start || VIM_ISWHITE(cmd[-1]));
+#endif
+    return c == '"';
 }
 
 #if defined(FEAT_SYN_HL) || defined(FEAT_SEARCH_EXTRA) || defined(FEAT_EVAL) \
@@ -7029,7 +7051,12 @@ ex_wincmd(exarg_T *eap)
 
     eap->nextcmd = check_nextcmd(p);
     p = skipwhite(p);
-    if (*p != NUL && *p != '"' && eap->nextcmd == NULL)
+    if (*p != NUL && *p != (
+#ifdef FEAT_EVAL
+	    in_vim9script() ? '#' :
+#endif
+		'"')
+	    && eap->nextcmd == NULL)
 	emsg(_(e_invarg));
     else if (!eap->skip)
     {
--- a/src/proto/ex_docmd.pro
+++ b/src/proto/ex_docmd.pro
@@ -5,6 +5,7 @@ int do_cmdline(char_u *cmdline, char_u *
 int getline_equal(char_u *(*fgetline)(int, void *, int, int), void *cookie, char_u *(*func)(int, void *, int, int));
 void *getline_cookie(char_u *(*fgetline)(int, void *, int, int), void *cookie);
 char_u *getline_peek(char_u *(*fgetline)(int, void *, int, int), void *cookie);
+int comment_start(char_u *p, int starts_with_colon);
 int parse_command_modifiers(exarg_T *eap, char **errormsg, int skip_only);
 void undo_cmdmod(exarg_T *eap, int save_msg_scroll);
 int parse_cmd_address(exarg_T *eap, char **errormsg, int silent);
--- a/src/testdir/test_vim9_disassemble.vim
+++ b/src/testdir/test_vim9_disassemble.vim
@@ -463,7 +463,7 @@ def Test_disassemble_update_instr()
         '\d RETURN',
         res)
 
-  " Calling the function will change UCALL into the faster DCALL
+  # Calling the function will change UCALL into the faster DCALL
   assert_equal('yes', FuncWithForwardCall())
 
   res = execute('disass s:FuncWithForwardCall')
@@ -1073,7 +1073,7 @@ def Test_disassemble_compare()
 
   let nr = 1
   for case in cases
-    " declare local variables to get a non-constant with the right type
+    # declare local variables to get a non-constant with the right type
     writefile(['def TestCase' .. nr .. '()',
              '  let isFalse = false',
              '  let isNull = v:null',
@@ -1121,7 +1121,7 @@ def Test_disassemble_compare_const()
     source Xdisassemble
     let instr = execute('disassemble TestCase' .. nr)
     if case[1]
-      " condition true, "echo 42" executed
+      # condition true, "echo 42" executed
       assert_match('TestCase' .. nr .. '.*' ..
           'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '.*' ..
           '\d PUSHNR 42.*' ..
@@ -1130,7 +1130,7 @@ def Test_disassemble_compare_const()
           '\d RETURN.*',
           instr)
     else
-      " condition false, function just returns
+      # condition false, function just returns
       assert_match('TestCase' .. nr .. '.*' ..
           'if ' .. substitute(case[0], '[[~]', '\\\0', 'g') .. '[ \n]*' ..
           'echo 42[ \n]*' ..
@@ -1245,7 +1245,7 @@ def Test_vim9script_forward_func()
   writefile(lines, 'Xdisassemble')
   source Xdisassemble
 
-  " check that the first function calls the second with DCALL
+  # check that the first function calls the second with DCALL
   assert_match('\<SNR>\d*_FuncOne\_s*' ..
         'return FuncTwo()\_s*' ..
         '\d DCALL <SNR>\d\+_FuncTwo(argc 0)\_s*' ..
--- a/src/testdir/test_vim9_expr.vim
+++ b/src/testdir/test_vim9_expr.vim
@@ -46,7 +46,7 @@ def Test_expr1()
 enddef
 
 def Test_expr1_vimscript()
-  " only checks line continuation
+  # only checks line continuation
   let lines =<< trim END
       vim9script
       let var = 1
@@ -127,7 +127,7 @@ def Test_expr2()
 enddef
 
 def Test_expr2_vimscript()
-  " check line continuation
+  # check line continuation
   let lines =<< trim END
       vim9script
       let var = 0
@@ -154,7 +154,7 @@ def Test_expr2_vimscript()
   END
   CheckScriptSuccess(lines)
 
-  " check keeping the value
+  # check keeping the value
   lines =<< trim END
       vim9script
       assert_equal(2, 2 || 0)
@@ -231,7 +231,7 @@ def Test_expr3()
 enddef
 
 def Test_expr3_vimscript()
-  " check line continuation
+  # check line continuation
   let lines =<< trim END
       vim9script
       let var = 0
@@ -258,7 +258,7 @@ def Test_expr3_vimscript()
   END
   CheckScriptSuccess(lines)
 
-  " check keeping the value
+  # check keeping the value
   lines =<< trim END
       vim9script
       assert_equal(0, 2 && 0)
@@ -625,7 +625,7 @@ def RetVoid()
 enddef
 
 def Test_expr4_vimscript()
-  " check line continuation
+  # check line continuation
   let lines =<< trim END
       vim9script
       let var = 0
@@ -668,7 +668,7 @@ def Test_expr4_vimscript()
   END
   CheckScriptSuccess(lines)
 
-  " spot check mismatching types
+  # spot check mismatching types
   lines =<< trim END
       vim9script
       echo '' == 0
@@ -791,7 +791,7 @@ def Test_expr5()
 enddef
 
 def Test_expr5_vim9script()
-  " only checks line continuation
+  # only checks line continuation
   let lines =<< trim END
       vim9script
       let var = 11
@@ -902,7 +902,7 @@ def Test_expr6()
 enddef
 
 def Test_expr6_vim9script()
-  " only checks line continuation
+  # only checks line continuation
   let lines =<< trim END
       vim9script
       let var = 11
@@ -1024,7 +1024,7 @@ let $TESTVAR = 'testvar'
 
 " test low level expression
 def Test_expr7_number()
-  " number constant
+  # number constant
   assert_equal(0, 0)
   assert_equal(654, 0654)
 
@@ -1034,7 +1034,7 @@ def Test_expr7_number()
 enddef
 
 def Test_expr7_float()
-  " float constant
+  # float constant
   if !has('float')
     MissingFeature 'float'
   else
@@ -1046,7 +1046,7 @@ def Test_expr7_float()
 enddef
 
 def Test_expr7_blob()
-  " blob constant
+  # blob constant
   assert_equal(g:blob_empty, 0z)
   assert_equal(g:blob_one, 0z01)
   assert_equal(g:blob_long, 0z0102.0304)
@@ -1055,7 +1055,7 @@ def Test_expr7_blob()
 enddef
 
 def Test_expr7_string()
-  " string constant
+  # string constant
   assert_equal(g:string_empty, '')
   assert_equal(g:string_empty, "")
   assert_equal(g:string_short, 'x')
@@ -1077,7 +1077,7 @@ def Test_expr7_vimvar()
 enddef
 
 def Test_expr7_special()
-  " special constant
+  # special constant
   assert_equal(g:special_true, true)
   assert_equal(g:special_false, false)
   assert_equal(g:special_true, v:true)
@@ -1106,7 +1106,7 @@ def Test_expr7_special_vim9script()
 enddef
 
 def Test_expr7_list()
-  " list
+  # list
   assert_equal(g:list_empty, [])
   assert_equal(g:list_empty, [  ])
   assert_equal(g:list_mixed, [1, 'b', false,])
@@ -1152,7 +1152,7 @@ def Test_expr7_lambda()
   assert_equal('result', La())
   assert_equal([1, 3, 5], [1, 2, 3]->map({key, val -> key + val}))
 
-  " line continuation inside lambda with "cond ? expr : expr" works
+  # line continuation inside lambda with "cond ? expr : expr" works
   let ll = range(3)
   map(ll, {k, v -> v % 2 ? {
 	    '111': 111 } : {}
@@ -1189,7 +1189,7 @@ def Test_expr7_lambda_vim9script()
 enddef
 
 def Test_expr7_dict()
-  " dictionary
+  # dictionary
   assert_equal(g:dict_empty, {})
   assert_equal(g:dict_empty, {  })
   assert_equal(g:dict_one, {'one': 1})
@@ -1316,7 +1316,7 @@ def Test_expr_member_vim9script()
 enddef
 
 def Test_expr7_option()
-  " option
+  # option
   set ts=11
   assert_equal(11, &ts)
   &ts = 9
@@ -1330,7 +1330,7 @@ def Test_expr7_option()
 enddef
 
 def Test_expr7_environment()
-  " environment variable
+  # environment variable
   assert_equal('testvar', $TESTVAR)
   assert_equal('', $ASDF_ASD_XXX)
 
@@ -1343,7 +1343,7 @@ def Test_expr7_register()
 enddef
 
 def Test_expr7_parens()
-  " (expr)
+  # (expr)
   assert_equal(4, (6 * 4) / 6)
   assert_equal(0, 6 * ( 4 / 6 ))
 
@@ -1474,7 +1474,7 @@ func CallMe2(one, two)
 endfunc
 
 def Test_expr7_trailing()
-  " user function call
+  # user function call
   assert_equal(123, g:CallMe(123))
   assert_equal(123, g:CallMe(  123))
   assert_equal(123, g:CallMe(123  ))
@@ -1482,26 +1482,26 @@ def Test_expr7_trailing()
   assert_equal('yesno', g:CallMe2( 'yes', 'no' ))
   assert_equal('nothing', g:CallMe('nothing'))
 
-  " partial call
+  # partial call
   let Part = function('g:CallMe')
   assert_equal('yes', Part('yes'))
 
-  " funcref call, using list index
+  # funcref call, using list index
   let l = []
   g:Funcrefs[0](l, 2)
   assert_equal([2], l)
 
-  " method call
+  # method call
   l = [2, 5, 6]
   l->map({k, v -> k + v})
   assert_equal([2, 6, 8], l)
 
-  " lambda method call
+  # lambda method call
   l = [2, 5]
   l->{l -> add(l, 8)}()
   assert_equal([2, 5, 8], l)
 
-  " dict member
+  # dict member
   let d = #{key: 123}
   assert_equal(123, d.key)
 enddef
--- a/src/testdir/test_vim9_func.vim
+++ b/src/testdir/test_vim9_func.vim
@@ -80,7 +80,7 @@ def Test_call_ufunc_count()
   Increment()
   Increment()
   Increment()
-  " works with and without :call
+  # works with and without :call
   assert_equal(4, g:counter)
   call assert_equal(4, g:counter)
   unlet g:counter
@@ -236,7 +236,7 @@ def ListArg(arg: list<string>)
 enddef
 
 def Test_assign_to_argument()
-  " works for dict and list
+  # works for dict and list
   let d: dict<string> = {}
   DictArg(d)
   assert_equal('value', d['key'])
@@ -266,19 +266,19 @@ let SomeFunc = function('len')
 let NotAFunc = 'text'
 
 def CombineFuncrefTypes()
-  " same arguments, different return type
+  # same arguments, different return type
   let Ref1: func(bool): string
   let Ref2: func(bool): number
   let Ref3: func(bool): any
   Ref3 = g:cond ? Ref1 : Ref2
 
-  " different number of arguments
+  # different number of arguments
   let Refa1: func(bool): number
   let Refa2: func(bool, number): number
   let Refa3: func: number
   Refa3 = g:cond ? Refa1 : Refa2
 
-  " different argument types
+  # different argument types
   let Refb1: func(bool, string): number
   let Refb2: func(string, number): number
   let Refb3: func(any, any): number
@@ -294,7 +294,7 @@ def DefinedEvenLater(arg: string): strin
 enddef
 
 def Test_error_in_nested_function()
-  " Error in called function requires unwinding the call stack.
+  # Error in called function requires unwinding the call stack.
   assert_fails('call FuncWithForwardCall()', 'E1096')
 enddef
 
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -113,7 +113,7 @@ def Test_vim9_single_char_vars()
   let lines =<< trim END
       vim9script
 
-      " single character variable declarations work
+      # single character variable declarations work
       let a: string
       let b: number
       let l: list<any>
@@ -122,7 +122,7 @@ def Test_vim9_single_char_vars()
       let v: number
       let w: number
 
-      " script-local variables can be used without s: prefix
+      # script-local variables can be used without s: prefix
       a = 'script-a'
       b = 111
       l = [1, 2, 3]
@@ -175,7 +175,7 @@ def Test_assignment_dict()
   let dict4: dict<any> = #{one: 1, two: '2'}
   let dict5: dict<blob> = #{one: 0z01, two: 0z02}
 
-  " overwrite
+  # overwrite
   dict3['key'] = 'another'
 
   call CheckDefExecFailure(['let dd = {}', 'dd[""] = 6'], 'E713:')
@@ -185,8 +185,8 @@ def Test_assignment_dict()
 enddef
 
 def Test_assignment_local()
-  " Test in a separated file in order not to the current buffer/window/tab is
-  " changed.
+  # 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'
@@ -259,7 +259,7 @@ def Test_assignment_default()
     assert_equal(test_null_channel(), thechannel)
 
     if has('unix') && executable('cat')
-      " check with non-null job and channel, types must match
+      # check with non-null job and channel, types must match
       thejob = job_start("cat ", #{})
       thechannel = job_getchannel(thejob)
       job_stop(thejob, 'kill')
@@ -394,7 +394,7 @@ def Test_unlet()
 enddef
 
 def Test_delfunction()
-  " Check function is defined in script namespace
+  # Check function is defined in script namespace
   CheckScriptSuccess([
       'vim9script',
       'func CheckMe()',
@@ -403,7 +403,7 @@ def Test_delfunction()
       'assert_equal(123, s:CheckMe())',
       ])
 
-  " Check function in script namespace cannot be deleted
+  # Check function in script namespace cannot be deleted
   CheckScriptFailure([
       'vim9script',
       'func DeleteMe1()',
@@ -586,7 +586,7 @@ def Test_try_catch_fails()
 enddef
 
 def Test_throw_vimscript()
-  " only checks line continuation
+  # only checks line continuation
   let lines =<< trim END
       vim9script
       try
@@ -600,7 +600,7 @@ def Test_throw_vimscript()
 enddef
 
 def Test_cexpr_vimscript()
-  " only checks line continuation
+  # only checks line continuation
   set errorformat=File\ %f\ line\ %l
   let lines =<< trim END
       vim9script
@@ -727,7 +727,7 @@ def Test_vim9_import_export()
   END
   writefile(import_in_def_lines, 'Ximport2.vim')
   source Ximport2.vim
-  " TODO: this should be 9879
+  # TODO: this should be 9879
   assert_equal(9876, g:imported)
   assert_equal(9883, g:imported_added)
   unlet g:imported
@@ -802,7 +802,7 @@ def Test_vim9_import_export()
   writefile(import_star_lines, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1045:')
 
-  " try to import something that exists but is not exported
+  # try to import something that exists but is not exported
   let import_not_exported_lines =<< trim END
     vim9script
     import name from './Xexport.vim'
@@ -810,7 +810,7 @@ def Test_vim9_import_export()
   writefile(import_not_exported_lines, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1049:')
 
-  " try to import something that is already defined
+  # try to import something that is already defined
   let import_already_defined =<< trim END
     vim9script
     let exported = 'something'
@@ -819,7 +819,7 @@ def Test_vim9_import_export()
   writefile(import_already_defined, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1073:')
 
-  " try to import something that is already defined
+  # try to import something that is already defined
   import_already_defined =<< trim END
     vim9script
     let exported = 'something'
@@ -828,7 +828,7 @@ def Test_vim9_import_export()
   writefile(import_already_defined, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1073:')
 
-  " try to import something that is already defined
+  # try to import something that is already defined
   import_already_defined =<< trim END
     vim9script
     let exported = 'something'
@@ -837,7 +837,7 @@ def Test_vim9_import_export()
   writefile(import_already_defined, 'Ximport.vim')
   assert_fails('source Ximport.vim', 'E1073:')
 
-  " import a very long name, requires making a copy
+  # import a very long name, requires making a copy
   let import_long_name_lines =<< trim END
     vim9script
     import name012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 from './Xexport.vim'
@@ -877,7 +877,7 @@ def Test_vim9_import_export()
   delete('Ximport3.vim')
   delete('Xexport.vim')
 
-  " Check that in a Vim9 script 'cpo' is set to the Vim default.
+  # Check that in a Vim9 script 'cpo' is set to the Vim default.
   set cpo&vi
   let cpo_before = &cpo
   let lines =<< trim END
@@ -962,12 +962,12 @@ def Test_vim9script_reload_import()
   writefile(testlines, 'Ximport.vim')
   source Ximport.vim
 
-  " Test that when not using "morelines" GetValtwo() and valtwo are still
-  " defined, because import doesn't reload a script.
+  # Test that when not using "morelines" GetValtwo() and valtwo are still
+  # defined, because import doesn't reload a script.
   writefile(lines, 'Xreload.vim')
   source Ximport.vim
 
-  " cannot declare a var twice
+  # cannot declare a var twice
   lines =<< trim END
     vim9script
     let valone = 1234
@@ -1185,7 +1185,7 @@ def Test_import_compile_error()
   try
     source Ximport.vim
   catch /E1001/
-    " Error should be fore the Xexported.vim file.
+    # Error should be fore the Xexported.vim file.
     assert_match('E1001: variable not found: notDefined', v:exception)
     assert_match('function <SNR>\d\+_ImpFunc\[1\]..<SNR>\d\+_ExpFunc, line 1', v:throwpoint)
   endtry
@@ -1195,7 +1195,7 @@ def Test_import_compile_error()
 enddef
 
 def Test_fixed_size_list()
-  " will be allocated as one piece of memory, check that changes work
+  # will be allocated as one piece of memory, check that changes work
   let l = [1, 2, 3, 4]
   l->remove(0)
   l->add(5)
@@ -1357,9 +1357,9 @@ def RunNested(i: number): number
   let x: number = 0
   if i % 2
     if 1
-      " comment
+      # comment
     else
-      " comment
+      # comment
     endif
     x += 1
   else
@@ -1401,7 +1401,7 @@ def Test_execute_cmd()
 enddef
 
 def Test_execute_cmd_vimscript()
-  " only checks line continuation
+  # only checks line continuation
   let lines =<< trim END
       vim9script
       execute 'g:someVar'
@@ -1441,7 +1441,7 @@ def Test_echomsg_cmd()
 enddef
 
 def Test_echomsg_cmd_vimscript()
-  " only checks line continuation
+  # only checks line continuation
   let lines =<< trim END
       vim9script
       echomsg 'here'
@@ -1461,7 +1461,7 @@ def Test_echoerr_cmd()
 enddef
 
 def Test_echoerr_cmd_vimscript()
-  " only checks line continuation
+  # only checks line continuation
   let lines =<< trim END
       vim9script
       try
@@ -1569,7 +1569,7 @@ def Test_automatic_line_continuation()
       'one',
       'two',
       'three',
-      ] " comment
+      ] # comment
   assert_equal(['one', 'two', 'three'], mylist)
 
   let mydict = {
@@ -1577,7 +1577,7 @@ def Test_automatic_line_continuation()
       'two': 2,
       'three':
           3,
-      } " comment
+      } # comment
   assert_equal({'one': 1, 'two': 2, 'three': 3}, mydict)
   mydict = #{
       one: 1,  # comment
@@ -1754,8 +1754,8 @@ def Test_vim9_comment()
       'hi clear This # comment',
       'hi clear # comment',
       ])
-  " not tested, because it doesn't give an error but a warning:
-  " hi clear This# comment',
+  # not tested, because it doesn't give an error but a warning:
+  # hi clear This# comment',
   CheckScriptFailure([
       'vim9script',
       'hi clear# comment',
@@ -2091,19 +2091,19 @@ def Test_vim9_comment_not_compiled()
       'bwipe!',
       ])
 
-"  CheckScriptFailure([
-"      'vim9script',
-"      'new'
-"      'call setline(1, ["# define pat", "last"])',
-"      ':$',
-"      'dsearch /pat/#comment',
-"      'bwipe!',
-"      ], 'E488:')
-"
-"  CheckScriptFailure([
-"      'vim9script',
-"      'func! SomeFunc()',
-"      ], 'E477:')
+  CheckScriptFailure([
+      'vim9script',
+      'new'
+      'call setline(1, ["# define pat", "last"])',
+      ':$',
+      'dsearch /pat/#comment',
+      'bwipe!',
+      ], 'E488:')
+
+  CheckScriptFailure([
+      'vim9script',
+      'func! SomeFunc()',
+      ], 'E477:')
 enddef
 
 def Test_finish()
@@ -2135,12 +2135,12 @@ def Test_let_func_call()
       return 'this'
     endfunc
     let val: string = GetValue() 
-    " env var is always a string
+    # env var is always a string
     let env = $TERM
   END
   writefile(lines, 'Xfinished')
   source Xfinished
-  " GetValue() is not called during discovery phase
+  # GetValue() is not called during discovery phase
   assert_equal(1, g:count)
 
   unlet g:count
@@ -2169,7 +2169,7 @@ def Test_let_declaration()
     g:var_uninit = var
     var = 'text'
     g:var_test = var
-    " prefixing s: is optional
+    # prefixing s: is optional
     s:var = 'prefixed'
     g:var_prefixed = s:var
 
@@ -2281,7 +2281,7 @@ def Test_source_vim9_from_legacy()
   source Xlegacy_script.vim
 
   assert_equal('global', g:global)
-"  unlet g:global
+  unlet g:global
 
   delete('Xlegacy_script.vim')
   delete('Xvim9_script.vim')
@@ -2301,7 +2301,7 @@ def Test_substitute_cmd()
   assert_equal('otherthing', getline(1))
   bwipe!
 
-  " also when the context is Vim9 script
+  # also when the context is Vim9 script
   let lines =<< trim END
     vim9script
     new
--- a/src/version.c
+++ b/src/version.c
@@ -755,6 +755,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1227,
+/**/
     1226,
 /**/
     1225,
--- a/src/vim9compile.c
+++ b/src/vim9compile.c
@@ -2420,7 +2420,7 @@ free_imported(cctx_T *cctx)
  * Return TRUE if "p" points at a "#" but not at "#{".
  */
     static int
-comment_start(char_u *p)
+vim9_comment_start(char_u *p)
 {
     return p[0] == '#' && p[1] != '{';
 }
@@ -2443,7 +2443,7 @@ peek_next_line_from_context(cctx_T *cctx
 	if (line == NULL)
 	    break;
 	p = skipwhite(line);
-	if (*p != NUL && !comment_start(p))
+	if (*p != NUL && !vim9_comment_start(p))
 	    return p;
     }
     return NULL;
@@ -2461,7 +2461,7 @@ may_peek_next_line(cctx_T *cctx, char_u 
     char_u *p = skipwhite(arg);
 
     *nextp = NULL;
-    if (*p == NUL || (VIM_ISWHITE(*arg) && comment_start(p)))
+    if (*p == NUL || (VIM_ISWHITE(*arg) && vim9_comment_start(p)))
     {
 	*nextp = peek_next_line_from_context(cctx);
 	if (*nextp != NULL)
@@ -2492,7 +2492,7 @@ next_line_from_context(cctx_T *cctx, int
 	cctx->ctx_line_start = line;
 	SOURCING_LNUM = cctx->ctx_lnum + 1;
     } while (line == NULL || *skipwhite(line) == NUL
-			  || (skip_comment && comment_start(skipwhite(line))));
+			  || (skip_comment && vim9_comment_start(skipwhite(line))));
     return line;
 }
 
@@ -2504,7 +2504,7 @@ next_line_from_context(cctx_T *cctx, int
     static int
 may_get_next_line(char_u *whitep, char_u **arg, cctx_T *cctx)
 {
-    if (**arg == NUL || (VIM_ISWHITE(*whitep) && comment_start(*arg)))
+    if (**arg == NUL || (VIM_ISWHITE(*whitep) && vim9_comment_start(*arg)))
     {
 	char_u *next = next_line_from_context(cctx, TRUE);
 
@@ -3100,7 +3100,7 @@ compile_list(char_u **arg, cctx_T *cctx)
 	{
 	    ++p;
 	    // Allow for following comment, after at least one space.
-	    if (VIM_ISWHITE(*p) && *skipwhite(p) == '"')
+	    if (VIM_ISWHITE(*p) && *skipwhite(p) == '#')
 		p += STRLEN(p);
 	    break;
 	}
@@ -3157,6 +3157,8 @@ compile_lambda(char_u **arg, cctx_T *cct
 
     if (ufunc->uf_def_status == UF_COMPILED)
 	return generate_FUNCREF(cctx, ufunc->uf_dfunc_idx);
+
+    func_ptr_unref(ufunc);
     return FAIL;
 }
 
@@ -3201,6 +3203,8 @@ compile_lambda_call(char_u **arg, cctx_T
 	// call the compiled function
 	ret = generate_CALL(cctx, ufunc, argcount);
 
+    if (ret == FAIL)
+	func_ptr_unref(ufunc);
     return ret;
 }
 
@@ -3327,7 +3331,7 @@ compile_dict(char_u **arg, cctx_T *cctx,
 
     // Allow for following comment, after at least one space.
     p = skipwhite(*arg);
-    if (VIM_ISWHITE(**arg) && (*p == '"' || comment_start(p)))
+    if (VIM_ISWHITE(**arg) && vim9_comment_start(p))
 	*arg += STRLEN(*arg);
 
     dict_unref(d);
@@ -3618,7 +3622,7 @@ compile_subscript(
     {
 	char_u *p = skipwhite(*arg);
 
-	if (*p == NUL || (VIM_ISWHITE(**arg) && comment_start(p)))
+	if (*p == NUL || (VIM_ISWHITE(**arg) && vim9_comment_start(p)))
 	{
 	    char_u *next = peek_next_line_from_context(cctx);