summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-05-19 18:41:26 +0200
committerBram Moolenaar <Bram@vim.org>2019-05-19 18:41:26 +0200
commitf5842c5a533346c4ff41ff666e465c85f1de35d5 (patch)
treeb73b76cf5bfce97cb1b3e8017cb8051f77209ea0
parent2b39d806f04c1a474b6d689a7970253850d4adb8 (diff)
downloadvim-git-f5842c5a533346c4ff41ff666e465c85f1de35d5.tar.gz
patch 8.1.1354: getting a list of text lines is clumsyv8.1.1354
Problem: Getting a list of text lines is clumsy. Solution: Add the =<< assignment. (Yegappan Lakshmanan, closes #4386)
-rw-r--r--runtime/doc/eval.txt38
-rw-r--r--src/eval.c112
-rw-r--r--src/testdir/test_let.vim54
-rw-r--r--src/version.c2
4 files changed, 206 insertions, 0 deletions
diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index e43f57bc0..e82cf3235 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -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
diff --git a/src/eval.c b/src/eval.c
index 7df545584..c0c6aa7ad 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -1225,6 +1225,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
* ":let var = expr" assignment command.
@@ -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] = '=';
diff --git a/src/testdir/test_let.vim b/src/testdir/test_let.vim
index 0ff281fe5..10670b002 100644
--- a/src/testdir/test_let.vim
+++ b/src/testdir/test_let.vim
@@ -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
diff --git a/src/version.c b/src/version.c
index d16354683..6d2416d13 100644
--- a/src/version.c
+++ b/src/version.c
@@ -768,6 +768,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1354,
+/**/
1353,
/**/
1352,