Merge pull request #20497 from zeertzjq/vim-8.2.2413

vim-patch:8.2.{2413,2421,2463,3884,3885,3886},9.0.0388: arglist patches
This commit is contained in:
zeertzjq 2022-10-05 23:11:36 +08:00 committed by GitHub
commit 28e228d60d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 252 additions and 447 deletions

View File

@ -27,19 +27,57 @@
#include "nvim/vim.h"
#include "nvim/window.h"
/// State used by the :all command to open all the files in the argument list in
/// separate windows.
typedef struct {
alist_T *alist; ///< argument list to be used
int had_tab;
bool keep_tabs;
bool forceit;
bool use_firstwin; ///< use first window for arglist
uint8_t *opened; ///< Array of weight for which args are open:
///< 0: not opened
///< 1: opened in other tab
///< 2: opened in curtab
///< 3: opened in curtab and curwin
int opened_len; ///< length of opened[]
win_T *new_curwin;
tabpage_T *new_curtab;
} arg_all_state_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "arglist.c.generated.h"
#endif
static char e_cannot_change_arglist_recursively[]
= N_("E1156: Cannot change the argument list recursively");
enum {
AL_SET = 1,
AL_ADD = 2,
AL_DEL = 3,
};
/// This flag is set whenever the argument list is being changed and calling a
/// function that might trigger an autocommand.
static bool arglist_locked = false;
static int check_arglist_locked(void)
{
if (arglist_locked) {
emsg(_(e_cannot_change_arglist_recursively));
return FAIL;
}
return OK;
}
/// Clear an argument list: free all file names and reset it to zero entries.
void alist_clear(alist_T *al)
{
if (check_arglist_locked() == FAIL) {
return;
}
#define FREE_AENTRY_FNAME(arg) xfree((arg)->ae_fname)
GA_DEEP_CLEAR(&al->al_ga, aentry_T, FREE_AENTRY_FNAME);
}
@ -107,13 +145,9 @@ void alist_expand(int *fnum_list, int fnum_len)
/// Takes over the allocated files[] and the allocated fnames in it.
void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_list, int fnum_len)
{
static int recursive = 0;
if (recursive) {
emsg(_(e_au_recursive));
if (check_arglist_locked() == FAIL) {
return;
}
recursive++;
alist_clear(al);
ga_grow(&al->al_ga, count);
@ -131,7 +165,9 @@ void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_l
// May set buffer name of a buffer previously used for the
// argument list, so that it's re-used by alist_add.
if (fnum_list != NULL && i < fnum_len) {
arglist_locked = true;
buf_set_name(fnum_list[i], files[i]);
arglist_locked = false;
}
alist_add(al, files[i], use_curbuf ? 2 : 1);
@ -143,7 +179,6 @@ void alist_set(alist_T *al, int count, char **files, int use_curbuf, int *fnum_l
if (al == &global_alist) {
arg_had_last = false;
}
recursive--;
}
/// Add file "fname" to argument list "al".
@ -155,6 +190,11 @@ void alist_add(alist_T *al, char *fname, int set_fnum)
if (fname == NULL) { // don't add NULL file names
return;
}
if (check_arglist_locked() == FAIL) {
return;
}
arglist_locked = true;
#ifdef BACKSLASH_IN_FILENAME
slash_adjust(fname);
#endif
@ -164,6 +204,8 @@ void alist_add(alist_T *al, char *fname, int set_fnum)
buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0));
}
al->al_ga.ga_len++;
arglist_locked = false;
}
#if defined(BACKSLASH_IN_FILENAME)
@ -285,7 +327,7 @@ static void alist_add_list(int count, char **files, int after, bool will_edit)
{
int old_argcount = ARGCOUNT;
ga_grow(&ALIST(curwin)->al_ga, count);
{
if (check_arglist_locked() != FAIL) {
if (after < 0) {
after = 0;
}
@ -296,11 +338,13 @@ static void alist_add_list(int count, char **files, int after, bool will_edit)
memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
(size_t)(ARGCOUNT - after) * sizeof(aentry_T));
}
arglist_locked = true;
for (int i = 0; i < count; i++) {
const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0);
ARGLIST[after + i].ae_fname = files[i];
ARGLIST[after + i].ae_fnum = buflist_add(files[i], flags);
}
arglist_locked = false;
ALIST(curwin)->al_ga.ga_len += count;
if (old_argcount > 0 && curwin->w_arg_idx >= after) {
curwin->w_arg_idx += count;
@ -371,6 +415,10 @@ static int do_arglist(char *str, int what, int after, bool will_edit)
char **exp_files;
int arg_escaped = true;
if (check_arglist_locked() == FAIL) {
return FAIL;
}
// Set default argument for ":argadd" command.
if (what == AL_ADD && *str == NUL) {
if (curbuf->b_ffname == NULL) {
@ -461,6 +509,9 @@ void check_arg_idx(win_T *win)
void ex_args(exarg_T *eap)
{
if (eap->cmdidx != CMD_args) {
if (check_arglist_locked() == FAIL) {
return;
}
alist_unlink(ALIST(curwin));
if (eap->cmdidx == CMD_argglobal) {
ALIST(curwin) = &global_alist;
@ -469,15 +520,21 @@ void ex_args(exarg_T *eap)
}
}
// ":args file ..": define new argument list, handle like ":next"
// Also for ":argslocal file .." and ":argsglobal file ..".
if (*eap->arg != NUL) {
// ":args file ..": define new argument list, handle like ":next"
// Also for ":argslocal file .." and ":argsglobal file ..".
ex_next(eap);
} else if (eap->cmdidx == CMD_args) {
// ":args": list arguments.
if (ARGCOUNT <= 0) {
if (check_arglist_locked() == FAIL) {
return;
}
ex_next(eap);
return;
}
// ":args": list arguments.
if (eap->cmdidx == CMD_args) {
if (ARGCOUNT <= 0) {
return; // empty argument list
}
char **items = xmalloc(sizeof(char *) * (size_t)ARGCOUNT);
@ -490,10 +547,14 @@ void ex_args(exarg_T *eap)
}
list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
xfree(items);
} else if (eap->cmdidx == CMD_arglocal) {
return;
}
// ":argslocal": make a local copy of the global argument list.
if (eap->cmdidx == CMD_arglocal) {
garray_T *gap = &curwin->w_alist->al_ga;
// ":argslocal": make a local copy of the global argument list.
ga_grow(gap, GARGCOUNT);
for (int i = 0; i < GARGCOUNT; i++) {
@ -686,6 +747,10 @@ void ex_argadd(exarg_T *eap)
/// ":argdelete"
void ex_argdelete(exarg_T *eap)
{
if (check_arglist_locked() == FAIL) {
return;
}
if (eap->addr_count > 0 || *eap->arg == NUL) {
// ":argdel" works like ":.argdel"
if (eap->addr_count == 0) {
@ -754,76 +819,32 @@ char *alist_name(aentry_T *aep)
return bp->b_fname;
}
/// do_arg_all(): Open up to 'count' windows, one for each argument.
///
/// @param forceit hide buffers in current windows
/// @param keep_tabs keep current tabs, for ":tab drop file"
static void do_arg_all(int count, int forceit, int keep_tabs)
/// Close all the windows containing files which are not in the argument list.
/// Used by the ":all" command.
static void arg_all_close_unused_windows(arg_all_state_T *aall)
{
uint8_t *opened; // Array of weight for which args are open:
// 0: not opened
// 1: opened in other tab
// 2: opened in curtab
// 3: opened in curtab and curwin
win_T *old_curwin = curwin;
tabpage_T *old_curtab = curtab;
int opened_len; // length of opened[]
int use_firstwin = false; // use first window for arglist
bool tab_drop_empty_window = false;
int split_ret = OK;
bool p_ea_save;
alist_T *alist; // argument list to be used
buf_T *buf;
tabpage_T *tpnext;
int had_tab = cmdmod.cmod_tab;
win_T *old_curwin, *last_curwin;
tabpage_T *old_curtab, *last_curtab;
win_T *new_curwin = NULL;
tabpage_T *new_curtab = NULL;
assert(firstwin != NULL); // satisfy coverity
if (ARGCOUNT <= 0) {
// Don't give an error message. We don't want it when the ":all" command is in the .vimrc.
return;
}
setpcmark();
opened_len = ARGCOUNT;
opened = xcalloc((size_t)opened_len, 1);
// Autocommands may do anything to the argument list. Make sure it's not
// freed while we are working here by "locking" it. We still have to
// watch out for its size to be changed.
alist = curwin->w_alist;
alist->al_refcount++;
old_curwin = curwin;
old_curtab = curtab;
// Try closing all windows that are not in the argument list.
// Also close windows that are not full width;
// When 'hidden' or "forceit" set the buffer becomes hidden.
// Windows that have a changed buffer and can't be hidden won't be closed.
// When the ":tab" modifier was used do this for all tab pages.
if (had_tab > 0) {
if (aall->had_tab > 0) {
goto_tabpage_tp(first_tabpage, true, true);
}
for (;;) {
win_T *wpnext = NULL;
tpnext = curtab->tp_next;
tabpage_T *tpnext = curtab->tp_next;
for (win_T *wp = firstwin; wp != NULL; wp = wpnext) {
int i;
wpnext = wp->w_next;
buf = wp->w_buffer;
buf_T *buf = wp->w_buffer;
if (buf->b_ffname == NULL
|| (!keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
i = opened_len;
|| (!aall->keep_tabs && (buf->b_nwindows > 1 || wp->w_width != Columns))) {
i = aall->opened_len;
} else {
// check if the buffer in this window is in the arglist
for (i = 0; i < opened_len; i++) {
if (i < alist->al_ga.ga_len
&& (AARGLIST(alist)[i].ae_fnum == buf->b_fnum
|| path_full_compare(alist_name(&AARGLIST(alist)[i]),
for (i = 0; i < aall->opened_len; i++) {
if (i < aall->alist->al_ga.ga_len
&& (AARGLIST(aall->alist)[i].ae_fnum == buf->b_fnum
|| path_full_compare(alist_name(&AARGLIST(aall->alist)[i]),
buf->b_ffname,
true, true) & kEqualFiles)) {
int weight = 1;
@ -835,23 +856,24 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
}
if (weight > (int)opened[i]) {
opened[i] = (uint8_t)weight;
if (weight > (int)aall->opened[i]) {
aall->opened[i] = (uint8_t)weight;
if (i == 0) {
if (new_curwin != NULL) {
new_curwin->w_arg_idx = opened_len;
if (aall->new_curwin != NULL) {
aall->new_curwin->w_arg_idx = aall->opened_len;
}
new_curwin = wp;
new_curtab = curtab;
aall->new_curwin = wp;
aall->new_curtab = curtab;
}
} else if (keep_tabs) {
i = opened_len;
} else if (aall->keep_tabs) {
i = aall->opened_len;
}
if (wp->w_alist != alist) {
// Use the current argument list for all windows containing a file from it.
if (wp->w_alist != aall->alist) {
// Use the current argument list for all windows
// containing a file from it.
alist_unlink(wp->w_alist);
wp->w_alist = alist;
wp->w_alist = aall->alist;
wp->w_alist->al_refcount++;
}
break;
@ -860,8 +882,8 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
wp->w_arg_idx = i;
if (i == opened_len && !keep_tabs) { // close this window
if (buf_hide(buf) || forceit || buf->b_nwindows > 1
if (i == aall->opened_len && !aall->keep_tabs) { // close this window
if (buf_hide(buf) || aall->forceit || buf->b_nwindows > 1
|| !bufIsChanged(buf)) {
// If the buffer was changed, and we would like to hide it, try autowriting.
if (!buf_hide(buf) && buf->b_nwindows <= 1 && bufIsChanged(buf)) {
@ -876,8 +898,8 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
// don't close last window
if (ONE_WINDOW
&& (first_tabpage->tp_next == NULL || !had_tab)) {
use_firstwin = true;
&& (first_tabpage->tp_next == NULL || !aall->had_tab)) {
aall->use_firstwin = true;
} else {
win_close(wp, !buf_hide(buf) && !bufIsChanged(buf), false);
// check if autocommands removed the next window
@ -891,7 +913,7 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
// Without the ":tab" modifier only do the current tab page.
if (had_tab == 0 || tpnext == NULL) {
if (aall->had_tab == 0 || tpnext == NULL) {
break;
}
@ -901,39 +923,35 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
goto_tabpage_tp(tpnext, true, true);
}
}
// Open a window for files in the argument list that don't have one.
// ARGCOUNT may change while doing this, because of autocommands.
if (count > opened_len || count <= 0) {
count = opened_len;
}
/// Open up to "count" windows for the files in the argument list "aall->alist".
static void arg_all_open_windows(arg_all_state_T *aall, int count)
{
bool tab_drop_empty_window = false;
// Don't execute Win/Buf Enter/Leave autocommands here.
autocmd_no_enter++;
autocmd_no_leave++;
last_curwin = curwin;
last_curtab = curtab;
win_enter(lastwin, false);
// ":tab drop file" should re-use an empty window to avoid "--remote-tab"
// leaving an empty tab page when executed locally.
if (keep_tabs && buf_is_empty(curbuf) && curbuf->b_nwindows == 1
if (aall->keep_tabs && buf_is_empty(curbuf) && curbuf->b_nwindows == 1
&& curbuf->b_ffname == NULL && !curbuf->b_changed) {
use_firstwin = true;
aall->use_firstwin = true;
tab_drop_empty_window = true;
}
int split_ret = OK;
for (int i = 0; i < count && !got_int; i++) {
if (alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
if (aall->alist == &global_alist && i == global_alist.al_ga.ga_len - 1) {
arg_had_last = true;
}
if (opened[i] > 0) {
if (aall->opened[i] > 0) {
// Move the already present window to below the current window
if (curwin->w_arg_idx != i) {
FOR_ALL_WINDOWS_IN_TAB(wp, curtab) {
if (wp->w_arg_idx == i) {
if (keep_tabs) {
new_curwin = wp;
new_curtab = curtab;
if (aall->keep_tabs) {
aall->new_curwin = wp;
aall->new_curtab = curtab;
} else if (wp->w_frame->fr_parent != curwin->w_frame->fr_parent) {
emsg(_("E249: window layout changed unexpectedly"));
i = count;
@ -950,8 +968,8 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
if (tab_drop_empty_window && i == count - 1) {
autocmd_no_enter--;
}
if (!use_firstwin) { // split current window
p_ea_save = p_ea;
if (!aall->use_firstwin) { // split current window
bool p_ea_save = p_ea;
p_ea = true; // use space from all windows
split_ret = win_split(0, WSP_ROOM | WSP_BELOW);
p_ea = p_ea_save;
@ -965,36 +983,101 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
// edit file "i"
curwin->w_arg_idx = i;
if (i == 0) {
new_curwin = curwin;
new_curtab = curtab;
aall->new_curwin = curwin;
aall->new_curtab = curtab;
}
(void)do_ecmd(0, alist_name(&AARGLIST(alist)[i]), NULL, NULL, ECMD_ONE,
(void)do_ecmd(0, alist_name(&AARGLIST(aall->alist)[i]), NULL, NULL, ECMD_ONE,
((buf_hide(curwin->w_buffer)
|| bufIsChanged(curwin->w_buffer))
? ECMD_HIDE : 0) + ECMD_OLDBUF,
|| bufIsChanged(curwin->w_buffer)) ? ECMD_HIDE : 0) + ECMD_OLDBUF,
curwin);
if (tab_drop_empty_window && i == count - 1) {
autocmd_no_enter++;
}
if (use_firstwin) {
if (aall->use_firstwin) {
autocmd_no_leave++;
}
use_firstwin = false;
aall->use_firstwin = false;
}
os_breakcheck();
// When ":tab" was used open a new tab for a new window repeatedly.
if (had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
if (aall->had_tab > 0 && tabpage_index(NULL) <= p_tpm) {
cmdmod.cmod_tab = 9999;
}
}
}
/// do_arg_all(): Open up to 'count' windows, one for each argument.
///
/// @param forceit hide buffers in current windows
/// @param keep_tabs keep current tabs, for ":tab drop file"
static void do_arg_all(int count, int forceit, int keep_tabs)
{
win_T *last_curwin;
tabpage_T *last_curtab;
bool prev_arglist_locked = arglist_locked;
assert(firstwin != NULL); // satisfy coverity
if (cmdwin_type != 0) {
emsg(_(e_cmdwin));
return;
}
if (ARGCOUNT <= 0) {
// Don't give an error message. We don't want it when the ":all" command is in the .vimrc.
return;
}
setpcmark();
arg_all_state_T aall = {
.use_firstwin = false,
.had_tab = cmdmod.cmod_tab,
.new_curwin = NULL,
.new_curtab = NULL,
.forceit = forceit,
.keep_tabs = keep_tabs,
.opened_len = ARGCOUNT,
.opened = xcalloc((size_t)ARGCOUNT, 1),
};
// Autocommands may do anything to the argument list. Make sure it's not
// freed while we are working here by "locking" it. We still have to
// watch out for its size to be changed.
aall.alist = curwin->w_alist;
aall.alist->al_refcount++;
arglist_locked = true;
// Try closing all windows that are not in the argument list.
// Also close windows that are not full width;
// When 'hidden' or "forceit" set the buffer becomes hidden.
// Windows that have a changed buffer and can't be hidden won't be closed.
// When the ":tab" modifier was used do this for all tab pages.
arg_all_close_unused_windows(&aall);
// Open a window for files in the argument list that don't have one.
// ARGCOUNT may change while doing this, because of autocommands.
if (count > aall.opened_len || count <= 0) {
count = aall.opened_len;
}
// Don't execute Win/Buf Enter/Leave autocommands here.
autocmd_no_enter++;
autocmd_no_leave++;
last_curwin = curwin;
last_curtab = curtab;
win_enter(lastwin, false);
// Open upto "count" windows.
arg_all_open_windows(&aall, count);
// Remove the "lock" on the argument list.
alist_unlink(alist);
alist_unlink(aall.alist);
arglist_locked = prev_arglist_locked;
autocmd_no_enter--;
// restore last referenced tabpage's curwin
if (last_curtab != new_curtab) {
if (last_curtab != aall.new_curtab) {
if (valid_tabpage(last_curtab)) {
goto_tabpage_tp(last_curtab, true, true);
}
@ -1003,15 +1086,15 @@ static void do_arg_all(int count, int forceit, int keep_tabs)
}
}
// to window with first arg
if (valid_tabpage(new_curtab)) {
goto_tabpage_tp(new_curtab, true, true);
if (valid_tabpage(aall.new_curtab)) {
goto_tabpage_tp(aall.new_curtab, true, true);
}
if (win_valid(new_curwin)) {
win_enter(new_curwin, false);
if (win_valid(aall.new_curwin)) {
win_enter(aall.new_curwin, false);
}
autocmd_no_leave--;
xfree(opened);
xfree(aall.opened);
}
/// ":all" and ":sall".

View File

@ -881,7 +881,7 @@ void do_autocmd(char *arg_in, int forceit)
}
} else {
if (*arg == '*' || *arg == NUL || *arg == '|') {
if (!forceit && *cmd != NUL) {
if (*cmd != NUL) {
emsg(_(e_cannot_define_autocommands_for_all_events));
} else {
do_all_autocmd_events(pat, once, nested, cmd, forceit, group);

View File

@ -509,18 +509,14 @@ func Test_arglist_autocmd()
new
" redefine arglist; go to Xxx1
next! Xxx1 Xxx2 Xxx3
" open window for all args; Reading Xxx2 will change the arglist and the
" third window will get Xxx1:
" win 1: Xxx1
" win 2: Xxx2
" win 3: Xxx1
all
" open window for all args; Reading Xxx2 will try to change the arglist and
" that will fail
call assert_fails("all", "E1156:")
call assert_equal('test file Xxx1', getline(1))
wincmd w
wincmd w
call assert_equal('test file Xxx1', getline(1))
rewind
call assert_equal('test file Xxx2', getline(1))
wincmd w
call assert_equal('test file Xxx3', getline(1))
autocmd! BufReadPost Xxx2
enew! | only
@ -591,4 +587,27 @@ func Test_quit_with_arglist()
call delete('.c.swp')
endfunc
" Test for ":all" not working when in the cmdline window
func Test_all_not_allowed_from_cmdwin()
au BufEnter * all
next x
" Use try/catch here, somehow assert_fails() doesn't work on MS-Windows
" console.
let caught = 'no'
try
exe ":norm! 7q?apat\<CR>"
catch /E11:/
let caught = 'yes'
endtry
call assert_equal('yes', caught)
au! BufEnter
endfunc
func Test_clear_arglist_in_all()
n 0 00 000 0000 00000 000000
au WinNew 0 n 0
call assert_fails("all", "E1156")
au! *
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@ -148,6 +148,12 @@ func Test_autocmd_bufunload_with_tabnext()
quit
endfunc
func Test_argdelete_in_next()
au BufNew,BufEnter,BufLeave,BufWinEnter * argdel
call assert_fails('next a b', 'E1156:')
au! BufNew,BufEnter,BufLeave,BufWinEnter *
endfunc
func Test_autocmd_bufwinleave_with_tabfirst()
tabedit
augroup sample
@ -2048,6 +2054,7 @@ endfunc
func Test_autocommand_all_events()
call assert_fails('au * * bwipe', 'E1155:')
call assert_fails('au * x bwipe', 'E1155:')
call assert_fails('au! * x bwipe', 'E1155:')
endfunc
func Test_autocmd_user()
@ -3004,6 +3011,15 @@ func Test_Visual_doautoall_redraw()
%bwipe!
endfunc
" This was using freed memory.
func Test_BufNew_arglocal()
arglocal
au BufNew * arglocal
call assert_fails('drop xx', 'E1156:')
au! BufNew
endfunc
func Test_autocmd_closes_window()
au BufNew,BufWinLeave * e %e
file yyy

View File

@ -1,85 +0,0 @@
-- Test for autocommand that redefines the argument list, when doing ":all".
local helpers = require('test.functional.helpers')(after_each)
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local command, dedent, eq = helpers.command, helpers.dedent, helpers.eq
local curbuf_contents = helpers.curbuf_contents
local poke_eventloop = helpers.poke_eventloop
describe('argument list', function()
setup(clear)
it('is working', function()
insert([[
start of test file Xxx
this is a test
this is a test
this is a test
this is a test
end of test file Xxx]])
poke_eventloop()
command('au BufReadPost Xxx2 next Xxx2 Xxx1')
command('/^start of')
-- Write test file Xxx1
feed('A1<Esc>:.,/end of/w! Xxx1<cr>')
-- Write test file Xxx2
feed('$r2:.,/end of/w! Xxx2<cr>')
-- Write test file Xxx3
feed('$r3:.,/end of/w! Xxx3<cr>')
poke_eventloop()
-- Redefine arglist; go to Xxx1
command('next! Xxx1 Xxx2 Xxx3')
-- Open window for all args
command('all')
-- Write contents of Xxx1
command('%yank A')
-- Append contents of last window (Xxx1)
feed('')
poke_eventloop()
command('%yank A')
-- should now be in Xxx2
command('rew')
-- Append contents of Xxx2
command('%yank A')
command('%d')
command('0put=@a')
command('$d')
eq(dedent([[
start of test file Xxx1
this is a test
this is a test
this is a test
this is a test
end of test file Xxx
start of test file Xxx1
this is a test
this is a test
this is a test
this is a test
end of test file Xxx
start of test file Xxx2
this is a test
this is a test
this is a test
this is a test
end of test file Xxx]]), curbuf_contents())
end)
teardown(function()
os.remove('Xxx1')
os.remove('Xxx2')
os.remove('Xxx3')
end)
end)

View File

@ -3,7 +3,6 @@
local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen')
local clear, command, eq = helpers.clear, helpers.command, helpers.eq
local eval, exc_exec, neq = helpers.eval, helpers.exc_exec, helpers.neq
local expect_exit = helpers.expect_exit
local feed = helpers.feed
local pcall_err = helpers.pcall_err
@ -11,233 +10,6 @@ local pcall_err = helpers.pcall_err
describe('argument list commands', function()
before_each(clear)
local function init_abc()
command('args a b c')
command('next')
end
local function reset_arglist()
command('arga a | %argd')
end
local function assert_fails(cmd, err)
neq(nil, exc_exec(cmd):find(err))
end
it('test that argidx() works', function()
command('args a b c')
command('last')
eq(2, eval('argidx()'))
command('%argdelete')
eq(0, eval('argidx()'))
command('args a b c')
eq(0, eval('argidx()'))
command('next')
eq(1, eval('argidx()'))
command('next')
eq(2, eval('argidx()'))
command('1argdelete')
eq(1, eval('argidx()'))
command('1argdelete')
eq(0, eval('argidx()'))
command('1argdelete')
eq(0, eval('argidx()'))
end)
it('test that argadd() works', function()
command('%argdelete')
command('argadd a b c')
eq(0, eval('argidx()'))
command('%argdelete')
command('argadd a')
eq(0, eval('argidx()'))
command('argadd b c d')
eq(0, eval('argidx()'))
init_abc()
command('argadd x')
eq({'a', 'b', 'x', 'c'}, eval('argv()'))
eq(1, eval('argidx()'))
init_abc()
command('0argadd x')
eq({'x', 'a', 'b', 'c'}, eval('argv()'))
eq(2, eval('argidx()'))
init_abc()
command('1argadd x')
eq({'a', 'x', 'b', 'c'}, eval('argv()'))
eq(2, eval('argidx()'))
init_abc()
command('$argadd x')
eq({'a', 'b', 'c', 'x'}, eval('argv()'))
eq(1, eval('argidx()'))
init_abc()
command('$argadd x')
command('+2argadd y')
eq({'a', 'b', 'c', 'x', 'y'}, eval('argv()'))
eq(1, eval('argidx()'))
command('%argd')
command('edit d')
command('arga')
eq(1, eval('len(argv())'))
eq('d', eval('get(argv(), 0, "")'))
command('%argd')
command('new')
command('arga')
eq(0, eval('len(argv())'))
end)
it('test for 0argadd and 0argedit', function()
reset_arglist()
command('arga a b c d')
command('2argu')
command('0arga added')
eq({'added', 'a', 'b', 'c', 'd'}, eval('argv()'))
command('%argd')
command('arga a b c d')
command('2argu')
command('0arge edited')
eq({'edited', 'a', 'b', 'c', 'd'}, eval('argv()'))
command('2argu')
command('arga third')
eq({'edited', 'a', 'third', 'b', 'c', 'd'}, eval('argv()'))
end)
it('test for argc()', function()
reset_arglist()
eq(0, eval('argc()'))
command('argadd a b')
eq(2, eval('argc()'))
end)
it('test for arglistid()', function()
reset_arglist()
command('arga a b')
eq(0, eval('arglistid()'))
command('split')
command('arglocal')
eq(1, eval('arglistid()'))
command('tabnew | tabfirst')
eq(0, eval('arglistid(2)'))
eq(1, eval('arglistid(1, 1)'))
eq(0, eval('arglistid(2, 1)'))
eq(1, eval('arglistid(1, 2)'))
command('tabonly | only | enew!')
command('argglobal')
eq(0, eval('arglistid()'))
end)
it('test for argv()', function()
reset_arglist()
eq({}, eval('argv()'))
eq('', eval('argv(2)'))
command('argadd a b c d')
eq('c', eval('argv(2)'))
end)
it('test for :argedit command', function()
reset_arglist()
command('argedit a')
eq({'a'}, eval('argv()'))
eq('a', eval('expand("%:t")'))
command('argedit b')
eq({'a', 'b'}, eval('argv()'))
eq('b', eval('expand("%:t")'))
command('argedit a')
eq({'a', 'b', 'a'}, eval('argv()'))
eq('a', eval('expand("%:t")'))
command('argedit c')
eq({'a', 'b', 'a', 'c'}, eval('argv()'))
command('0argedit x')
eq({'x', 'a', 'b', 'a', 'c'}, eval('argv()'))
command('set nohidden')
command('enew! | set modified')
assert_fails('argedit y', 'E37:')
command('argedit! y')
eq({'x', 'y', 'y', 'a', 'b', 'a', 'c'}, eval('argv()'))
command('set hidden')
command('%argd')
command('argedit a b')
eq({'a', 'b'}, eval('argv()'))
end)
it('test for :argdelete command', function()
reset_arglist()
command('args aa a aaa b bb')
command('argdelete a*')
eq({'b', 'bb'}, eval('argv()'))
eq('aa', eval('expand("%:t")'))
command('last')
command('argdelete %')
eq({'b'}, eval('argv()'))
assert_fails('argdelete', 'E610:')
assert_fails('1,100argdelete', 'E16:')
reset_arglist()
command('args a b c d')
command('next')
command('argdel')
eq({'a', 'c', 'd'}, eval('argv()'))
command('%argdel')
end)
it('test for the :next, :prev, :first, :last, :rewind commands', function()
reset_arglist()
command('args a b c d')
command('last')
eq(3, eval('argidx()'))
assert_fails('next', 'E165:')
command('prev')
eq(2, eval('argidx()'))
command('Next')
eq(1, eval('argidx()'))
command('first')
eq(0, eval('argidx()'))
assert_fails('prev', 'E164:')
command('3next')
eq(3, eval('argidx()'))
command('rewind')
eq(0, eval('argidx()'))
command('%argd')
end)
it('test for autocommand that redefines the argument list, when doing ":all"', function()
command('autocmd BufReadPost Xxx2 next Xxx2 Xxx1')
command("call writefile(['test file Xxx1'], 'Xxx1')")
command("call writefile(['test file Xxx2'], 'Xxx2')")
command("call writefile(['test file Xxx3'], 'Xxx3')")
command('new')
-- redefine arglist; go to Xxx1
command('next! Xxx1 Xxx2 Xxx3')
-- open window for all args
command('all')
eq('test file Xxx1', eval('getline(1)'))
command('wincmd w')
command('wincmd w')
eq('test file Xxx1', eval('getline(1)'))
-- should now be in Xxx2
command('rewind')
eq('test file Xxx2', eval('getline(1)'))
command('autocmd! BufReadPost Xxx2')
command('enew! | only')
command("call delete('Xxx1')")
command("call delete('Xxx2')")
command("call delete('Xxx3')")
command('argdelete Xxx*')
command('bwipe! Xxx1 Xxx2 Xxx3')
end)
it('quitting Vim with unedited files in the argument list throws E173', function()
command('set nomore')
command('args a b c')