forked from aniani/vim
patch 8.2.2951: Vim9: cannot use heredoc for :python, :lua, etc.
Problem: Vim9: cannot use heredoc in :def function for :python, :lua, etc. Solution: Concatenate the heredoc lines and pass them in the ISN_EXEC_SPLIT instruction.
This commit is contained in:
@@ -121,6 +121,23 @@ def Test_disassemble_exec_expr()
|
|||||||
res)
|
res)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
if has('python3')
|
||||||
|
def s:PyHeredoc()
|
||||||
|
python3 << EOF
|
||||||
|
print('hello')
|
||||||
|
EOF
|
||||||
|
enddef
|
||||||
|
|
||||||
|
def Test_disassemble_python_heredoc()
|
||||||
|
var res = execute('disass s:PyHeredoc')
|
||||||
|
assert_match('<SNR>\d*_PyHeredoc.*' ..
|
||||||
|
" python3 << EOF^@ print('hello')^@EOF\\_s*" ..
|
||||||
|
'\d EXEC_SPLIT python3 << EOF^@ print(''hello'')^@EOF\_s*' ..
|
||||||
|
'\d RETURN 0',
|
||||||
|
res)
|
||||||
|
enddef
|
||||||
|
endif
|
||||||
|
|
||||||
def s:Substitute()
|
def s:Substitute()
|
||||||
var expr = "abc"
|
var expr = "abc"
|
||||||
:%s/a/\=expr/&g#c
|
:%s/a/\=expr/&g#c
|
||||||
|
@@ -2758,5 +2758,33 @@ def Test_closing_brace_at_start_of_line()
|
|||||||
call CheckDefAndScriptSuccess(lines)
|
call CheckDefAndScriptSuccess(lines)
|
||||||
enddef
|
enddef
|
||||||
|
|
||||||
|
if has('python3')
|
||||||
|
def Test_python3_heredoc()
|
||||||
|
py3 << trim EOF
|
||||||
|
import vim
|
||||||
|
vim.vars['didit'] = 'yes'
|
||||||
|
EOF
|
||||||
|
assert_equal('yes', g:didit)
|
||||||
|
|
||||||
|
python3 << trim EOF
|
||||||
|
import vim
|
||||||
|
vim.vars['didit'] = 'again'
|
||||||
|
EOF
|
||||||
|
assert_equal('again', g:didit)
|
||||||
|
enddef
|
||||||
|
endif
|
||||||
|
|
||||||
|
" This messes up syntax highlight, keep near the end.
|
||||||
|
if has('lua')
|
||||||
|
def Test_lua_heredoc()
|
||||||
|
g:d = {}
|
||||||
|
lua << trim EOF
|
||||||
|
x = vim.eval('g:d')
|
||||||
|
x['key'] = 'val'
|
||||||
|
EOF
|
||||||
|
assert_equal('val', g:d.key)
|
||||||
|
enddef
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
" vim: ts=8 sw=2 sts=2 expandtab tw=80 fdm=marker
|
||||||
|
@@ -631,8 +631,12 @@ get_function_body(
|
|||||||
char_u *skip_until = NULL;
|
char_u *skip_until = NULL;
|
||||||
int ret = FAIL;
|
int ret = FAIL;
|
||||||
int is_heredoc = FALSE;
|
int is_heredoc = FALSE;
|
||||||
|
int heredoc_concat_len = 0;
|
||||||
|
garray_T heredoc_ga;
|
||||||
char_u *heredoc_trimmed = NULL;
|
char_u *heredoc_trimmed = NULL;
|
||||||
|
|
||||||
|
ga_init2(&heredoc_ga, 1, 500);
|
||||||
|
|
||||||
// Detect having skipped over comment lines to find the return
|
// Detect having skipped over comment lines to find the return
|
||||||
// type. Add NULL lines to keep the line count correct.
|
// type. Add NULL lines to keep the line count correct.
|
||||||
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
|
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
|
||||||
@@ -733,6 +737,20 @@ get_function_body(
|
|||||||
getline_options = vim9_function
|
getline_options = vim9_function
|
||||||
? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
|
? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
|
||||||
is_heredoc = FALSE;
|
is_heredoc = FALSE;
|
||||||
|
|
||||||
|
if (heredoc_concat_len > 0)
|
||||||
|
{
|
||||||
|
// Replace the starting line with all the concatenated
|
||||||
|
// lines.
|
||||||
|
ga_concat(&heredoc_ga, theline);
|
||||||
|
vim_free(((char_u **)(newlines->ga_data))[
|
||||||
|
heredoc_concat_len - 1]);
|
||||||
|
((char_u **)(newlines->ga_data))[
|
||||||
|
heredoc_concat_len - 1] = heredoc_ga.ga_data;
|
||||||
|
ga_init(&heredoc_ga);
|
||||||
|
heredoc_concat_len = 0;
|
||||||
|
theline += STRLEN(theline); // skip the "EOF"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -886,6 +904,8 @@ get_function_body(
|
|||||||
skip_until = vim_strnsave(p, skiptowhite(p) - p);
|
skip_until = vim_strnsave(p, skiptowhite(p) - p);
|
||||||
getline_options = GETLINE_NONE;
|
getline_options = GETLINE_NONE;
|
||||||
is_heredoc = TRUE;
|
is_heredoc = TRUE;
|
||||||
|
if (eap->cmdidx == CMD_def)
|
||||||
|
heredoc_concat_len = newlines->ga_len + 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for ":cmd v =<< [trim] EOF"
|
// Check for ":cmd v =<< [trim] EOF"
|
||||||
@@ -928,10 +948,21 @@ get_function_body(
|
|||||||
if (ga_grow(newlines, 1 + sourcing_lnum_off) == FAIL)
|
if (ga_grow(newlines, 1 + sourcing_lnum_off) == FAIL)
|
||||||
goto theend;
|
goto theend;
|
||||||
|
|
||||||
|
if (heredoc_concat_len > 0)
|
||||||
|
{
|
||||||
|
// For a :def function "python << EOF" concatenats all the lines,
|
||||||
|
// to be used for the instruction later.
|
||||||
|
ga_concat(&heredoc_ga, theline);
|
||||||
|
ga_concat(&heredoc_ga, (char_u *)"\n");
|
||||||
|
p = vim_strsave((char_u *)"");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
// Copy the line to newly allocated memory. get_one_sourceline()
|
// Copy the line to newly allocated memory. get_one_sourceline()
|
||||||
// allocates 250 bytes per line, this saves 80% on average. The cost
|
// allocates 250 bytes per line, this saves 80% on average. The
|
||||||
// is an extra alloc/free.
|
// cost is an extra alloc/free.
|
||||||
p = vim_strsave(theline);
|
p = vim_strsave(theline);
|
||||||
|
}
|
||||||
if (p == NULL)
|
if (p == NULL)
|
||||||
goto theend;
|
goto theend;
|
||||||
((char_u **)(newlines->ga_data))[newlines->ga_len++] = p;
|
((char_u **)(newlines->ga_data))[newlines->ga_len++] = p;
|
||||||
@@ -953,6 +984,7 @@ get_function_body(
|
|||||||
theend:
|
theend:
|
||||||
vim_free(skip_until);
|
vim_free(skip_until);
|
||||||
vim_free(heredoc_trimmed);
|
vim_free(heredoc_trimmed);
|
||||||
|
vim_free(heredoc_ga.ga_data);
|
||||||
need_wait_return |= saved_wait_return;
|
need_wait_return |= saved_wait_return;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@@ -1436,6 +1468,7 @@ deref_func_name(
|
|||||||
|
|
||||||
cc = name[*lenp];
|
cc = name[*lenp];
|
||||||
name[*lenp] = NUL;
|
name[*lenp] = NUL;
|
||||||
|
|
||||||
v = find_var(name, &ht, no_autoload);
|
v = find_var(name, &ht, no_autoload);
|
||||||
name[*lenp] = cc;
|
name[*lenp] = cc;
|
||||||
if (v != NULL)
|
if (v != NULL)
|
||||||
|
@@ -750,6 +750,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 */
|
||||||
|
/**/
|
||||||
|
2951,
|
||||||
/**/
|
/**/
|
||||||
2950,
|
2950,
|
||||||
/**/
|
/**/
|
||||||
|
@@ -14,6 +14,7 @@
|
|||||||
typedef enum {
|
typedef enum {
|
||||||
ISN_EXEC, // execute Ex command line isn_arg.string
|
ISN_EXEC, // execute Ex command line isn_arg.string
|
||||||
ISN_EXECCONCAT, // execute Ex command from isn_arg.number items on stack
|
ISN_EXECCONCAT, // execute Ex command from isn_arg.number items on stack
|
||||||
|
ISN_EXEC_SPLIT, // execute Ex command from isn_arg.string split at NL
|
||||||
ISN_LEGACY_EVAL, // evaluate expression isn_arg.string with legacy syntax.
|
ISN_LEGACY_EVAL, // evaluate expression isn_arg.string with legacy syntax.
|
||||||
ISN_ECHO, // echo isn_arg.echo.echo_count items on top of stack
|
ISN_ECHO, // echo isn_arg.echo.echo_count items on top of stack
|
||||||
ISN_EXECUTE, // execute Ex commands isn_arg.number items on top of stack
|
ISN_EXECUTE, // execute Ex commands isn_arg.number items on top of stack
|
||||||
|
@@ -8668,6 +8668,29 @@ theend:
|
|||||||
return nextcmd;
|
return nextcmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A script command with heredoc, e.g.
|
||||||
|
* ruby << EOF
|
||||||
|
* command
|
||||||
|
* EOF
|
||||||
|
* Has been turned into one long line with NL characters by
|
||||||
|
* get_function_body():
|
||||||
|
* ruby << EOF<NL> command<NL>EOF
|
||||||
|
*/
|
||||||
|
static char_u *
|
||||||
|
compile_script(char_u *line, cctx_T *cctx)
|
||||||
|
{
|
||||||
|
if (cctx->ctx_skip != SKIP_YES)
|
||||||
|
{
|
||||||
|
isn_T *isn;
|
||||||
|
|
||||||
|
if ((isn = generate_instr(cctx, ISN_EXEC_SPLIT)) == NULL)
|
||||||
|
return NULL;
|
||||||
|
isn->isn_arg.string = vim_strsave(line);
|
||||||
|
}
|
||||||
|
return (char_u *)"";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* :s/pat/repl/
|
* :s/pat/repl/
|
||||||
@@ -9480,18 +9503,28 @@ compile_def_function(
|
|||||||
line = (char_u *)"";
|
line = (char_u *)"";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
case CMD_lua:
|
||||||
if (cctx.ctx_skip == SKIP_YES)
|
case CMD_mzscheme:
|
||||||
{
|
case CMD_perl:
|
||||||
// We don't check for a next command here.
|
case CMD_py3:
|
||||||
line = (char_u *)"";
|
case CMD_python3:
|
||||||
}
|
case CMD_python:
|
||||||
|
case CMD_pythonx:
|
||||||
|
case CMD_ruby:
|
||||||
|
case CMD_tcl:
|
||||||
|
ea.arg = p;
|
||||||
|
if (vim_strchr(line, '\n') == NULL)
|
||||||
|
line = compile_exec(line, &ea, &cctx);
|
||||||
else
|
else
|
||||||
{
|
// heredoc lines have been concatenated with NL
|
||||||
|
// characters in get_function_body()
|
||||||
|
line = compile_script(line, &cctx);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
// Not recognized, execute with do_cmdline_cmd().
|
// Not recognized, execute with do_cmdline_cmd().
|
||||||
ea.arg = p;
|
ea.arg = p;
|
||||||
line = compile_exec(line, &ea, &cctx);
|
line = compile_exec(line, &ea, &cctx);
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
nextline:
|
nextline:
|
||||||
@@ -9674,6 +9707,7 @@ delete_instr(isn_T *isn)
|
|||||||
{
|
{
|
||||||
case ISN_DEF:
|
case ISN_DEF:
|
||||||
case ISN_EXEC:
|
case ISN_EXEC:
|
||||||
|
case ISN_EXEC_SPLIT:
|
||||||
case ISN_LEGACY_EVAL:
|
case ISN_LEGACY_EVAL:
|
||||||
case ISN_LOADAUTO:
|
case ISN_LOADAUTO:
|
||||||
case ISN_LOADB:
|
case ISN_LOADB:
|
||||||
|
@@ -1213,6 +1213,37 @@ get_script_svar(scriptref_T *sref, ectx_T *ectx)
|
|||||||
return sv;
|
return sv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Function passed to do_cmdline() for splitting a script joined by NL
|
||||||
|
* characters.
|
||||||
|
*/
|
||||||
|
static char_u *
|
||||||
|
get_split_sourceline(
|
||||||
|
int c UNUSED,
|
||||||
|
void *cookie,
|
||||||
|
int indent UNUSED,
|
||||||
|
getline_opt_T options UNUSED)
|
||||||
|
{
|
||||||
|
source_cookie_T *sp = (source_cookie_T *)cookie;
|
||||||
|
char_u *p;
|
||||||
|
char_u *line;
|
||||||
|
|
||||||
|
if (*sp->nextline == NUL)
|
||||||
|
return NULL;
|
||||||
|
p = vim_strchr(sp->nextline, '\n');
|
||||||
|
if (p == NULL)
|
||||||
|
{
|
||||||
|
line = vim_strsave(sp->nextline);
|
||||||
|
sp->nextline += STRLEN(sp->nextline);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
line = vim_strnsave(sp->nextline, p - sp->nextline);
|
||||||
|
sp->nextline = p + 1;
|
||||||
|
}
|
||||||
|
return line;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Execute a function by "name".
|
* Execute a function by "name".
|
||||||
* This can be a builtin function, user function or a funcref.
|
* This can be a builtin function, user function or a funcref.
|
||||||
@@ -1425,6 +1456,24 @@ exec_instructions(ectx_T *ectx)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// execute Ex command line split at NL characters.
|
||||||
|
case ISN_EXEC_SPLIT:
|
||||||
|
{
|
||||||
|
source_cookie_T cookie;
|
||||||
|
|
||||||
|
SOURCING_LNUM = iptr->isn_lnum;
|
||||||
|
CLEAR_FIELD(cookie);
|
||||||
|
cookie.sourcing_lnum = iptr->isn_lnum - 1;
|
||||||
|
cookie.nextline = iptr->isn_arg.string;
|
||||||
|
if (do_cmdline(get_split_sourceline(0, &cookie, 0, 0),
|
||||||
|
get_split_sourceline, &cookie,
|
||||||
|
DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_KEYTYPED)
|
||||||
|
== FAIL
|
||||||
|
|| did_emsg)
|
||||||
|
goto on_error;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
// Evaluate an expression with legacy syntax, push it onto the
|
// Evaluate an expression with legacy syntax, push it onto the
|
||||||
// stack.
|
// stack.
|
||||||
case ISN_LEGACY_EVAL:
|
case ISN_LEGACY_EVAL:
|
||||||
@@ -4536,6 +4585,9 @@ list_instructions(char *pfx, isn_T *instr, int instr_count, ufunc_T *ufunc)
|
|||||||
case ISN_EXEC:
|
case ISN_EXEC:
|
||||||
smsg("%s%4d EXEC %s", pfx, current, iptr->isn_arg.string);
|
smsg("%s%4d EXEC %s", pfx, current, iptr->isn_arg.string);
|
||||||
break;
|
break;
|
||||||
|
case ISN_EXEC_SPLIT:
|
||||||
|
smsg("%s%4d EXEC_SPLIT %s", pfx, current, iptr->isn_arg.string);
|
||||||
|
break;
|
||||||
case ISN_LEGACY_EVAL:
|
case ISN_LEGACY_EVAL:
|
||||||
smsg("%s%4d EVAL legacy %s", pfx, current,
|
smsg("%s%4d EVAL legacy %s", pfx, current,
|
||||||
iptr->isn_arg.string);
|
iptr->isn_arg.string);
|
||||||
|
Reference in New Issue
Block a user