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:
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,
|
||||||
/**/
|
/**/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user