mirror of
				https://github.com/vim/vim.git
				synced 2025-10-31 09:57:14 -04:00 
			
		
		
		
	patch 9.1.0312: heredocs are not supported for :commands
Problem:  heredocs are not supported for :commands
          (@balki)
Solution: Add heredoc support
          (Yegappan Lakshmanan)
fixes: #14491
closes: #14528
Signed-off-by: Yegappan Lakshmanan <yegappan@yahoo.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
			
			
This commit is contained in:
		
				
					committed by
					
						 Christian Brabandt
						Christian Brabandt
					
				
			
			
				
	
			
			
			
						parent
						
							a1dcd76ce7
						
					
				
				
					commit
					e74cad3321
				
			| @@ -1,4 +1,4 @@ | ||||
| *vim9.txt*	For Vim version 9.1.  Last change: 2024 Jan 12 | ||||
| *vim9.txt*	For Vim version 9.1.  Last change: 2024 Apr 12 | ||||
|  | ||||
|  | ||||
| 		  VIM REFERENCE MANUAL	  by Bram Moolenaar | ||||
| @@ -641,6 +641,14 @@ No command can follow the "{", only a comment can be used there. | ||||
| The block can also be used for defining a user command.  Inside the block Vim9 | ||||
| syntax will be used. | ||||
|  | ||||
| This is an example of using here-docs: > | ||||
|     com SomeCommand { | ||||
|         g:someVar =<< trim eval END | ||||
|           ccc | ||||
|           ddd | ||||
|         END | ||||
|       } | ||||
|  | ||||
| If the statements include a dictionary, its closing bracket must not be | ||||
| written at the start of a line.  Otherwise, it would be parsed as the end of | ||||
| the block.  This does not work: > | ||||
|   | ||||
| @@ -2088,6 +2088,17 @@ skiptowhite(char_u *p) | ||||
|     return p; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * skiptowhite: skip over text until ' ' or '\t' or newline or NUL. | ||||
|  */ | ||||
|     char_u * | ||||
| skiptowhite_or_nl(char_u *p) | ||||
| { | ||||
|     while (*p != ' ' && *p != '\t' && *p != NL && *p != NUL) | ||||
| 	++p; | ||||
|     return p; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * skiptowhite_esc: Like skiptowhite(), but also skip escaped chars | ||||
|  */ | ||||
|   | ||||
| @@ -779,8 +779,10 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) | ||||
|     int		eval_failed = FALSE; | ||||
|     cctx_T	*cctx = vim9compile ? eap->cookie : NULL; | ||||
|     int		count = 0; | ||||
|     int		heredoc_in_string = FALSE; | ||||
|     char_u	*line_arg = NULL; | ||||
|  | ||||
|     if (eap->ea_getline == NULL) | ||||
|     if (eap->ea_getline == NULL && vim_strchr(cmd, '\n') == NULL) | ||||
|     { | ||||
| 	emsg(_(e_cannot_use_heredoc_here)); | ||||
| 	return NULL; | ||||
| @@ -824,8 +826,14 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) | ||||
|     if (*cmd != NUL && *cmd != comment_char) | ||||
|     { | ||||
| 	marker = skipwhite(cmd); | ||||
| 	p = skiptowhite(marker); | ||||
| 	if (*skipwhite(p) != NUL && *skipwhite(p) != comment_char) | ||||
| 	p = skiptowhite_or_nl(marker); | ||||
| 	if (*p == NL) | ||||
| 	{ | ||||
| 	    // heredoc in a string | ||||
| 	    line_arg = p + 1; | ||||
| 	    heredoc_in_string = TRUE; | ||||
| 	} | ||||
| 	else if (*skipwhite(p) != NUL && *skipwhite(p) != comment_char) | ||||
| 	{ | ||||
| 	    semsg(_(e_trailing_characters_str), p); | ||||
| 	    return NULL; | ||||
| @@ -859,6 +867,31 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) | ||||
| 	int	mi = 0; | ||||
| 	int	ti = 0; | ||||
|  | ||||
| 	if (heredoc_in_string) | ||||
| 	{ | ||||
| 	    char_u	*next_line; | ||||
|  | ||||
| 	    // heredoc in a string separated by newlines.  Get the next line | ||||
| 	    // from the string. | ||||
|  | ||||
| 	    if (*line_arg == NUL) | ||||
| 	    { | ||||
| 		semsg(_(e_missing_end_marker_str), marker); | ||||
| 		break; | ||||
| 	    } | ||||
|  | ||||
| 	    theline = line_arg; | ||||
| 	    next_line = vim_strchr(theline, '\n'); | ||||
| 	    if (next_line == NULL) | ||||
| 		line_arg += STRLEN(line_arg); | ||||
| 	    else | ||||
| 	    { | ||||
| 		*next_line = NUL; | ||||
| 		line_arg = next_line + 1; | ||||
| 	    } | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 	    vim_free(theline); | ||||
| 	    theline = eap->ea_getline(NUL, eap->cookie, 0, FALSE); | ||||
| 	    if (theline == NULL) | ||||
| @@ -866,6 +899,7 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) | ||||
| 		semsg(_(e_missing_end_marker_str), marker); | ||||
| 		break; | ||||
| 	    } | ||||
| 	} | ||||
|  | ||||
| 	// with "trim": skip the indent matching the :let line to find the | ||||
| 	// marker | ||||
| @@ -911,6 +945,8 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) | ||||
| 	} | ||||
| 	else | ||||
| 	{ | ||||
| 	    int	    free_str = FALSE; | ||||
|  | ||||
| 	    if (evalstr && !eap->skip) | ||||
| 	    { | ||||
| 		str = eval_all_expr_in_str(str); | ||||
| @@ -920,14 +956,19 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) | ||||
| 		    eval_failed = TRUE; | ||||
| 		    continue; | ||||
| 		} | ||||
| 		vim_free(theline); | ||||
| 		theline = str; | ||||
| 		free_str = TRUE; | ||||
| 	    } | ||||
|  | ||||
| 	    if (list_append_string(l, str, -1) == FAIL) | ||||
| 		break; | ||||
| 	    if (free_str) | ||||
| 		vim_free(str); | ||||
| 	} | ||||
|     } | ||||
|     if (heredoc_in_string) | ||||
| 	// Next command follows the heredoc in the string. | ||||
| 	eap->nextcmd = line_arg; | ||||
|     else | ||||
| 	vim_free(theline); | ||||
|     vim_free(text_indent); | ||||
|  | ||||
|   | ||||
| @@ -61,6 +61,7 @@ int vim_isalpha(int c); | ||||
| int vim_toupper(int c); | ||||
| int vim_tolower(int c); | ||||
| char_u *skiptowhite(char_u *p); | ||||
| char_u *skiptowhite_or_nl(char_u *p); | ||||
| char_u *skiptowhite_esc(char_u *p); | ||||
| long getdigits(char_u **pp); | ||||
| long getdigits_quoted(char_u **pp); | ||||
|   | ||||
| @@ -715,6 +715,20 @@ END | ||||
|   LINES | ||||
|   call v9.CheckScriptFailure(lines, 'E15:') | ||||
|  | ||||
|   " Test for using heredoc in a single string using execute() | ||||
|   call assert_equal(["['one', 'two']"], | ||||
|     \ execute("let x =<< trim END\n  one\n  two\nEND\necho x")->split("\n")) | ||||
|   call assert_equal(["['  one', '  two']"], | ||||
|     \ execute("let x =<< END\n  one\n  two\nEND\necho x")->split("\n")) | ||||
|   let cmd = 'execute("let x =<< END\n  one\n  two\necho x")' | ||||
|   call assert_fails(cmd, "E990: Missing end marker 'END'") | ||||
|   let cmd = 'execute("let x =<<\n  one\n  two\necho x")' | ||||
|   call assert_fails(cmd, "E990: Missing end marker ''") | ||||
|   let cmd = 'execute("let x =<< trim\n  one\n  two\necho x")' | ||||
|   call assert_fails(cmd, "E221: Marker cannot start with lower case letter") | ||||
|   let cmd = 'execute("let x =<< eval END\n  one\n  two{y}\nEND\necho x")' | ||||
|   call assert_fails(cmd, 'E121: Undefined variable: y') | ||||
|  | ||||
|   " skipped heredoc | ||||
|   if 0 | ||||
|     let msg =<< trim eval END | ||||
|   | ||||
| @@ -458,7 +458,7 @@ func s:InvokeSomeCommand() | ||||
|   SomeCommand | ||||
| endfunc | ||||
|  | ||||
| def Test_autocommand_block() | ||||
| def Test_command_block() | ||||
|   com SomeCommand { | ||||
|       g:someVar = 'some' | ||||
|     } | ||||
| @@ -469,7 +469,105 @@ def Test_autocommand_block() | ||||
|   unlet g:someVar | ||||
| enddef | ||||
|  | ||||
| def Test_command_block() | ||||
| " Test for using heredoc in a :command command block | ||||
| def Test_command_block_heredoc() | ||||
|   var lines =<< trim CODE | ||||
|     vim9script | ||||
|     com SomeCommand { | ||||
|         g:someVar =<< trim END | ||||
|           aaa | ||||
|           bbb | ||||
|         END | ||||
|       } | ||||
|     SomeCommand | ||||
|     assert_equal(['aaa', 'bbb'], g:someVar) | ||||
|     def Foo() | ||||
|       g:someVar = [] | ||||
|       SomeCommand | ||||
|       assert_equal(['aaa', 'bbb'], g:someVar) | ||||
|     enddef | ||||
|     Foo() | ||||
|     delcommand SomeCommand | ||||
|     unlet g:someVar | ||||
|   CODE | ||||
|   v9.CheckSourceSuccess( lines) | ||||
|  | ||||
|   # Execute a command with heredoc in a block | ||||
|   lines =<< trim CODE | ||||
|     vim9script | ||||
|     com SomeCommand { | ||||
|         g:someVar =<< trim END | ||||
|           aaa | ||||
|           bbb | ||||
|         END | ||||
|       } | ||||
|     execute('SomeCommand') | ||||
|     assert_equal(['aaa', 'bbb'], g:someVar) | ||||
|     delcommand SomeCommand | ||||
|     unlet g:someVar | ||||
|   CODE | ||||
|   v9.CheckSourceSuccess(lines) | ||||
|  | ||||
|   # heredoc evaluation | ||||
|   lines =<< trim CODE | ||||
|     vim9script | ||||
|     com SomeCommand { | ||||
|         var suffix = '---' | ||||
|         g:someVar =<< trim eval END | ||||
|           ccc{suffix} | ||||
|           ddd | ||||
|         END | ||||
|       } | ||||
|     SomeCommand | ||||
|     assert_equal(['ccc---', 'ddd'], g:someVar) | ||||
|     def Foo() | ||||
|       g:someVar = [] | ||||
|       SomeCommand | ||||
|       assert_equal(['ccc---', 'ddd'], g:someVar) | ||||
|     enddef | ||||
|     Foo() | ||||
|     delcommand SomeCommand | ||||
|     unlet g:someVar | ||||
|   CODE | ||||
|   v9.CheckSourceSuccess(lines) | ||||
|  | ||||
|   # command following heredoc | ||||
|   lines =<< trim CODE | ||||
|     vim9script | ||||
|     com SomeCommand { | ||||
|         var l =<< trim END | ||||
|           eee | ||||
|           fff | ||||
|         END | ||||
|         g:someVar = l | ||||
|       } | ||||
|     SomeCommand | ||||
|     assert_equal(['eee', 'fff'], g:someVar) | ||||
|     delcommand SomeCommand | ||||
|     unlet g:someVar | ||||
|   CODE | ||||
|   v9.CheckSourceSuccess(lines) | ||||
|  | ||||
|   # Error in heredoc | ||||
|   lines =<< trim CODE | ||||
|     vim9script | ||||
|     com SomeCommand { | ||||
|         g:someVar =<< trim END | ||||
|           eee | ||||
|           fff | ||||
|       } | ||||
|     try | ||||
|       SomeCommand | ||||
|     catch | ||||
|       assert_match("E990: Missing end marker 'END'", v:exception) | ||||
|     endtry | ||||
|     delcommand SomeCommand | ||||
|     unlet g:someVar | ||||
|   CODE | ||||
|   v9.CheckSourceSuccess(lines) | ||||
| enddef | ||||
|  | ||||
| def Test_autocommand_block() | ||||
|   au BufNew *.xml { | ||||
|       g:otherVar = 'other' | ||||
|     } | ||||
|   | ||||
| @@ -704,6 +704,8 @@ static char *(features[]) = | ||||
|  | ||||
| static int included_patches[] = | ||||
| {   /* Add new patch number below this line */ | ||||
| /**/ | ||||
|     312, | ||||
| /**/ | ||||
|     311, | ||||
| /**/ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user