Mercurial > vim
changeset 16704:a927fdf9a4b0 v8.1.1354
patch 8.1.1354: getting a list of text lines is clumsy
commit https://github.com/vim/vim/commit/f5842c5a533346c4ff41ff666e465c85f1de35d5
Author: Bram Moolenaar <Bram@vim.org>
Date: Sun May 19 18:41:26 2019 +0200
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 https://github.com/vim/vim/issues/4386)
author | Bram Moolenaar <Bram@vim.org> |
---|---|
date | Sun, 19 May 2019 18:45:05 +0200 |
parents | d4eb78b4b086 |
children | 033ac0bfd3ba |
files | runtime/doc/eval.txt src/eval.c src/testdir/test_let.vim src/version.c |
diffstat | 4 files changed, 206 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -11416,6 +11416,44 @@ 7. Commands *expression-commands* 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
--- 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] = '=';
--- 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