Patch 8.2.4594
Problem:    Need to write script to a file to be able to source them.
Solution:   Make ":source" use lines from the current buffer. (Yegappan
            Lakshmanan et al., closes #9967)
Files:      runtime/doc/repeat.txt, runtime/doc/todo.txt, src/alloc.c,
            src/digraph.c, src/eval.c, src/ex_cmds.h, src/scriptfile.c,
            src/proto/scriptfile.pro, src/vim9script.c,
            src/testdir/test_source.vim


*** ../vim-8.2.4593/runtime/doc/repeat.txt      2022-01-18 16:25:58.618309943 
+0000
--- runtime/doc/repeat.txt      2022-03-19 12:36:45.098120789 +0000
***************
*** 188,193 ****
--- 197,208 ----
  :so[urce] {file}      Read Ex commands from {file}.  These are commands that
                        start with a ":".
                        Triggers the |SourcePre| autocommand.
+ 
+ :[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.
+ 
                                                        *:source!*
  :so[urce]! {file}     Read Vim commands from {file}.  These are commands
                        that are executed from Normal mode, like you type
*** ../vim-8.2.4593/runtime/doc/todo.txt        2021-02-13 17:24:19.322119004 
+0000
--- runtime/doc/todo.txt        2022-03-19 12:36:45.098120789 +0000
***************
*** 4336,4347 ****
      restore option values.  Especially useful for new options.  Problem: how
      to avoid a performance penalty (esp. for string options)?
  -   range for ":exec", pass it on to the executed command.  (Webb)
- 8   ":{range}source": source the lines from the current file.
-       You can already yank lines and use :@" to execute them.
-       Most of do_source() would not be used, need a new function.
-       It's easy when not doing breakpoints or profiling.
-     Requires copying the lines into a list and then creating a function to
-     execute lines from the list.  Similar to getnextac().
  7   ":include" command: just like ":source" but doesn't start a new scriptID?
      Will be tricky for the list of script names.
  8   Have a look at VSEL.  Would it be useful to include? (Bigham)
--- 4280,4285 ----
*** ../vim-8.2.4593/src/alloc.c 2022-01-29 15:19:19.542172491 +0000
--- src/alloc.c 2022-03-19 12:36:45.098120789 +0000
***************
*** 845,851 ****
      void
  ga_concat_len(garray_T *gap, char_u *s, size_t len)
  {
!     if (s == NULL || *s == NUL)
        return;
      if (ga_grow(gap, (int)len) == OK)
      {
--- 845,851 ----
      void
  ga_concat_len(garray_T *gap, char_u *s, size_t len)
  {
!     if (s == NULL || *s == NUL || len == 0)
        return;
      if (ga_grow(gap, (int)len) == OK)
      {
*** ../vim-8.2.4593/src/digraph.c       2022-01-31 14:59:33.510943820 +0000
--- src/digraph.c       2022-03-19 12:36:45.098120789 +0000
***************
*** 2507,2513 ****
      int               i;
      char_u    *save_cpo = p_cpo;
  
!     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
      {
        emsg(_(e_using_loadkeymap_not_in_sourced_file));
        return;
--- 2507,2513 ----
      int               i;
      char_u    *save_cpo = p_cpo;
  
!     if (!sourcing_a_script(eap))
      {
        emsg(_(e_using_loadkeymap_not_in_sourced_file));
        return;
*** ../vim-8.2.4593/src/eval.c  2022-03-18 19:44:44.939089425 +0000
--- src/eval.c  2022-03-19 12:36:45.098120789 +0000
***************
*** 140,146 ****
      if (eap != NULL)
      {
        evalarg->eval_cstack = eap->cstack;
!       if (getline_equal(eap->getline, eap->cookie, getsourceline))
        {
            evalarg->eval_getline = eap->getline;
            evalarg->eval_cookie = eap->cookie;
--- 140,146 ----
      if (eap != NULL)
      {
        evalarg->eval_cstack = eap->cstack;
!       if (sourcing_a_script(eap))
        {
            evalarg->eval_getline = eap->getline;
            evalarg->eval_cookie = eap->cookie;
*** ../vim-8.2.4593/src/ex_cmds.h       2022-03-06 14:51:19.058997629 +0000
--- src/ex_cmds.h       2022-03-19 12:36:45.098120789 +0000
***************
*** 1428,1435 ****
        
EX_RANGE|EX_ZEROR|EX_EXTRA|EX_TRLBAR|EX_NOTRLCOM|EX_CTRLV|EX_CMDWIN|EX_LOCK_OK,
        ADDR_OTHER),
  EXCMD(CMD_source,     "source",       ex_source,
!       EX_BANG|EX_FILE1|EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
!       ADDR_NONE),
  EXCMD(CMD_sort,               "sort",         ex_sort,
        EX_RANGE|EX_DFLALL|EX_WHOLEFOLD|EX_BANG|EX_EXTRA|EX_NOTRLCOM|EX_MODIFY,
        ADDR_LINES),
--- 1428,1435 ----
        
EX_RANGE|EX_ZEROR|EX_EXTRA|EX_TRLBAR|EX_NOTRLCOM|EX_CTRLV|EX_CMDWIN|EX_LOCK_OK,
        ADDR_OTHER),
  EXCMD(CMD_source,     "source",       ex_source,
!       
EX_RANGE|EX_DFLALL|EX_BANG|EX_FILE1|EX_TRLBAR|EX_SBOXOK|EX_CMDWIN|EX_LOCK_OK,
!       ADDR_LINES),
  EXCMD(CMD_sort,               "sort",         ex_sort,
        EX_RANGE|EX_DFLALL|EX_WHOLEFOLD|EX_BANG|EX_EXTRA|EX_NOTRLCOM|EX_MODIFY,
        ADDR_LINES),
*** ../vim-8.2.4593/src/scriptfile.c    2022-02-12 13:30:12.760432016 +0000
--- src/scriptfile.c    2022-03-19 12:51:07.033932219 +0000
***************
*** 18,23 ****
--- 18,28 ----
  static garray_T               ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
  #endif
  
+ // last used sequence number for sourcing scripts (current_sctx.sc_seq)
+ #ifdef FEAT_EVAL
+ static int            last_current_SID_seq = 0;
+ #endif
+ 
  /*
   * Initialize the execution stack.
   */
***************
*** 1074,1085 ****
      return OK;
  }
  
      static void
  cmd_source(char_u *fname, exarg_T *eap)
  {
!     if (*fname == NUL)
!       emsg(_(e_argument_required));
  
      else if (eap != NULL && eap->forceit)
        // ":source!": read Normal mode commands
        // Need to execute the commands directly.  This is required at least
--- 1079,1348 ----
      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)
  {
!     if (*fname != NUL && eap != NULL && eap->addr_count > 0)
!     {
!       // if a filename is specified to :source, then a range is not allowed
!       emsg(_(e_no_range_allowed));
!       return;
!     }
  
+     if (eap != NULL && *fname == NUL)
+     {
+       if (eap->forceit)
+           // a file name is needed to source normal mode commands
+           emsg(_(e_argument_required));
+       else
+           // source ex commands from the current buffer
+           cmd_source_buffer(eap);
+     }
      else if (eap != NULL && eap->forceit)
        // ":source!": read Normal mode commands
        // Need to execute the commands directly.  This is required at least
***************
*** 1240,1246 ****
      int                           retval = FAIL;
      sctx_T                save_current_sctx;
  #ifdef FEAT_EVAL
-     static int                    last_current_SID_seq = 0;
      funccal_entry_T       funccalp_entry;
      int                           save_debug_break_level = debug_break_level;
      int                           sid;
--- 1503,1508 ----
***************
*** 2016,2021 ****
--- 2278,2294 ----
  }
  
  /*
+  * Returns TRUE if sourcing a script either from a file or a buffer.
+  * Otherwise returns FALSE.
+  */
+     int
+ sourcing_a_script(exarg_T *eap)
+ {
+     return (getline_equal(eap->getline, eap->cookie, getsourceline)
+           || getline_equal(eap->getline, eap->cookie, source_getbufline));
+ }
+ 
+ /*
   * ":scriptencoding": Set encoding conversion for a sourced script.
   */
      void
***************
*** 2024,2030 ****
      source_cookie_T   *sp;
      char_u            *name;
  
!     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
      {
        emsg(_(e_scriptencoding_used_outside_of_sourced_file));
        return;
--- 2297,2303 ----
      source_cookie_T   *sp;
      char_u            *name;
  
!     if (!sourcing_a_script(eap))
      {
        emsg(_(e_scriptencoding_used_outside_of_sourced_file));
        return;
***************
*** 2055,2061 ****
  {
      int               nr;
  
!     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
      {
        emsg(_(e_scriptversion_used_outside_of_sourced_file));
        return;
--- 2328,2334 ----
  {
      int               nr;
  
!     if (!sourcing_a_script(eap))
      {
        emsg(_(e_scriptversion_used_outside_of_sourced_file));
        return;
***************
*** 2087,2093 ****
      void
  ex_finish(exarg_T *eap)
  {
!     if (getline_equal(eap->getline, eap->cookie, getsourceline))
        do_finish(eap, FALSE);
      else
        emsg(_(e_finish_used_outside_of_sourced_file));
--- 2360,2366 ----
      void
  ex_finish(exarg_T *eap)
  {
!     if (sourcing_a_script(eap))
        do_finish(eap, FALSE);
      else
        emsg(_(e_finish_used_outside_of_sourced_file));
*** ../vim-8.2.4593/src/proto/scriptfile.pro    2022-01-10 18:06:58.682381797 
+0000
--- src/proto/scriptfile.pro    2022-03-19 12:36:45.102120794 +0000
***************
*** 42,45 ****
--- 42,46 ----
  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 : */
*** ../vim-8.2.4593/src/vim9script.c    2022-03-08 13:18:10.809020782 +0000
--- src/vim9script.c    2022-03-19 12:36:45.102120794 +0000
***************
*** 71,77 ****
      int                   found_noclear = FALSE;
      char_u        *p;
  
!     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
      {
        emsg(_(e_vim9script_can_only_be_used_in_script));
        return;
--- 71,77 ----
      int                   found_noclear = FALSE;
      char_u        *p;
  
!     if (!sourcing_a_script(eap))
      {
        emsg(_(e_vim9script_can_only_be_used_in_script));
        return;
***************
*** 633,639 ****
      char_u    *cmd_end;
      evalarg_T evalarg;
  
!     if (!getline_equal(eap->getline, eap->cookie, getsourceline))
      {
        emsg(_(e_import_can_only_be_used_in_script));
        return;
--- 633,639 ----
      char_u    *cmd_end;
      evalarg_T evalarg;
  
!     if (!sourcing_a_script(eap))
      {
        emsg(_(e_import_can_only_be_used_in_script));
        return;
*** ../vim-8.2.4593/src/testdir/test_source.vim 2021-12-27 17:21:38.020449109 
+0000
--- src/testdir/test_source.vim 2022-03-19 12:36:45.102120794 +0000
***************
*** 94,99 ****
--- 94,105 ----
    call assert_fails('scriptencoding utf-8', 'E167:')
    call assert_fails('finish', 'E168:')
    call assert_fails('scriptversion 2', 'E984:')
+   call assert_fails('source!', 'E471:')
+   new
+   call setline(1, ['', '', '', ''])
+   call assert_fails('1,3source Xscript.vim', 'E481:')
+   call assert_fails('1,3source! Xscript.vim', 'E481:')
+   bw!
  endfunc
  
  " Test for sourcing a script recursively
***************
*** 110,113 ****
--- 116,348 ----
    call StopVimInTerminal(buf)
  endfunc
  
+ " Test for sourcing a script from the current buffer
+ func Test_source_buffer()
+   new
+   " Source a simple script
+   let lines =<< trim END
+     let a = "Test"
+     let b = 20
+ 
+     let c = [1.1]
+   END
+   call setline(1, lines)
+   source
+   call assert_equal(['Test', 20, [1.1]], [g:a, g:b, g:c])
+ 
+   " Source a range of lines in the current buffer
+   %d _
+   let lines =<< trim END
+     let a = 10
+     let a += 20
+     let a += 30
+     let a += 40
+   END
+   call setline(1, lines)
+   .source
+   call assert_equal(10, g:a)
+   3source
+   call assert_equal(40, g:a)
+   2,3source
+   call assert_equal(90, g:a)
+ 
+   " Source a script with line continuation lines
+   %d _
+   let lines =<< trim END
+     let m = [
+       \   1,
+       \   2,
+       \ ]
+     call add(m, 3)
+   END
+   call setline(1, lines)
+   source
+   call assert_equal([1, 2, 3], g:m)
+   " Source a script with line continuation lines and a comment
+   %d _
+   let lines =<< trim END
+     let m = [
+       "\ first entry
+       \   'a',
+       "\ second entry
+       \   'b',
+       \ ]
+     " third entry
+     call add(m, 'c')
+   END
+   call setline(1, lines)
+   source
+   call assert_equal(['a', 'b', 'c'], g:m)
+   " Source an incomplete line continuation line
+   %d _
+   let lines =<< trim END
+     let k = [
+       \
+   END
+   call setline(1, lines)
+   call assert_fails('source', 'E697:')
+   " Source a function with a for loop
+   %d _
+   let lines =<< trim END
+     let m = []
+     " test function
+     func! Xtest()
+       for i in range(5, 7)
+         call add(g:m, i)
+       endfor
+     endfunc
+     call Xtest()
+   END
+   call setline(1, lines)
+   source
+   call assert_equal([5, 6, 7], g:m)
+   " Source an empty buffer
+   %d _
+   source
+ 
+   " test for script local functions and variables
+   let lines =<< trim END
+     let s:var1 = 10
+     func s:F1()
+       let s:var1 += 1
+       return s:var1
+     endfunc
+     func s:F2()
+     endfunc
+     let g:ScriptID = expand("<SID>")
+   END
+   call setline(1, lines)
+   source
+   call assert_true(g:ScriptID != '')
+   call assert_true(exists('*' .. g:ScriptID .. 'F1'))
+   call assert_true(exists('*' .. g:ScriptID .. 'F2'))
+   call assert_equal(11, call(g:ScriptID .. 'F1', []))
+ 
+   " the same script ID should be used even if the buffer is sourced more than
+   " once
+   %d _
+   let lines =<< trim END
+     let g:ScriptID = expand("<SID>")
+     let g:Count += 1
+   END
+   call setline(1, lines)
+   let g:Count = 0
+   source
+   call assert_true(g:ScriptID != '')
+   let scid = g:ScriptID
+   source
+   call assert_equal(scid, g:ScriptID)
+   call assert_equal(2, g:Count)
+   source
+   call assert_equal(scid, g:ScriptID)
+   call assert_equal(3, g:Count)
+ 
+   " test for the script line number
+   %d _
+   let lines =<< trim END
+     " comment
+     let g:Slnum1 = expand("<slnum>")
+     let i = 1 +
+            \ 2 +
+           "\ comment
+            \ 3
+     let g:Slnum2 = expand("<slnum>")
+   END
+   call setline(1, lines)
+   source
+   call assert_equal('2', g:Slnum1)
+   call assert_equal('7', g:Slnum2)
+ 
+   " test for retaining the same script number across source calls
+   let lines =<< trim END
+      let g:ScriptID1 = expand("<SID>")
+      let g:Slnum1 = expand("<slnum>")
+      let l =<< trim END
+        let g:Slnum2 = expand("<slnum>")
+        let g:ScriptID2 = expand("<SID>")
+      END
+      new
+      call setline(1, l)
+      source
+      bw!
+      let g:ScriptID3 = expand("<SID>")
+      let g:Slnum3 = expand("<slnum>")
+   END
+   call writefile(lines, 'Xscript')
+   source Xscript
+   call assert_true(g:ScriptID1 != g:ScriptID2)
+   call assert_equal(g:ScriptID1, g:ScriptID3)
+   call assert_equal('2', g:Slnum1)
+   call assert_equal('1', g:Slnum2)
+   call assert_equal('12', g:Slnum3)
+   call delete('Xscript')
+ 
+   " test for sourcing a heredoc
+   %d _
+   let lines =<< trim END
+      let a = 1
+      let heredoc =<< trim DATA
+         red
+           green
+         blue
+      DATA
+      let b = 2
+   END
+   call setline(1, lines)
+   source
+   call assert_equal(['red', '  green', 'blue'], g:heredoc)
+ 
+   " test for a while and for statement
+   %d _
+   let lines =<< trim END
+      let a = 0
+      let b = 1
+      while b <= 10
+        let a += 10
+        let b += 1
+      endwhile
+      for i in range(5)
+        let a += 10
+      endfor
+   END
+   call setline(1, lines)
+   source
+   call assert_equal(150, g:a)
+ 
+   " test for sourcing the same buffer multiple times after changing a function
+   %d _
+   let lines =<< trim END
+      func Xtestfunc()
+        return "one"
+      endfunc
+   END
+   call setline(1, lines)
+   source
+   call assert_equal("one", Xtestfunc())
+   call setline(2, '  return "two"')
+   source
+   call assert_equal("two", Xtestfunc())
+   call setline(2, '  return "three"')
+   source
+   call assert_equal("three", Xtestfunc())
+   delfunc Xtestfunc
+ 
+   " test for sourcing a Vim9 script
+   %d _
+   let lines =<< trim END
+      vim9script
+ 
+      # check dict
+      var x: number = 10
+      def g:Xtestfunc(): number
+        return x
+      enddef
+   END
+   call setline(1, lines)
+   source
+   call assert_equal(10, Xtestfunc())
+ 
+   %bw!
+ endfunc
+ 
  " vim: shiftwidth=2 sts=2 expandtab
*** ../vim-8.2.4593/src/version.c       2022-03-19 11:42:13.449717210 +0000
--- src/version.c       2022-03-19 12:47:58.202122527 +0000
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     4594,
  /**/

-- 
GALAHAD: No, please.  Please! I can defeat them!  There's only a hundred.
GIRLS:   He will beat us easily.  We haven't a chance.
                 "Monty Python and the Holy Grail" PYTHON (MONTY) PICTURES LTD

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///                                                                      \\\
\\\        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/20220319125738.A6D1B1C13E5%40moolenaar.net.

Raspunde prin e-mail lui