Skip to content
Permalink
Browse files

patch 8.1.1354: getting a list of text lines is clumsy

Problem:    Getting a list of text lines is clumsy.
Solution:   Add the =<< assignment. (Yegappan Lakshmanan, closes #4386)
  • Loading branch information...
brammool committed May 19, 2019
1 parent 2b39d80 commit f5842c5a533346c4ff41ff666e465c85f1de35d5
Showing with 206 additions and 0 deletions.
  1. +38 −0 runtime/doc/eval.txt
  2. +112 −0 src/eval.c
  3. +54 −0 src/testdir/test_let.vim
  4. +2 −0 src/version.c
@@ -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
@@ -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] = '=';
@@ -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
@@ -767,6 +767,8 @@ static char *(features[]) =

static int included_patches[] =
{ /* Add new patch number below this line */
/**/
1354,
/**/
1353,
/**/

0 comments on commit f5842c5

Please sign in to comment.
You can’t perform that action at this time.