mirror of
https://github.com/neovim/neovim.git
synced 2025-09-30 23:18:33 +00:00
fix(:source, nvim_exec): handle Vimscript line continuations #14809
Problem: Anonymous :source (no args) and nvim_exec() don't support Vimscript line continuations. Solution: Factor out the concat logic into concat_continued_line() and a CONCAT_CONTINUED_LINES macro for simple concatenations where lines are fetched individually. Closes #14807
This commit is contained in:
@@ -1829,6 +1829,59 @@ static void cmd_source(char_u *fname, exarg_T *eap)
|
||||
}
|
||||
}
|
||||
|
||||
/// Concatenate VimL line if it starts with a line continuation into a growarray
|
||||
/// (excluding the continuation chars and leading whitespace)
|
||||
///
|
||||
/// @note Growsize of the growarray may be changed to speed up concatenations!
|
||||
///
|
||||
/// @param ga the growarray to append to
|
||||
/// @param init_growsize the starting growsize value of the growarray
|
||||
/// @param p pointer to the beginning of the line to consider
|
||||
/// @param len the length of this line
|
||||
///
|
||||
/// @return true if this line did begin with a continuation (the next line
|
||||
/// should also be considered, if it exists); false otherwise
|
||||
static bool concat_continued_line(garray_T *const ga, const int init_growsize,
|
||||
const char_u *const p, size_t len)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
const char_u *const line = skipwhite_len(p, len);
|
||||
len -= (size_t)(line - p);
|
||||
// Skip lines starting with '\" ', concat lines starting with '\'
|
||||
if (len >= 3 && STRNCMP(line, "\"\\ ", 3) == 0) {
|
||||
return true;
|
||||
} else if (len == 0 || line[0] != '\\') {
|
||||
return false;
|
||||
}
|
||||
if (ga->ga_len > init_growsize) {
|
||||
ga_set_growsize(ga, MAX(ga->ga_len, 8000));
|
||||
}
|
||||
ga_concat_len(ga, (const char *)line + 1, len - 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Concatenate (possibly many) VimL lines starting with line continuations into
|
||||
/// a growarray. @see concat_continued_line
|
||||
///
|
||||
/// @note All parameters, excluding `ga`, accept expressions that are evaluated
|
||||
/// once for each line; side-effects may be triggered many times!
|
||||
///
|
||||
/// @param ga the growarray to append to
|
||||
/// @param cond should evaluate to true if a next line exists
|
||||
/// @param line should evaluate to the current line
|
||||
/// @param next should handle fetching the next line when evaluated
|
||||
#define CONCAT_CONTINUED_LINES(ga, cond, line, next) \
|
||||
do { \
|
||||
garray_T *const ga_ = (ga); \
|
||||
const int init_growsize_ = ga_->ga_growsize; \
|
||||
for (; (cond); (next)) { \
|
||||
const char_u *const line_ = (line); \
|
||||
if (!concat_continued_line(ga_, init_growsize_, line_, STRLEN(line_))) { \
|
||||
break; \
|
||||
} \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
typedef struct {
|
||||
linenr_T curr_lnum;
|
||||
const linenr_T final_lnum;
|
||||
@@ -1845,9 +1898,15 @@ static char_u *get_buffer_line(int c, void *cookie, int indent, bool do_concat)
|
||||
if (p->curr_lnum > p->final_lnum) {
|
||||
return NULL;
|
||||
}
|
||||
char_u *curr_line = ml_get(p->curr_lnum);
|
||||
p->curr_lnum++;
|
||||
return (char_u *)xstrdup((const char *)curr_line);
|
||||
garray_T ga;
|
||||
ga_init(&ga, sizeof(char_u), 400);
|
||||
ga_concat(&ga, ml_get(p->curr_lnum++));
|
||||
if (do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) {
|
||||
CONCAT_CONTINUED_LINES(&ga, p->curr_lnum <= p->final_lnum,
|
||||
ml_get(p->curr_lnum), p->curr_lnum++);
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
return ga.ga_data;
|
||||
}
|
||||
|
||||
static void cmd_source_buffer(const exarg_T *eap)
|
||||
@@ -1919,17 +1978,27 @@ typedef struct {
|
||||
static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat)
|
||||
{
|
||||
GetStrLineCookie *p = cookie;
|
||||
size_t i = p->offset;
|
||||
if (strlen((char *)p->buf) <= p->offset) {
|
||||
if (STRLEN(p->buf) <= p->offset) {
|
||||
return NULL;
|
||||
}
|
||||
while (!(p->buf[i] == '\n' || p->buf[i] == '\0')) {
|
||||
i++;
|
||||
const char_u *line = p->buf + p->offset;
|
||||
const char_u *eol = skip_to_newline(line);
|
||||
garray_T ga;
|
||||
ga_init(&ga, sizeof(char_u), 400);
|
||||
ga_concat_len(&ga, (const char *)line, (size_t)(eol - line));
|
||||
if (do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) {
|
||||
while (eol[0] != NUL) {
|
||||
line = eol + 1;
|
||||
const char_u *const next_eol = skip_to_newline(line);
|
||||
if (!concat_continued_line(&ga, 400, line, (size_t)(next_eol - line))) {
|
||||
break;
|
||||
}
|
||||
eol = next_eol;
|
||||
}
|
||||
}
|
||||
size_t line_length = i - p->offset;
|
||||
char_u *buf = xmemdupz(p->buf + p->offset, line_length);
|
||||
p->offset = i + 1;
|
||||
return buf;
|
||||
ga_append(&ga, NUL);
|
||||
p->offset = (size_t)(eol - p->buf) + 1;
|
||||
return ga.ga_data;
|
||||
}
|
||||
|
||||
static int source_using_linegetter(void *cookie,
|
||||
@@ -2421,27 +2490,9 @@ char_u *getsourceline(int c, void *cookie, int indent, bool do_concat)
|
||||
|
||||
ga_init(&ga, (int)sizeof(char_u), 400);
|
||||
ga_concat(&ga, line);
|
||||
if (*p == '\\') {
|
||||
ga_concat(&ga, p + 1);
|
||||
}
|
||||
for (;; ) {
|
||||
xfree(sp->nextline);
|
||||
sp->nextline = get_one_sourceline(sp);
|
||||
if (sp->nextline == NULL) {
|
||||
break;
|
||||
}
|
||||
p = skipwhite(sp->nextline);
|
||||
if (*p == '\\') {
|
||||
// Adjust the growsize to the current length to speed up
|
||||
// concatenating many lines.
|
||||
if (ga.ga_len > 400) {
|
||||
ga_set_growsize(&ga, (ga.ga_len > 8000) ? 8000 : ga.ga_len);
|
||||
}
|
||||
ga_concat(&ga, p + 1);
|
||||
} else if (p[0] != '"' || p[1] != '\\' || p[2] != ' ') {
|
||||
break;
|
||||
}
|
||||
}
|
||||
CONCAT_CONTINUED_LINES(&ga, sp->nextline != NULL, sp->nextline,
|
||||
(xfree(sp->nextline),
|
||||
sp->nextline = get_one_sourceline(sp)));
|
||||
ga_append(&ga, NUL);
|
||||
xfree(line);
|
||||
line = ga.ga_data;
|
||||
|
Reference in New Issue
Block a user