[RFC] ":source" sources from current buffer if filename is omitted (#11444)

Fix https://github.com/neovim/neovim/issues/8722
This commit is contained in:
Vikram Pal 2021-02-18 11:55:51 +05:30 committed by GitHub
parent d08e983c6b
commit 4d5dbea4f4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 103 additions and 14 deletions

View File

@ -2521,8 +2521,8 @@ module.cmds = {
},
{
command='source',
flags=bit.bor(BANG, FILE1, TRLBAR, SBOXOK, CMDWIN),
addr_type='ADDR_NONE',
flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, FILE1, TRLBAR, SBOXOK, CMDWIN),
addr_type='ADDR_LINES',
func='ex_source',
},
{

View File

@ -29,6 +29,7 @@
#include "nvim/getchar.h"
#include "nvim/mark.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
#include "nvim/message.h"
#include "nvim/misc1.h"
#include "nvim/garray.h"
@ -2526,7 +2527,7 @@ void ex_pyxdo(exarg_T *eap)
}
}
/// ":source {fname}"
/// ":source [{fname}]"
void ex_source(exarg_T *eap)
{
cmd_source(eap->arg, eap);
@ -2535,7 +2536,7 @@ void ex_source(exarg_T *eap)
static void cmd_source(char_u *fname, exarg_T *eap)
{
if (*fname == NUL) {
EMSG(_(e_argreq));
cmd_source_buffer(eap);
} else if (eap != NULL && eap->forceit) {
// ":source!": read Normal mode commands
// Need to execute the commands directly. This is required at least
@ -2553,6 +2554,37 @@ static void cmd_source(char_u *fname, exarg_T *eap)
}
}
typedef struct {
linenr_T curr_lnum;
const linenr_T final_lnum;
} GetBufferLineCookie;
/// Get one line from the current selection in the buffer.
/// Called by do_cmdline() when it's called from cmd_source_buffer().
///
/// @return pointer to allocated line, or NULL for end-of-file or
/// some error.
static char_u *get_buffer_line(int c, void *cookie, int indent, bool do_concat)
{
GetBufferLineCookie *p = cookie;
if (p->curr_lnum > p->final_lnum) {
return NULL;
}
char_u *curr_line = ml_get(p->curr_lnum);
p->curr_lnum++;
return (char_u *)xstrdup((const char *)curr_line);
}
static void cmd_source_buffer(exarg_T *eap)
{
GetBufferLineCookie cookie = {
.curr_lnum = eap->line1,
.final_lnum = eap->line2,
};
source_using_linegetter((void *)&cookie, get_buffer_line,
":source (no file)");
}
/// ":source" and associated commands.
///
/// @return address holding the next breakpoint line for a source cookie
@ -2624,10 +2656,9 @@ static char_u *get_str_line(int c, void *cookie, int indent, bool do_concat)
return (char_u *)xstrdup(buf);
}
/// Executes lines in `src` as Ex commands.
///
/// @see do_source()
int do_source_str(const char *cmd, const char *traceback_name)
static int source_using_linegetter(void *cookie,
LineGetter fgetline,
const char *traceback_name)
{
char_u *save_sourcing_name = sourcing_name;
linenr_T save_sourcing_lnum = sourcing_lnum;
@ -2642,22 +2673,33 @@ int do_source_str(const char *cmd, const char *traceback_name)
}
sourcing_lnum = 0;
GetStrLineCookie cookie = {
.buf = (char_u *)cmd,
.offset = 0,
};
const sctx_T save_current_sctx = current_sctx;
current_sctx.sc_sid = SID_STR;
current_sctx.sc_seq = 0;
current_sctx.sc_lnum = save_sourcing_lnum;
int retval = do_cmdline(NULL, get_str_line, (void *)&cookie,
funccal_entry_T entry;
save_funccal(&entry);
int retval = do_cmdline(NULL, fgetline, cookie,
DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT);
current_sctx = save_current_sctx;
sourcing_lnum = save_sourcing_lnum;
sourcing_name = save_sourcing_name;
current_sctx = save_current_sctx;
restore_funccal();
return retval;
}
/// Executes lines in `src` as Ex commands.
///
/// @see do_source()
int do_source_str(const char *cmd, const char *traceback_name)
{
GetStrLineCookie cookie = {
.buf = (char_u *)cmd,
.offset = 0,
};
return source_using_linegetter((void *)&cookie, get_str_line, traceback_name);
}
/// Reads the file `fname` and executes its lines as Ex commands.
///
/// This function may be called recursively!

View File

@ -0,0 +1,47 @@
local helpers = require('test.functional.helpers')(after_each)
local command = helpers.command
local insert = helpers.insert
local eq = helpers.eq
local clear = helpers.clear
local meths = helpers.meths
local feed = helpers.feed
local feed_command = helpers.feed_command
describe(':source', function()
before_each(function()
clear()
end)
it('current buffer', function()
insert('let a = 2')
command('source')
eq('2', meths.exec('echo a', true))
end)
it('selection in current buffer', function()
insert(
'let a = 2\n'..
'let a = 3\n'..
'let a = 4\n')
-- Source the 2nd line only
feed('ggjV')
feed_command(':source')
eq('3', meths.exec('echo a', true))
-- Source from 2nd line to end of file
feed('ggjVG')
feed_command(':source')
eq('4', meths.exec('echo a', true))
end)
it('multiline heredoc command', function()
insert(
'lua << EOF\n'..
'y = 4\n'..
'EOF\n')
command('source')
eq('4', meths.exec('echo luaeval("y")', true))
end)
end)