Patch 8.2.4603
Problem:    Sourcing buffer lines is too complicated.
Solution:   Simplify the code. Make it possible to source Vim9 script lines.
            (Yegappan Lakshmanan, closes #9974)
Files:      runtime/doc/repeat.txt, src/ex_docmd.c, src/proto/scriptfile.pro,
            src/scriptfile.c, src/structs.h, src/testdir/test_source.vim


*** ../vim-8.2.4602/runtime/doc/repeat.txt      2022-03-19 12:56:42.525503835 
+0000
--- runtime/doc/repeat.txt      2022-03-21 19:32:54.478829840 +0000
***************
*** 192,198 ****
  :[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
--- 201,213 ----
  :[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. 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
***************
*** 414,423 ****
  ':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.
  
  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
--- 431,440 ----
  ':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 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
*** ../vim-8.2.4602/src/ex_docmd.c      2022-03-18 19:44:44.935089439 +0000
--- src/ex_docmd.c      2022-03-21 19:32:54.478829840 +0000
***************
*** 2572,2578 ****
  #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)
            && current_sctx.sc_sid > 0
            && ea.cmdidx != CMD_endif
            && (cstack->cs_idx < 0
--- 2572,2578 ----
  #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 (sourcing_a_script(&ea)
            && current_sctx.sc_sid > 0
            && ea.cmdidx != CMD_endif
            && (cstack->cs_idx < 0
*** ../vim-8.2.4602/src/proto/scriptfile.pro    2022-03-19 12:56:42.529503830 
+0000
--- src/proto/scriptfile.pro    2022-03-21 19:32:54.478829840 +0000
***************
*** 32,37 ****
--- 32,38 ----
  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,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 : */
--- 43,46 ----
*** ../vim-8.2.4602/src/scriptfile.c    2022-03-19 12:56:42.529503830 +0000
--- src/scriptfile.c    2022-03-21 19:42:41.068686515 +0000
***************
*** 23,28 ****
--- 23,30 ----
  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,1329 ****
      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)
  {
--- 1081,1086 ----
***************
*** 1341,1347 ****
            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
--- 1098,1104 ----
            emsg(_(e_argument_required));
        else
            // source ex commands from the current buffer
!           do_source_ext(NULL, FALSE, FALSE, NULL, eap);
      }
      else if (eap != NULL && eap->forceit)
        // ":source!": read Normal mode commands
***************
*** 1480,1500 ****
  #endif
  
  /*
!  * do_source: 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.
   *
   * 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(
      char_u    *fname,
      int               check_other,        // check for .vimrc and _vimrc
      int               is_vimrc,           // DOSO_ value
!     int               *ret_sid UNUSED)
  {
      source_cookie_T       cookie;
      char_u                *p;
--- 1237,1309 ----
  #endif
  
  /*
!  * 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.
   */
!     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,
!     exarg_T   *eap)
  {
      source_cookie_T       cookie;
      char_u                *p;
***************
*** 1520,1536 ****
      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;
      }
  #ifdef FEAT_EVAL
      estack_compiling = FALSE;
--- 1329,1356 ----
      int                           trigger_source_post = FALSE;
      ESTACK_CHECK_DECLARATION
  
!     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,1577 ****
      // Apply SourcePre autocommands, they may get the file.
      apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf);
  
  #ifdef USE_FOPEN_NOINH
!     cookie.fp = fopen_noinh_readbin((char *)fname_exp);
  #else
!     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,
--- 1387,1400 ----
      // 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);
  #else
!       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,1600 ****
        }
      }
  
!     if (cookie.fp == NULL)
      {
        if (p_verbose > 0)
        {
--- 1417,1423 ----
        }
      }
  
!     if (cookie.fp == NULL && !cookie.source_from_buf)
      {
        if (p_verbose > 0)
        {
***************
*** 1632,1643 ****
        cookie.fileformat = EOL_DOS;
      else
        cookie.fileformat = EOL_UNKNOWN;
-     cookie.error = FALSE;
  #endif
  
!     cookie.nextline = NULL;
!     cookie.sourcing_lnum = 0;
!     cookie.finished = FALSE;
  
  #ifdef FEAT_EVAL
      // Check if this script has a breakpoint.
--- 1455,1468 ----
        cookie.fileformat = EOL_DOS;
      else
        cookie.fileformat = EOL_UNKNOWN;
  #endif
  
!     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,1667 ****
      sticky_cmdmod_flags = 0;
  
      save_current_sctx = current_sctx;
!     current_sctx.sc_version = 1;  // default script version
  
  #ifdef FEAT_EVAL
  # ifdef FEAT_PROFILE
--- 1486,1497 ----
      sticky_cmdmod_flags = 0;
  
      save_current_sctx = current_sctx;
!     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,1880 ****
  #endif
      current_sctx = save_current_sctx;
  
!     fclose(cookie.fp);
      vim_free(cookie.nextline);
      vim_free(firstline);
      convert_setup(&cookie.conv, NULL, NULL);
--- 1704,1713 ----
  #endif
      current_sctx = save_current_sctx;
  
!     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,1896 ****
--- 1724,1740 ----
      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,2048 ****
        // 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;
        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
--- 1882,1902 ----
        // make room to read at least 120 (more) characters
        if (ga_grow(&ga, 120) == FAIL)
            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,2151 ****
  
  #ifdef FEAT_EVAL
      // If breakpoints have been added/deleted need to check for it.
!     if (sp->dbg_tick < debug_tick)
      {
        sp->breakpoint = dbg_find_breakpoint(TRUE, sp->fname, SOURCING_LNUM);
        sp->dbg_tick = debug_tick;
--- 1999,2005 ----
  
  #ifdef FEAT_EVAL
      // If breakpoints have been added/deleted need to check for it.
!     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,2167 ****
  
      // 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)
        line = NULL;
      else if (sp->nextline == NULL)
        line = get_one_sourceline(sp);
--- 2015,2021 ----
  
      // 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->source_from_buf && sp->fp == NULL))
        line = NULL;
      else if (sp->nextline == NULL)
        line = get_one_sourceline(sp);
***************
*** 2265,2271 ****
  
  #ifdef FEAT_EVAL
      // Did we encounter a breakpoint?
!     if (sp->breakpoint != 0 && sp->breakpoint <= SOURCING_LNUM)
      {
        dbg_breakpoint(sp->fname, SOURCING_LNUM);
        // Find next breakpoint.
--- 2119,2126 ----
  
  #ifdef FEAT_EVAL
      // Did we encounter a breakpoint?
!     if (!sp->source_from_buf && sp->breakpoint != 0
!           && sp->breakpoint <= SOURCING_LNUM)
      {
        dbg_breakpoint(sp->fname, SOURCING_LNUM);
        // Find next breakpoint.
***************
*** 2284,2291 ****
      int
  sourcing_a_script(exarg_T *eap)
  {
!     return (getline_equal(eap->getline, eap->cookie, getsourceline)
!           || getline_equal(eap->getline, eap->cookie, source_getbufline));
  }
  
  /*
--- 2139,2145 ----
      int
  sourcing_a_script(exarg_T *eap)
  {
!     return (getline_equal(eap->getline, eap->cookie, getsourceline));
  }
  
  /*
*** ../vim-8.2.4602/src/structs.h       2022-02-24 13:28:36.570222354 +0000
--- src/structs.h       2022-03-21 19:40:09.457226040 +0000
***************
*** 4426,4431 ****
--- 4426,4434 ----
      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
*** ../vim-8.2.4602/src/testdir/test_source.vim 2022-03-19 12:56:42.533503825 
+0000
--- src/testdir/test_source.vim 2022-03-21 19:32:54.482829827 +0000
***************
*** 146,151 ****
--- 146,168 ----
    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,332 ****
--- 344,406 ----
    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,347 ****
--- 416,613 ----
    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
  
*** ../vim-8.2.4602/src/version.c       2022-03-20 21:14:08.438143810 +0000
--- src/version.c       2022-03-21 19:38:55.261492905 +0000
***************
*** 752,753 ****
--- 752,755 ----
  {   /* Add new patch number below this line */
+ /**/
+     4603,
  /**/

-- 
Not too long ago, unzipping in public was illegal...

 /// 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/20220321194554.2A3D41C783D%40moolenaar.net.

Raspunde prin e-mail lui