patch 9.1.0312: heredocs are not supported for :commands

Commit: 
https://github.com/vim/vim/commit/e74cad3321ce1dcefc1fc64f617511275b6cd930
Author: Yegappan Lakshmanan <yegap...@yahoo.com>
Date:   Fri Apr 12 18:48:35 2024 +0200

    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 <yegap...@yahoo.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt
index 7dd2ab018..ffb5c2e19 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 470698f0e..4dcde149f 100644
--- a/src/charset.c
+++ b/src/charset.c
@@ -2088,6 +2088,17 @@ skiptowhite(char_u *p)
     return p;
 }
 
+/*
+ * skiptowhite: skip over text until ' ' or '  ' or newline or NUL.
+ */
+    char_u *
+skiptowhite_or_nl(char_u *p)
+{
+    while (*p != ' ' && *p != '        ' && *p != NL && *p != NUL)
+       ++p;
+    return p;
+}
+
 /*
  * skiptowhite_esc: Like skiptowhite(), but also skip escaped chars
  */
diff --git a/src/evalvars.c b/src/evalvars.c
index f16d4757f..62728ed8a 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, '
') == 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, '
');
+           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 a74731931..a4f6c453d 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 5ee2e9b3c..e6d9cae8e 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
  one
  two
END
echo x")->split("
"))
+  call assert_equal(["['  one', '  two']"],
+    \ execute("let x =<< END
  one
  two
END
echo x")->split("
"))
+  let cmd = 'execute("let x =<< END
  one
  two
echo x")'
+  call assert_fails(cmd, "E990: Missing end marker 'END'")
+  let cmd = 'execute("let x =<<
  one
  two
echo x")'
+  call assert_fails(cmd, "E990: Missing end marker ''")
+  let cmd = 'execute("let x =<< trim
  one
  two
echo x")'
+  call assert_fails(cmd, "E221: Marker cannot start with lower case letter")
+  let cmd = 'execute("let x =<< eval END
  one
  two{y}
END
echo 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 cd693e599..136c81db7 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 f2d28ced7..1a924e723 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    312,
 /**/
     311,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1rvKFy-006SLm-8R%40256bit.org.

Raspunde prin e-mail lui