# HG changeset patch # User Bram Moolenaar # Date 1627414208 -7200 # Node ID b80e4e9c49885869a864e0ddc41e31ba8b6ae85b # Parent d5abf60e9872334197c6e21e0fa9877391197832 patch 8.2.3228: cannot use a simple block for the :command argument Commit: https://github.com/vim/vim/commit/5d7c2df536c17db4a9c61e0760bdcf78d0db7330 Author: Bram Moolenaar Date: Tue Jul 27 21:17:32 2021 +0200 patch 8.2.3228: cannot use a simple block for the :command argument Problem: Cannot use a simple block for the :command argument. (Maarten Tournoij) Solution: Recognize a simple {} block. (issue #8623) diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1572,6 +1572,16 @@ feature. Use the full name for new scri Replacement text ~ +The {repl} argument is normally one long string, possibly with "|" separated +commands. A special case is when the argument is "{", then the following +lines, up to a line starting with "}" are used and |Vim9| syntax applies. +Example: > + :command MyCommand { + echo 'hello' + g:calledMyCommand = true + } +No nesting is supported. + The replacement text {repl} for a user defined command is scanned for special escape sequences, using <...> notation. Escape sequences are replaced with values from the entered command line, and all other text is copied unchanged. diff --git a/src/misc2.c b/src/misc2.c --- a/src/misc2.c +++ b/src/misc2.c @@ -1488,7 +1488,6 @@ ga_grow_inner(garray_T *gap, int n) return OK; } -#if defined(FEAT_EVAL) || defined(FEAT_SEARCHPATH) || defined(PROTO) /* * For a growing array that contains a list of strings: concatenate all the * strings with a separating "sep". @@ -1524,27 +1523,27 @@ ga_concat_strings(garray_T *gap, char *s } return s; } -#endif - -#if defined(FEAT_VIMINFO) || defined(FEAT_EVAL) || defined(PROTO) + /* * Make a copy of string "p" and add it to "gap". - * When out of memory nothing changes. + * When out of memory nothing changes and FAIL is returned. */ - void + int ga_add_string(garray_T *gap, char_u *p) { char_u *cp = vim_strsave(p); - if (cp != NULL) + if (cp == NULL) + return FAIL; + + if (ga_grow(gap, 1) == FAIL) { - if (ga_grow(gap, 1) == OK) - ((char_u **)(gap->ga_data))[gap->ga_len++] = cp; - else - vim_free(cp); + vim_free(cp); + return FAIL; } + ((char_u **)(gap->ga_data))[gap->ga_len++] = cp; + return OK; } -#endif /* * Concatenate a string to a growarray which contains bytes. diff --git a/src/proto/misc2.pro b/src/proto/misc2.pro --- a/src/proto/misc2.pro +++ b/src/proto/misc2.pro @@ -43,7 +43,7 @@ void ga_init2(garray_T *gap, int itemsiz int ga_grow(garray_T *gap, int n); int ga_grow_inner(garray_T *gap, int n); char_u *ga_concat_strings(garray_T *gap, char *sep); -void ga_add_string(garray_T *gap, char_u *p); +int ga_add_string(garray_T *gap, char_u *p); void ga_concat(garray_T *gap, char_u *s); void ga_append(garray_T *gap, int c); void append_ga_line(garray_T *gap); diff --git a/src/testdir/test_usercommands.vim b/src/testdir/test_usercommands.vim --- a/src/testdir/test_usercommands.vim +++ b/src/testdir/test_usercommands.vim @@ -622,4 +622,22 @@ func Test_usercmd_custom() delfunc T2 endfunc +func Test_usercmd_with_block() + command DoSomething { + g:didit = 'yes' + g:didmore = 'more' + } + DoSomething + call assert_equal('yes', g:didit) + call assert_equal('more', g:didmore) + unlet g:didit + unlet g:didmore + + let lines =<< trim END + command DoesNotEnd { + echo 'hello' + END + call CheckScriptFailure(lines, 'E1026:') +endfunc + " vim: shiftwidth=2 sts=2 expandtab diff --git a/src/usercmd.c b/src/usercmd.c --- a/src/usercmd.c +++ b/src/usercmd.c @@ -115,6 +115,7 @@ static struct }; #define UC_BUFFER 1 // -buffer: local to current buffer +#define UC_VIM9 2 // {} argument: Vim9 syntax. /* * Search for a user command that matches "eap->cmd". @@ -872,10 +873,10 @@ uc_add_command( replace_termcodes(rep, &rep_buf, 0, NULL); if (rep_buf == NULL) { - // Can't replace termcodes - try using the string as is + // can't replace termcodes - try using the string as is rep_buf = vim_strsave(rep); - // Give up if out of memory + // give up if out of memory if (rep_buf == NULL) return FAIL; } @@ -955,6 +956,8 @@ uc_add_command( cmd->uc_def = def; cmd->uc_compl = compl; cmd->uc_script_ctx = current_sctx; + if (flags & UC_VIM9) + cmd->uc_script_ctx.sc_version = SCRIPT_VERSION_VIM9; #ifdef FEAT_EVAL cmd->uc_script_ctx.sc_lnum += SOURCING_LNUM; cmd->uc_compl_arg = compl_arg; @@ -1037,8 +1040,46 @@ ex_command(exarg_T *eap) (char_u *)_(e_complete_used_without_nargs), TRUE, TRUE); } else + { + char_u *tofree = NULL; + + if (*p == '{' && ends_excmd2(eap->arg, skipwhite(p + 1)) + && eap->getline != NULL) + { + garray_T ga; + char_u *line = NULL; + + ga_init2(&ga, sizeof(char_u *), 10); + if (ga_add_string(&ga, p) == FAIL) + return; + + // Read lines between '{' and '}'. Does not support nesting or + // here-doc constructs. + // + for (;;) + { + vim_free(line); + if ((line = eap->getline(':', eap->cookie, + 0, GETLINE_CONCAT_CONTBAR)) == NULL) + { + emsg(_(e_missing_rcurly)); + break; + } + if (ga_add_string(&ga, line) == FAIL) + break; + if (*skipwhite(line) == '}') + break; + } + vim_free(line); + p = tofree = ga_concat_strings(&ga, "\n"); + ga_clear_strings(&ga); + flags |= UC_VIM9; + } + uc_add_command(name, end - name, p, argt, def, flags, compl, compl_arg, addr_type_arg, eap->forceit); + vim_free(tofree); + } } /* diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -756,6 +756,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 3228, +/**/ 3227, /**/ 3226,