0
0
mirror of https://github.com/vim/vim.git synced 2025-07-25 10:54:51 -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 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: >

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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