1
0
forked from aniani/vim

patch 8.2.0997: cannot execute a register containing line continuation

Problem:    Cannot execute a register containing line continuation.
Solution:   Concatenate lines where needed. (Yegappan Lakshmanan,
            closes #6272)
This commit is contained in:
Bram Moolenaar
2020-06-17 21:47:23 +02:00
parent 40a019f157
commit 856c1110c1
4 changed files with 169 additions and 1 deletions

View File

@@ -163,6 +163,11 @@ q Stops recording. (Implementation note: The 'q' that
result of evaluating the expression is executed as an result of evaluating the expression is executed as an
Ex command. Ex command.
Mappings are not recognized in these commands. 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 Future: Will execute the register for each line in the
address range. address range.

View File

@@ -473,6 +473,73 @@ set_execreg_lastc(int lastc)
execreg_lastc = 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. * Execute a yank register: copy it into the stuff buffer.
* *
@@ -579,6 +646,8 @@ do_execreg(
for (i = y_current->y_size; --i >= 0; ) for (i = y_current->y_size; --i >= 0; )
{ {
char_u *escaped; char_u *escaped;
char_u *str;
int free_str = FALSE;
// insert NL between lines and after last line if type is MLINE // insert NL between lines and after last line if type is MLINE
if (y_current->y_type == MLINE || i < y_current->y_size - 1 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) if (ins_typebuf((char_u *)"\n", remap, 0, TRUE, silent) == FAIL)
return 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) if (escaped == NULL)
return FAIL; return FAIL;
retval = ins_typebuf(escaped, remap, 0, TRUE, silent); retval = ins_typebuf(escaped, remap, 0, TRUE, silent);

View File

@@ -557,4 +557,80 @@ func Test_v_register()
bwipe! bwipe!
endfunc 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: shiftwidth=2 sts=2 expandtab

View File

@@ -754,6 +754,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
997,
/**/ /**/
996, 996,
/**/ /**/