mirror of
https://github.com/neovim/neovim.git
synced 2024-12-29 14:41:06 -07:00
[RFC] ":source" sources from current buffer if filename is omitted (#11444)
Fix https://github.com/neovim/neovim/issues/8722
This commit is contained in:
parent
d08e983c6b
commit
4d5dbea4f4
@ -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',
|
||||
},
|
||||
{
|
||||
|
@ -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!
|
||||
|
47
test/functional/ex_cmds/source_spec.lua
Normal file
47
test/functional/ex_cmds/source_spec.lua
Normal 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)
|
Loading…
Reference in New Issue
Block a user