Compare commits

...

8 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
Bram Moolenaar
f90c855c71 patch 8.2.2634: 'tagfunc' does not indicate using a pattern
Problem:    'tagfunc' does not indicate using a pattern.
Solution:   Add the "r" flag. (Andy Massimino, closes #7982)
2021-03-21 14:49:57 +01:00
24 changed files with 697 additions and 418 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

@@ -888,19 +888,25 @@ like |CTRL-]|.
The function used for generating the taglist is specified by setting the
'tagfunc' option. The function will be called with three arguments:
a:pattern The tag identifier used during the tag search.
a:flags List of flags to control the function behavior.
a:pattern The tag identifier or pattern used during the tag search.
a:flags String containing flags to control the function behavior.
a:info Dict containing the following entries:
buf_ffname Full filename which can be used for priority.
user_data Custom data String, if stored in the tag
stack previously by tagfunc.
Currently two flags may be passed to the tag function:
Currently up to three flags may be passed to the tag function:
'c' The function was invoked by a normal command being processed
(mnemonic: the tag function may use the context around the
cursor to perform a better job of generating the tag list.)
'i' In Insert mode, the user was completing a tag (with
|i_CTRL-X_CTRL-]|).
|i_CTRL-X_CTRL-]| or 'completeopt' contains `t`).
'r' The first argument to tagfunc should be interpreted as a
|pattern| (see |tag-regexp|), such as when using: >
:tag /pat
< It is also given when completing in insert mode.
If this flag is not present, the argument is usually taken
literally as the full tag name.
Note that when 'tagfunc' is set, the priority of the tags described in
|tag-priority| does not apply. Instead, the priority is exactly as the

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

@@ -1308,7 +1308,7 @@ find_tagfunc_tags(
int result = FAIL;
typval_T args[4];
typval_T rettv;
char_u flagString[3];
char_u flagString[4];
dict_T *d;
taggy_T *tag = &curwin->w_tagstack[curwin->w_tagstackidx];
@@ -1335,9 +1335,10 @@ find_tagfunc_tags(
args[3].v_type = VAR_UNKNOWN;
vim_snprintf((char *)flagString, sizeof(flagString),
"%s%s",
"%s%s%s",
g_tag_at_cursor ? "c": "",
flags & TAG_INS_COMP ? "i": "");
flags & TAG_INS_COMP ? "i": "",
flags & TAG_REGEXP ? "r": "");
save_pos = curwin->w_cursor;
result = call_vim_function(curbuf->b_p_tfu, 3, args, &rettv);

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

@@ -43,12 +43,24 @@ func Test_tagfunc()
call assert_equal('one', g:tagfunc_args[0])
call assert_equal('c', g:tagfunc_args[1])
let g:tagfunc_args=[]
execute "tag /foo$"
call assert_equal('foo$', g:tagfunc_args[0])
call assert_equal('r', g:tagfunc_args[1])
set cpt=t
let g:tagfunc_args=[]
execute "normal! i\<c-n>\<c-y>"
call assert_equal('ci', g:tagfunc_args[1])
call assert_equal('\<\k\k', g:tagfunc_args[0])
call assert_equal('cir', g:tagfunc_args[1])
call assert_equal('nothing1', getline('.')[0:7])
let g:tagfunc_args=[]
execute "normal! ono\<c-n>\<c-n>\<c-y>"
call assert_equal('\<no', g:tagfunc_args[0])
call assert_equal('cir', g:tagfunc_args[1])
call assert_equal('nothing2', getline('.')[0:7])
func BadTagFunc1(...)
return 0
endfunc

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,22 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
2641,
/**/
2640,
/**/
2639,
/**/
2638,
/**/
2637,
/**/
2636,
/**/
2635,
/**/
2634,
/**/
2633,
/**/

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)