# HG changeset patch # User Bram Moolenaar # Date 1647892804 -3600 # Node ID e1d1fa6ba1ed04995d173187dc4600f698225466 # Parent 81034dff92a40ee6dfe6d8c788e905da2b4ac759 patch 8.2.4603: sourcing buffer lines is too complicated Commit: https://github.com/vim/vim/commit/85b43c6cb7d56919e245622f4e42db6d8bee4194 Author: Yegappan Lakshmanan Date: Mon Mar 21 19:45:17 2022 +0000 patch 8.2.4603: sourcing buffer lines is too complicated Problem: Sourcing buffer lines is too complicated. Solution: Simplify the code. Make it possible to source Vim9 script lines. (Yegappan Lakshmanan, closes #9974) diff --git a/runtime/doc/repeat.txt b/runtime/doc/repeat.txt --- a/runtime/doc/repeat.txt +++ b/runtime/doc/repeat.txt @@ -201,7 +201,13 @@ For writing a Vim script, see chapter 41 :[range]so[urce] Read Ex commands from the [range] of lines in the current buffer. When sourcing commands from the current buffer, the same script-ID || is used - even if the buffer is sourced multiple times. + even if the buffer is sourced multiple times. If a + buffer is sourced more than once, then the functions + in the buffer are redefined again. + Sourcing a buffer with a Vim9 script more than once + works like |vim9-reload|. + To source a script in the Vim9 context, the |:vim9cmd| + modifier can be used. *:source!* :so[urce]! {file} Read Vim commands from {file}. These are commands @@ -425,10 +431,10 @@ An alternative is to put the commands in ':source!' command. Useful for long command sequences. Can be combined with the ':map' command to put complicated commands under a function key. -The ':source' command reads Ex commands from a file line by line. You will -have to type any needed keyboard input. The ':source!' command reads from a -script file character by character, interpreting each character as if you -typed it. +The ':source' command reads Ex commands from a file or a buffer line by line. +You will have to type any needed keyboard input. The ':source!' command reads +from a script file character by character, interpreting each character as if +you typed it. Example: When you give the ":!ls" command you get the |hit-enter| prompt. If you ':source' a file with the line "!ls" in it, you will have to type the diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -2572,7 +2572,7 @@ do_one_cmd( #ifdef FEAT_EVAL // Set flag that any command was executed, used by ex_vim9script(). // Not if this was a command that wasn't executed or :endif. - if (getline_equal(ea.getline, ea.cookie, getsourceline) + if (sourcing_a_script(&ea) && current_sctx.sc_sid > 0 && ea.cmdidx != CMD_endif && (cstack->cs_idx < 0 diff --git a/src/proto/scriptfile.pro b/src/proto/scriptfile.pro --- a/src/proto/scriptfile.pro +++ b/src/proto/scriptfile.pro @@ -32,6 +32,7 @@ void free_scriptnames(void); void free_autoload_scriptnames(void); linenr_T get_sourced_lnum(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie); char_u *getsourceline(int c, void *cookie, int indent, getline_opt_T options); +int sourcing_a_script(exarg_T *eap); void ex_scriptencoding(exarg_T *eap); void ex_scriptversion(exarg_T *eap); void ex_finish(exarg_T *eap); @@ -42,5 +43,4 @@ char_u *get_autoload_prefix(scriptitem_T char_u *may_prefix_autoload(char_u *name); char_u *autoload_name(char_u *name); int script_autoload(char_u *name, int reload); -int sourcing_a_script(exarg_T *eap); /* vim: set ft=c : */ diff --git a/src/scriptfile.c b/src/scriptfile.c --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -23,6 +23,8 @@ static garray_T ga_loaded = {0, 0, size static int last_current_SID_seq = 0; #endif +static int do_source_ext(char_u *fname, int check_other, int is_vimrc, int *ret_sid, exarg_T *eap); + /* * Initialize the execution stack. */ @@ -1079,251 +1081,6 @@ ExpandPackAddDir( return OK; } -/* - * Cookie used to source Ex commands from a buffer. - */ -typedef struct -{ - garray_T lines_to_source; - int lnum; - linenr_T sourcing_lnum; -} bufline_cookie_T; - -/* - * Concatenate a Vim script line if it starts with a line continuation into a - * growarray (excluding the continuation chars and leading whitespace). - * Growsize of the growarray may be changed to speed up concatenations! - * - * Returns TRUE if this line did begin with a continuation (the next line - * should also be considered, if it exists); FALSE otherwise. - */ - static int -concat_continued_line( - garray_T *ga, - int init_growsize, - char_u *nextline, - int options) -{ - int comment_char = in_vim9script() ? '#' : '"'; - char_u *p = skipwhite(nextline); - int contline; - int do_vim9_all = in_vim9script() - && options == GETLINE_CONCAT_ALL; - int do_bar_cont = do_vim9_all - || options == GETLINE_CONCAT_CONTBAR; - - if (*p == NUL) - return FALSE; - - // Concatenate the next line when it starts with a backslash. - /* Also check for a comment in between continuation lines: "\ */ - // Also check for a Vim9 comment, empty line, line starting with '|', - // but not "||". - if ((p[0] == comment_char && p[1] == '\\' && p[2] == ' ') - || (do_vim9_all && (*p == NUL - || vim9_comment_start(p)))) - return TRUE; - - contline = (*p == '\\' || (do_bar_cont && p[0] == '|' && p[1] != '|')); - if (!contline) - return FALSE; - - // Adjust the growsize to the current length to speed up concatenating many - // lines. - if (ga->ga_len > init_growsize) - ga->ga_growsize = ga->ga_len > 8000 ? 8000 : ga->ga_len; - if (*p == '\\') - ga_concat(ga, (char_u *)p + 1); - else if (*p == '|') - { - ga_concat(ga, (char_u *)" "); - ga_concat(ga, p); - } - - return TRUE; -} - -/* - * Get one full line from a sourced string (in-memory, no file). - * Called by do_cmdline() when it's called from source_using_linegetter(). - * - * Returns a pointer to allocated line, or NULL for end-of-file. - */ - static char_u * -source_getbufline( - int c UNUSED, - void *cookie, - int indent UNUSED, - getline_opt_T opts) -{ - bufline_cookie_T *p = cookie; - char_u *line; - garray_T ga; - - SOURCING_LNUM = p->sourcing_lnum + 1; - - if (p->lnum >= p->lines_to_source.ga_len) - return NULL; - line = ((char_u **)p->lines_to_source.ga_data)[p->lnum]; - - ga_init2(&ga, sizeof(char_u), 400); - ga_concat(&ga, (char_u *)line); - p->lnum++; - - if ((opts != GETLINE_NONE) && vim_strchr(p_cpo, CPO_CONCAT) == NULL) - { - while (p->lnum < p->lines_to_source.ga_len) - { - line = ((char_u **)p->lines_to_source.ga_data)[p->lnum]; - if (!concat_continued_line(&ga, 400, line, opts)) - break; - p->sourcing_lnum++; - p->lnum++; - } - } - ga_append(&ga, NUL); - p->sourcing_lnum++; - - return ga.ga_data; -} - -/* - * Source Ex commands from the lines in 'cookie'. - */ - static int -do_sourcebuffer( - void *cookie, - char_u *scriptname) -{ - char_u *save_sourcing_name = SOURCING_NAME; - linenr_T save_sourcing_lnum = SOURCING_LNUM; - char_u sourcing_name_buf[256]; - sctx_T save_current_sctx; -#ifdef FEAT_EVAL - int sid; - funccal_entry_T funccalp_entry; - int save_estack_compiling = estack_compiling; - scriptitem_T *si = NULL; -#endif - int save_sticky_cmdmod_flags = sticky_cmdmod_flags; - int retval = FAIL; - ESTACK_CHECK_DECLARATION - - if (save_sourcing_name == NULL) - SOURCING_NAME = (char_u *)scriptname; - else - { - vim_snprintf((char *)sourcing_name_buf, sizeof(sourcing_name_buf), - "%s called at %s:%ld", scriptname, save_sourcing_name, - save_sourcing_lnum); - SOURCING_NAME = sourcing_name_buf; - } - SOURCING_LNUM = 0; - - // Keep the sourcing name/lnum, for recursive calls. - estack_push(ETYPE_SCRIPT, scriptname, 0); - ESTACK_CHECK_SETUP - - // "legacy" does not apply to commands in the script - sticky_cmdmod_flags = 0; - - save_current_sctx = current_sctx; - current_sctx.sc_version = 1; // default script version -#ifdef FEAT_EVAL - estack_compiling = FALSE; - // Always use a new sequence number. - current_sctx.sc_seq = ++last_current_SID_seq; - current_sctx.sc_lnum = save_sourcing_lnum; - save_funccal(&funccalp_entry); - - sid = find_script_by_name(scriptname); - if (sid < 0) - { - int error = OK; - - // First time sourcing this buffer, create a new script item. - - sid = get_new_scriptitem(&error); - if (error == FAIL) - goto theend; - current_sctx.sc_sid = sid; - si = SCRIPT_ITEM(current_sctx.sc_sid); - si->sn_name = vim_strsave(scriptname); - si->sn_state = SN_STATE_NEW; - } - else - { - // the buffer was sourced previously, reuse the script ID. - current_sctx.sc_sid = sid; - si = SCRIPT_ITEM(current_sctx.sc_sid); - si->sn_state = SN_STATE_RELOAD; - } -#endif - - retval = do_cmdline(NULL, source_getbufline, cookie, - DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT); - - if (got_int) - emsg(_(e_interrupted)); - -#ifdef FEAT_EVAL -theend: -#endif - ESTACK_CHECK_NOW - estack_pop(); - current_sctx = save_current_sctx; - SOURCING_LNUM = save_sourcing_lnum; - SOURCING_NAME = save_sourcing_name; - sticky_cmdmod_flags = save_sticky_cmdmod_flags; -#ifdef FEAT_EVAL - restore_funccal(); - estack_compiling = save_estack_compiling; -#endif - - return retval; -} - -/* - * :source Ex commands from the current buffer - */ - static void -cmd_source_buffer(exarg_T *eap) -{ - char_u *line = NULL; - linenr_T curr_lnum; - bufline_cookie_T cp; - char_u sname[32]; - - if (curbuf == NULL) - return; - - // Use ":source buffer=" as the script name - vim_snprintf((char *)sname, sizeof(sname), ":source buffer=%d", - curbuf->b_fnum); - - ga_init2(&cp.lines_to_source, sizeof(char_u *), 100); - - // Copy the lines from the buffer into a grow array - for (curr_lnum = eap->line1; curr_lnum <= eap->line2; curr_lnum++) - { - line = vim_strsave(ml_get(curr_lnum)); - if (line == NULL) - goto errret; - if (ga_add_string(&cp.lines_to_source, line) == FAIL) - goto errret; - line = NULL; - } - cp.sourcing_lnum = 0; - cp.lnum = 0; - - // Execute the Ex commands - do_sourcebuffer((void *)&cp, (char_u *)sname); - -errret: - vim_free(line); - ga_clear_strings(&cp.lines_to_source); -} - static void cmd_source(char_u *fname, exarg_T *eap) { @@ -1341,7 +1098,7 @@ cmd_source(char_u *fname, exarg_T *eap) emsg(_(e_argument_required)); else // source ex commands from the current buffer - cmd_source_buffer(eap); + do_source_ext(NULL, FALSE, FALSE, NULL, eap); } else if (eap != NULL && eap->forceit) // ":source!": read Normal mode commands @@ -1480,21 +1237,73 @@ fopen_noinh_readbin(char *filename) #endif /* - * do_source: Read the file "fname" and execute its lines as EX commands. + * Initialization for sourcing lines from the current buffer. Reads all the + * lines from the buffer and stores it in the cookie grow array. + * Returns a pointer to the name ":source buffer=" on success and NULL on + * failure. + */ + static char_u * +do_source_buffer_init(source_cookie_T *sp, exarg_T *eap) +{ + linenr_T curr_lnum; + char_u *line = NULL; + char_u *fname; + + CLEAR_FIELD(*sp); + + if (curbuf == NULL) + return NULL; + + // Use ":source buffer=" as the script name + vim_snprintf((char *)IObuff, IOSIZE, ":source buffer=%d", curbuf->b_fnum); + fname = vim_strsave(IObuff); + if (fname == NULL) + return NULL; + + ga_init2(&sp->buflines, sizeof(char_u *), 100); + + // Copy the lines from the buffer into a grow array + for (curr_lnum = eap->line1; curr_lnum <= eap->line2; curr_lnum++) + { + line = vim_strsave(ml_get(curr_lnum)); + if (line == NULL) + goto errret; + if (ga_add_string(&sp->buflines, line) == FAIL) + goto errret; + line = NULL; + } + sp->buf_lnum = 0; + sp->source_from_buf = TRUE; + + return fname; + +errret: + vim_free(fname); + vim_free(line); + ga_clear_strings(&sp->buflines); + return NULL; +} + +/* + * Read the file "fname" and execute its lines as EX commands. * When "ret_sid" is not NULL and we loaded the script before, don't load it * again. * + * The 'eap' argument is used when sourcing lines from a buffer instead of a + * file. + * * This function may be called recursively! * * Return FAIL if file could not be opened, OK otherwise. * If a scriptitem_T was found or created "*ret_sid" is set to the SID. */ - int -do_source( + static int +do_source_ext( char_u *fname, int check_other, // check for .vimrc and _vimrc int is_vimrc, // DOSO_ value - int *ret_sid UNUSED) + int *ret_sid UNUSED, + exarg_T *eap) { source_cookie_T cookie; char_u *p; @@ -1520,17 +1329,28 @@ do_source( int trigger_source_post = FALSE; ESTACK_CHECK_DECLARATION - p = expand_env_save(fname); - if (p == NULL) - return retval; - fname_exp = fix_fname(p); - vim_free(p); - if (fname_exp == NULL) - return retval; - if (mch_isdir(fname_exp)) + CLEAR_FIELD(cookie); + if (fname == NULL) + { + // sourcing lines from a buffer + fname_exp = do_source_buffer_init(&cookie, eap); + if (fname_exp == NULL) + return FAIL; + } + else { - smsg(_("Cannot source a directory: \"%s\""), fname); - goto theend; + p = expand_env_save(fname); + if (p == NULL) + return retval; + fname_exp = fix_fname(p); + vim_free(p); + if (fname_exp == NULL) + return retval; + if (mch_isdir(fname_exp)) + { + smsg(_("Cannot source a directory: \"%s\""), fname); + goto theend; + } } #ifdef FEAT_EVAL estack_compiling = FALSE; @@ -1567,11 +1387,14 @@ do_source( // Apply SourcePre autocommands, they may get the file. apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf); + if (!cookie.source_from_buf) + { #ifdef USE_FOPEN_NOINH - cookie.fp = fopen_noinh_readbin((char *)fname_exp); + cookie.fp = fopen_noinh_readbin((char *)fname_exp); #else - cookie.fp = mch_fopen((char *)fname_exp, READBIN); + cookie.fp = mch_fopen((char *)fname_exp, READBIN); #endif + } if (cookie.fp == NULL && check_other) { // Try again, replacing file name ".vimrc" by "_vimrc" or vice versa, @@ -1594,7 +1417,7 @@ do_source( } } - if (cookie.fp == NULL) + if (cookie.fp == NULL && !cookie.source_from_buf) { if (p_verbose > 0) { @@ -1632,12 +1455,14 @@ do_source( cookie.fileformat = EOL_DOS; else cookie.fileformat = EOL_UNKNOWN; - cookie.error = FALSE; #endif - cookie.nextline = NULL; - cookie.sourcing_lnum = 0; - cookie.finished = FALSE; + if (fname == NULL) + // When sourcing a range of lines from a buffer, use the buffer line + // number. + cookie.sourcing_lnum = eap->line1 - 1; + else + cookie.sourcing_lnum = 0; #ifdef FEAT_EVAL // Check if this script has a breakpoint. @@ -1661,7 +1486,12 @@ do_source( sticky_cmdmod_flags = 0; save_current_sctx = current_sctx; - current_sctx.sc_version = 1; // default script version + if (cmdmod.cmod_flags & CMOD_VIM9CMD) + // When the ":vim9cmd" command modifier is used, source the script as a + // Vim9 script. + current_sctx.sc_version = SCRIPT_VERSION_VIM9; + else + current_sctx.sc_version = 1; // default script version #ifdef FEAT_EVAL # ifdef FEAT_PROFILE @@ -1874,7 +1704,10 @@ almosttheend: #endif current_sctx = save_current_sctx; - fclose(cookie.fp); + if (cookie.fp != NULL) + fclose(cookie.fp); + if (cookie.source_from_buf) + ga_clear_strings(&cookie.buflines); vim_free(cookie.nextline); vim_free(firstline); convert_setup(&cookie.conv, NULL, NULL); @@ -1891,6 +1724,17 @@ theend: return retval; } + int +do_source( + char_u *fname, + int check_other, // check for .vimrc and _vimrc + int is_vimrc, // DOSO_ value + int *ret_sid UNUSED) +{ + return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL); +} + + #if defined(FEAT_EVAL) || defined(PROTO) /* @@ -2038,11 +1882,21 @@ get_one_sourceline(source_cookie_T *sp) // make room to read at least 120 (more) characters if (ga_grow(&ga, 120) == FAIL) break; - buf = (char_u *)ga.ga_data; - - if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, - sp->fp) == NULL) - break; + if (sp->source_from_buf) + { + if (sp->buf_lnum >= sp->buflines.ga_len) + break; // all the lines are processed + ga_concat(&ga, ((char_u **)sp->buflines.ga_data)[sp->buf_lnum]); + sp->buf_lnum++; + buf = (char_u *)ga.ga_data; + } + else + { + buf = (char_u *)ga.ga_data; + if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, + sp->fp) == NULL) + break; + } len = ga.ga_len + (int)STRLEN(buf + ga.ga_len); #ifdef USE_CRNL // Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the @@ -2145,7 +1999,7 @@ getsourceline( #ifdef FEAT_EVAL // If breakpoints have been added/deleted need to check for it. - if (sp->dbg_tick < debug_tick) + if ((sp->dbg_tick < debug_tick) && !sp->source_from_buf) { sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, SOURCING_LNUM); sp->dbg_tick = debug_tick; @@ -2161,7 +2015,7 @@ getsourceline( // Get current line. If there is a read-ahead line, use it, otherwise get // one now. "fp" is NULL if actually using a string. - if (sp->finished || sp->fp == NULL) + if (sp->finished || (!sp->source_from_buf && sp->fp == NULL)) line = NULL; else if (sp->nextline == NULL) line = get_one_sourceline(sp); @@ -2265,7 +2119,8 @@ getsourceline( #ifdef FEAT_EVAL // Did we encounter a breakpoint? - if (sp->breakpoint != 0 && sp->breakpoint <= SOURCING_LNUM) + if (!sp->source_from_buf && sp->breakpoint != 0 + && sp->breakpoint <= SOURCING_LNUM) { dbg_breakpoint(sp->fname, SOURCING_LNUM); // Find next breakpoint. @@ -2284,8 +2139,7 @@ getsourceline( int sourcing_a_script(exarg_T *eap) { - return (getline_equal(eap->getline, eap->cookie, getsourceline) - || getline_equal(eap->getline, eap->cookie, source_getbufline)); + return (getline_equal(eap->getline, eap->cookie, getsourceline)); } /* diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -4426,6 +4426,9 @@ typedef struct { char_u *nextline; // if not NULL: line that was read ahead linenr_T sourcing_lnum; // line number of the source file int finished; // ":finish" used + int source_from_buf;// TRUE if sourcing from current buffer + int buf_lnum; // line number in the current buffer + garray_T buflines; // lines in the current buffer #ifdef USE_CRNL int fileformat; // EOL_UNKNOWN, EOL_UNIX or EOL_DOS int error; // TRUE if LF found after CR-LF diff --git a/src/testdir/test_source.vim b/src/testdir/test_source.vim --- a/src/testdir/test_source.vim +++ b/src/testdir/test_source.vim @@ -146,6 +146,23 @@ func Test_source_buffer() 2,3source call assert_equal(90, g:a) + " Make sure the script line number is correct when sourcing a range of + " lines. + %d _ + let lines =<< trim END + Line 1 + Line 2 + func Xtestfunc() + return expand("") + endfunc + Line 3 + Line 4 + END + call setline(1, lines) + 3,5source + call assert_equal('4', Xtestfunc()) + delfunc Xtestfunc + " Source a script with line continuation lines %d _ let lines =<< trim END @@ -327,6 +344,63 @@ func Test_source_buffer() call assert_equal("three", Xtestfunc()) delfunc Xtestfunc + " test for using try/catch + %d _ + let lines =<< trim END + let Trace = '1' + try + let a1 = b1 + catch + let Trace ..= '2' + finally + let Trace ..= '3' + endtry + END + call setline(1, lines) + source + call assert_equal("123", g:Trace) + + " test with the finish command + %d _ + let lines =<< trim END + let g:Color = 'blue' + finish + let g:Color = 'green' + END + call setline(1, lines) + source + call assert_equal('blue', g:Color) + + " Test for the SourcePre and SourcePost autocmds + augroup Xtest + au! + au SourcePre * let g:XsourcePre=4 + \ | let g:XsourcePreFile = expand("") + au SourcePost * let g:XsourcePost=6 + \ | let g:XsourcePostFile = expand("") + augroup END + %d _ + let lines =<< trim END + let a = 1 + END + call setline(1, lines) + source + call assert_equal(4, g:XsourcePre) + call assert_equal(6, g:XsourcePost) + call assert_equal(':source buffer=' .. bufnr(), g:XsourcePreFile) + call assert_equal(':source buffer=' .. bufnr(), g:XsourcePostFile) + augroup Xtest + au! + augroup END + augroup! Xtest + + %bw! +endfunc + +" Test for sourcing a Vim9 script from the current buffer +func Test_source_buffer_vim9() + new + " test for sourcing a Vim9 script %d _ let lines =<< trim END @@ -342,6 +416,198 @@ func Test_source_buffer() source call assert_equal(10, Xtestfunc()) + " test for sourcing a vim9 script with line continuation + %d _ + let lines =<< trim END + vim9script + + g:Str1 = "hello " + .. "world" + .. ", how are you?" + g:Colors = [ + 'red', + # comment + 'blue' + ] + g:Dict = { + a: 22, + # comment + b: 33 + } + + # calling a function with line continuation + def Sum(...values: list): number + var sum: number = 0 + for v in values + sum += v + endfor + return sum + enddef + g:Total1 = Sum(10, + 20, + 30) + + var i: number = 0 + while i < 10 + # while loop + i += + 1 + endwhile + g:Count1 = i + + # for loop + g:Count2 = 0 + for j in range(10, 20) + g:Count2 += + i + endfor + + g:Total2 = 10 + + 20 - + 5 + + g:Result1 = g:Total2 > 1 + ? 'red' + : 'blue' + + g:Str2 = 'x' + ->repeat(10) + ->trim() + ->strpart(4) + + g:Result2 = g:Dict + .a + + augroup Test + au! + au BufNewFile Xfile g:readFile = 1 + | g:readExtra = 2 + augroup END + g:readFile = 0 + g:readExtra = 0 + new Xfile + bwipe! + augroup Test + au! + augroup END + END + call setline(1, lines) + source + call assert_equal("hello world, how are you?", g:Str1) + call assert_equal(['red', 'blue'], g:Colors) + call assert_equal(#{a: 22, b: 33}, g:Dict) + call assert_equal(60, g:Total1) + call assert_equal(10, g:Count1) + call assert_equal(110, g:Count2) + call assert_equal(25, g:Total2) + call assert_equal('red', g:Result1) + call assert_equal('xxxxxx', g:Str2) + call assert_equal(22, g:Result2) + call assert_equal(1, g:readFile) + call assert_equal(2, g:readExtra) + + " test for sourcing the same buffer multiple times after changing a function + %d _ + let lines =<< trim END + vim9script + def g:Xtestfunc(): string + return "one" + enddef + END + call setline(1, lines) + source + call assert_equal("one", Xtestfunc()) + call setline(3, ' return "two"') + source + call assert_equal("two", Xtestfunc()) + call setline(3, ' return "three"') + source + call assert_equal("three", Xtestfunc()) + delfunc Xtestfunc + + " Test for sourcing a range of lines. Make sure the script line number is + " correct. + %d _ + let lines =<< trim END + Line 1 + Line 2 + vim9script + def g:Xtestfunc(): string + return expand("") + enddef + Line 3 + Line 4 + END + call setline(1, lines) + 3,6source + call assert_equal('5', Xtestfunc()) + delfunc Xtestfunc + + " test for sourcing a heredoc + %d _ + let lines =<< trim END + vim9script + var a = 1 + g:heredoc =<< trim DATA + red + green + blue + DATA + var b = 2 + END + call setline(1, lines) + source + call assert_equal(['red', ' green', 'blue'], g:heredoc) + + " test for using the :vim9cmd modifier + %d _ + let lines =<< trim END + first line + g:Math = { + pi: 3.12, + e: 2.71828 + } + g:Editors = [ + 'vim', + # comment + 'nano' + ] + last line + END + call setline(1, lines) + vim9cmd :2,10source + call assert_equal(#{pi: 3.12, e: 2.71828}, g:Math) + call assert_equal(['vim', 'nano'], g:Editors) + + " test for using try/catch + %d _ + let lines =<< trim END + vim9script + g:Trace = '1' + try + a1 = b1 + catch + g:Trace ..= '2' + finally + g:Trace ..= '3' + endtry + END + call setline(1, lines) + source + call assert_equal('123', g:Trace) + + " test with the finish command + %d _ + let lines =<< trim END + vim9script + g:Color = 'red' + finish + g:Color = 'blue' + END + call setline(1, lines) + source + call assert_equal('red', g:Color) + %bw! endfunc diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 4603, +/**/ 4602, /**/ 4601,