diff options
-rw-r--r-- | runtime/doc/map.txt | 10 | ||||
-rw-r--r-- | src/misc2.c | 21 | ||||
-rw-r--r-- | src/proto/misc2.pro | 2 | ||||
-rw-r--r-- | src/testdir/test_usercommands.vim | 18 | ||||
-rw-r--r-- | src/usercmd.c | 45 | ||||
-rw-r--r-- | src/version.c | 2 |
6 files changed, 84 insertions, 14 deletions
diff --git a/runtime/doc/map.txt b/runtime/doc/map.txt index 970bbc923..8d5caf43a 100644 --- a/runtime/doc/map.txt +++ b/runtime/doc/map.txt @@ -1572,6 +1572,16 @@ feature. Use the full name for new scripts. 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 index 8e99b01a5..bc984b219 100644 --- 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 *sep) } 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 index 5f7d70927..5ecd5958f 100644 --- a/src/proto/misc2.pro +++ b/src/proto/misc2.pro @@ -43,7 +43,7 @@ void ga_init2(garray_T *gap, int itemsize, int growsize); 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 index f0f056f88..df8893709 100644 --- 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 index 42b9014f3..09e7b26b3 100644 --- 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 index cdf5d4e4e..e38274ef3 100644 --- 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, |