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:
@@ -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.
|
||||||
|
|
||||||
|
@@ -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);
|
||||||
|
@@ -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
|
||||||
|
@@ -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,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user