]> granicus.if.org Git - vim/commitdiff
patch 8.1.1354: getting a list of text lines is clumsy v8.1.1354
authorBram Moolenaar <Bram@vim.org>
Sun, 19 May 2019 16:41:26 +0000 (18:41 +0200)
committerBram Moolenaar <Bram@vim.org>
Sun, 19 May 2019 16:41:26 +0000 (18:41 +0200)
Problem:    Getting a list of text lines is clumsy.
Solution:   Add the =<< assignment. (Yegappan Lakshmanan, closes #4386)

runtime/doc/eval.txt
src/eval.c
src/testdir/test_let.vim
src/version.c

index e43f57bc0c12b04cf5f8b19ad4536ab319205868..e82cf3235260cb1bd3205f2e7c1b3b923baaf97b 100644 (file)
@@ -11416,6 +11416,44 @@ This does NOT work: >
                        Like above, but append/add/subtract the value for each
                        |List| item.
 
+                                       *:let=<<* *:let-heredoc* *E990* *E991*
+:let {var-name} =<< [trim] {marker}
+text...
+text...
+{marker}
+                       Set internal variable {var-name} to a List containing
+                       the lines of text bounded by the string {marker}.
+                       {marker} must not contain white space.
+                       The last line should end only with the {marker} string
+                       without any other character.  Watch out for white
+                       space after {marker}!
+                       If {marker} is not supplied, then "." is used as the
+                       default marker.
+
+                       Any white space characters in the lines of text are
+                       preserved.  If "trim" is specified before {marker},
+                       then all the leading indentation exactly matching the
+                       leading indentation before `let` is stripped from the
+                       input lines and the line containing {marker}.  Note
+                       that the difference between space and tab matters
+                       here.
+
+                       If {var-name} didn't exist yet, it is created.
+                       Cannot be followed by another command, but can be
+                       followed by a comment.
+
+                       Examples: >
+                               let var1 =<< END
+                       Sample text 1
+                           Sample text 2
+                       Sample text 3
+                       END
+
+                               let data =<< trim DATA
+                               1 2 3 4
+                               5 6 7 8
+                               DATA
+<
                                                                *E121*
 :let {var-name}        ..      List the value of variable {var-name}.  Multiple
                        variable names may be given.  Special names recognized
index 7df5455849a45a2d480bd62f2a77f48ec485567b..c0c6aa7ad50ae52537dc0364234a39d4b3bd5f67 100644 (file)
@@ -1224,6 +1224,102 @@ eval_foldexpr(char_u *arg, int *cp)
 }
 #endif
 
+/*
+ * Get a list of lines from a HERE document. The here document is a list of
+ * lines surrounded by a marker.
+ *     cmd << {marker}
+ *       {line1}
+ *       {line2}
+ *       ....
+ *     {marker}
+ *
+ * The {marker} is a string. If the optional 'trim' word is supplied before the
+ * marker, then the leading indentation before the lines (matching the
+ * indentation in the 'cmd' line) is stripped.
+ * Returns a List with {lines} or NULL.
+ */
+    static list_T *
+heredoc_get(exarg_T *eap, char_u *cmd)
+{
+    char_u     *theline;
+    char_u     *marker;
+    list_T     *l;
+    char_u     *p;
+    int                indent_len = 0;
+
+    if (eap->getline == NULL)
+    {
+       emsg(_("E991: cannot use =<< here"));
+       return NULL;
+    }
+
+    // Check for the optional 'trim' word before the marker
+    cmd = skipwhite(cmd);
+    if (STRNCMP(cmd, "trim", 4) == 0 && (cmd[4] == NUL || VIM_ISWHITE(cmd[4])))
+    {
+       cmd = skipwhite(cmd + 4);
+
+       // Trim the indentation from all the lines in the here document
+       // The amount of indentation trimmed is the same as the indentation of
+       // the :let command line.
+       p = *eap->cmdlinep;
+       while (VIM_ISWHITE(*p))
+       {
+           p++;
+           indent_len++;
+       }
+    }
+
+    // The marker is the next word.  Default marker is "."
+    if (*cmd != NUL && *cmd != '"')
+    {
+       marker = skipwhite(cmd);
+       p = skiptowhite(marker);
+       if (*skipwhite(p) != NUL && *skipwhite(p) != '"')
+       {
+           emsg(_(e_trailing));
+           return NULL;
+       }
+       *p = NUL;
+    }
+    else
+       marker = (char_u *)".";
+
+    l = list_alloc();
+    if (l == NULL)
+       return NULL;
+
+    for (;;)
+    {
+       int     i = 0;
+
+       theline = eap->getline(NUL, eap->cookie, 0);
+       if (theline != NULL && indent_len > 0)
+       {
+           // trim the indent matching the first line
+           if (STRNCMP(theline, *eap->cmdlinep, indent_len) == 0)
+               i = indent_len;
+       }
+
+       if (theline == NULL)
+       {
+           semsg(_("E990: Missing end marker '%s'"), marker);
+           break;
+       }
+       if (STRCMP(marker, theline + i) == 0)
+       {
+           vim_free(theline);
+           break;
+       }
+
+       if (list_append_string(l, theline + i, -1) == FAIL)
+           break;
+       vim_free(theline);
+    }
+
+    return l;
+}
+
 /*
  * ":let"                      list all variable values
  * ":let var1 var2"            list variable values
@@ -1286,6 +1382,22 @@ ex_let(exarg_T *eap)
        }
        eap->nextcmd = check_nextcmd(arg);
     }
+    else if (expr[0] == '=' && expr[1] == '<' && expr[2] == '<')
+    {
+       list_T  *l;
+
+       // HERE document
+       l = heredoc_get(eap, expr + 3);
+       if (l != NULL)
+       {
+           rettv_list_set(&rettv, l);
+           op[0] = '=';
+           op[1] = NUL;
+           (void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
+                                                                         op);
+           clear_tv(&rettv);
+       }
+    }
     else
     {
        op[0] = '=';
index 0ff281fe50f2194511c68003c79ab858aedb17ec..10670b002a477ef1b0b7b5b4077820f8e0de636f 100644 (file)
@@ -151,3 +151,57 @@ func Test_let_utf8_environment()
   let $a = 'ĀĒĪŌŪあいうえお'
   call assert_equal('ĀĒĪŌŪあいうえお', $a)
 endfunc
+
+" Test for the setting a variable using the heredoc syntax
+func Test_let_heredoc()
+  let var1 =<< END
+Some sample text
+       Text with indent
+  !@#$%^&*()-+_={}|[]\~`:";'<>?,./
+END
+
+  call assert_equal(["Some sample text", "\tText with indent", "  !@#$%^&*()-+_={}|[]\\~`:\";'<>?,./"], var1)
+
+  let var2 =<<
+Editor
+.
+  call assert_equal(['Editor'], var2)
+
+  let var3 =<<END
+END
+  call assert_equal([], var3)
+
+  let var3 =<<END
+vim
+
+end
+  END
+END 
+END
+  call assert_equal(['vim', '', 'end', '  END', 'END '], var3)
+
+       let var1 =<< trim END
+       Line1
+         Line2
+               Line3
+        END
+       END
+  call assert_equal(['Line1', '  Line2', "\tLine3", ' END'], var1)
+
+  let var1 =<< trim
+    Line1
+  .
+  call assert_equal(['  Line1'], var1)
+
+  call assert_fails('let v =<< marker', 'E991:')
+  call assert_fails('call WrongSyntax()', 'E488:')
+  call assert_fails('call MissingEnd()', 'E990:')
+endfunc
+
+func WrongSyntax()
+  let fail =<< that there
+endfunc
+
+func MissingEnd()
+  let fail =<< END
+endfunc
index d163546831a87b2ebef4ca70674179112922d8de..6d2416d1353fc541362de71d65e3301efc1d3f07 100644 (file)
@@ -767,6 +767,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    1354,
 /**/
     1353,
 /**/