]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 8.2.0997: cannot execute a register containing line continuation 6282/head v8.2.0997
authorBram Moolenaar <Bram@vim.org>
Wed, 17 Jun 2020 19:47:23 +0000 (21:47 +0200)
committerBram Moolenaar <Bram@vim.org>
Wed, 17 Jun 2020 19:47:23 +0000 (21:47 +0200)
Problem:    Cannot execute a register containing line continuation.
Solution:   Concatenate lines where needed. (Yegappan Lakshmanan,
            closes #6272)

runtime/doc/repeat.txt
src/register.c
src/testdir/test_registers.vim
src/version.c

index 29cdd73bb36256aa6a364c792db3a044e1fc8ff4..7b0efddbb8b8d6c428d47c45922bc674d4a4803a 100644 (file)
@@ -163,6 +163,11 @@ q                  Stops recording.  (Implementation note: The 'q' that
                        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.
 
index 2db5136d0f62c23ff89136b87e5b505362127ddd..49f7a7c86af67a2293ec15c4571f1a2b1fc771f8 100644 (file)
@@ -473,6 +473,73 @@ set_execreg_lastc(int lastc)
     execreg_lastc = lastc;
 }
 
+/*
+ * 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.
  *
@@ -579,6 +646,8 @@ do_execreg(
        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,7 +656,23 @@ do_execreg(
                if (ins_typebuf((char_u *)"\n", remap, 0, TRUE, silent) == FAIL)
                    return FAIL;
            }
-           escaped = vim_strsave_escape_csi(y_current->y_array[i]);
+
+           // Handle line-continuation for :@<register>
+           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);
index 22fac08455d74b175952e1a3f9bcb6210d5b1059..627799c49319cc7a4e805d8a07edceaa63285652 100644 (file)
@@ -557,4 +557,80 @@ func Test_v_register()
   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
index f46bb4759442aa41607cbb60ebe1c8a0b41a6499..25ada8ca77fdf60437c4550e87d8460cbef898cc 100644 (file)
@@ -754,6 +754,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    997,
 /**/
     996,
 /**/