Compare commits

...

7 Commits

Author SHA1 Message Date
Bram Moolenaar
2cec027af4 patch 8.2.2641: display test fails because of lacking redraw
Problem:    Display test fails because of lacking redraw.
Solution:   Add a redraw command.
2021-03-22 17:30:47 +01:00
Bram Moolenaar
f1387285e2 patch 8.2.2640: screenstring() returns non-existing composing characters
Problem:    screenstring() returns non-existing composing characters.
Solution:   Only use composing characters if there is a base character.
2021-03-22 17:11:15 +01:00
Bram Moolenaar
5ea79a2599 patch 8.2.2639: build failure when fsync() is not available
Problem:    Build failure when fsync() is not available.
Solution:   Add #ifdef.
2021-03-22 16:45:35 +01:00
Bram Moolenaar
4c86830fc5 patch 8.2.2638: cannot write a message to the terminal from the GUI
Problem:    Cannot write a message to the terminal from the GUI.
Solution:   Add :echoconsole and use it in the test runner. (issue #7975)
2021-03-22 16:19:45 +01:00
Bram Moolenaar
09f8b3a022 patch 8.2.2637: prop_remove() causes a redraw even when nothing changed
Problem:    prop_remove() causes a redraw even when nothing changed.
Solution:   Only redraw if a property was removed.
2021-03-21 22:29:54 +01:00
Bram Moolenaar
67da21a147 patch 8.2.2636: memory leak when compiling inline function
Problem:    Memory leak when compiling inline function.
Solution:   Free the prefetched line.
2021-03-21 22:12:34 +01:00
Bram Moolenaar
7a6eaa06f9 patch 8.2.2635: Vim9: cannot define an inline function
Problem:    Vim9: cannot define an inline function.
Solution:   Make an inline function mostly work.
2021-03-21 20:53:29 +01:00
21 changed files with 668 additions and 410 deletions

View File

@@ -13141,7 +13141,12 @@ text...
< If you just want a highlighted message use |:echohl|.
And to get a beep: >
:exe "normal \<Esc>"
<
:echoc[onsole] {expr1} .. *:echoc* *:echoconsole*
Intended for testing: works like `:echomsg` but when
running in the GUI and started from a terminal write
the text to stdout.
*:eval*
:eval {expr} Evaluate {expr} and discard the result. Example: >
:eval Getlist()->Filter()->append('$')

View File

@@ -377,3 +377,7 @@ EXTERN char e_import_as_name_not_supported_here[]
INIT(= N_("E1169: 'import * as {name}' not supported here"));
EXTERN char e_cannot_use_hash_curly_to_start_comment[]
INIT(= N_("E1170: Cannot use #{ to start a comment"));
EXTERN char e_missing_end_block[]
INIT(= N_("E1171: Missing } after inline function"));
EXTERN char e_cannot_use_default_values_in_lambda[]
INIT(= N_("E1172: Cannot use default values in a lambda"));

View File

@@ -2179,8 +2179,8 @@ clear_evalarg(evalarg_T *evalarg, exarg_T *eap)
evalarg->eval_tofree = NULL;
}
vim_free(evalarg->eval_tofree_lambda);
evalarg->eval_tofree_lambda = NULL;
VIM_CLEAR(evalarg->eval_tofree_cmdline);
VIM_CLEAR(evalarg->eval_tofree_lambda);
}
}
@@ -6117,6 +6117,7 @@ get_echo_attr(void)
* ":execute expr1 ..." execute the result of an expression.
* ":echomsg expr1 ..." Print a message
* ":echoerr expr1 ..." Print an error
* ":echoconsole expr1 ..." Print a message on stdout
* Each gets spaces around each argument and a newline at the end for
* echo commands
*/
@@ -6194,6 +6195,11 @@ ex_execute(exarg_T *eap)
msg_attr(ga.ga_data, echo_attr);
out_flush();
}
else if (eap->cmdidx == CMD_echoconsole)
{
ui_write(ga.ga_data, (int)STRLEN(ga.ga_data), TRUE);
ui_write((char_u *)"\r\n", 2, TRUE);
}
else if (eap->cmdidx == CMD_echoerr)
{
int save_did_emsg = did_emsg;

View File

@@ -7902,7 +7902,7 @@ f_screenstring(typval_T *argvars, typval_T *rettv)
c = ScreenLines[off];
buflen += mb_char2bytes(c, buf);
if (enc_utf8)
if (enc_utf8 && ScreenLinesUC[off] != 0)
for (i = 0; i < Screen_mco && ScreenLinesC[i][off] != 0; ++i)
buflen += mb_char2bytes(ScreenLinesC[i][off], buf + buflen);

View File

@@ -10,27 +10,27 @@ static const unsigned short cmdidxs1[26] =
/* c */ 43,
/* d */ 109,
/* e */ 134,
/* f */ 157,
/* g */ 174,
/* h */ 180,
/* i */ 189,
/* j */ 208,
/* k */ 210,
/* l */ 215,
/* m */ 277,
/* n */ 295,
/* o */ 315,
/* p */ 327,
/* q */ 366,
/* r */ 369,
/* s */ 389,
/* t */ 458,
/* u */ 503,
/* v */ 514,
/* w */ 535,
/* x */ 549,
/* y */ 559,
/* z */ 560
/* f */ 158,
/* g */ 175,
/* h */ 181,
/* i */ 190,
/* j */ 209,
/* k */ 211,
/* l */ 216,
/* m */ 278,
/* n */ 296,
/* o */ 316,
/* p */ 328,
/* q */ 367,
/* r */ 370,
/* s */ 390,
/* t */ 459,
/* u */ 504,
/* v */ 515,
/* w */ 536,
/* x */ 550,
/* y */ 560,
/* z */ 561
};
/*
@@ -45,7 +45,7 @@ static const unsigned char cmdidxs2[26][26] =
/* b */ { 2, 0, 0, 5, 6, 8, 0, 0, 0, 0, 0, 9, 10, 11, 12, 13, 0, 14, 0, 0, 0, 0, 23, 0, 0, 0 },
/* c */ { 3, 12, 16, 18, 20, 22, 25, 0, 0, 0, 0, 33, 37, 40, 46, 56, 58, 59, 60, 0, 62, 0, 65, 0, 0, 0 },
/* d */ { 0, 0, 0, 0, 0, 0, 0, 0, 8, 18, 0, 19, 0, 0, 20, 0, 0, 22, 23, 0, 0, 0, 0, 0, 0, 0 },
/* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 7, 9, 10, 0, 0, 0, 0, 0, 0, 0, 17, 0, 18, 0, 0 },
/* e */ { 1, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 8, 10, 11, 0, 0, 0, 0, 0, 0, 0, 18, 0, 19, 0, 0 },
/* f */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0 },
/* g */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 0, 0, 4, 5, 0, 0, 0, 0 },
/* h */ { 5, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
@@ -69,4 +69,4 @@ static const unsigned char cmdidxs2[26][26] =
/* z */ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
static const int command_count = 575;
static const int command_count = 576;

View File

@@ -530,6 +530,9 @@ EXCMD(CMD_echohl, "echohl", ex_echohl,
EXCMD(CMD_echomsg, "echomsg", ex_execute,
EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_echoconsole, "echoconsole", ex_execute,
EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),
EXCMD(CMD_echon, "echon", ex_echo,
EX_EXTRA|EX_NOTRLCOM|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
ADDR_NONE),

View File

@@ -1898,7 +1898,8 @@ EXTERN listitem_T range_list_item;
// Passed to an eval() function to enable evaluation.
EXTERN evalarg_T EVALARG_EVALUATE
# ifdef DO_INIT
= {EVAL_EVALUATE, 0, NULL, NULL, NULL, {0, 0, 0, 0, NULL}, NULL, NULL}
= {EVAL_EVALUATE, 0, NULL, NULL, NULL, {0, 0, 0, 0, NULL},
NULL, NULL, NULL}
# endif
;
#endif

View File

@@ -2026,8 +2026,9 @@ ga_clear_strings(garray_T *gap)
{
int i;
for (i = 0; i < gap->ga_len; ++i)
vim_free(((char_u **)(gap->ga_data))[i]);
if (gap->ga_data != NULL)
for (i = 0; i < gap->ga_len; ++i)
vim_free(((char_u **)(gap->ga_data))[i]);
ga_clear(gap);
}

View File

@@ -1,11 +1,11 @@
/* ui.c */
void ui_write(char_u *s, int len);
void ui_write(char_u *s, int len, int console);
void ui_inchar_undo(char_u *s, int len);
int ui_inchar(char_u *buf, int maxlen, long wtime, int tb_change_cnt);
int inchar_loop(char_u *buf, int maxlen, long wtime, int tb_change_cnt, int (*wait_func)(long wtime, int *interrupted, int ignore_input), int (*resize_func)(int check_only));
int ui_wait_for_chars_or_timer(long wtime, int (*wait_func)(long wtime, int *interrupted, int ignore_input), int *interrupted, int ignore_input);
int ui_char_avail(void);
void ui_delay(long msec, int ignoreinput);
void ui_delay(long msec_arg, int ignoreinput);
void ui_suspend(void);
void suspend_shell(void);
int ui_get_shellsize(void);

View File

@@ -14,6 +14,7 @@ char_u *to_name_end(char_u *arg, int use_namespace);
char_u *to_name_const_end(char_u *arg);
exprtype_T get_compare_type(char_u *p, int *len, int *type_is);
void error_white_both(char_u *op, int len);
void fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx);
int assignment_len(char_u *p, int *heredoc);
void vim9_declare_error(char_u *name);
int check_vim9_unlet(char_u *name);

View File

@@ -1882,6 +1882,9 @@ typedef struct {
// pointer to the last line obtained with getsourceline()
char_u *eval_tofree;
// pointer to the last line of an inline function
char_u *eval_tofree_cmdline;
// pointer to the lines concatenated for a lambda.
char_u *eval_tofree_lambda;
} evalarg_T;

View File

@@ -2545,7 +2545,7 @@ out_flush(void)
// set out_pos to 0 before ui_write, to avoid recursiveness
len = out_pos;
out_pos = 0;
ui_write(out_buf, len);
ui_write(out_buf, len, FALSE);
#ifdef FEAT_JOB_CHANNEL
if (ch_log_output)
{

View File

@@ -162,7 +162,7 @@ function GetAllocId(name)
endfunc
func RunTheTest(test)
echo 'Executing ' . a:test
echoconsole 'Executing ' . a:test
if has('reltime')
let func_start = reltime()
endif

View File

@@ -268,12 +268,13 @@ func Test_eob_fillchars()
call assert_fails(':set fillchars=eob:<ff>', 'E474:')
" default is ~
new
redraw
call assert_equal('~', Screenline(2))
set fillchars=eob:+
redraw!
redraw
call assert_equal('+', Screenline(2))
set fillchars=eob:\
redraw!
redraw
call assert_equal(' ', nr2char(screenchar(2, 1)))
set fillchars&
close

View File

@@ -146,7 +146,7 @@ func Test_listchars()
set list
" Non-breaking space
let nbsp = nr2char(0xa0)
call append(0, [ ">".nbsp."<" ])
call append(0, [ ">" .. nbsp .. "<" ])
let expected = '>X< '
@@ -193,12 +193,8 @@ func Test_listchars_unicode()
set list
let nbsp = nr2char(0xa0)
call append(0, [
\ "a\tb c".nbsp."d"
\ ])
let expected = [
\ 'a←↔↔↔↔↔→b␣c≠d⇔'
\ ]
call append(0, ["a\tb c" .. nbsp .. "d"])
let expected = ['a←↔↔↔↔↔→b␣c≠d⇔']
redraw!
call cursor(1, 1)
call assert_equal(expected, ScreenLines(1, virtcol('$')))
@@ -221,10 +217,10 @@ func Test_listchars_composing()
let nbsp1 = nr2char(0xa0)
let nbsp2 = nr2char(0x202f)
call append(0, [
\ " \u3099\t \u309A".nbsp1.nbsp1."\u0302".nbsp2.nbsp2."\u0302",
\ " \u3099\t \u309A" .. nbsp1 .. nbsp1 .. "\u0302" .. nbsp2 .. nbsp2 .. "\u0302",
\ ])
let expected = [
\ "_ \u3099^I \u309A=".nbsp1."\u0302=".nbsp2."\u0302$"
\ "_ \u3099^I \u309A=" .. nbsp1 .. "\u0302=" .. nbsp2 .. "\u0302$"
\ ]
redraw!
call cursor(1, 1)

View File

@@ -1946,6 +1946,25 @@ def Test_expr7_lambda()
CheckScriptSuccess(lines)
enddef
def Test_expr7_lambda_block()
var lines =<< trim END
var Func = (s: string): string => {
return 'hello ' .. s
}
assert_equal('hello there', Func('there'))
var ll = range(3)
var dll = mapnew(ll, (k, v): string => {
if v % 2
return 'yes'
endif
return 'no'
})
assert_equal(['no', 'yes', 'no'], dll)
END
CheckDefAndScriptSuccess(lines)
enddef
def NewLambdaWithComments(): func
return (x) =>
# some comment

View File

@@ -920,7 +920,8 @@ f_prop_remove(typval_T *argvars, typval_T *rettv)
}
}
}
redraw_buf_later(buf, NOT_VALID);
if (rettv->vval.v_number > 0)
redraw_buf_later(buf, NOT_VALID);
}
/*

View File

@@ -18,10 +18,14 @@
#include "vim.h"
void
ui_write(char_u *s, int len)
ui_write(char_u *s, int len, int console UNUSED)
{
#ifdef FEAT_GUI
if (gui.in_use && !gui.dying && !gui.starting)
if (gui.in_use && !gui.dying && !gui.starting
# ifndef NO_CONSOLE
&& !console
# endif
)
{
gui_write(s, len);
if (p_wd)
@@ -33,7 +37,7 @@ ui_write(char_u *s, int len)
// Don't output anything in silent mode ("ex -s") unless 'verbose' set
if (!(silent_mode && p_verbose == 0))
{
#if !defined(MSWIN)
# if !defined(MSWIN)
char_u *tofree = NULL;
if (output_conv.vc_type != CONV_NONE)
@@ -43,9 +47,13 @@ ui_write(char_u *s, int len)
if (tofree != NULL)
s = tofree;
}
#endif
# endif
mch_write(s, len);
# if defined(HAVE_FSYNC)
if (console && s[len - 1] == '\n')
vim_fsync(1);
# endif
# if !defined(MSWIN)
if (output_conv.vc_type != CONV_NONE)

View File

@@ -397,6 +397,25 @@ parse_argument_types(ufunc_T *fp, garray_T *argtypes, int varargs)
return OK;
}
static int
parse_return_type(ufunc_T *fp, char_u *ret_type)
{
if (ret_type == NULL)
fp->uf_ret_type = &t_void;
else
{
char_u *p = ret_type;
fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE);
if (fp->uf_ret_type == NULL)
{
fp->uf_ret_type = &t_void;
return FAIL;
}
}
return OK;
}
/*
* Register function "fp" as using "current_funccal" as its scope.
*/
@@ -536,7 +555,502 @@ skip_arrow(
}
/*
* Parse a lambda expression and get a Funcref from "*arg".
* Check if "*cmd" points to a function command and if so advance "*cmd" and
* return TRUE.
* Otherwise return FALSE;
* Do not consider "function(" to be a command.
*/
static int
is_function_cmd(char_u **cmd)
{
char_u *p = *cmd;
if (checkforcmd(&p, "function", 2))
{
if (*p == '(')
return FALSE;
*cmd = p;
return TRUE;
}
return FALSE;
}
/*
* Read the body of a function, put every line in "newlines".
* "newlines" must already have been initialized.
* "eap->cmdidx" is CMD_function, CMD_def or CMD_block;
*/
static int
get_function_body(
exarg_T *eap,
garray_T *newlines,
char_u *line_arg_in,
char_u **line_to_free)
{
linenr_T sourcing_lnum_top = SOURCING_LNUM;
linenr_T sourcing_lnum_off;
int saved_wait_return = need_wait_return;
char_u *line_arg = line_arg_in;
int vim9_function = eap->cmdidx == CMD_def
|| eap->cmdidx == CMD_block;
#define MAX_FUNC_NESTING 50
char nesting_def[MAX_FUNC_NESTING];
int nesting = 0;
getline_opt_T getline_options;
int indent = 2;
char_u *skip_until = NULL;
int ret = FAIL;
int is_heredoc = FALSE;
char_u *heredoc_trimmed = NULL;
// Detect having skipped over comment lines to find the return
// type. Add NULL lines to keep the line count correct.
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
if (SOURCING_LNUM < sourcing_lnum_off)
{
sourcing_lnum_off -= SOURCING_LNUM;
if (ga_grow(newlines, sourcing_lnum_off) == FAIL)
goto theend;
while (sourcing_lnum_off-- > 0)
((char_u **)(newlines->ga_data))[newlines->ga_len++] = NULL;
}
nesting_def[nesting] = vim9_function;
getline_options = vim9_function
? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
for (;;)
{
char_u *theline;
char_u *p;
char_u *arg;
if (KeyTyped)
{
msg_scroll = TRUE;
saved_wait_return = FALSE;
}
need_wait_return = FALSE;
if (line_arg != NULL)
{
// Use eap->arg, split up in parts by line breaks.
theline = line_arg;
p = vim_strchr(theline, '\n');
if (p == NULL)
line_arg += STRLEN(line_arg);
else
{
*p = NUL;
line_arg = p + 1;
}
}
else
{
vim_free(*line_to_free);
if (eap->getline == NULL)
theline = getcmdline(':', 0L, indent, getline_options);
else
theline = eap->getline(':', eap->cookie, indent,
getline_options);
*line_to_free = theline;
}
if (KeyTyped)
lines_left = Rows - 1;
if (theline == NULL)
{
// Use the start of the function for the line number.
SOURCING_LNUM = sourcing_lnum_top;
if (skip_until != NULL)
semsg(_(e_missing_heredoc_end_marker_str), skip_until);
else if (eap->cmdidx == CMD_def)
emsg(_(e_missing_enddef));
else if (eap->cmdidx == CMD_block)
emsg(_(e_missing_end_block));
else
emsg(_("E126: Missing :endfunction"));
goto theend;
}
// Detect line continuation: SOURCING_LNUM increased more than one.
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
if (SOURCING_LNUM < sourcing_lnum_off)
sourcing_lnum_off -= SOURCING_LNUM;
else
sourcing_lnum_off = 0;
if (skip_until != NULL)
{
// Don't check for ":endfunc"/":enddef" between
// * ":append" and "."
// * ":python <<EOF" and "EOF"
// * ":let {var-name} =<< [trim] {marker}" and "{marker}"
if (heredoc_trimmed == NULL
|| (is_heredoc && skipwhite(theline) == theline)
|| STRNCMP(theline, heredoc_trimmed,
STRLEN(heredoc_trimmed)) == 0)
{
if (heredoc_trimmed == NULL)
p = theline;
else if (is_heredoc)
p = skipwhite(theline) == theline
? theline : theline + STRLEN(heredoc_trimmed);
else
p = theline + STRLEN(heredoc_trimmed);
if (STRCMP(p, skip_until) == 0)
{
VIM_CLEAR(skip_until);
VIM_CLEAR(heredoc_trimmed);
getline_options = vim9_function
? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
is_heredoc = FALSE;
}
}
}
else
{
int c;
// skip ':' and blanks
for (p = theline; VIM_ISWHITE(*p) || *p == ':'; ++p)
;
// Check for "endfunction", "enddef" or "}".
// When a ":" follows it must be a dict key; "enddef: value,"
if ((nesting == 0 && eap->cmdidx == CMD_block)
? *p == '}'
: (checkforcmd(&p, nesting_def[nesting]
? "enddef" : "endfunction", 4)
&& *p != ':'))
{
if (nesting-- == 0)
{
char_u *nextcmd = NULL;
if (*p == '|' || *p == '}')
nextcmd = p + 1;
else if (line_arg != NULL && *skipwhite(line_arg) != NUL)
nextcmd = line_arg;
else if (*p != NUL && *p != (vim9_function ? '#' : '"')
&& p_verbose > 0
&& eap->cmdidx != CMD_block)
give_warning2(eap->cmdidx == CMD_def
? (char_u *)_("W1001: Text found after :enddef: %s")
: (char_u *)_("W22: Text found after :endfunction: %s"),
p, TRUE);
if (nextcmd != NULL)
{
// Another command follows. If the line came from "eap"
// we can simply point into it, otherwise we need to
// change "eap->cmdlinep".
eap->nextcmd = nextcmd;
if (*line_to_free != NULL)
{
vim_free(*eap->cmdlinep);
*eap->cmdlinep = *line_to_free;
*line_to_free = NULL;
}
}
break;
}
}
// Check for mismatched "endfunc" or "enddef".
// We don't check for "def" inside "func" thus we also can't check
// for "enddef".
// We continue to find the end of the function, although we might
// not find it.
else if (nesting_def[nesting])
{
if (checkforcmd(&p, "endfunction", 4) && *p != ':')
emsg(_(e_mismatched_endfunction));
}
else if (eap->cmdidx == CMD_def && checkforcmd(&p, "enddef", 4))
emsg(_(e_mismatched_enddef));
// Increase indent inside "if", "while", "for" and "try", decrease
// at "end".
if (indent > 2 && (*p == '}' || STRNCMP(p, "end", 3) == 0))
indent -= 2;
else if (STRNCMP(p, "if", 2) == 0
|| STRNCMP(p, "wh", 2) == 0
|| STRNCMP(p, "for", 3) == 0
|| STRNCMP(p, "try", 3) == 0)
indent += 2;
// Check for defining a function inside this function.
// Only recognize "def" inside "def", not inside "function",
// For backwards compatibility, see Test_function_python().
c = *p;
if (is_function_cmd(&p)
|| (eap->cmdidx == CMD_def && checkforcmd(&p, "def", 3)))
{
if (*p == '!')
p = skipwhite(p + 1);
p += eval_fname_script(p);
vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
NULL, NULL));
if (*skipwhite(p) == '(')
{
if (nesting == MAX_FUNC_NESTING - 1)
emsg(_(e_function_nesting_too_deep));
else
{
++nesting;
nesting_def[nesting] = (c == 'd');
indent += 2;
}
}
}
// Check for ":append", ":change", ":insert". Not for :def.
p = skip_range(p, FALSE, NULL);
if (!vim9_function
&& ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
|| (p[0] == 'c'
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'h'
&& (!ASCII_ISALPHA(p[2]) || (p[2] == 'a'
&& (STRNCMP(&p[3], "nge", 3) != 0
|| !ASCII_ISALPHA(p[6])))))))
|| (p[0] == 'i'
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
&& (!ASCII_ISALPHA(p[2])
|| (p[2] == 's'
&& (!ASCII_ISALPHA(p[3])
|| p[3] == 'e'))))))))
skip_until = vim_strsave((char_u *)".");
// Check for ":python <<EOF", ":tcl <<EOF", etc.
arg = skipwhite(skiptowhite(p));
if (arg[0] == '<' && arg[1] =='<'
&& ((p[0] == 'p' && p[1] == 'y'
&& (!ASCII_ISALNUM(p[2]) || p[2] == 't'
|| ((p[2] == '3' || p[2] == 'x')
&& !ASCII_ISALPHA(p[3]))))
|| (p[0] == 'p' && p[1] == 'e'
&& (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
|| (p[0] == 't' && p[1] == 'c'
&& (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
|| (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
&& !ASCII_ISALPHA(p[3]))
|| (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
&& (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
|| (p[0] == 'm' && p[1] == 'z'
&& (!ASCII_ISALPHA(p[2]) || p[2] == 's'))
))
{
// ":python <<" continues until a dot, like ":append"
p = skipwhite(arg + 2);
if (STRNCMP(p, "trim", 4) == 0)
{
// Ignore leading white space.
p = skipwhite(p + 4);
heredoc_trimmed = vim_strnsave(theline,
skipwhite(theline) - theline);
}
if (*p == NUL)
skip_until = vim_strsave((char_u *)".");
else
skip_until = vim_strnsave(p, skiptowhite(p) - p);
getline_options = GETLINE_NONE;
is_heredoc = TRUE;
}
// Check for ":cmd v =<< [trim] EOF"
// and ":cmd [a, b] =<< [trim] EOF"
// and "lines =<< [trim] EOF" for Vim9
// Where "cmd" can be "let", "var", "final" or "const".
arg = skipwhite(skiptowhite(p));
if (*arg == '[')
arg = vim_strchr(arg, ']');
if (arg != NULL)
{
int found = (eap->cmdidx == CMD_def && arg[0] == '='
&& arg[1] == '<' && arg[2] =='<');
if (!found)
// skip over the argument after "cmd"
arg = skipwhite(skiptowhite(arg));
if (found || (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
&& (checkforcmd(&p, "let", 2)
|| checkforcmd(&p, "var", 3)
|| checkforcmd(&p, "final", 5)
|| checkforcmd(&p, "const", 5))))
{
p = skipwhite(arg + 3);
if (STRNCMP(p, "trim", 4) == 0)
{
// Ignore leading white space.
p = skipwhite(p + 4);
heredoc_trimmed = vim_strnsave(theline,
skipwhite(theline) - theline);
}
skip_until = vim_strnsave(p, skiptowhite(p) - p);
getline_options = GETLINE_NONE;
is_heredoc = TRUE;
}
}
}
// Add the line to the function.
if (ga_grow(newlines, 1 + sourcing_lnum_off) == FAIL)
goto theend;
// Copy the line to newly allocated memory. get_one_sourceline()
// allocates 250 bytes per line, this saves 80% on average. The cost
// is an extra alloc/free.
p = vim_strsave(theline);
if (p == NULL)
goto theend;
((char_u **)(newlines->ga_data))[newlines->ga_len++] = p;
// Add NULL lines for continuation lines, so that the line count is
// equal to the index in the growarray.
while (sourcing_lnum_off-- > 0)
((char_u **)(newlines->ga_data))[newlines->ga_len++] = NULL;
// Check for end of eap->arg.
if (line_arg != NULL && *line_arg == NUL)
line_arg = NULL;
}
// Don't define the function when skipping commands or when an error was
// detected.
if (!eap->skip && !did_emsg)
ret = OK;
theend:
vim_free(skip_until);
vim_free(heredoc_trimmed);
need_wait_return |= saved_wait_return;
return ret;
}
/*
* Handle the body of a lambda. *arg points to the "{", process statements
* until the matching "}".
* When successful "rettv" is set to a funcref.
*/
static int
lambda_function_body(
char_u **arg,
typval_T *rettv,
evalarg_T *evalarg,
garray_T *newargs,
garray_T *argtypes,
int varargs,
garray_T *default_args,
char_u *ret_type)
{
int evaluate = evalarg != NULL
&& (evalarg->eval_flags & EVAL_EVALUATE);
ufunc_T *ufunc;
exarg_T eap;
garray_T newlines;
char_u *cmdline = NULL;
int ret = FAIL;
char_u *line_to_free = NULL;
partial_T *pt;
char_u *name;
int lnum_save = -1;
linenr_T sourcing_lnum_top = SOURCING_LNUM;
CLEAR_FIELD(eap);
eap.cmdidx = CMD_block;
eap.forceit = FALSE;
eap.arg = *arg + 1;
eap.cmdlinep = &cmdline;
eap.skip = !evaluate;
if (evalarg->eval_cctx != NULL)
fill_exarg_from_cctx(&eap, evalarg->eval_cctx);
else
{
eap.getline = evalarg->eval_getline;
eap.cookie = evalarg->eval_cookie;
}
ga_init2(&newlines, (int)sizeof(char_u *), 10);
if (get_function_body(&eap, &newlines, NULL, &line_to_free) == FAIL)
{
vim_free(cmdline);
goto erret;
}
if (cmdline != NULL)
{
// Something comes after the "}".
*arg = eap.nextcmd;
// "arg" points into cmdline, need to keep the line and free it later.
vim_free(evalarg->eval_tofree_cmdline);
evalarg->eval_tofree_cmdline = cmdline;
}
else
*arg = (char_u *)"";
name = get_lambda_name();
ufunc = alloc_clear(offsetof(ufunc_T, uf_name) + STRLEN(name) + 1);
if (ufunc == NULL)
goto erret;
set_ufunc_name(ufunc, name);
if (hash_add(&func_hashtab, UF2HIKEY(ufunc)) == FAIL)
{
vim_free(ufunc);
goto erret;
}
ufunc->uf_refcount = 1;
ufunc->uf_args = *newargs;
newargs->ga_data = NULL;
ufunc->uf_def_args = *default_args;
default_args->ga_data = NULL;
ufunc->uf_func_type = &t_func_any;
// error messages are for the first function line
lnum_save = SOURCING_LNUM;
SOURCING_LNUM = sourcing_lnum_top;
// parse argument types
if (parse_argument_types(ufunc, argtypes, varargs) == FAIL)
{
SOURCING_LNUM = lnum_save;
goto erret;
}
// parse the return type, if any
if (parse_return_type(ufunc, ret_type) == FAIL)
goto erret;
pt = ALLOC_CLEAR_ONE(partial_T);
if (pt == NULL)
goto erret;
pt->pt_func = ufunc;
pt->pt_refcount = 1;
ufunc->uf_lines = newlines;
newlines.ga_data = NULL;
if (sandbox)
ufunc->uf_flags |= FC_SANDBOX;
if (!ASCII_ISUPPER(*ufunc->uf_name))
ufunc->uf_flags |= FC_VIM9;
ufunc->uf_script_ctx = current_sctx;
ufunc->uf_script_ctx_version = current_sctx.sc_version;
ufunc->uf_script_ctx.sc_lnum += sourcing_lnum_top;
set_function_type(ufunc);
rettv->vval.v_partial = pt;
rettv->v_type = VAR_PARTIAL;
ret = OK;
erret:
if (lnum_save >= 0)
SOURCING_LNUM = lnum_save;
vim_free(line_to_free);
ga_clear_strings(&newlines);
ga_clear_strings(newargs);
ga_clear_strings(default_args);
return ret;
}
/*
* Parse a lambda expression and get a Funcref from "*arg" into "rettv".
* "arg" points to the { in "{arg -> expr}" or the ( in "(arg) => expr"
* When "types_optional" is TRUE optionally take argument types.
* Return OK or FAIL. Returns NOTDONE for dict or {expr}.
@@ -554,6 +1068,7 @@ get_lambda_tv(
garray_T newlines;
garray_T *pnewargs;
garray_T argtypes;
garray_T default_args;
ufunc_T *fp = NULL;
partial_T *pt = NULL;
int varargs;
@@ -596,7 +1111,8 @@ get_lambda_tv(
*arg += 1;
ret = get_function_args(arg, equal_arrow ? ')' : '-', pnewargs,
types_optional ? &argtypes : NULL, types_optional, evalarg,
&varargs, NULL, FALSE, NULL, NULL);
&varargs, &default_args,
FALSE, NULL, NULL);
if (ret == FAIL
|| (s = skip_arrow(*arg, equal_arrow, &ret_type,
equal_arrow || in_vim9script() ? &white_error : NULL)) == NULL)
@@ -624,9 +1140,15 @@ get_lambda_tv(
// Recognize "{" as the start of a function body.
if (equal_arrow && **arg == '{')
{
// TODO: process the function body upto the "}".
// Return type is required then.
emsg("Lambda function body not supported yet");
if (lambda_function_body(arg, rettv, evalarg, pnewargs,
types_optional ? &argtypes : NULL, varargs,
&default_args, ret_type) == FAIL)
goto errret;
goto theend;
}
if (default_args.ga_len > 0)
{
emsg(_(e_cannot_use_default_values_in_lambda));
goto errret;
}
@@ -732,6 +1254,7 @@ get_lambda_tv(
hash_add(&func_hashtab, UF2HIKEY(fp));
}
theend:
eval_lavars_used = old_eval_lavars;
if (evalarg != NULL && evalarg->eval_tofree == NULL)
evalarg->eval_tofree = tofree1;
@@ -745,6 +1268,7 @@ get_lambda_tv(
errret:
ga_clear_strings(&newargs);
ga_clear_strings(&newlines);
ga_clear_strings(&default_args);
if (types_optional)
ga_clear_strings(&argtypes);
vim_free(fp);
@@ -2459,7 +2983,7 @@ call_func(
{
// Check that the argument types are OK for the types of the funcref.
if (check_argument_types(funcexe->check_type, argvars, argcount,
name) == FAIL)
(name != NULL) ? name : funcname) == FAIL)
error = FCERR_OTHER;
}
@@ -3005,27 +3529,6 @@ list_functions(regmatch_T *regmatch)
}
}
/*
* Check if "*cmd" points to a function command and if so advance "*cmd" and
* return TRUE.
* Otherwise return FALSE;
* Do not consider "function(" to be a command.
*/
static int
is_function_cmd(char_u **cmd)
{
char_u *p = *cmd;
if (checkforcmd(&p, "function", 2))
{
if (*p == '(')
return FALSE;
*cmd = p;
return TRUE;
}
return FALSE;
}
/*
* ":function" also supporting nested ":def".
* When "name_arg" is not NULL this is a nested function, using "name_arg" for
@@ -3035,12 +3538,10 @@ is_function_cmd(char_u **cmd)
ufunc_T *
define_function(exarg_T *eap, char_u *name_arg)
{
char_u *theline;
char_u *line_to_free = NULL;
int j;
int c;
int saved_did_emsg;
int saved_wait_return = need_wait_return;
char_u *name = name_arg;
int is_global = FALSE;
char_u *p;
@@ -3056,21 +3557,12 @@ define_function(exarg_T *eap, char_u *name_arg)
char_u *ret_type = NULL;
ufunc_T *fp = NULL;
int overwrite = FALSE;
int indent;
int nesting;
#define MAX_FUNC_NESTING 50
char nesting_def[MAX_FUNC_NESTING];
dictitem_T *v;
funcdict_T fudi;
static int func_nr = 0; // number for nameless function
int paren;
hashitem_T *hi;
getline_opt_T getline_options;
linenr_T sourcing_lnum_off;
linenr_T sourcing_lnum_top;
int is_heredoc = FALSE;
char_u *skip_until = NULL;
char_u *heredoc_trimmed = NULL;
int vim9script = in_vim9script();
imported_T *import = NULL;
@@ -3263,7 +3755,7 @@ define_function(exarg_T *eap, char_u *name_arg)
goto ret_free;
}
ga_init2(&newlines, (int)sizeof(char_u *), 3);
ga_init2(&newlines, (int)sizeof(char_u *), 10);
if (!eap->skip && name_arg == NULL)
{
@@ -3399,309 +3891,7 @@ define_function(exarg_T *eap, char_u *name_arg)
// Save the starting line number.
sourcing_lnum_top = SOURCING_LNUM;
// Detect having skipped over comment lines to find the return
// type. Add NULL lines to keep the line count correct.
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
if (SOURCING_LNUM < sourcing_lnum_off)
{
sourcing_lnum_off -= SOURCING_LNUM;
if (ga_grow(&newlines, sourcing_lnum_off) == FAIL)
goto erret;
while (sourcing_lnum_off-- > 0)
((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL;
}
indent = 2;
nesting = 0;
nesting_def[nesting] = (eap->cmdidx == CMD_def);
getline_options = eap->cmdidx == CMD_def
? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
for (;;)
{
if (KeyTyped)
{
msg_scroll = TRUE;
saved_wait_return = FALSE;
}
need_wait_return = FALSE;
if (line_arg != NULL)
{
// Use eap->arg, split up in parts by line breaks.
theline = line_arg;
p = vim_strchr(theline, '\n');
if (p == NULL)
line_arg += STRLEN(line_arg);
else
{
*p = NUL;
line_arg = p + 1;
}
}
else
{
vim_free(line_to_free);
if (eap->getline == NULL)
theline = getcmdline(':', 0L, indent, getline_options);
else
theline = eap->getline(':', eap->cookie, indent,
getline_options);
line_to_free = theline;
}
if (KeyTyped)
lines_left = Rows - 1;
if (theline == NULL)
{
// Use the start of the function for the line number.
SOURCING_LNUM = sourcing_lnum_top;
if (skip_until != NULL)
semsg(_(e_missing_heredoc_end_marker_str), skip_until);
else if (eap->cmdidx == CMD_def)
emsg(_(e_missing_enddef));
else
emsg(_("E126: Missing :endfunction"));
goto erret;
}
// Detect line continuation: SOURCING_LNUM increased more than one.
sourcing_lnum_off = get_sourced_lnum(eap->getline, eap->cookie);
if (SOURCING_LNUM < sourcing_lnum_off)
sourcing_lnum_off -= SOURCING_LNUM;
else
sourcing_lnum_off = 0;
if (skip_until != NULL)
{
// Don't check for ":endfunc"/":enddef" between
// * ":append" and "."
// * ":python <<EOF" and "EOF"
// * ":let {var-name} =<< [trim] {marker}" and "{marker}"
if (heredoc_trimmed == NULL
|| (is_heredoc && skipwhite(theline) == theline)
|| STRNCMP(theline, heredoc_trimmed,
STRLEN(heredoc_trimmed)) == 0)
{
if (heredoc_trimmed == NULL)
p = theline;
else if (is_heredoc)
p = skipwhite(theline) == theline
? theline : theline + STRLEN(heredoc_trimmed);
else
p = theline + STRLEN(heredoc_trimmed);
if (STRCMP(p, skip_until) == 0)
{
VIM_CLEAR(skip_until);
VIM_CLEAR(heredoc_trimmed);
getline_options = eap->cmdidx == CMD_def
? GETLINE_CONCAT_CONTBAR : GETLINE_CONCAT_CONT;
is_heredoc = FALSE;
}
}
}
else
{
// skip ':' and blanks
for (p = theline; VIM_ISWHITE(*p) || *p == ':'; ++p)
;
// Check for "endfunction" or "enddef".
// When a ":" follows it must be a dict key; "enddef: value,"
if (checkforcmd(&p, nesting_def[nesting]
? "enddef" : "endfunction", 4)
&& *p != ':')
{
if (nesting-- == 0)
{
char_u *nextcmd = NULL;
if (*p == '|')
nextcmd = p + 1;
else if (line_arg != NULL && *skipwhite(line_arg) != NUL)
nextcmd = line_arg;
else if (*p != NUL && *p != '"' && p_verbose > 0)
give_warning2(eap->cmdidx == CMD_def
? (char_u *)_("W1001: Text found after :enddef: %s")
: (char_u *)_("W22: Text found after :endfunction: %s"),
p, TRUE);
if (nextcmd != NULL)
{
// Another command follows. If the line came from "eap"
// we can simply point into it, otherwise we need to
// change "eap->cmdlinep".
eap->nextcmd = nextcmd;
if (line_to_free != NULL)
{
vim_free(*eap->cmdlinep);
*eap->cmdlinep = line_to_free;
line_to_free = NULL;
}
}
break;
}
}
// Check for mismatched "endfunc" or "enddef".
// We don't check for "def" inside "func" thus we also can't check
// for "enddef".
// We continue to find the end of the function, although we might
// not find it.
else if (nesting_def[nesting])
{
if (checkforcmd(&p, "endfunction", 4) && *p != ':')
emsg(_(e_mismatched_endfunction));
}
else if (eap->cmdidx == CMD_def && checkforcmd(&p, "enddef", 4))
emsg(_(e_mismatched_enddef));
// Increase indent inside "if", "while", "for" and "try", decrease
// at "end".
if (indent > 2 && (*p == '}' || STRNCMP(p, "end", 3) == 0))
indent -= 2;
else if (STRNCMP(p, "if", 2) == 0
|| STRNCMP(p, "wh", 2) == 0
|| STRNCMP(p, "for", 3) == 0
|| STRNCMP(p, "try", 3) == 0)
indent += 2;
// Check for defining a function inside this function.
// Only recognize "def" inside "def", not inside "function",
// For backwards compatibility, see Test_function_python().
c = *p;
if (is_function_cmd(&p)
|| (eap->cmdidx == CMD_def && checkforcmd(&p, "def", 3)))
{
if (*p == '!')
p = skipwhite(p + 1);
p += eval_fname_script(p);
vim_free(trans_function_name(&p, NULL, TRUE, 0, NULL,
NULL, NULL));
if (*skipwhite(p) == '(')
{
if (nesting == MAX_FUNC_NESTING - 1)
emsg(_(e_function_nesting_too_deep));
else
{
++nesting;
nesting_def[nesting] = (c == 'd');
indent += 2;
}
}
}
// Check for ":append", ":change", ":insert". Not for :def.
p = skip_range(p, FALSE, NULL);
if (eap->cmdidx != CMD_def
&& ((p[0] == 'a' && (!ASCII_ISALPHA(p[1]) || p[1] == 'p'))
|| (p[0] == 'c'
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'h'
&& (!ASCII_ISALPHA(p[2]) || (p[2] == 'a'
&& (STRNCMP(&p[3], "nge", 3) != 0
|| !ASCII_ISALPHA(p[6])))))))
|| (p[0] == 'i'
&& (!ASCII_ISALPHA(p[1]) || (p[1] == 'n'
&& (!ASCII_ISALPHA(p[2])
|| (p[2] == 's'
&& (!ASCII_ISALPHA(p[3])
|| p[3] == 'e'))))))))
skip_until = vim_strsave((char_u *)".");
// Check for ":python <<EOF", ":tcl <<EOF", etc.
arg = skipwhite(skiptowhite(p));
if (arg[0] == '<' && arg[1] =='<'
&& ((p[0] == 'p' && p[1] == 'y'
&& (!ASCII_ISALNUM(p[2]) || p[2] == 't'
|| ((p[2] == '3' || p[2] == 'x')
&& !ASCII_ISALPHA(p[3]))))
|| (p[0] == 'p' && p[1] == 'e'
&& (!ASCII_ISALPHA(p[2]) || p[2] == 'r'))
|| (p[0] == 't' && p[1] == 'c'
&& (!ASCII_ISALPHA(p[2]) || p[2] == 'l'))
|| (p[0] == 'l' && p[1] == 'u' && p[2] == 'a'
&& !ASCII_ISALPHA(p[3]))
|| (p[0] == 'r' && p[1] == 'u' && p[2] == 'b'
&& (!ASCII_ISALPHA(p[3]) || p[3] == 'y'))
|| (p[0] == 'm' && p[1] == 'z'
&& (!ASCII_ISALPHA(p[2]) || p[2] == 's'))
))
{
// ":python <<" continues until a dot, like ":append"
p = skipwhite(arg + 2);
if (STRNCMP(p, "trim", 4) == 0)
{
// Ignore leading white space.
p = skipwhite(p + 4);
heredoc_trimmed = vim_strnsave(theline,
skipwhite(theline) - theline);
}
if (*p == NUL)
skip_until = vim_strsave((char_u *)".");
else
skip_until = vim_strnsave(p, skiptowhite(p) - p);
getline_options = GETLINE_NONE;
is_heredoc = TRUE;
}
// Check for ":cmd v =<< [trim] EOF"
// and ":cmd [a, b] =<< [trim] EOF"
// and "lines =<< [trim] EOF" for Vim9
// Where "cmd" can be "let", "var", "final" or "const".
arg = skipwhite(skiptowhite(p));
if (*arg == '[')
arg = vim_strchr(arg, ']');
if (arg != NULL)
{
int found = (eap->cmdidx == CMD_def && arg[0] == '='
&& arg[1] == '<' && arg[2] =='<');
if (!found)
// skip over the argument after "cmd"
arg = skipwhite(skiptowhite(arg));
if (found || (arg[0] == '=' && arg[1] == '<' && arg[2] =='<'
&& (checkforcmd(&p, "let", 2)
|| checkforcmd(&p, "var", 3)
|| checkforcmd(&p, "final", 5)
|| checkforcmd(&p, "const", 5))))
{
p = skipwhite(arg + 3);
if (STRNCMP(p, "trim", 4) == 0)
{
// Ignore leading white space.
p = skipwhite(p + 4);
heredoc_trimmed = vim_strnsave(theline,
skipwhite(theline) - theline);
}
skip_until = vim_strnsave(p, skiptowhite(p) - p);
getline_options = GETLINE_NONE;
is_heredoc = TRUE;
}
}
}
// Add the line to the function.
if (ga_grow(&newlines, 1 + sourcing_lnum_off) == FAIL)
goto erret;
// Copy the line to newly allocated memory. get_one_sourceline()
// allocates 250 bytes per line, this saves 80% on average. The cost
// is an extra alloc/free.
p = vim_strsave(theline);
if (p == NULL)
goto erret;
((char_u **)(newlines.ga_data))[newlines.ga_len++] = p;
// Add NULL lines for continuation lines, so that the line count is
// equal to the index in the growarray.
while (sourcing_lnum_off-- > 0)
((char_u **)(newlines.ga_data))[newlines.ga_len++] = NULL;
// Check for end of eap->arg.
if (line_arg != NULL && *line_arg == NUL)
line_arg = NULL;
}
// Don't define the function when skipping commands or when an error was
// detected.
if (eap->skip || did_emsg)
if (get_function_body(eap, &newlines, line_arg, &line_to_free) == FAIL)
goto erret;
/*
@@ -3933,18 +4123,10 @@ define_function(exarg_T *eap, char_u *name_arg)
varargs = FALSE;
// parse the return type, if any
if (ret_type == NULL)
fp->uf_ret_type = &t_void;
else
if (parse_return_type(fp, ret_type) == FAIL)
{
p = ret_type;
fp->uf_ret_type = parse_type(&p, &fp->uf_type_list, TRUE);
if (fp->uf_ret_type == NULL)
{
fp->uf_ret_type = &t_void;
SOURCING_LNUM = lnum_save;
goto erret;
}
SOURCING_LNUM = lnum_save;
goto erret;
}
SOURCING_LNUM = lnum_save;
}
@@ -4004,15 +4186,12 @@ errret_2:
VIM_CLEAR(fp->uf_arg_types);
ret_free:
ga_clear_strings(&argtypes);
vim_free(skip_until);
vim_free(heredoc_trimmed);
vim_free(line_to_free);
vim_free(fudi.fd_newkey);
if (name != name_arg)
vim_free(name);
vim_free(ret_type);
did_emsg |= saved_did_emsg;
need_wait_return |= saved_wait_return;
return fp;
}

View File

@@ -750,6 +750,20 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2641,
/**/
2640,
/**/
2639,
/**/
2638,
/**/
2637,
/**/
2636,
/**/
2635,
/**/
2634,
/**/

View File

@@ -3171,7 +3171,7 @@ compile_list(char_u **arg, cctx_T *cctx, ppconst_T *ppconst)
/*
* Parse a lambda: "(arg, arg) => expr"
* "*arg" points to the '{'.
* "*arg" points to the '('.
* Returns OK/FAIL when a lambda is recognized, NOTDONE if it's not a lambda.
*/
static int
@@ -3202,6 +3202,16 @@ compile_lambda(char_u **arg, cctx_T *cctx)
// Compile the function into instructions.
compile_def_function(ufunc, TRUE, PROFILING(ufunc), cctx);
// evalarg.eval_tofree_cmdline may have a copy of the last line and "*arg"
// points into it. Point to the original line to avoid a dangling pointer.
if (evalarg.eval_tofree_cmdline != NULL)
{
size_t off = *arg - evalarg.eval_tofree_cmdline;
*arg = ((char_u **)cctx->ctx_ufunc->uf_lines.ga_data)[cctx->ctx_lnum]
+ off;
}
clear_evalarg(&evalarg, NULL);
if (ufunc->uf_def_status == UF_COMPILED)
@@ -5126,6 +5136,13 @@ exarg_getline(
}
}
void
fill_exarg_from_cctx(exarg_T *eap, cctx_T *cctx)
{
eap->getline = exarg_getline;
eap->cookie = cctx;
}
/*
* Compile a nested :def command.
*/
@@ -5176,9 +5193,8 @@ compile_nested_function(exarg_T *eap, cctx_T *cctx)
return NULL;
eap->arg = name_end;
eap->getline = exarg_getline;
eap->cookie = cctx;
eap->skip = cctx->ctx_skip == SKIP_YES;
fill_exarg_from_cctx(eap, cctx);
eap->forceit = FALSE;
lambda_name = vim_strsave(get_lambda_name());
if (lambda_name == NULL)