summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYegappan Lakshmanan <yegappan@yahoo.com>2024-04-12 18:48:35 +0200
committerChristian Brabandt <cb@256bit.org>2024-04-12 18:53:08 +0200
commite74cad3321ce1dcefc1fc64f617511275b6cd930 (patch)
treeb29c4a7843c748750a448800846bbff1b1dfde19
parenta1dcd76ce791b5b8bd093765a99b71aa163300a5 (diff)
patch 9.1.0312: heredocs are not supported for :commandsv9.1.0312
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>
-rw-r--r--runtime/doc/vim9.txt10
-rw-r--r--src/charset.c11
-rw-r--r--src/evalvars.c63
-rw-r--r--src/proto/charset.pro1
-rw-r--r--src/testdir/test_let.vim14
-rw-r--r--src/testdir/test_vim9_script.vim102
-rw-r--r--src/version.c2
7 files changed, 189 insertions, 14 deletions
diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt
index 7dd2ab0183..ffb5c2e19a 100644
--- a/runtime/doc/vim9.txt
+++ b/runtime/doc/vim9.txt
@@ -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: >
diff --git a/src/charset.c b/src/charset.c
index 470698f0e0..4dcde149f3 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -2089,6 +2089,17 @@ skiptowhite(char_u *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
*/
char_u *
diff --git a/src/evalvars.c b/src/evalvars.c
index f16d4757f2..62728ed8ab 100644
--- a/src/evalvars.c
+++ b/src/evalvars.c
@@ -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)
diff --git a/src/proto/charset.pro b/src/proto/charset.pro
index a74731931d..a4f6c453d9 100644
--- a/src/proto/charset.pro
+++ b/src/proto/charset.pro
@@ -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);
diff --git a/src/testdir/test_let.vim b/src/testdir/test_let.vim
index 5ee2e9b3c4..e6d9cae8e5 100644
--- a/src/testdir/test_let.vim
+++ b/src/testdir/test_let.vim
@@ -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
diff --git a/src/testdir/test_vim9_script.vim b/src/testdir/test_vim9_script.vim
index cd693e5993..136c81db7d 100644
--- a/src/testdir/test_vim9_script.vim
+++ b/src/testdir/test_vim9_script.vim
@@ -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'
}
diff --git a/src/version.c b/src/version.c
index f2d28ced7a..1a924e7232 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 312,
+/**/
311,
/**/
310,