mirror of
				https://github.com/vim/vim.git
				synced 2025-10-30 09:47:20 -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 | 		  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 | The block can also be used for defining a user command.  Inside the block Vim9 | ||||||
| syntax will be used. | 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 | 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 | written at the start of a line.  Otherwise, it would be parsed as the end of | ||||||
| the block.  This does not work: > | the block.  This does not work: > | ||||||
|   | |||||||
| @@ -2088,6 +2088,17 @@ skiptowhite(char_u *p) | |||||||
|     return 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 |  * 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; |     int		eval_failed = FALSE; | ||||||
|     cctx_T	*cctx = vim9compile ? eap->cookie : NULL; |     cctx_T	*cctx = vim9compile ? eap->cookie : NULL; | ||||||
|     int		count = 0; |     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)); | 	emsg(_(e_cannot_use_heredoc_here)); | ||||||
| 	return NULL; | 	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) |     if (*cmd != NUL && *cmd != comment_char) | ||||||
|     { |     { | ||||||
| 	marker = skipwhite(cmd); | 	marker = skipwhite(cmd); | ||||||
| 	p = skiptowhite(marker); | 	p = skiptowhite_or_nl(marker); | ||||||
| 	if (*skipwhite(p) != NUL && *skipwhite(p) != comment_char) | 	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); | 	    semsg(_(e_trailing_characters_str), p); | ||||||
| 	    return NULL; | 	    return NULL; | ||||||
| @@ -859,12 +867,38 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) | |||||||
| 	int	mi = 0; | 	int	mi = 0; | ||||||
| 	int	ti = 0; | 	int	ti = 0; | ||||||
|  |  | ||||||
| 	vim_free(theline); | 	if (heredoc_in_string) | ||||||
| 	theline = eap->ea_getline(NUL, eap->cookie, 0, FALSE); |  | ||||||
| 	if (theline == NULL) |  | ||||||
| 	{ | 	{ | ||||||
| 	    semsg(_(e_missing_end_marker_str), marker); | 	    char_u	*next_line; | ||||||
| 	    break; |  | ||||||
|  | 	    // 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) | ||||||
|  | 	    { | ||||||
|  | 		semsg(_(e_missing_end_marker_str), marker); | ||||||
|  | 		break; | ||||||
|  | 	    } | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	// with "trim": skip the indent matching the :let line to find the | 	// with "trim": skip the indent matching the :let line to find the | ||||||
| @@ -911,6 +945,8 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) | |||||||
| 	} | 	} | ||||||
| 	else | 	else | ||||||
| 	{ | 	{ | ||||||
|  | 	    int	    free_str = FALSE; | ||||||
|  |  | ||||||
| 	    if (evalstr && !eap->skip) | 	    if (evalstr && !eap->skip) | ||||||
| 	    { | 	    { | ||||||
| 		str = eval_all_expr_in_str(str); | 		str = eval_all_expr_in_str(str); | ||||||
| @@ -920,15 +956,20 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile) | |||||||
| 		    eval_failed = TRUE; | 		    eval_failed = TRUE; | ||||||
| 		    continue; | 		    continue; | ||||||
| 		} | 		} | ||||||
| 		vim_free(theline); | 		free_str = TRUE; | ||||||
| 		theline = str; |  | ||||||
| 	    } | 	    } | ||||||
|  |  | ||||||
| 	    if (list_append_string(l, str, -1) == FAIL) | 	    if (list_append_string(l, str, -1) == FAIL) | ||||||
| 		break; | 		break; | ||||||
|  | 	    if (free_str) | ||||||
|  | 		vim_free(str); | ||||||
| 	} | 	} | ||||||
|     } |     } | ||||||
|     vim_free(theline); |     if (heredoc_in_string) | ||||||
|  | 	// Next command follows the heredoc in the string. | ||||||
|  | 	eap->nextcmd = line_arg; | ||||||
|  |     else | ||||||
|  | 	vim_free(theline); | ||||||
|     vim_free(text_indent); |     vim_free(text_indent); | ||||||
|  |  | ||||||
|     if (vim9compile && cctx->ctx_skip != SKIP_YES && !eval_failed) |     if (vim9compile && cctx->ctx_skip != SKIP_YES && !eval_failed) | ||||||
|   | |||||||
| @@ -61,6 +61,7 @@ int vim_isalpha(int c); | |||||||
| int vim_toupper(int c); | int vim_toupper(int c); | ||||||
| int vim_tolower(int c); | int vim_tolower(int c); | ||||||
| char_u *skiptowhite(char_u *p); | char_u *skiptowhite(char_u *p); | ||||||
|  | char_u *skiptowhite_or_nl(char_u *p); | ||||||
| char_u *skiptowhite_esc(char_u *p); | char_u *skiptowhite_esc(char_u *p); | ||||||
| long getdigits(char_u **pp); | long getdigits(char_u **pp); | ||||||
| long getdigits_quoted(char_u **pp); | long getdigits_quoted(char_u **pp); | ||||||
|   | |||||||
| @@ -715,6 +715,20 @@ END | |||||||
|   LINES |   LINES | ||||||
|   call v9.CheckScriptFailure(lines, 'E15:') |   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 |   " skipped heredoc | ||||||
|   if 0 |   if 0 | ||||||
|     let msg =<< trim eval END |     let msg =<< trim eval END | ||||||
|   | |||||||
| @@ -458,7 +458,7 @@ func s:InvokeSomeCommand() | |||||||
|   SomeCommand |   SomeCommand | ||||||
| endfunc | endfunc | ||||||
|  |  | ||||||
| def Test_autocommand_block() | def Test_command_block() | ||||||
|   com SomeCommand { |   com SomeCommand { | ||||||
|       g:someVar = 'some' |       g:someVar = 'some' | ||||||
|     } |     } | ||||||
| @@ -469,7 +469,105 @@ def Test_autocommand_block() | |||||||
|   unlet g:someVar |   unlet g:someVar | ||||||
| enddef | 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 { |   au BufNew *.xml { | ||||||
|       g:otherVar = 'other' |       g:otherVar = 'other' | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -704,6 +704,8 @@ static char *(features[]) = | |||||||
|  |  | ||||||
| static int included_patches[] = | static int included_patches[] = | ||||||
| {   /* Add new patch number below this line */ | {   /* Add new patch number below this line */ | ||||||
|  | /**/ | ||||||
|  |     312, | ||||||
| /**/ | /**/ | ||||||
|     311, |     311, | ||||||
| /**/ | /**/ | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user