mirror of
https://github.com/neovim/neovim.git
synced 2025-09-30 06:58:35 +00: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:
@@ -2521,8 +2521,8 @@ module.cmds = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
command='source',
|
command='source',
|
||||||
flags=bit.bor(BANG, FILE1, TRLBAR, SBOXOK, CMDWIN),
|
flags=bit.bor(RANGE, DFLALL, WHOLEFOLD, BANG, FILE1, TRLBAR, SBOXOK, CMDWIN),
|
||||||
addr_type='ADDR_NONE',
|
addr_type='ADDR_LINES',
|
||||||
func='ex_source',
|
func='ex_source',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
#include "nvim/getchar.h"
|
#include "nvim/getchar.h"
|
||||||
#include "nvim/mark.h"
|
#include "nvim/mark.h"
|
||||||
#include "nvim/mbyte.h"
|
#include "nvim/mbyte.h"
|
||||||
|
#include "nvim/memline.h"
|
||||||
#include "nvim/message.h"
|
#include "nvim/message.h"
|
||||||
#include "nvim/misc1.h"
|
#include "nvim/misc1.h"
|
||||||
#include "nvim/garray.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)
|
void ex_source(exarg_T *eap)
|
||||||
{
|
{
|
||||||
cmd_source(eap->arg, 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)
|
static void cmd_source(char_u *fname, exarg_T *eap)
|
||||||
{
|
{
|
||||||
if (*fname == NUL) {
|
if (*fname == NUL) {
|
||||||
EMSG(_(e_argreq));
|
cmd_source_buffer(eap);
|
||||||
} else if (eap != NULL && eap->forceit) {
|
} else if (eap != NULL && eap->forceit) {
|
||||||
// ":source!": read Normal mode commands
|
// ":source!": read Normal mode commands
|
||||||
// Need to execute the commands directly. This is required at least
|
// 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.
|
/// ":source" and associated commands.
|
||||||
///
|
///
|
||||||
/// @return address holding the next breakpoint line for a source cookie
|
/// @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);
|
return (char_u *)xstrdup(buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Executes lines in `src` as Ex commands.
|
static int source_using_linegetter(void *cookie,
|
||||||
///
|
LineGetter fgetline,
|
||||||
/// @see do_source()
|
const char *traceback_name)
|
||||||
int do_source_str(const char *cmd, const char *traceback_name)
|
|
||||||
{
|
{
|
||||||
char_u *save_sourcing_name = sourcing_name;
|
char_u *save_sourcing_name = sourcing_name;
|
||||||
linenr_T save_sourcing_lnum = sourcing_lnum;
|
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;
|
sourcing_lnum = 0;
|
||||||
|
|
||||||
GetStrLineCookie cookie = {
|
|
||||||
.buf = (char_u *)cmd,
|
|
||||||
.offset = 0,
|
|
||||||
};
|
|
||||||
const sctx_T save_current_sctx = current_sctx;
|
const sctx_T save_current_sctx = current_sctx;
|
||||||
current_sctx.sc_sid = SID_STR;
|
current_sctx.sc_sid = SID_STR;
|
||||||
current_sctx.sc_seq = 0;
|
current_sctx.sc_seq = 0;
|
||||||
current_sctx.sc_lnum = save_sourcing_lnum;
|
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);
|
DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT);
|
||||||
current_sctx = save_current_sctx;
|
|
||||||
sourcing_lnum = save_sourcing_lnum;
|
sourcing_lnum = save_sourcing_lnum;
|
||||||
sourcing_name = save_sourcing_name;
|
sourcing_name = save_sourcing_name;
|
||||||
|
current_sctx = save_current_sctx;
|
||||||
|
restore_funccal();
|
||||||
return retval;
|
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.
|
/// Reads the file `fname` and executes its lines as Ex commands.
|
||||||
///
|
///
|
||||||
/// This function may be called recursively!
|
/// 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)
|
Reference in New Issue
Block a user