vim-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 vim/vim#9974)

85b43c6cb7

This commit changes the behavior of sourcing buffer lines to always have
a script ID, although sourcing the same buffer always produces the same
script ID.

vim-patch:9.1.0372: Calling CLEAR_FIELD() on the same struct twice

Problem:  Calling CLEAR_FIELD() on the same struct twice.
Solution: Remove the second CLEAR_FIELD().  Move the assignment of
          cookie.sourceing_lnum (zeertzjq).

closes: vim/vim#14627

f68517c167

Co-authored-by: Yegappan Lakshmanan <yegappan@yahoo.com>
This commit is contained in:
zeertzjq
2024-04-24 16:20:19 +08:00
parent a3a9f86d4a
commit 6750d00fe9
13 changed files with 748 additions and 213 deletions

View File

@@ -201,7 +201,9 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
code if 'filetype' is "lua" or its filename ends with code if 'filetype' is "lua" or its filename ends with
".lua". When sourcing commands or Lua code from the ".lua". When sourcing commands or Lua code from the
current buffer, the same script-ID |<SID>| is used 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.
*:source!* *:source!*
:so[urce]! {file} :so[urce]! {file}
@@ -394,10 +396,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 ':source!' command. Useful for long command sequences. Can be combined with
the ':map' command to put complicated commands under a function key. 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 The ':source' command reads Ex commands from a file or a buffer line by line.
have to type any needed keyboard input. The ':source!' command reads from a You will have to type any needed keyboard input. The ':source!' command reads
script file character by character, interpreting each character as if you from a script file character by character, interpreting each character as if
typed it. you typed it.
Example: When you give the ":!ls" command you get the |hit-enter| prompt. If 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 you ':source' a file with the line "!ls" in it, you will have to type the

View File

@@ -1461,19 +1461,12 @@ static void nlua_typval_exec(const char *lcmd, size_t lcmd_len, const char *name
} }
} }
void nlua_source_str(const char *code, char *name) void nlua_exec_ga(garray_T *ga, char *name)
{ {
const sctx_T save_current_sctx = current_sctx; char *code = ga_concat_strings_sep(ga, "\n");
current_sctx.sc_sid = SID_STR;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = 0;
estack_push(ETYPE_SCRIPT, name, 0);
size_t len = strlen(code); size_t len = strlen(code);
nlua_typval_exec(code, len, name, NULL, 0, false, NULL); nlua_typval_exec(code, len, name, NULL, 0, false, NULL);
xfree(code);
estack_pop();
current_sctx = save_current_sctx;
} }
/// Call a LuaCallable given some typvals /// Call a LuaCallable given some typvals

View File

@@ -8,6 +8,7 @@
#include "nvim/api/private/helpers.h" #include "nvim/api/private/helpers.h"
#include "nvim/cmdexpand_defs.h" // IWYU pragma: keep #include "nvim/cmdexpand_defs.h" // IWYU pragma: keep
#include "nvim/ex_cmds_defs.h" // IWYU pragma: keep #include "nvim/ex_cmds_defs.h" // IWYU pragma: keep
#include "nvim/garray_defs.h" // IWYU pragma: keep
#include "nvim/macros_defs.h" #include "nvim/macros_defs.h"
#include "nvim/types_defs.h" #include "nvim/types_defs.h"
#include "nvim/usercmd.h" // IWYU pragma: keep #include "nvim/usercmd.h" // IWYU pragma: keep

View File

@@ -75,7 +75,10 @@ typedef struct {
FILE *fp; ///< opened file for sourcing FILE *fp; ///< opened file for sourcing
char *nextline; ///< if not NULL: line that was read ahead char *nextline; ///< if not NULL: line that was read ahead
linenr_T sourcing_lnum; ///< line number of the source file linenr_T sourcing_lnum; ///< line number of the source file
int finished; ///< ":finish" used bool finished; ///< ":finish" used
bool source_from_buf_or_str; ///< true if sourcing from a buffer or string
int buf_lnum; ///< line number in the buffer or string
garray_T buflines; ///< lines in the buffer or string
#ifdef USE_CRNL #ifdef USE_CRNL
int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS
bool error; ///< true if LF found after CR-LF bool error; ///< true if LF found after CR-LF
@@ -1947,42 +1950,6 @@ static bool concat_continued_line(garray_T *const ga, const int init_growsize, c
return true; return true;
} }
typedef struct {
char *buf;
size_t offset;
} GetStrLineCookie;
/// Get one full line from a sourced string (in-memory, no file).
/// Called by do_cmdline() when it's called from do_source_str().
///
/// @return pointer to allocated line, or NULL for end-of-file or
/// some error.
static char *get_str_line(int c, void *cookie, int indent, bool do_concat)
{
GetStrLineCookie *p = cookie;
if (strlen(p->buf) <= p->offset) {
return NULL;
}
const char *line = p->buf + p->offset;
const char *eol = skip_to_newline(line);
garray_T ga;
ga_init(&ga, sizeof(char), 400);
ga_concat_len(&ga, line, (size_t)(eol - line));
if (do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) {
while (eol[0] != NUL) {
line = eol + 1;
const char *const next_eol = skip_to_newline(line);
if (!concat_continued_line(&ga, 400, line, (size_t)(next_eol - line))) {
break;
}
eol = next_eol;
}
}
ga_append(&ga, NUL);
p->offset = (size_t)(eol - p->buf) + 1;
return ga.ga_data;
}
/// Create a new script item and allocate script-local vars. @see new_script_vars /// Create a new script item and allocate script-local vars. @see new_script_vars
/// ///
/// @param name File name of the script. NULL for anonymous :source. /// @param name File name of the script. NULL for anonymous :source.
@@ -1990,6 +1957,7 @@ static char *get_str_line(int c, void *cookie, int indent, bool do_concat)
/// ///
/// @return pointer to the created script item. /// @return pointer to the created script item.
scriptitem_T *new_script_item(char *const name, scid_T *const sid_out) scriptitem_T *new_script_item(char *const name, scid_T *const sid_out)
FUNC_ATTR_NONNULL_RET
{ {
static scid_T last_current_SID = 0; static scid_T last_current_SID = 0;
const scid_T sid = ++last_current_SID; const scid_T sid = ++last_current_SID;
@@ -2012,99 +1980,97 @@ scriptitem_T *new_script_item(char *const name, scid_T *const sid_out)
return SCRIPT_ITEM(sid); return SCRIPT_ITEM(sid);
} }
static int source_using_linegetter(void *cookie, LineGetter fgetline, const char *traceback_name) /// 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 *do_source_buffer_init(source_cookie_T *sp, const exarg_T *eap, bool ex_lua)
FUNC_ATTR_NONNULL_ALL
{ {
char *save_sourcing_name = SOURCING_NAME; if (curbuf == NULL) {
linenr_T save_sourcing_lnum = SOURCING_LNUM; return NULL;
char sourcing_name_buf[256];
char *sname;
if (save_sourcing_name == NULL) {
sname = (char *)traceback_name;
} else {
snprintf(sourcing_name_buf, sizeof(sourcing_name_buf),
"%s called at %s:%" PRIdLINENR, traceback_name, save_sourcing_name,
save_sourcing_lnum);
sname = sourcing_name_buf;
} }
estack_push(ETYPE_SCRIPT, sname, 0);
const sctx_T save_current_sctx = current_sctx; if (ex_lua) {
if (!script_is_lua(current_sctx.sc_sid)) { // Use ":{range}lua buffer=<num>" as the script name
current_sctx.sc_sid = SID_STR; snprintf(IObuff, IOSIZE, ":{range}lua buffer=%d", curbuf->b_fnum);
} else {
// Use ":source buffer=<num>" as the script name
snprintf(IObuff, IOSIZE, ":source buffer=%d", curbuf->b_fnum);
} }
current_sctx.sc_seq = 0; char *fname = xstrdup(IObuff);
current_sctx.sc_lnum = save_sourcing_lnum;
funccal_entry_T entry; ga_init(&sp->buflines, sizeof(char *), 100);
save_funccal(&entry); // Copy the lines from the buffer into a grow array
int retval = do_cmdline(NULL, fgetline, cookie, for (linenr_T curr_lnum = eap->line1; curr_lnum <= eap->line2; curr_lnum++) {
DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT); GA_APPEND(char *, &sp->buflines, xstrdup(ml_get(curr_lnum)));
estack_pop(); }
current_sctx = save_current_sctx; sp->buf_lnum = 0;
restore_funccal(); sp->source_from_buf_or_str = true;
return retval; // When sourcing a range of lines from a buffer, use buffer line number.
sp->sourcing_lnum = eap->line1 - 1;
return fname;
}
/// Initialization for sourcing lines from a string. Reads all the
/// lines from the string and stores it in the cookie grow array.
static void do_source_str_init(source_cookie_T *sp, const char *str)
FUNC_ATTR_NONNULL_ALL
{
ga_init(&sp->buflines, sizeof(char *), 100);
// Copy the lines from the string into a grow array
while (*str != NUL) {
const char *eol = skip_to_newline(str);
GA_APPEND(char *, &sp->buflines, xmemdupz(str, (size_t)(eol - str)));
str = eol + (*eol != NUL);
}
sp->buf_lnum = 0;
sp->source_from_buf_or_str = true;
} }
void cmd_source_buffer(const exarg_T *const eap, bool ex_lua) void cmd_source_buffer(const exarg_T *const eap, bool ex_lua)
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_NONNULL_ALL
{ {
if (curbuf == NULL) { do_source_ext(NULL, false, DOSO_NONE, NULL, eap, ex_lua, NULL);
return;
}
garray_T ga;
ga_init(&ga, sizeof(char), 400);
const linenr_T final_lnum = eap->line2;
// Copy the contents to be executed.
for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) {
// Adjust growsize to current length to speed up concatenating many lines.
if (ga.ga_len > 400) {
ga_set_growsize(&ga, MIN(ga.ga_len, 8000));
}
ga_concat(&ga, ml_get(curr_lnum));
ga_append(&ga, NL);
}
((char *)ga.ga_data)[ga.ga_len - 1] = NUL;
if (ex_lua || strequal(curbuf->b_p_ft, "lua")
|| (curbuf->b_fname && path_with_extension(curbuf->b_fname, "lua"))) {
char *name = ex_lua ? ":{range}lua" : ":source (no file)";
nlua_source_str(ga.ga_data, name);
} else {
const GetStrLineCookie cookie = {
.buf = ga.ga_data,
.offset = 0,
};
source_using_linegetter((void *)&cookie, get_str_line, ":source (no file)");
}
ga_clear(&ga);
} }
/// Executes lines in `src` as Ex commands. /// Executes lines in `str` as Ex commands.
/// ///
/// @see do_source() /// @see do_source_ext()
int do_source_str(const char *cmd, const char *traceback_name) int do_source_str(const char *str, char *traceback_name)
FUNC_ATTR_NONNULL_ALL
{ {
GetStrLineCookie cookie = { char *const sourcing_name = SOURCING_NAME;
.buf = (char *)cmd, const linenr_T sourcing_lnum = SOURCING_LNUM;
.offset = 0, char sname_buf[256];
}; if (sourcing_name != NULL) {
return source_using_linegetter((void *)&cookie, get_str_line, traceback_name); snprintf(sname_buf, sizeof(sname_buf), "%s called at %s:%" PRIdLINENR,
traceback_name, sourcing_name, sourcing_lnum);
traceback_name = sname_buf;
}
return do_source_ext(traceback_name, false, DOSO_NONE, NULL, NULL, false, str);
} }
/// When fname is a 'lua' file nlua_exec_file() is invoked to source it. /// When fname is a .lua file nlua_exec_file() is invoked to source it.
/// Otherwise reads the file `fname` and executes its lines as Ex commands. /// Otherwise reads the file `fname` and executes its lines as Ex commands.
/// ///
/// This function may be called recursively! /// This function may be called recursively!
/// ///
/// @see do_source_str /// @see do_source_str
/// ///
/// @param fname /// @param fname if NULL, source from the current buffer
/// @param check_other check for .vimrc and _vimrc /// @param check_other check for .vimrc and _vimrc
/// @param is_vimrc DOSO_ value /// @param is_vimrc DOSO_ value
/// @param ret_sid if not NULL and we loaded the script before, don't load it again /// @param ret_sid if not NULL and we loaded the script before, don't load it again
/// @param eap used when sourcing lines from a buffer instead of a file
/// @param str if not NULL, source from the given string
/// ///
/// @return FAIL if file could not be opened, OK otherwise /// @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. /// If a scriptitem_T was found or created "*ret_sid" is set to the SID.
int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid) static int do_source_ext(char *const fname, const bool check_other, const int is_vimrc,
int *const ret_sid, const exarg_T *const eap, const bool ex_lua,
const char *const str)
{ {
source_cookie_T cookie; source_cookie_T cookie;
uint8_t *firstline = NULL; uint8_t *firstline = NULL;
@@ -2114,22 +2080,36 @@ int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
proftime_T wait_start; proftime_T wait_start;
bool trigger_source_post = false; bool trigger_source_post = false;
char *p = expand_env_save(fname); CLEAR_FIELD(cookie);
if (p == NULL) { char *fname_exp = NULL;
return retval; if (fname == NULL) {
} assert(str == NULL);
char *fname_exp = fix_fname(p); // sourcing lines from a buffer
xfree(p); fname_exp = do_source_buffer_init(&cookie, eap, ex_lua);
if (fname_exp == NULL) { if (fname_exp == NULL) {
return retval; return FAIL;
} }
if (os_isdir(fname_exp)) { } else if (str != NULL) {
smsg(0, _("Cannot source a directory: \"%s\""), fname); do_source_str_init(&cookie, str);
goto theend; fname_exp = xstrdup(fname);
} else {
char *p = expand_env_save(fname);
if (p == NULL) {
return retval;
}
fname_exp = fix_fname(p);
xfree(p);
if (fname_exp == NULL) {
return retval;
}
if (os_isdir(fname_exp)) {
smsg(0, _("Cannot source a directory: \"%s\""), fname);
goto theend;
}
} }
// See if we loaded this script before. // See if we loaded this script before.
int sid = find_script_by_name(fname_exp); int sid = str != NULL ? SID_STR : find_script_by_name(fname_exp);
if (sid > 0 && ret_sid != NULL) { if (sid > 0 && ret_sid != NULL) {
// Already loaded and no need to load again, return here. // Already loaded and no need to load again, return here.
*ret_sid = sid; *ret_sid = sid;
@@ -2137,26 +2117,30 @@ int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
goto theend; goto theend;
} }
// Apply SourceCmd autocommands, they should get the file and source it. if (str == NULL) {
if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL) // Apply SourceCmd autocommands, they should get the file and source it.
&& apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp, if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL)
false, curbuf)) { && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp,
retval = aborting() ? FAIL : OK; false, curbuf)) {
if (retval == OK) { retval = aborting() ? FAIL : OK;
// Apply SourcePost autocommands. if (retval == OK) {
apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf); // Apply SourcePost autocommands.
apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf);
}
goto theend;
} }
goto theend;
// Apply SourcePre autocommands, they may get the file.
apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, false, curbuf);
} }
// Apply SourcePre autocommands, they may get the file. if (!cookie.source_from_buf_or_str) {
apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, false, curbuf); cookie.fp = fopen_noinh_readbin(fname_exp);
}
cookie.fp = fopen_noinh_readbin(fname_exp);
if (cookie.fp == NULL && check_other) { if (cookie.fp == NULL && check_other) {
// Try again, replacing file name ".nvimrc" by "_nvimrc" or vice versa, // Try again, replacing file name ".nvimrc" by "_nvimrc" or vice versa,
// and ".exrc" by "_exrc" or vice versa. // and ".exrc" by "_exrc" or vice versa.
p = path_tail(fname_exp); char *p = path_tail(fname_exp);
if ((*p == '.' || *p == '_') if ((*p == '.' || *p == '_')
&& (STRICMP(p + 1, "nvimrc") == 0 || STRICMP(p + 1, "exrc") == 0)) { && (STRICMP(p + 1, "nvimrc") == 0 || STRICMP(p + 1, "exrc") == 0)) {
*p = (*p == '_') ? '.' : '_'; *p = (*p == '_') ? '.' : '_';
@@ -2164,7 +2148,7 @@ int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
} }
} }
if (cookie.fp == NULL) { if (cookie.fp == NULL && !cookie.source_from_buf_or_str) {
if (p_verbose > 1) { if (p_verbose > 1) {
verbose_enter(); verbose_enter();
if (SOURCING_NAME == NULL) { if (SOURCING_NAME == NULL) {
@@ -2201,13 +2185,8 @@ int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
} else { } else {
cookie.fileformat = EOL_UNKNOWN; cookie.fileformat = EOL_UNKNOWN;
} }
cookie.error = false;
#endif #endif
cookie.nextline = NULL;
cookie.sourcing_lnum = 0;
cookie.finished = false;
// Check if this script has a breakpoint. // Check if this script has a breakpoint.
cookie.breakpoint = dbg_find_breakpoint(true, fname_exp, 0); cookie.breakpoint = dbg_find_breakpoint(true, fname_exp, 0);
cookie.fname = fname_exp; cookie.fname = fname_exp;
@@ -2236,15 +2215,13 @@ int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
const sctx_T save_current_sctx = current_sctx; const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_lnum = 0;
// Always use a new sequence number. // Always use a new sequence number.
current_sctx.sc_seq = ++last_current_SID_seq; current_sctx.sc_seq = ++last_current_SID_seq;
if (sid > 0) { if (sid > 0) {
// loading the same script again // loading the same script again
si = SCRIPT_ITEM(sid); si = SCRIPT_ITEM(sid);
} else { } else if (str == NULL) {
// It's new, generate a new SID. // It's new, generate a new SID.
si = new_script_item(fname_exp, &sid); si = new_script_item(fname_exp, &sid);
si->sn_lua = path_with_extension(fname_exp, "lua"); si->sn_lua = path_with_extension(fname_exp, "lua");
@@ -2253,12 +2230,20 @@ int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
*ret_sid = sid; *ret_sid = sid;
} }
} }
current_sctx.sc_sid = sid; // Sourcing a string doesn't allocate a script item immediately.
assert((si != NULL) == (str == NULL));
// Don't change sc_sid to SID_STR when sourcing a string from a Lua script,
// as keeping the current sc_sid allows more useful :verbose messages.
if (str == NULL || !script_is_lua(current_sctx.sc_sid)) {
current_sctx.sc_sid = sid;
current_sctx.sc_lnum = 0;
}
// Keep the sourcing name/lnum, for recursive calls. // Keep the sourcing name/lnum, for recursive calls.
estack_push(ETYPE_SCRIPT, si->sn_name, 0); estack_push(ETYPE_SCRIPT, si != NULL ? si->sn_name : fname_exp, 0);
if (l_do_profiling == PROF_YES) { if (l_do_profiling == PROF_YES && si != NULL) {
bool forceit = false; bool forceit = false;
// Check if we do profiling for this script. // Check if we do profiling for this script.
@@ -2275,7 +2260,12 @@ int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
cookie.conv.vc_type = CONV_NONE; // no conversion cookie.conv.vc_type = CONV_NONE; // no conversion
if (si->sn_lua) { if (fname == NULL
&& (ex_lua || strequal(curbuf->b_p_ft, "lua")
|| (curbuf->b_fname && path_with_extension(curbuf->b_fname, "lua")))) {
// Source lines from the current buffer as lua
nlua_exec_ga(&cookie.buflines, fname_exp);
} else if (si != NULL && si->sn_lua) {
// Source the file as lua // Source the file as lua
nlua_exec_file(fname_exp); nlua_exec_file(fname_exp);
} else { } else {
@@ -2285,7 +2275,7 @@ int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
&& firstline[1] == 0xbb && firstline[2] == 0xbf) { && firstline[1] == 0xbb && firstline[2] == 0xbf) {
// Found BOM; setup conversion, skip over BOM and recode the line. // Found BOM; setup conversion, skip over BOM and recode the line.
convert_setup(&cookie.conv, "utf-8", p_enc); convert_setup(&cookie.conv, "utf-8", p_enc);
p = string_convert(&cookie.conv, (char *)firstline + 3, NULL); char *p = string_convert(&cookie.conv, (char *)firstline + 3, NULL);
if (p == NULL) { if (p == NULL) {
p = xstrdup((char *)firstline + 3); p = xstrdup((char *)firstline + 3);
} }
@@ -2298,7 +2288,7 @@ int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
} }
retval = OK; retval = OK;
if (l_do_profiling == PROF_YES) { if (l_do_profiling == PROF_YES && si != NULL) {
// Get "si" again, "script_items" may have been reallocated. // Get "si" again, "script_items" may have been reallocated.
si = SCRIPT_ITEM(current_sctx.sc_sid); si = SCRIPT_ITEM(current_sctx.sc_sid);
if (si->sn_prof_on) { if (si->sn_prof_on) {
@@ -2345,12 +2335,17 @@ int do_source(char *fname, int check_other, int is_vimrc, int *ret_sid)
if (l_do_profiling == PROF_YES) { if (l_do_profiling == PROF_YES) {
prof_child_exit(&wait_start); // leaving a child now prof_child_exit(&wait_start); // leaving a child now
} }
fclose(cookie.fp); if (cookie.fp != NULL) {
fclose(cookie.fp);
}
if (cookie.source_from_buf_or_str) {
ga_clear_strings(&cookie.buflines);
}
xfree(cookie.nextline); xfree(cookie.nextline);
xfree(firstline); xfree(firstline);
convert_setup(&cookie.conv, NULL, NULL); convert_setup(&cookie.conv, NULL, NULL);
if (trigger_source_post) { if (str == NULL && trigger_source_post) {
apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf); apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf);
} }
@@ -2359,6 +2354,13 @@ theend:
return retval; return retval;
} }
/// @param check_other check for .vimrc and _vimrc
/// @param is_vimrc DOSO_ value
int do_source(char *fname, bool check_other, int is_vimrc, int *ret_sid)
{
return do_source_ext(fname, check_other, is_vimrc, ret_sid, NULL, false, NULL);
}
/// Checks if the script with the given script ID is a Lua script. /// Checks if the script with the given script ID is a Lua script.
bool script_is_lua(scid_T sid) bool script_is_lua(scid_T sid)
{ {
@@ -2621,7 +2623,7 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
char *line; char *line;
// If breakpoints have been added/deleted need to check for it. // 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_or_str) {
sp->breakpoint = dbg_find_breakpoint(true, sp->fname, SOURCING_LNUM); sp->breakpoint = dbg_find_breakpoint(true, sp->fname, SOURCING_LNUM);
sp->dbg_tick = debug_tick; sp->dbg_tick = debug_tick;
} }
@@ -2632,7 +2634,7 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
SOURCING_LNUM = sp->sourcing_lnum + 1; SOURCING_LNUM = sp->sourcing_lnum + 1;
// Get current line. If there is a read-ahead line, use it, otherwise get // Get current line. If there is a read-ahead line, use it, otherwise get
// one now. "fp" is NULL if actually using a string. // one now. "fp" is NULL if actually using a string.
if (sp->finished || sp->fp == NULL) { if (sp->finished || (!sp->source_from_buf_or_str && sp->fp == NULL)) {
line = NULL; line = NULL;
} else if (sp->nextline == NULL) { } else if (sp->nextline == NULL) {
line = get_one_sourceline(sp); line = get_one_sourceline(sp);
@@ -2685,7 +2687,8 @@ char *getsourceline(int c, void *cookie, int indent, bool do_concat)
} }
// Did we encounter a breakpoint? // Did we encounter a breakpoint?
if (sp->breakpoint != 0 && sp->breakpoint <= SOURCING_LNUM) { if (!sp->source_from_buf_or_str
&& sp->breakpoint != 0 && sp->breakpoint <= SOURCING_LNUM) {
dbg_breakpoint(sp->fname, SOURCING_LNUM); dbg_breakpoint(sp->fname, SOURCING_LNUM);
// Find next breakpoint. // Find next breakpoint.
sp->breakpoint = dbg_find_breakpoint(true, sp->fname, SOURCING_LNUM); sp->breakpoint = dbg_find_breakpoint(true, sp->fname, SOURCING_LNUM);
@@ -2714,17 +2717,23 @@ static char *get_one_sourceline(source_cookie_T *sp)
while (true) { while (true) {
// make room to read at least 120 (more) characters // make room to read at least 120 (more) characters
ga_grow(&ga, 120); ga_grow(&ga, 120);
buf = ga.ga_data; if (sp->source_from_buf_or_str) {
if (sp->buf_lnum >= sp->buflines.ga_len) {
retry: break; // all the lines are processed
errno = 0; }
if (fgets(buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, ga_concat(&ga, ((char **)sp->buflines.ga_data)[sp->buf_lnum]);
sp->fp) == NULL) { sp->buf_lnum++;
if (errno == EINTR) { buf = (char *)ga.ga_data;
goto retry; } else {
buf = ga.ga_data;
retry:
errno = 0;
if (fgets(buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, sp->fp) == NULL) {
if (errno == EINTR) {
goto retry;
}
break;
} }
break;
} }
len = ga.ga_len + (int)strlen(buf + ga.ga_len); len = ga.ga_len + (int)strlen(buf + ga.ga_len);
#ifdef USE_CRNL #ifdef USE_CRNL
@@ -2797,7 +2806,7 @@ retry:
return NULL; return NULL;
} }
/// Returns true if sourcing a script either from a file or a buffer. /// Returns true if sourcing a script either from a file or a buffer or a string.
/// Otherwise returns false. /// Otherwise returns false.
int sourcing_a_script(exarg_T *eap) int sourcing_a_script(exarg_T *eap)
{ {

View File

@@ -140,7 +140,7 @@ describe('API', function()
it(':verbose set {option}?', function() it(':verbose set {option}?', function()
api.nvim_exec2('set nowrap', { output = false }) api.nvim_exec2('set nowrap', { output = false })
eq( eq(
{ output = 'nowrap\n\tLast set from anonymous :source' }, { output = 'nowrap\n\tLast set from anonymous :source line 1' },
api.nvim_exec2('verbose set wrap?', { output = true }) api.nvim_exec2('verbose set wrap?', { output = true })
) )
@@ -153,7 +153,7 @@ describe('API', function()
{ output = false } { output = false }
) )
eq( eq(
{ output = 'nowrap\n\tLast set from anonymous :source (script id 1)' }, { output = 'nowrap\n\tLast set from anonymous :source (script id 1) line 2' },
api.nvim_exec2('verbose set wrap?', { output = true }) api.nvim_exec2('verbose set wrap?', { output = true })
) )
end) end)
@@ -296,16 +296,21 @@ describe('API', function()
eq('ñxx', api.nvim_get_current_line()) eq('ñxx', api.nvim_get_current_line())
end) end)
it('can use :finish', function()
api.nvim_exec2('let g:var = 123\nfinish\nlet g:var = 456', {})
eq(123, api.nvim_get_var('var'))
end)
it('execution error', function() it('execution error', function()
eq( eq(
'nvim_exec2(): Vim:E492: Not an editor command: bogus_command', 'nvim_exec2(), line 1: Vim:E492: Not an editor command: bogus_command',
pcall_err(request, 'nvim_exec2', 'bogus_command', {}) pcall_err(request, 'nvim_exec2', 'bogus_command', {})
) )
eq('', api.nvim_eval('v:errmsg')) -- v:errmsg was not updated. eq('', api.nvim_eval('v:errmsg')) -- v:errmsg was not updated.
eq('', eval('v:exception')) eq('', eval('v:exception'))
eq( eq(
'nvim_exec2(): Vim(buffer):E86: Buffer 23487 does not exist', 'nvim_exec2(), line 1: Vim(buffer):E86: Buffer 23487 does not exist',
pcall_err(request, 'nvim_exec2', 'buffer 23487', {}) pcall_err(request, 'nvim_exec2', 'buffer 23487', {})
) )
eq('', eval('v:errmsg')) -- v:errmsg was not updated. eq('', eval('v:errmsg')) -- v:errmsg was not updated.
@@ -338,17 +343,28 @@ describe('API', function()
write_file(sourcing_fname, 'call nvim_exec2("source ' .. fname .. '", {"output": v:false})\n') write_file(sourcing_fname, 'call nvim_exec2("source ' .. fname .. '", {"output": v:false})\n')
api.nvim_exec2('set verbose=2', { output = false }) api.nvim_exec2('set verbose=2', { output = false })
local traceback_output = dedent([[ local traceback_output = dedent([[
line 0: sourcing "%s" sourcing "nvim_exec2()"
line 0: sourcing "%s" line 1: sourcing "nvim_exec2() called at nvim_exec2():1"
line 1: sourcing "%s"
line 1: sourcing "nvim_exec2() called at %s:1"
line 1: sourcing "%s"
hello hello
finished sourcing %s finished sourcing %s
continuing in nvim_exec2() called at %s:1 continuing in nvim_exec2() called at %s:1
finished sourcing nvim_exec2() called at %s:1
continuing in %s
finished sourcing %s finished sourcing %s
continuing in nvim_exec2() called at nvim_exec2():0]]):format( continuing in nvim_exec2() called at nvim_exec2():1
finished sourcing nvim_exec2() called at nvim_exec2():1
continuing in nvim_exec2()
finished sourcing nvim_exec2()]]):format(
sourcing_fname,
sourcing_fname, sourcing_fname,
fname, fname,
fname, fname,
sourcing_fname, sourcing_fname,
sourcing_fname,
sourcing_fname,
sourcing_fname sourcing_fname
) )
eq( eq(

View File

@@ -97,14 +97,26 @@ describe(':cquit', function()
end) end)
it('exits with redir msg for multiple exit codes after :cquit 1 2', function() it('exits with redir msg for multiple exit codes after :cquit 1 2', function()
test_cq('cquit 1 2', nil, 'nvim_exec2(): Vim(cquit):E488: Trailing characters: 2: cquit 1 2') test_cq(
'cquit 1 2',
nil,
'nvim_exec2(), line 1: Vim(cquit):E488: Trailing characters: 2: cquit 1 2'
)
end) end)
it('exits with redir msg for non-number exit code after :cquit X', function() it('exits with redir msg for non-number exit code after :cquit X', function()
test_cq('cquit X', nil, 'nvim_exec2(): Vim(cquit):E488: Trailing characters: X: cquit X') test_cq(
'cquit X',
nil,
'nvim_exec2(), line 1: Vim(cquit):E488: Trailing characters: X: cquit X'
)
end) end)
it('exits with redir msg for negative exit code after :cquit -1', function() it('exits with redir msg for negative exit code after :cquit -1', function()
test_cq('cquit -1', nil, 'nvim_exec2(): Vim(cquit):E488: Trailing characters: -1: cquit -1') test_cq(
'cquit -1',
nil,
'nvim_exec2(), line 1: Vim(cquit):E488: Trailing characters: -1: cquit -1'
)
end) end)
end) end)

View File

@@ -42,59 +42,62 @@ describe('named marks', function()
it('errors when set out of range with :mark', function() it('errors when set out of range with :mark', function()
command('edit ' .. file1) command('edit ' .. file1)
local err = pcall_err(n.exec_capture, '1000mark x') local err = pcall_err(n.exec_capture, '1000mark x')
eq('nvim_exec2(): Vim(mark):E16: Invalid range: 1000mark x', err) eq('nvim_exec2(), line 1: Vim(mark):E16: Invalid range: 1000mark x', err)
end) end)
it('errors when set out of range with :k', function() it('errors when set out of range with :k', function()
command('edit ' .. file1) command('edit ' .. file1)
local err = pcall_err(n.exec_capture, '1000kx') local err = pcall_err(n.exec_capture, '1000kx')
eq('nvim_exec2(): Vim(k):E16: Invalid range: 1000kx', err) eq('nvim_exec2(), line 1: Vim(k):E16: Invalid range: 1000kx', err)
end) end)
it('errors on unknown mark name with :mark', function() it('errors on unknown mark name with :mark', function()
command('edit ' .. file1) command('edit ' .. file1)
local err = pcall_err(n.exec_capture, 'mark #') local err = pcall_err(n.exec_capture, 'mark #')
eq('nvim_exec2(): Vim(mark):E191: Argument must be a letter or forward/backward quote', err) eq(
'nvim_exec2(), line 1: Vim(mark):E191: Argument must be a letter or forward/backward quote',
err
)
end) end)
it("errors on unknown mark name with '", function() it("errors on unknown mark name with '", function()
command('edit ' .. file1) command('edit ' .. file1)
local err = pcall_err(n.exec_capture, "normal! '#") local err = pcall_err(n.exec_capture, "normal! '#")
eq('nvim_exec2(): Vim(normal):E78: Unknown mark', err) eq('nvim_exec2(), line 1: Vim(normal):E78: Unknown mark', err)
end) end)
it('errors on unknown mark name with `', function() it('errors on unknown mark name with `', function()
command('edit ' .. file1) command('edit ' .. file1)
local err = pcall_err(n.exec_capture, 'normal! `#') local err = pcall_err(n.exec_capture, 'normal! `#')
eq('nvim_exec2(): Vim(normal):E78: Unknown mark', err) eq('nvim_exec2(), line 1: Vim(normal):E78: Unknown mark', err)
end) end)
it("errors when moving to a mark that is not set with '", function() it("errors when moving to a mark that is not set with '", function()
command('edit ' .. file1) command('edit ' .. file1)
local err = pcall_err(n.exec_capture, "normal! 'z") local err = pcall_err(n.exec_capture, "normal! 'z")
eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err)
err = pcall_err(n.exec_capture, "normal! '.") err = pcall_err(n.exec_capture, "normal! '.")
eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err)
end) end)
it('errors when moving to a mark that is not set with `', function() it('errors when moving to a mark that is not set with `', function()
command('edit ' .. file1) command('edit ' .. file1)
local err = pcall_err(n.exec_capture, 'normal! `z') local err = pcall_err(n.exec_capture, 'normal! `z')
eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err)
err = pcall_err(n.exec_capture, 'normal! `>') err = pcall_err(n.exec_capture, 'normal! `>')
eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err)
end) end)
it("errors when moving to a global mark that is not set with '", function() it("errors when moving to a global mark that is not set with '", function()
command('edit ' .. file1) command('edit ' .. file1)
local err = pcall_err(n.exec_capture, "normal! 'Z") local err = pcall_err(n.exec_capture, "normal! 'Z")
eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err)
end) end)
it('errors when moving to a global mark that is not set with `', function() it('errors when moving to a global mark that is not set with `', function()
command('edit ' .. file1) command('edit ' .. file1)
local err = pcall_err(n.exec_capture, 'normal! `Z') local err = pcall_err(n.exec_capture, 'normal! `Z')
eq('nvim_exec2(): Vim(normal):E20: Mark not set', err) eq('nvim_exec2(), line 1: Vim(normal):E20: Mark not set', err)
end) end)
it("can move to them using '", function() it("can move to them using '", function()
@@ -169,7 +172,7 @@ describe('named marks', function()
command('next') command('next')
command('bw! ' .. file1) command('bw! ' .. file1)
local err = pcall_err(n.exec_capture, "normal! 'A") local err = pcall_err(n.exec_capture, "normal! 'A")
eq('nvim_exec2(): Vim(normal):E92: Buffer 1 not found', err) eq('nvim_exec2(), line 1: Vim(normal):E92: Buffer 1 not found', err)
os.remove(file1) os.remove(file1)
end) end)

View File

@@ -101,16 +101,14 @@ describe(':source', function()
\ k: "v" \ k: "v"
"\ (o_o) "\ (o_o)
\ } \ }
let c = expand("<SID>")->empty() let c = expand("<SID>")
let s:s = 0zbeef.cafe let s:s = 0zbeef.cafe
let d = s:s]]) let d = s:s]])
command('source') command('source')
eq('2', exec_capture('echo a')) eq('2', exec_capture('echo a'))
eq("{'k': 'v'}", exec_capture('echo b')) eq("{'k': 'v'}", exec_capture('echo b'))
eq('<SNR>1_', exec_capture('echo c'))
-- Script items are created only on script var access
eq('1', exec_capture('echo c'))
eq('0zBEEFCAFE', exec_capture('echo d')) eq('0zBEEFCAFE', exec_capture('echo d'))
exec('set cpoptions+=C') exec('set cpoptions+=C')
@@ -136,6 +134,10 @@ describe(':source', function()
feed_command(':source') feed_command(':source')
eq('3', exec_capture('echo a')) eq('3', exec_capture('echo a'))
-- Source last line only
feed_command(':$source')
eq('Vim(echo):E117: Unknown function: s:C', exc_exec('echo D()'))
-- Source from 2nd line to end of file -- Source from 2nd line to end of file
feed('ggjVG') feed('ggjVG')
feed_command(':source') feed_command(':source')
@@ -143,9 +145,9 @@ describe(':source', function()
eq("{'K': 'V'}", exec_capture('echo b')) eq("{'K': 'V'}", exec_capture('echo b'))
eq('<SNR>1_C()', exec_capture('echo D()')) eq('<SNR>1_C()', exec_capture('echo D()'))
-- Source last line only -- Source last line after the lines that define s:C() have been sourced
feed_command(':$source') feed_command(':$source')
eq('Vim(echo):E117: Unknown function: s:C', exc_exec('echo D()')) eq('<SNR>1_C()', exec_capture('echo D()'))
exec('set cpoptions+=C') exec('set cpoptions+=C')
eq("Vim(let):E723: Missing end of Dictionary '}': ", exc_exec("'<,'>source")) eq("Vim(let):E723: Missing end of Dictionary '}': ", exc_exec("'<,'>source"))
@@ -248,9 +250,9 @@ describe(':source', function()
eq(12, eval('g:c')) eq(12, eval('g:c'))
eq(' \\ 1\n "\\ 2', exec_lua('return _G.a')) eq(' \\ 1\n "\\ 2', exec_lua('return _G.a'))
eq(':source (no file)', api.nvim_get_var('sfile_value')) eq(':source buffer=1', api.nvim_get_var('sfile_value'))
eq(':source (no file)', api.nvim_get_var('stack_value')) eq(':source buffer=1', api.nvim_get_var('stack_value'))
eq(':source (no file)', api.nvim_get_var('script_value')) eq(':source buffer=1', api.nvim_get_var('script_value'))
end) end)
end end

View File

@@ -256,7 +256,7 @@ TestHL2 xxx guibg=Green
local result = exec_capture(':verbose set tw?') local result = exec_capture(':verbose set tw?')
local loc = get_last_set_location(40) local loc = get_last_set_location(40)
if loc == 'Lua (run Nvim with -V1 for more details)' then if loc == 'Lua (run Nvim with -V1 for more details)' then
loc = 'anonymous :source (script id 1)' loc = 'anonymous :source (script id 1) line 5'
end end
eq( eq(
string.format( string.format(

View File

@@ -148,10 +148,10 @@ describe('assert function:', function()
call assert_true('', 'file two') call assert_true('', 'file two')
]]) ]])
expected_errors({ expected_errors({
'nvim_exec2(): equal assertion failed: Expected 1 but got 100', 'nvim_exec2() line 1: equal assertion failed: Expected 1 but got 100',
"nvim_exec2(): true assertion failed: Expected False but got 'true'", "nvim_exec2() line 2: true assertion failed: Expected False but got 'true'",
"nvim_exec2(): false assertion failed: Expected True but got 'false'", "nvim_exec2() line 3: false assertion failed: Expected True but got 'false'",
"nvim_exec2(): file two: Expected True but got ''", "nvim_exec2() line 1: file two: Expected True but got ''",
}) })
end) end)
end) end)

View File

@@ -207,7 +207,8 @@ describe(':lua', function()
-- ":{range}lua" fails on invalid Lua code. -- ":{range}lua" fails on invalid Lua code.
eq( eq(
[[:{range}lua: Vim(lua):E5107: Error loading lua [string ":{range}lua"]:0: '=' expected near '<eof>']], [[:{range}lua buffer=1: Vim(lua):E5107: Error loading lua ]]
.. [[[string ":{range}lua buffer=1"]:0: '=' expected near '<eof>']],
pcall_err(command, '1lua') pcall_err(command, '1lua')
) )

View File

@@ -3956,7 +3956,7 @@ stack traceback:
it('failure modes', function() it('failure modes', function()
matches( matches(
'nvim_exec2%(%): Vim:E492: Not an editor command: fooooo', 'nvim_exec2%(%), line 1: Vim:E492: Not an editor command: fooooo',
pcall_err(exec_lua, [[vim.api.nvim_win_call(0, function() vim.cmd 'fooooo' end)]]) pcall_err(exec_lua, [[vim.api.nvim_win_call(0, function() vim.cmd 'fooooo' end)]])
) )
eq( eq(

View File

@@ -114,4 +114,500 @@ func Test_nested_script()
call StopVimInTerminal(buf) call StopVimInTerminal(buf)
endfunc 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)
" 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
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 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()
throw 'Skipped: Vim9 script is N/A'
new
" 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())
" 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: shiftwidth=2 sts=2 expandtab " vim: shiftwidth=2 sts=2 expandtab