]> granicus.if.org Git - vim/commitdiff
patch 8.2.4603: sourcing buffer lines is too complicated v8.2.4603
authorYegappan Lakshmanan <yegappan@yahoo.com>
Mon, 21 Mar 2022 19:45:17 +0000 (19:45 +0000)
committerBram Moolenaar <Bram@vim.org>
Mon, 21 Mar 2022 19:45:17 +0000 (19:45 +0000)
Problem:    Sourcing buffer lines is too complicated.
Solution:   Simplify the code. Make it possible to source Vim9 script lines.
            (Yegappan Lakshmanan, closes #9974)

runtime/doc/repeat.txt
src/ex_docmd.c
src/proto/scriptfile.pro
src/scriptfile.c
src/structs.h
src/testdir/test_source.vim
src/version.c

index a775af03a61a82c99266a64a4699e0ac5fe4f967..f7756c1c06842b89d8aa5a705931b963d391646a 100644 (file)
@@ -201,7 +201,13 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
 :[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 |<SID>| 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 a file, and execute them with the
 ':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
index 463d840780cdb2c99a51cf91d9380c910f93ae8e..c33dcbca01101e27398ea59a9ba8095851d0fb0b 100644 (file)
@@ -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
index d0ebdda103fd444e0d00c5483dbdf9b4e9695098..510fb56688608566cbf926eed36f3c409efcb828 100644 (file)
@@ -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 *si);
 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 : */
index 9778843423bc4e0f501433db514b701860364233..3faed4a700636e482491186779125a5243dca41c 100644 (file)
@@ -23,6 +23,8 @@ static garray_T               ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
 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=<num>" 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=<n>" 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=<num>" 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))
-    {
-       smsg(_("Cannot source a directory: \"%s\""), fname);
-       goto theend;
+    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
+    {
+       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));
 }
 
 /*
index 1a98004527c76d8695f309ca4f3685d183ed4daf..a83d9ea643b191f92526f5eabfc2eb314a9f1bf3 100644 (file)
@@ -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
index bdf50e1bebe9e4df89a7d442ace23bf7aef277da..251625aab1549a67244cfdbbc12d606332555248 100644 (file)
@@ -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("<sflnum>")
+     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("<afile>")
+    au SourcePost * let g:XsourcePost=6
+          \ | let g:XsourcePostFile = expand("<afile>")
+  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>): 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("<sflnum>")
+     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
 
index e262953d7437bb8cf1f9b6edf4788c24311b8f16..3874b93c290328cf2ec2c1c5d752d4c73181f85e 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    4603,
 /**/
     4602,
 /**/