mirror of
https://github.com/vim/vim.git
synced 2025-09-25 03:54:15 -04:00
patch 8.2.4603: sourcing buffer lines is too complicated
Problem: Sourcing buffer lines is too complicated. Solution: Simplify the code. Make it possible to source Vim9 script lines. (Yegappan Lakshmanan, closes #9974)
This commit is contained in:
committed by
Bram Moolenaar
parent
397a87ac1c
commit
85b43c6cb7
@@ -201,7 +201,13 @@ For writing a Vim script, see chapter 41 of the user manual |usr_41.txt|.
|
|||||||
:[range]so[urce] Read Ex commands from the [range] of lines in the
|
:[range]so[urce] Read Ex commands from the [range] of lines in the
|
||||||
current buffer. When sourcing commands from the
|
current buffer. When sourcing commands 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.
|
||||||
|
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!*
|
*:source!*
|
||||||
:so[urce]! {file} Read Vim commands from {file}. These are commands
|
:so[urce]! {file} Read Vim commands from {file}. These are commands
|
||||||
@@ -425,10 +431,10 @@ An alternative is to put the commands in a file, and execute them with the
|
|||||||
':source!' command. Useful for long command sequences. Can be combined with
|
':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
|
||||||
|
@@ -2572,7 +2572,7 @@ do_one_cmd(
|
|||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
// Set flag that any command was executed, used by ex_vim9script().
|
// Set flag that any command was executed, used by ex_vim9script().
|
||||||
// Not if this was a command that wasn't executed or :endif.
|
// Not if this was a command that wasn't executed or :endif.
|
||||||
if (getline_equal(ea.getline, ea.cookie, getsourceline)
|
if (sourcing_a_script(&ea)
|
||||||
&& current_sctx.sc_sid > 0
|
&& current_sctx.sc_sid > 0
|
||||||
&& ea.cmdidx != CMD_endif
|
&& ea.cmdidx != CMD_endif
|
||||||
&& (cstack->cs_idx < 0
|
&& (cstack->cs_idx < 0
|
||||||
|
@@ -32,6 +32,7 @@ void free_scriptnames(void);
|
|||||||
void free_autoload_scriptnames(void);
|
void free_autoload_scriptnames(void);
|
||||||
linenr_T get_sourced_lnum(char_u *(*fgetline)(int, void *, int, getline_opt_T), void *cookie);
|
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);
|
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_scriptencoding(exarg_T *eap);
|
||||||
void ex_scriptversion(exarg_T *eap);
|
void ex_scriptversion(exarg_T *eap);
|
||||||
void ex_finish(exarg_T *eap);
|
void ex_finish(exarg_T *eap);
|
||||||
@@ -42,5 +43,4 @@ char_u *get_autoload_prefix(scriptitem_T *si);
|
|||||||
char_u *may_prefix_autoload(char_u *name);
|
char_u *may_prefix_autoload(char_u *name);
|
||||||
char_u *autoload_name(char_u *name);
|
char_u *autoload_name(char_u *name);
|
||||||
int script_autoload(char_u *name, int reload);
|
int script_autoload(char_u *name, int reload);
|
||||||
int sourcing_a_script(exarg_T *eap);
|
|
||||||
/* vim: set ft=c : */
|
/* vim: set ft=c : */
|
||||||
|
412
src/scriptfile.c
412
src/scriptfile.c
@@ -23,6 +23,8 @@ static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL};
|
|||||||
static int last_current_SID_seq = 0;
|
static int last_current_SID_seq = 0;
|
||||||
#endif
|
#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.
|
* Initialize the execution stack.
|
||||||
*/
|
*/
|
||||||
@@ -1079,251 +1081,6 @@ ExpandPackAddDir(
|
|||||||
return OK;
|
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
|
static void
|
||||||
cmd_source(char_u *fname, exarg_T *eap)
|
cmd_source(char_u *fname, exarg_T *eap)
|
||||||
{
|
{
|
||||||
@@ -1341,7 +1098,7 @@ cmd_source(char_u *fname, exarg_T *eap)
|
|||||||
emsg(_(e_argument_required));
|
emsg(_(e_argument_required));
|
||||||
else
|
else
|
||||||
// source ex commands from the current buffer
|
// source ex commands from the current buffer
|
||||||
cmd_source_buffer(eap);
|
do_source_ext(NULL, FALSE, FALSE, NULL, eap);
|
||||||
}
|
}
|
||||||
else if (eap != NULL && eap->forceit)
|
else if (eap != NULL && eap->forceit)
|
||||||
// ":source!": read Normal mode commands
|
// ":source!": read Normal mode commands
|
||||||
@@ -1480,21 +1237,73 @@ fopen_noinh_readbin(char *filename)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* do_source: Read the file "fname" and execute its lines as EX commands.
|
* Initialization for sourcing lines from the current buffer. Reads all the
|
||||||
|
* lines from the buffer and stores it in the cookie grow array.
|
||||||
|
* Returns a pointer to the name ":source buffer=<n>" on success and NULL on
|
||||||
|
* failure.
|
||||||
|
*/
|
||||||
|
static char_u *
|
||||||
|
do_source_buffer_init(source_cookie_T *sp, exarg_T *eap)
|
||||||
|
{
|
||||||
|
linenr_T curr_lnum;
|
||||||
|
char_u *line = NULL;
|
||||||
|
char_u *fname;
|
||||||
|
|
||||||
|
CLEAR_FIELD(*sp);
|
||||||
|
|
||||||
|
if (curbuf == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
// Use ":source buffer=<num>" as the script name
|
||||||
|
vim_snprintf((char *)IObuff, IOSIZE, ":source buffer=%d", curbuf->b_fnum);
|
||||||
|
fname = vim_strsave(IObuff);
|
||||||
|
if (fname == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ga_init2(&sp->buflines, sizeof(char_u *), 100);
|
||||||
|
|
||||||
|
// Copy the lines from the buffer into a grow array
|
||||||
|
for (curr_lnum = eap->line1; curr_lnum <= eap->line2; curr_lnum++)
|
||||||
|
{
|
||||||
|
line = vim_strsave(ml_get(curr_lnum));
|
||||||
|
if (line == NULL)
|
||||||
|
goto errret;
|
||||||
|
if (ga_add_string(&sp->buflines, line) == FAIL)
|
||||||
|
goto errret;
|
||||||
|
line = NULL;
|
||||||
|
}
|
||||||
|
sp->buf_lnum = 0;
|
||||||
|
sp->source_from_buf = TRUE;
|
||||||
|
|
||||||
|
return fname;
|
||||||
|
|
||||||
|
errret:
|
||||||
|
vim_free(fname);
|
||||||
|
vim_free(line);
|
||||||
|
ga_clear_strings(&sp->buflines);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Read the file "fname" and execute its lines as EX commands.
|
||||||
* When "ret_sid" is not NULL and we loaded the script before, don't load it
|
* When "ret_sid" is not NULL and we loaded the script before, don't load it
|
||||||
* again.
|
* again.
|
||||||
*
|
*
|
||||||
|
* The 'eap' argument is used when sourcing lines from a buffer instead of a
|
||||||
|
* file.
|
||||||
|
*
|
||||||
* This function may be called recursively!
|
* This function may be called recursively!
|
||||||
*
|
*
|
||||||
* 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
|
static int
|
||||||
do_source(
|
do_source_ext(
|
||||||
char_u *fname,
|
char_u *fname,
|
||||||
int check_other, // check for .vimrc and _vimrc
|
int check_other, // check for .vimrc and _vimrc
|
||||||
int is_vimrc, // DOSO_ value
|
int is_vimrc, // DOSO_ value
|
||||||
int *ret_sid UNUSED)
|
int *ret_sid UNUSED,
|
||||||
|
exarg_T *eap)
|
||||||
{
|
{
|
||||||
source_cookie_T cookie;
|
source_cookie_T cookie;
|
||||||
char_u *p;
|
char_u *p;
|
||||||
@@ -1520,17 +1329,28 @@ do_source(
|
|||||||
int trigger_source_post = FALSE;
|
int trigger_source_post = FALSE;
|
||||||
ESTACK_CHECK_DECLARATION
|
ESTACK_CHECK_DECLARATION
|
||||||
|
|
||||||
p = expand_env_save(fname);
|
CLEAR_FIELD(cookie);
|
||||||
if (p == NULL)
|
if (fname == 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);
|
// sourcing lines from a buffer
|
||||||
goto theend;
|
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
|
#ifdef FEAT_EVAL
|
||||||
estack_compiling = FALSE;
|
estack_compiling = FALSE;
|
||||||
@@ -1567,11 +1387,14 @@ do_source(
|
|||||||
// Apply SourcePre autocommands, they may get the file.
|
// Apply SourcePre autocommands, they may get the file.
|
||||||
apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf);
|
apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, FALSE, curbuf);
|
||||||
|
|
||||||
|
if (!cookie.source_from_buf)
|
||||||
|
{
|
||||||
#ifdef USE_FOPEN_NOINH
|
#ifdef USE_FOPEN_NOINH
|
||||||
cookie.fp = fopen_noinh_readbin((char *)fname_exp);
|
cookie.fp = fopen_noinh_readbin((char *)fname_exp);
|
||||||
#else
|
#else
|
||||||
cookie.fp = mch_fopen((char *)fname_exp, READBIN);
|
cookie.fp = mch_fopen((char *)fname_exp, READBIN);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
if (cookie.fp == NULL && check_other)
|
if (cookie.fp == NULL && check_other)
|
||||||
{
|
{
|
||||||
// Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
|
// Try again, replacing file name ".vimrc" by "_vimrc" or vice versa,
|
||||||
@@ -1594,7 +1417,7 @@ do_source(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cookie.fp == NULL)
|
if (cookie.fp == NULL && !cookie.source_from_buf)
|
||||||
{
|
{
|
||||||
if (p_verbose > 0)
|
if (p_verbose > 0)
|
||||||
{
|
{
|
||||||
@@ -1632,12 +1455,14 @@ do_source(
|
|||||||
cookie.fileformat = EOL_DOS;
|
cookie.fileformat = EOL_DOS;
|
||||||
else
|
else
|
||||||
cookie.fileformat = EOL_UNKNOWN;
|
cookie.fileformat = EOL_UNKNOWN;
|
||||||
cookie.error = FALSE;
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
cookie.nextline = NULL;
|
if (fname == NULL)
|
||||||
cookie.sourcing_lnum = 0;
|
// When sourcing a range of lines from a buffer, use the buffer line
|
||||||
cookie.finished = FALSE;
|
// number.
|
||||||
|
cookie.sourcing_lnum = eap->line1 - 1;
|
||||||
|
else
|
||||||
|
cookie.sourcing_lnum = 0;
|
||||||
|
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
// Check if this script has a breakpoint.
|
// Check if this script has a breakpoint.
|
||||||
@@ -1661,7 +1486,12 @@ do_source(
|
|||||||
sticky_cmdmod_flags = 0;
|
sticky_cmdmod_flags = 0;
|
||||||
|
|
||||||
save_current_sctx = current_sctx;
|
save_current_sctx = current_sctx;
|
||||||
current_sctx.sc_version = 1; // default script version
|
if (cmdmod.cmod_flags & CMOD_VIM9CMD)
|
||||||
|
// When the ":vim9cmd" command modifier is used, source the script as a
|
||||||
|
// Vim9 script.
|
||||||
|
current_sctx.sc_version = SCRIPT_VERSION_VIM9;
|
||||||
|
else
|
||||||
|
current_sctx.sc_version = 1; // default script version
|
||||||
|
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
# ifdef FEAT_PROFILE
|
# ifdef FEAT_PROFILE
|
||||||
@@ -1874,7 +1704,10 @@ almosttheend:
|
|||||||
#endif
|
#endif
|
||||||
current_sctx = save_current_sctx;
|
current_sctx = save_current_sctx;
|
||||||
|
|
||||||
fclose(cookie.fp);
|
if (cookie.fp != NULL)
|
||||||
|
fclose(cookie.fp);
|
||||||
|
if (cookie.source_from_buf)
|
||||||
|
ga_clear_strings(&cookie.buflines);
|
||||||
vim_free(cookie.nextline);
|
vim_free(cookie.nextline);
|
||||||
vim_free(firstline);
|
vim_free(firstline);
|
||||||
convert_setup(&cookie.conv, NULL, NULL);
|
convert_setup(&cookie.conv, NULL, NULL);
|
||||||
@@ -1891,6 +1724,17 @@ theend:
|
|||||||
return retval;
|
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)
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -2038,11 +1882,21 @@ get_one_sourceline(source_cookie_T *sp)
|
|||||||
// make room to read at least 120 (more) characters
|
// make room to read at least 120 (more) characters
|
||||||
if (ga_grow(&ga, 120) == FAIL)
|
if (ga_grow(&ga, 120) == FAIL)
|
||||||
break;
|
break;
|
||||||
buf = (char_u *)ga.ga_data;
|
if (sp->source_from_buf)
|
||||||
|
{
|
||||||
if (fgets((char *)buf + ga.ga_len, ga.ga_maxlen - ga.ga_len,
|
if (sp->buf_lnum >= sp->buflines.ga_len)
|
||||||
sp->fp) == NULL)
|
break; // all the lines are processed
|
||||||
break;
|
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);
|
len = ga.ga_len + (int)STRLEN(buf + ga.ga_len);
|
||||||
#ifdef USE_CRNL
|
#ifdef USE_CRNL
|
||||||
// Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
|
// Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the
|
||||||
@@ -2145,7 +1999,7 @@ getsourceline(
|
|||||||
|
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
// 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)
|
||||||
{
|
{
|
||||||
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;
|
||||||
@@ -2161,7 +2015,7 @@ getsourceline(
|
|||||||
|
|
||||||
// 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 && 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);
|
||||||
@@ -2265,7 +2119,8 @@ getsourceline(
|
|||||||
|
|
||||||
#ifdef FEAT_EVAL
|
#ifdef FEAT_EVAL
|
||||||
// Did we encounter a breakpoint?
|
// Did we encounter a breakpoint?
|
||||||
if (sp->breakpoint != 0 && sp->breakpoint <= SOURCING_LNUM)
|
if (!sp->source_from_buf && sp->breakpoint != 0
|
||||||
|
&& sp->breakpoint <= SOURCING_LNUM)
|
||||||
{
|
{
|
||||||
dbg_breakpoint(sp->fname, SOURCING_LNUM);
|
dbg_breakpoint(sp->fname, SOURCING_LNUM);
|
||||||
// Find next breakpoint.
|
// Find next breakpoint.
|
||||||
@@ -2284,8 +2139,7 @@ getsourceline(
|
|||||||
int
|
int
|
||||||
sourcing_a_script(exarg_T *eap)
|
sourcing_a_script(exarg_T *eap)
|
||||||
{
|
{
|
||||||
return (getline_equal(eap->getline, eap->cookie, getsourceline)
|
return (getline_equal(eap->getline, eap->cookie, getsourceline));
|
||||||
|| getline_equal(eap->getline, eap->cookie, source_getbufline));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -4426,6 +4426,9 @@ typedef struct {
|
|||||||
char_u *nextline; // if not NULL: line that was read ahead
|
char_u *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
|
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
|
#ifdef USE_CRNL
|
||||||
int fileformat; // EOL_UNKNOWN, EOL_UNIX or EOL_DOS
|
int fileformat; // EOL_UNKNOWN, EOL_UNIX or EOL_DOS
|
||||||
int error; // TRUE if LF found after CR-LF
|
int error; // TRUE if LF found after CR-LF
|
||||||
|
@@ -146,6 +146,23 @@ func Test_source_buffer()
|
|||||||
2,3source
|
2,3source
|
||||||
call assert_equal(90, g:a)
|
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
|
" Source a script with line continuation lines
|
||||||
%d _
|
%d _
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
@@ -327,6 +344,63 @@ func Test_source_buffer()
|
|||||||
call assert_equal("three", Xtestfunc())
|
call assert_equal("three", Xtestfunc())
|
||||||
delfunc 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
|
" test for sourcing a Vim9 script
|
||||||
%d _
|
%d _
|
||||||
let lines =<< trim END
|
let lines =<< trim END
|
||||||
@@ -342,6 +416,198 @@ func Test_source_buffer()
|
|||||||
source
|
source
|
||||||
call assert_equal(10, Xtestfunc())
|
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!
|
%bw!
|
||||||
endfunc
|
endfunc
|
||||||
|
|
||||||
|
@@ -750,6 +750,8 @@ static char *(features[]) =
|
|||||||
|
|
||||||
static int included_patches[] =
|
static int included_patches[] =
|
||||||
{ /* Add new patch number below this line */
|
{ /* Add new patch number below this line */
|
||||||
|
/**/
|
||||||
|
4603,
|
||||||
/**/
|
/**/
|
||||||
4602,
|
4602,
|
||||||
/**/
|
/**/
|
||||||
|
Reference in New Issue
Block a user