Skip to content

Commit e4db17f

Browse files
committed
patch 8.2.3271: Vim9: cannot use :command or :au with block in :def function
Problem: Vim9: cannot use :command or :au with a block in a :def function. Solution: Recognize the start of the block.
1 parent 0d4d9ee commit e4db17f

File tree

7 files changed

+98
-20
lines changed

7 files changed

+98
-20
lines changed

src/ex_docmd.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2741,7 +2741,7 @@ checkforcmd(
27412741
* Check for an Ex command with optional tail, not followed by "(".
27422742
* If there is a match advance "pp" to the argument and return TRUE.
27432743
*/
2744-
static int
2744+
int
27452745
checkforcmd_noparen(
27462746
char_u **pp, // start of command
27472747
char *cmd, // name of command

src/proto/ex_docmd.pro

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ void *getline_cookie(char_u *(*fgetline)(int, void *, int, getline_opt_T), void
99
char_u *getline_peek(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie);
1010
char *ex_errmsg(char *msg, char_u *arg);
1111
int checkforcmd(char_u **pp, char *cmd, int len);
12+
int checkforcmd_noparen(char_u **pp, char *cmd, int len);
1213
int parse_command_modifiers(exarg_T *eap, char **errormsg, cmdmod_T *cmod, int skip_only);
1314
int has_cmdmod(cmdmod_T *cmod);
1415
int cmdmod_error(void);

src/testdir/test_vim9_script.vim

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,34 @@ def Test_block_local_vars_with_func()
334334
CheckScriptSuccess(lines)
335335
enddef
336336

337+
" legacy func for command that's defined later
338+
func InvokeSomeCommand()
339+
SomeCommand
340+
endfunc
341+
342+
def Test_autocommand_block()
343+
com SomeCommand {
344+
g:someVar = 'some'
345+
}
346+
InvokeSomeCommand()
347+
assert_equal('some', g:someVar)
348+
349+
delcommand SomeCommand
350+
unlet g:someVar
351+
enddef
352+
353+
def Test_command_block()
354+
au BufNew *.xml {
355+
g:otherVar = 'other'
356+
}
357+
split other.xml
358+
assert_equal('other', g:otherVar)
359+
360+
bwipe!
361+
au! BufNew *.xml
362+
unlet g:otherVar
363+
enddef
364+
337365
func g:NoSuchFunc()
338366
echo 'none'
339367
endfunc

src/usercmd.c

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -983,29 +983,32 @@ may_get_cmd_block(exarg_T *eap, char_u *p, char_u **tofree, int *flags)
983983
if (*p == '{' && ends_excmd2(eap->arg, skipwhite(p + 1))
984984
&& eap->getline != NULL)
985985
{
986-
garray_T ga;
987-
char_u *line = NULL;
986+
garray_T ga;
987+
char_u *line = NULL;
988988

989989
ga_init2(&ga, sizeof(char_u *), 10);
990990
if (ga_add_string(&ga, p) == FAIL)
991991
return retp;
992992

993-
// Read lines between '{' and '}'. Does not support nesting or
994-
// here-doc constructs.
995-
for (;;)
996-
{
997-
vim_free(line);
998-
if ((line = eap->getline(':', eap->cookie,
999-
0, GETLINE_CONCAT_CONTBAR)) == NULL)
993+
// If the argument ends in "}" it must have been concatenated already
994+
// for ISN_EXEC.
995+
if (p[STRLEN(p) - 1] != '}')
996+
// Read lines between '{' and '}'. Does not support nesting or
997+
// here-doc constructs.
998+
for (;;)
1000999
{
1001-
emsg(_(e_missing_rcurly));
1002-
break;
1000+
vim_free(line);
1001+
if ((line = eap->getline(':', eap->cookie,
1002+
0, GETLINE_CONCAT_CONTBAR)) == NULL)
1003+
{
1004+
emsg(_(e_missing_rcurly));
1005+
break;
1006+
}
1007+
if (ga_add_string(&ga, line) == FAIL)
1008+
break;
1009+
if (*skipwhite(line) == '}')
1010+
break;
10031011
}
1004-
if (ga_add_string(&ga, line) == FAIL)
1005-
break;
1006-
if (*skipwhite(line) == '}')
1007-
break;
1008-
}
10091012
vim_free(line);
10101013
retp = *tofree = ga_concat_strings(&ga, "\n");
10111014
ga_clear_strings(&ga);

src/userfunc.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -903,12 +903,25 @@ get_function_body(
903903
--end;
904904
if (end > p && *end == '{')
905905
{
906+
int is_block;
907+
908+
// check for trailing "=> {": start of an inline function
906909
--end;
907910
while (end > p && VIM_ISWHITE(*end))
908911
--end;
909-
if (end > p + 2 && end[-1] == '=' && end[0] == '>')
912+
is_block = end > p + 2 && end[-1] == '=' && end[0] == '>';
913+
if (!is_block)
914+
{
915+
char_u *s = p;
916+
917+
// check for line starting with "au" for :autocmd or
918+
// "com" for :command, these can use a {} block
919+
is_block = checkforcmd_noparen(&s, "autocmd", 2)
920+
|| checkforcmd_noparen(&s, "command", 3);
921+
}
922+
923+
if (is_block)
910924
{
911-
// found trailing "=> {", start of an inline function
912925
if (nesting == MAX_FUNC_NESTING - 1)
913926
emsg(_(e_function_nesting_too_deep));
914927
else

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,6 +755,8 @@ static char *(features[]) =
755755

756756
static int included_patches[] =
757757
{ /* Add new patch number below this line */
758+
/**/
759+
3271,
758760
/**/
759761
3270,
760762
/**/

src/vim9compile.c

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8861,11 +8861,13 @@ compile_put(char_u *arg, exarg_T *eap, cctx_T *cctx)
88618861
* A command that is not compiled, execute with legacy code.
88628862
*/
88638863
static char_u *
8864-
compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
8864+
compile_exec(char_u *line_arg, exarg_T *eap, cctx_T *cctx)
88658865
{
8866+
char_u *line = line_arg;
88668867
char_u *p;
88678868
int has_expr = FALSE;
88688869
char_u *nextcmd = (char_u *)"";
8870+
char_u *tofree = NULL;
88698871

88708872
if (cctx->ctx_skip == SKIP_YES)
88718873
goto theend;
@@ -8922,6 +8924,34 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
89228924
nextcmd = p + 1;
89238925
}
89248926
}
8927+
else if (eap->cmdidx == CMD_command || eap->cmdidx == CMD_autocmd)
8928+
{
8929+
// If there is a trailing '{' read lines until the '}'
8930+
p = eap->arg + STRLEN(eap->arg) - 1;
8931+
while (p > eap->arg && VIM_ISWHITE(*p))
8932+
--p;
8933+
if (*p == '{')
8934+
{
8935+
exarg_T ea;
8936+
int flags; // unused
8937+
int start_lnum = SOURCING_LNUM;
8938+
8939+
CLEAR_FIELD(ea);
8940+
ea.arg = eap->arg;
8941+
fill_exarg_from_cctx(&ea, cctx);
8942+
(void)may_get_cmd_block(&ea, p, &tofree, &flags);
8943+
if (tofree != NULL)
8944+
{
8945+
*p = NUL;
8946+
line = concat_str(line, tofree);
8947+
if (line == NULL)
8948+
goto theend;
8949+
vim_free(tofree);
8950+
tofree = line;
8951+
SOURCING_LNUM = start_lnum;
8952+
}
8953+
}
8954+
}
89258955
}
89268956

89278957
if (eap->cmdidx == CMD_syntax && STRNCMP(eap->arg, "include ", 8) == 0)
@@ -9008,6 +9038,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
90089038
--nextcmd;
90099039
*nextcmd = '|';
90109040
}
9041+
vim_free(tofree);
90119042

90129043
return nextcmd;
90139044
}

0 commit comments

Comments
 (0)