To: vim_dev@googlegroups.com Subject: Patch 8.2.0997 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ Patch 8.2.0997 Problem: Cannot execute a register containing line continuation. Solution: Concatenate lines where needed. (Yegappan Lakshmanan, closes #6272) Files: runtime/doc/repeat.txt, src/register.c, src/testdir/test_registers.vim *** ../vim-8.2.0996/runtime/doc/repeat.txt 2019-12-17 21:27:14.690319902 +0100 --- runtime/doc/repeat.txt 2020-06-17 21:43:51.089410095 +0200 *************** *** 158,163 **** --- 163,173 ---- result of evaluating the expression is executed as an Ex command. Mappings are not recognized in these commands. + When the |line-continuation| character (\) is present + at the beginning of a line in a linewise register, + then it is combined with the previous line. This is + useful for yanking and executing parts of a Vim + script. Future: Will execute the register for each line in the address range. *** ../vim-8.2.0996/src/register.c 2020-06-12 22:59:07.270097188 +0200 --- src/register.c 2020-06-17 21:43:51.089410095 +0200 *************** *** 474,479 **** --- 474,546 ---- } /* + * When executing a register as a series of ex-commands, if the + * line-continuation character is used for a line, then join it with one or + * more previous lines. Note that lines are processed backwards starting from + * the last line in the register. + * + * Arguments: + * lines - list of lines in the register + * idx - index of the line starting with \ or "\. Join this line with all the + * immediate predecessor lines that start with a \ and the first line + * that doesn't start with a \. Lines that start with a comment "\ + * character are ignored. + * + * Returns the concatenated line. The index of the line that should be + * processed next is returned in idx. + */ + static char_u * + execreg_line_continuation(char_u **lines, long *idx) + { + garray_T ga; + long i = *idx; + char_u *p; + int cmd_start; + int cmd_end = i; + int j; + char_u *str; + + ga_init2(&ga, (int)sizeof(char_u), 400); + + // search backwards to find the first line of this command. + // Any line not starting with \ or "\ is the start of the + // command. + while (--i > 0) + { + p = skipwhite(lines[i]); + if (*p != '\\' && (p[0] != '"' || p[1] != '\\' || p[2] != ' ')) + break; + } + cmd_start = i; + + // join all the lines + ga_concat(&ga, lines[cmd_start]); + for (j = cmd_start + 1; j <= cmd_end; j++) + { + p = skipwhite(lines[j]); + if (*p == '\\') + { + // Adjust the growsize to the current length to + // speed up concatenating many lines. + if (ga.ga_len > 400) + { + if (ga.ga_len > 8000) + ga.ga_growsize = 8000; + else + ga.ga_growsize = ga.ga_len; + } + ga_concat(&ga, p + 1); + } + } + ga_append(&ga, NUL); + str = vim_strsave(ga.ga_data); + ga_clear(&ga); + + *idx = i; + return str; + } + + /* * Execute a yank register: copy it into the stuff buffer. * * Return FAIL for failure, OK otherwise. *************** *** 579,584 **** --- 646,653 ---- for (i = y_current->y_size; --i >= 0; ) { char_u *escaped; + char_u *str; + int free_str = FALSE; // insert NL between lines and after last line if type is MLINE if (y_current->y_type == MLINE || i < y_current->y_size - 1 *************** *** 587,593 **** if (ins_typebuf((char_u *)"\n", remap, 0, TRUE, silent) == FAIL) return FAIL; } ! escaped = vim_strsave_escape_csi(y_current->y_array[i]); if (escaped == NULL) return FAIL; retval = ins_typebuf(escaped, remap, 0, TRUE, silent); --- 656,678 ---- if (ins_typebuf((char_u *)"\n", remap, 0, TRUE, silent) == FAIL) return FAIL; } ! ! // Handle line-continuation for :@ ! str = y_current->y_array[i]; ! if (colon && i > 0) ! { ! p = skipwhite(str); ! if (*p == '\\' || (p[0] == '"' && p[1] == '\\' && p[2] == ' ')) ! { ! str = execreg_line_continuation(y_current->y_array, &i); ! if (str == NULL) ! return FAIL; ! free_str = TRUE; ! } ! } ! escaped = vim_strsave_escape_csi(str); ! if (free_str) ! vim_free(str); if (escaped == NULL) return FAIL; retval = ins_typebuf(escaped, remap, 0, TRUE, silent); *** ../vim-8.2.0996/src/testdir/test_registers.vim 2020-06-07 21:31:14.778405833 +0200 --- src/testdir/test_registers.vim 2020-06-17 21:43:51.089410095 +0200 *************** *** 557,560 **** --- 557,636 ---- bwipe! endfunc + " Test for executing the contents of a register as an Ex command with line + " continuation. + func Test_execute_reg_as_ex_cmd() + " Line continuation with just two lines + let code =<< trim END + let l = [ + \ 1] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1], l) + + " Line continuation with more than two lines + let code =<< trim END + let l = [ + \ 1, + \ 2, + \ 3] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2, 3], l) + + " use comments interspersed with code + let code =<< trim END + let l = [ + "\ one + \ 1, + "\ two + \ 2, + "\ three + \ 3] + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2, 3], l) + + " use line continuation in the middle + let code =<< trim END + let a = "one" + let l = [ + \ 1, + \ 2] + let b = "two" + END + let @r = code->join("\n") + let l = [] + @r + call assert_equal([1, 2], l) + call assert_equal("one", a) + call assert_equal("two", b) + + " only one line with a \ + let @r = "\\let l = 1" + call assert_fails('@r', 'E10:') + + " only one line with a "\ + let @r = ' "\ let i = 1' + @r + call assert_false(exists('i')) + + " first line also begins with a \ + let @r = "\\let l = [\n\\ 1]" + call assert_fails('@r', 'E10:') + + " Test with a large number of lines + let @r = "let str = \n" + let @r ..= repeat(" \\ 'abcdefghijklmnopqrstuvwxyz' ..\n", 312) + let @r ..= ' \ ""' + @r + call assert_equal(repeat('abcdefghijklmnopqrstuvwxyz', 312), str) + endfunc + " vim: shiftwidth=2 sts=2 expandtab *** ../vim-8.2.0996/src/version.c 2020-06-17 21:41:31.973819210 +0200 --- src/version.c 2020-06-17 21:45:29.945120090 +0200 *************** *** 756,757 **** --- 756,759 ---- { /* Add new patch number below this line */ + /**/ + 997, /**/ -- GUEST: He's killed the best man! SECOND GUEST: (holding a limp WOMAN) He's killed my auntie. FATHER: No, please! This is supposed to be a happy occasion! Let's not bicker and argue about who killed who ... "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///