0
0
mirror of https://github.com/vim/vim.git synced 2025-07-24 10:45:12 -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:
Yegappan Lakshmanan 2024-04-12 18:48:35 +02:00 committed by Christian Brabandt
parent a1dcd76ce7
commit e74cad3321
No known key found for this signature in database
GPG Key ID: F3F92DA383FDDE09
7 changed files with 189 additions and 14 deletions

View File

@ -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: >

View File

@ -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
*/

View File

@ -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,12 +867,38 @@ heredoc_get(exarg_T *eap, char_u *cmd, int script_get, int vim9compile)
int mi = 0;
int ti = 0;
vim_free(theline);
theline = eap->ea_getline(NUL, eap->cookie, 0, FALSE);
if (theline == NULL)
if (heredoc_in_string)
{
semsg(_(e_missing_end_marker_str), marker);
break;
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)
{
semsg(_(e_missing_end_marker_str), marker);
break;
}
}
// 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
{
int free_str = FALSE;
if (evalstr && !eap->skip)
{
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;
continue;
}
vim_free(theline);
theline = str;
free_str = TRUE;
}
if (list_append_string(l, str, -1) == FAIL)
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);
if (vim9compile && cctx->ctx_skip != SKIP_YES && !eval_failed)

View File

@ -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);

View File

@ -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

View File

@ -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'
}

View File

@ -704,6 +704,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
312,
/**/
311,
/**/