2019-08-17 14:38:55 +02:00
|
|
|
/* vi:set ts=8 sts=4 sw=4 noet:
|
|
|
|
*
|
|
|
|
* VIM - Vi IMproved by Bram Moolenaar
|
|
|
|
*
|
|
|
|
* Do ":help uganda" in Vim to read copying and usage conditions.
|
|
|
|
* Do ":help credits" in Vim to see a list of people who contributed.
|
|
|
|
* See README.txt for an overview of the Vim source code.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* arglist.c: functions for dealing with the argument list
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "vim.h"
|
|
|
|
|
|
|
|
#define AL_SET 1
|
|
|
|
#define AL_ADD 2
|
|
|
|
#define AL_DEL 3
|
|
|
|
|
2021-01-28 14:24:55 +01:00
|
|
|
// This flag is set whenever the argument list is being changed and calling a
|
|
|
|
// function that might trigger an autocommand.
|
|
|
|
static int arglist_locked = FALSE;
|
|
|
|
|
|
|
|
static int
|
|
|
|
check_arglist_locked(void)
|
|
|
|
{
|
|
|
|
if (arglist_locked)
|
|
|
|
{
|
|
|
|
emsg(_(e_cannot_change_arglist_recursively));
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2019-08-17 14:38:55 +02:00
|
|
|
/*
|
|
|
|
* Clear an argument list: free all file names and reset it to zero entries.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
alist_clear(alist_T *al)
|
|
|
|
{
|
2021-01-28 14:24:55 +01:00
|
|
|
if (check_arglist_locked() == FAIL)
|
|
|
|
return;
|
2019-08-17 14:38:55 +02:00
|
|
|
while (--al->al_ga.ga_len >= 0)
|
|
|
|
vim_free(AARGLIST(al)[al->al_ga.ga_len].ae_fname);
|
|
|
|
ga_clear(&al->al_ga);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Init an argument list.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
alist_init(alist_T *al)
|
|
|
|
{
|
2022-01-08 16:19:22 +00:00
|
|
|
ga_init2(&al->al_ga, sizeof(aentry_T), 5);
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Remove a reference from an argument list.
|
|
|
|
* Ignored when the argument list is the global one.
|
|
|
|
* If the argument list is no longer used by any window, free it.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
alist_unlink(alist_T *al)
|
|
|
|
{
|
|
|
|
if (al != &global_alist && --al->al_refcount <= 0)
|
|
|
|
{
|
|
|
|
alist_clear(al);
|
|
|
|
vim_free(al);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Create a new argument list and use it for the current window.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
alist_new(void)
|
|
|
|
{
|
|
|
|
curwin->w_alist = ALLOC_ONE(alist_T);
|
|
|
|
if (curwin->w_alist == NULL)
|
|
|
|
{
|
|
|
|
curwin->w_alist = &global_alist;
|
|
|
|
++global_alist.al_refcount;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
curwin->w_alist->al_refcount = 1;
|
|
|
|
curwin->w_alist->id = ++max_alist_id;
|
|
|
|
alist_init(curwin->w_alist);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if !defined(UNIX) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Expand the file names in the global argument list.
|
|
|
|
* If "fnum_list" is not NULL, use "fnum_list[fnum_len]" as a list of buffer
|
|
|
|
* numbers to be re-used.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
alist_expand(int *fnum_list, int fnum_len)
|
|
|
|
{
|
|
|
|
char_u **old_arg_files;
|
|
|
|
int old_arg_count;
|
|
|
|
char_u **new_arg_files;
|
|
|
|
int new_arg_file_count;
|
|
|
|
char_u *save_p_su = p_su;
|
|
|
|
int i;
|
|
|
|
|
2022-09-05 14:33:47 +01:00
|
|
|
old_arg_files = ALLOC_MULT(char_u *, GARGCOUNT);
|
|
|
|
if (old_arg_files == NULL)
|
|
|
|
return;
|
|
|
|
|
2019-08-17 14:38:55 +02:00
|
|
|
// Don't use 'suffixes' here. This should work like the shell did the
|
|
|
|
// expansion. Also, the vimrc file isn't read yet, thus the user
|
|
|
|
// can't set the options.
|
|
|
|
p_su = empty_option;
|
2022-09-05 14:33:47 +01:00
|
|
|
for (i = 0; i < GARGCOUNT; ++i)
|
|
|
|
old_arg_files[i] = vim_strsave(GARGLIST[i].ae_fname);
|
|
|
|
old_arg_count = GARGCOUNT;
|
|
|
|
if (expand_wildcards(old_arg_count, old_arg_files,
|
|
|
|
&new_arg_file_count, &new_arg_files,
|
|
|
|
EW_FILE|EW_NOTFOUND|EW_ADDSLASH|EW_NOERROR) == OK
|
|
|
|
&& new_arg_file_count > 0)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-09-05 14:33:47 +01:00
|
|
|
alist_set(&global_alist, new_arg_file_count, new_arg_files,
|
|
|
|
TRUE, fnum_list, fnum_len);
|
|
|
|
FreeWild(old_arg_count, old_arg_files);
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
p_su = save_p_su;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the argument list for the current window.
|
|
|
|
* Takes over the allocated files[] and the allocated fnames in it.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
alist_set(
|
|
|
|
alist_T *al,
|
|
|
|
int count,
|
|
|
|
char_u **files,
|
|
|
|
int use_curbuf,
|
|
|
|
int *fnum_list,
|
|
|
|
int fnum_len)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
2021-01-28 14:24:55 +01:00
|
|
|
if (check_arglist_locked() == FAIL)
|
2019-08-17 14:38:55 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
alist_clear(al);
|
2021-08-02 19:10:38 +02:00
|
|
|
if (GA_GROW_OK(&al->al_ga, count))
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
if (got_int)
|
|
|
|
{
|
|
|
|
// When adding many buffers this can take a long time. Allow
|
|
|
|
// interrupting here.
|
|
|
|
while (i < count)
|
|
|
|
vim_free(files[i++]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
2021-01-28 14:24:55 +01:00
|
|
|
{
|
|
|
|
arglist_locked = TRUE;
|
2019-08-17 14:38:55 +02:00
|
|
|
buf_set_name(fnum_list[i], files[i]);
|
2021-01-28 14:24:55 +01:00
|
|
|
arglist_locked = FALSE;
|
|
|
|
}
|
2019-08-17 14:38:55 +02:00
|
|
|
|
|
|
|
alist_add(al, files[i], use_curbuf ? 2 : 1);
|
|
|
|
ui_breakcheck();
|
|
|
|
}
|
|
|
|
vim_free(files);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
FreeWild(count, files);
|
|
|
|
if (al == &global_alist)
|
|
|
|
arg_had_last = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add file "fname" to argument list "al".
|
|
|
|
* "fname" must have been allocated and "al" must have been checked for room.
|
2024-08-15 22:15:28 +02:00
|
|
|
*
|
|
|
|
* May trigger Buf* autocommands
|
2019-08-17 14:38:55 +02:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
alist_add(
|
|
|
|
alist_T *al,
|
|
|
|
char_u *fname,
|
|
|
|
int set_fnum) // 1: set buffer number; 2: re-use curbuf
|
|
|
|
{
|
|
|
|
if (fname == NULL) // don't add NULL file names
|
|
|
|
return;
|
2021-01-28 14:24:55 +01:00
|
|
|
if (check_arglist_locked() == FAIL)
|
|
|
|
return;
|
|
|
|
arglist_locked = TRUE;
|
2024-08-15 22:15:28 +02:00
|
|
|
curwin->w_locked = TRUE;
|
2021-01-28 14:24:55 +01:00
|
|
|
|
2019-08-17 14:38:55 +02:00
|
|
|
#ifdef BACKSLASH_IN_FILENAME
|
|
|
|
slash_adjust(fname);
|
|
|
|
#endif
|
|
|
|
AARGLIST(al)[al->al_ga.ga_len].ae_fname = fname;
|
|
|
|
if (set_fnum > 0)
|
|
|
|
AARGLIST(al)[al->al_ga.ga_len].ae_fnum =
|
|
|
|
buflist_add(fname, BLN_LISTED | (set_fnum == 2 ? BLN_CURBUF : 0));
|
|
|
|
++al->al_ga.ga_len;
|
2021-01-28 14:24:55 +01:00
|
|
|
|
|
|
|
arglist_locked = FALSE;
|
2024-08-15 22:15:28 +02:00
|
|
|
curwin->w_locked = FALSE;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(BACKSLASH_IN_FILENAME) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* Adjust slashes in file names. Called after 'shellslash' was set.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
alist_slash_adjust(void)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
win_T *wp;
|
|
|
|
tabpage_T *tp;
|
|
|
|
|
|
|
|
for (i = 0; i < GARGCOUNT; ++i)
|
|
|
|
if (GARGLIST[i].ae_fname != NULL)
|
|
|
|
slash_adjust(GARGLIST[i].ae_fname);
|
|
|
|
FOR_ALL_TAB_WINDOWS(tp, wp)
|
|
|
|
if (wp->w_alist != &global_alist)
|
|
|
|
for (i = 0; i < WARGCOUNT(wp); ++i)
|
|
|
|
if (WARGLIST(wp)[i].ae_fname != NULL)
|
|
|
|
slash_adjust(WARGLIST(wp)[i].ae_fname);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Isolate one argument, taking backticks.
|
|
|
|
* Changes the argument in-place, puts a NUL after it. Backticks remain.
|
|
|
|
* Return a pointer to the start of the next argument.
|
|
|
|
*/
|
|
|
|
static char_u *
|
|
|
|
do_one_arg(char_u *str)
|
|
|
|
{
|
|
|
|
char_u *p;
|
|
|
|
int inbacktick;
|
|
|
|
|
|
|
|
inbacktick = FALSE;
|
|
|
|
for (p = str; *str; ++str)
|
|
|
|
{
|
|
|
|
// When the backslash is used for escaping the special meaning of a
|
|
|
|
// character we need to keep it until wildcard expansion.
|
|
|
|
if (rem_backslash(str))
|
|
|
|
{
|
|
|
|
*p++ = *str++;
|
|
|
|
*p++ = *str;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// An item ends at a space not in backticks
|
|
|
|
if (!inbacktick && vim_isspace(*str))
|
|
|
|
break;
|
|
|
|
if (*str == '`')
|
|
|
|
inbacktick ^= TRUE;
|
|
|
|
*p++ = *str;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
str = skipwhite(str);
|
|
|
|
*p = NUL;
|
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Separate the arguments in "str" and return a list of pointers in the
|
|
|
|
* growarray "gap".
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
get_arglist(garray_T *gap, char_u *str, int escaped)
|
|
|
|
{
|
2022-01-08 16:19:22 +00:00
|
|
|
ga_init2(gap, sizeof(char_u *), 20);
|
2019-08-17 14:38:55 +02:00
|
|
|
while (*str != NUL)
|
|
|
|
{
|
|
|
|
if (ga_grow(gap, 1) == FAIL)
|
|
|
|
{
|
|
|
|
ga_clear(gap);
|
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
((char_u **)gap->ga_data)[gap->ga_len++] = str;
|
|
|
|
|
|
|
|
// If str is escaped, don't handle backslashes or spaces
|
|
|
|
if (!escaped)
|
|
|
|
return OK;
|
|
|
|
|
|
|
|
// Isolate one argument, change it in-place, put a NUL after it.
|
|
|
|
str = do_one_arg(str);
|
|
|
|
}
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
2022-10-13 22:12:15 +01:00
|
|
|
#if defined(FEAT_QUICKFIX) || defined(FEAT_SYN_HL) || defined(FEAT_SPELL) || defined(PROTO)
|
2019-08-17 14:38:55 +02:00
|
|
|
/*
|
|
|
|
* Parse a list of arguments (file names), expand them and return in
|
|
|
|
* "fnames[fcountp]". When "wig" is TRUE, removes files matching 'wildignore'.
|
|
|
|
* Return FAIL or OK.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
get_arglist_exp(
|
|
|
|
char_u *str,
|
|
|
|
int *fcountp,
|
|
|
|
char_u ***fnamesp,
|
|
|
|
int wig)
|
|
|
|
{
|
|
|
|
garray_T ga;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (get_arglist(&ga, str, TRUE) == FAIL)
|
|
|
|
return FAIL;
|
|
|
|
if (wig == TRUE)
|
|
|
|
i = expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
|
2021-01-30 18:09:06 +01:00
|
|
|
fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD);
|
2019-08-17 14:38:55 +02:00
|
|
|
else
|
|
|
|
i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data,
|
2021-01-30 18:09:06 +01:00
|
|
|
fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD);
|
2019-08-17 14:38:55 +02:00
|
|
|
|
|
|
|
ga_clear(&ga);
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check the validity of the arg_idx for each other window.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
alist_check_arg_idx(void)
|
|
|
|
{
|
|
|
|
win_T *win;
|
|
|
|
tabpage_T *tp;
|
|
|
|
|
|
|
|
FOR_ALL_TAB_WINDOWS(tp, win)
|
|
|
|
if (win->w_alist == curwin->w_alist)
|
|
|
|
check_arg_idx(win);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Add files[count] to the arglist of the current window after arg "after".
|
|
|
|
* The file names in files[count] must have been allocated and are taken over.
|
|
|
|
* Files[] itself is not taken over.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
alist_add_list(
|
|
|
|
int count,
|
|
|
|
char_u **files,
|
|
|
|
int after, // where to add: 0 = before first one
|
|
|
|
int will_edit) // will edit adding argument
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int old_argcount = ARGCOUNT;
|
|
|
|
|
2021-01-28 14:24:55 +01:00
|
|
|
if (check_arglist_locked() != FAIL
|
2021-08-02 19:10:38 +02:00
|
|
|
&& GA_GROW_OK(&ALIST(curwin)->al_ga, count))
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
|
|
|
if (after < 0)
|
|
|
|
after = 0;
|
|
|
|
if (after > ARGCOUNT)
|
|
|
|
after = ARGCOUNT;
|
|
|
|
if (after < ARGCOUNT)
|
|
|
|
mch_memmove(&(ARGLIST[after + count]), &(ARGLIST[after]),
|
|
|
|
(ARGCOUNT - after) * sizeof(aentry_T));
|
2021-01-28 14:24:55 +01:00
|
|
|
arglist_locked = TRUE;
|
2024-08-15 22:15:28 +02:00
|
|
|
curwin->w_locked = TRUE;
|
2019-08-17 14:38:55 +02:00
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
}
|
2021-01-28 14:24:55 +01:00
|
|
|
arglist_locked = FALSE;
|
2024-08-15 22:15:28 +02:00
|
|
|
curwin->w_locked = FALSE;
|
2019-08-17 14:38:55 +02:00
|
|
|
ALIST(curwin)->al_ga.ga_len += count;
|
|
|
|
if (old_argcount > 0 && curwin->w_arg_idx >= after)
|
|
|
|
curwin->w_arg_idx += count;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < count; ++i)
|
|
|
|
vim_free(files[i]);
|
|
|
|
}
|
|
|
|
|
2022-09-05 14:33:47 +01:00
|
|
|
/*
|
|
|
|
* Delete the file names in 'alist_ga' from the argument list.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
arglist_del_files(garray_T *alist_ga)
|
|
|
|
{
|
|
|
|
regmatch_T regmatch;
|
|
|
|
int didone;
|
|
|
|
int i;
|
|
|
|
char_u *p;
|
|
|
|
int match;
|
|
|
|
|
|
|
|
// Delete the items: use each item as a regexp and find a match in the
|
|
|
|
// argument list.
|
|
|
|
regmatch.rm_ic = p_fic; // ignore case when 'fileignorecase' is set
|
|
|
|
for (i = 0; i < alist_ga->ga_len && !got_int; ++i)
|
|
|
|
{
|
|
|
|
p = ((char_u **)alist_ga->ga_data)[i];
|
|
|
|
p = file_pat_to_reg_pat(p, NULL, NULL, FALSE);
|
|
|
|
if (p == NULL)
|
|
|
|
break;
|
|
|
|
regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0);
|
|
|
|
if (regmatch.regprog == NULL)
|
|
|
|
{
|
|
|
|
vim_free(p);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
didone = FALSE;
|
|
|
|
for (match = 0; match < ARGCOUNT; ++match)
|
2022-11-24 00:09:02 +00:00
|
|
|
if (vim_regexec(®match, alist_name(&ARGLIST[match]), (colnr_T)0))
|
2022-09-05 14:33:47 +01:00
|
|
|
{
|
|
|
|
didone = TRUE;
|
|
|
|
vim_free(ARGLIST[match].ae_fname);
|
|
|
|
mch_memmove(ARGLIST + match, ARGLIST + match + 1,
|
|
|
|
(ARGCOUNT - match - 1) * sizeof(aentry_T));
|
|
|
|
--ALIST(curwin)->al_ga.ga_len;
|
|
|
|
if (curwin->w_arg_idx > match)
|
|
|
|
--curwin->w_arg_idx;
|
|
|
|
--match;
|
|
|
|
}
|
|
|
|
|
|
|
|
vim_regfree(regmatch.regprog);
|
|
|
|
vim_free(p);
|
|
|
|
if (!didone)
|
|
|
|
semsg(_(e_no_match_str_2), ((char_u **)alist_ga->ga_data)[i]);
|
|
|
|
}
|
|
|
|
ga_clear(alist_ga);
|
|
|
|
}
|
|
|
|
|
2019-08-17 14:38:55 +02:00
|
|
|
/*
|
|
|
|
* "what" == AL_SET: Redefine the argument list to 'str'.
|
|
|
|
* "what" == AL_ADD: add files in 'str' to the argument list after "after".
|
|
|
|
* "what" == AL_DEL: remove files in 'str' from the argument list.
|
|
|
|
*
|
|
|
|
* Return FAIL for failure, OK otherwise.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
do_arglist(
|
|
|
|
char_u *str,
|
|
|
|
int what,
|
|
|
|
int after UNUSED, // 0 means before first one
|
|
|
|
int will_edit) // will edit added argument
|
|
|
|
{
|
|
|
|
garray_T new_ga;
|
|
|
|
int exp_count;
|
|
|
|
char_u **exp_files;
|
|
|
|
int i;
|
|
|
|
int arg_escaped = TRUE;
|
|
|
|
|
2021-01-28 14:24:55 +01:00
|
|
|
if (check_arglist_locked() == FAIL)
|
|
|
|
return FAIL;
|
|
|
|
|
2019-08-17 14:38:55 +02:00
|
|
|
// Set default argument for ":argadd" command.
|
|
|
|
if (what == AL_ADD && *str == NUL)
|
|
|
|
{
|
|
|
|
if (curbuf->b_ffname == NULL)
|
|
|
|
return FAIL;
|
|
|
|
str = curbuf->b_fname;
|
|
|
|
arg_escaped = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Collect all file name arguments in "new_ga".
|
|
|
|
if (get_arglist(&new_ga, str, arg_escaped) == FAIL)
|
|
|
|
return FAIL;
|
|
|
|
|
|
|
|
if (what == AL_DEL)
|
2022-09-05 14:33:47 +01:00
|
|
|
arglist_del_files(&new_ga);
|
2019-08-17 14:38:55 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
i = expand_wildcards(new_ga.ga_len, (char_u **)new_ga.ga_data,
|
|
|
|
&exp_count, &exp_files, EW_DIR|EW_FILE|EW_ADDSLASH|EW_NOTFOUND);
|
|
|
|
ga_clear(&new_ga);
|
|
|
|
if (i == FAIL || exp_count == 0)
|
|
|
|
{
|
2022-01-01 14:19:49 +00:00
|
|
|
emsg(_(e_no_match));
|
2019-08-17 14:38:55 +02:00
|
|
|
return FAIL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (what == AL_ADD)
|
|
|
|
{
|
|
|
|
alist_add_list(exp_count, exp_files, after, will_edit);
|
|
|
|
vim_free(exp_files);
|
|
|
|
}
|
|
|
|
else // what == AL_SET
|
|
|
|
alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
alist_check_arg_idx();
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Redefine the argument list.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
set_arglist(char_u *str)
|
|
|
|
{
|
2024-01-23 21:27:19 +01:00
|
|
|
do_arglist(str, AL_SET, 0, TRUE);
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Return TRUE if window "win" is editing the file at the current argument
|
|
|
|
* index.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
editing_arg_idx(win_T *win)
|
|
|
|
{
|
|
|
|
return !(win->w_arg_idx >= WARGCOUNT(win)
|
|
|
|
|| (win->w_buffer->b_fnum
|
|
|
|
!= WARGLIST(win)[win->w_arg_idx].ae_fnum
|
|
|
|
&& (win->w_buffer->b_ffname == NULL
|
|
|
|
|| !(fullpathcmp(
|
|
|
|
alist_name(&WARGLIST(win)[win->w_arg_idx]),
|
|
|
|
win->w_buffer->b_ffname, TRUE, TRUE) & FPC_SAME))));
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Check if window "win" is editing the w_arg_idx file in its argument list.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
check_arg_idx(win_T *win)
|
|
|
|
{
|
|
|
|
if (WARGCOUNT(win) > 1 && !editing_arg_idx(win))
|
|
|
|
{
|
|
|
|
// We are not editing the current entry in the argument list.
|
|
|
|
// Set "arg_had_last" if we are editing the last one.
|
|
|
|
win->w_arg_idx_invalid = TRUE;
|
|
|
|
if (win->w_arg_idx != WARGCOUNT(win) - 1
|
|
|
|
&& arg_had_last == FALSE
|
|
|
|
&& ALIST(win) == &global_alist
|
|
|
|
&& GARGCOUNT > 0
|
|
|
|
&& win->w_arg_idx < GARGCOUNT
|
|
|
|
&& (win->w_buffer->b_fnum == GARGLIST[GARGCOUNT - 1].ae_fnum
|
|
|
|
|| (win->w_buffer->b_ffname != NULL
|
|
|
|
&& (fullpathcmp(alist_name(&GARGLIST[GARGCOUNT - 1]),
|
|
|
|
win->w_buffer->b_ffname, TRUE, TRUE) & FPC_SAME))))
|
|
|
|
arg_had_last = TRUE;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// We are editing the current entry in the argument list.
|
|
|
|
// Set "arg_had_last" if it's also the last one
|
|
|
|
win->w_arg_idx_invalid = FALSE;
|
|
|
|
if (win->w_arg_idx == WARGCOUNT(win) - 1
|
|
|
|
&& win->w_alist == &global_alist)
|
|
|
|
arg_had_last = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":args", ":argslocal" and ":argsglobal".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_args(exarg_T *eap)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (eap->cmdidx != CMD_args)
|
|
|
|
{
|
2021-02-03 21:23:29 +01:00
|
|
|
if (check_arglist_locked() == FAIL)
|
|
|
|
return;
|
2019-08-17 14:38:55 +02:00
|
|
|
alist_unlink(ALIST(curwin));
|
|
|
|
if (eap->cmdidx == CMD_argglobal)
|
|
|
|
ALIST(curwin) = &global_alist;
|
|
|
|
else // eap->cmdidx == CMD_arglocal
|
|
|
|
alist_new();
|
|
|
|
}
|
|
|
|
|
2022-09-05 18:27:47 +01:00
|
|
|
// ":args file ..": define new argument list, handle like ":next"
|
|
|
|
// Also for ":argslocal file .." and ":argsglobal file ..".
|
2019-08-17 14:38:55 +02:00
|
|
|
if (*eap->arg != NUL)
|
|
|
|
{
|
2021-02-03 21:23:29 +01:00
|
|
|
if (check_arglist_locked() == FAIL)
|
|
|
|
return;
|
2019-08-17 14:38:55 +02:00
|
|
|
ex_next(eap);
|
2022-09-05 18:27:47 +01:00
|
|
|
return;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
2022-09-05 18:27:47 +01:00
|
|
|
|
|
|
|
// ":args": list arguments.
|
|
|
|
if (eap->cmdidx == CMD_args)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-09-05 14:33:47 +01:00
|
|
|
char_u **items;
|
|
|
|
|
|
|
|
if (ARGCOUNT <= 0)
|
2022-09-05 18:27:47 +01:00
|
|
|
return; // empty argument list
|
2019-08-17 14:38:55 +02:00
|
|
|
|
2022-09-05 14:33:47 +01:00
|
|
|
items = ALLOC_MULT(char_u *, ARGCOUNT);
|
|
|
|
if (items == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// Overwrite the command, for a short list there is no scrolling
|
|
|
|
// required and no wait_return().
|
|
|
|
gotocmdline(TRUE);
|
|
|
|
|
|
|
|
for (i = 0; i < ARGCOUNT; ++i)
|
|
|
|
items[i] = alist_name(&ARGLIST[i]);
|
|
|
|
list_in_columns(items, ARGCOUNT, curwin->w_arg_idx);
|
|
|
|
vim_free(items);
|
2022-09-05 18:27:47 +01:00
|
|
|
|
|
|
|
return;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
2022-09-05 18:27:47 +01:00
|
|
|
|
|
|
|
// ":argslocal": make a local copy of the global argument list.
|
|
|
|
if (eap->cmdidx == CMD_arglocal)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
|
|
|
garray_T *gap = &curwin->w_alist->al_ga;
|
|
|
|
|
2022-09-05 14:33:47 +01:00
|
|
|
if (GA_GROW_FAILS(gap, GARGCOUNT))
|
|
|
|
return;
|
|
|
|
|
|
|
|
for (i = 0; i < GARGCOUNT; ++i)
|
|
|
|
if (GARGLIST[i].ae_fname != NULL)
|
|
|
|
{
|
|
|
|
AARGLIST(curwin->w_alist)[gap->ga_len].ae_fname =
|
|
|
|
vim_strsave(GARGLIST[i].ae_fname);
|
|
|
|
AARGLIST(curwin->w_alist)[gap->ga_len].ae_fnum =
|
|
|
|
GARGLIST[i].ae_fnum;
|
|
|
|
++gap->ga_len;
|
|
|
|
}
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":previous", ":sprevious", ":Next" and ":sNext".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_previous(exarg_T *eap)
|
|
|
|
{
|
|
|
|
// If past the last one already, go to the last one.
|
|
|
|
if (curwin->w_arg_idx - (int)eap->line2 >= ARGCOUNT)
|
|
|
|
do_argfile(eap, ARGCOUNT - 1);
|
|
|
|
else
|
|
|
|
do_argfile(eap, curwin->w_arg_idx - (int)eap->line2);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":rewind", ":first", ":sfirst" and ":srewind".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_rewind(exarg_T *eap)
|
|
|
|
{
|
|
|
|
do_argfile(eap, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":last" and ":slast".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_last(exarg_T *eap)
|
|
|
|
{
|
|
|
|
do_argfile(eap, ARGCOUNT - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":argument" and ":sargument".
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_argument(exarg_T *eap)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (eap->addr_count > 0)
|
|
|
|
i = eap->line2 - 1;
|
|
|
|
else
|
|
|
|
i = curwin->w_arg_idx;
|
|
|
|
do_argfile(eap, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Edit file "argn" of the argument lists.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
do_argfile(exarg_T *eap, int argn)
|
|
|
|
{
|
|
|
|
int other;
|
|
|
|
char_u *p;
|
|
|
|
int old_arg_idx = curwin->w_arg_idx;
|
2024-03-03 16:16:47 +01:00
|
|
|
int is_split_cmd = *eap->cmd == 's';
|
2019-08-17 14:38:55 +02:00
|
|
|
|
2020-02-01 23:04:24 +01:00
|
|
|
if (ERROR_IF_ANY_POPUP_WINDOW)
|
2019-08-17 14:38:55 +02:00
|
|
|
return;
|
|
|
|
if (argn < 0 || argn >= ARGCOUNT)
|
|
|
|
{
|
|
|
|
if (ARGCOUNT <= 1)
|
2021-12-31 17:25:48 +00:00
|
|
|
emsg(_(e_there_is_only_one_file_to_edit));
|
2019-08-17 14:38:55 +02:00
|
|
|
else if (argn < 0)
|
2021-12-31 17:25:48 +00:00
|
|
|
emsg(_(e_cannot_go_before_first_file));
|
2019-08-17 14:38:55 +02:00
|
|
|
else
|
2021-12-31 17:25:48 +00:00
|
|
|
emsg(_(e_cannot_go_beyond_last_file));
|
2023-01-02 16:54:53 +00:00
|
|
|
|
|
|
|
return;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
2023-01-02 16:54:53 +00:00
|
|
|
|
2024-03-03 16:16:47 +01:00
|
|
|
if (!is_split_cmd
|
|
|
|
&& (&ARGLIST[argn])->ae_fnum != curbuf->b_fnum
|
|
|
|
&& !check_can_set_curbuf_forceit(eap->forceit))
|
|
|
|
return;
|
|
|
|
|
2023-01-02 16:54:53 +00:00
|
|
|
setpcmark();
|
2019-08-17 14:38:55 +02:00
|
|
|
#ifdef FEAT_GUI
|
2023-01-02 16:54:53 +00:00
|
|
|
need_mouse_correct = TRUE;
|
2019-08-17 14:38:55 +02:00
|
|
|
#endif
|
|
|
|
|
2023-01-02 16:54:53 +00:00
|
|
|
// split window or create new tab page first
|
2024-03-03 16:16:47 +01:00
|
|
|
if (is_split_cmd || cmdmod.cmod_tab != 0)
|
2023-01-02 16:54:53 +00:00
|
|
|
{
|
|
|
|
if (win_split(0, 0) == FAIL)
|
|
|
|
return;
|
|
|
|
RESET_BINDING(curwin);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// if 'hidden' set, only check for changed file when re-editing
|
|
|
|
// the same buffer
|
|
|
|
other = TRUE;
|
|
|
|
if (buf_hide(curbuf))
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2023-01-02 16:54:53 +00:00
|
|
|
p = fix_fname(alist_name(&ARGLIST[argn]));
|
|
|
|
other = otherfile(p);
|
|
|
|
vim_free(p);
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
2023-01-02 16:54:53 +00:00
|
|
|
if ((!buf_hide(curbuf) || !other)
|
|
|
|
&& check_changed(curbuf, CCGD_AW
|
|
|
|
| (other ? 0 : CCGD_MULTWIN)
|
|
|
|
| (eap->forceit ? CCGD_FORCEIT : 0)
|
|
|
|
| CCGD_EXCMD))
|
|
|
|
return;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
2023-01-02 16:54:53 +00:00
|
|
|
|
|
|
|
curwin->w_arg_idx = argn;
|
|
|
|
if (argn == ARGCOUNT - 1 && curwin->w_alist == &global_alist)
|
|
|
|
arg_had_last = TRUE;
|
|
|
|
|
|
|
|
// Edit the file; always use the last known line number.
|
|
|
|
// When it fails (e.g. Abort for already edited file) restore the
|
|
|
|
// argument index.
|
|
|
|
if (do_ecmd(0, alist_name(&ARGLIST[curwin->w_arg_idx]), NULL,
|
|
|
|
eap, ECMD_LAST,
|
|
|
|
(buf_hide(curwin->w_buffer) ? ECMD_HIDE : 0)
|
|
|
|
+ (eap->forceit ? ECMD_FORCEIT : 0), curwin) == FAIL)
|
|
|
|
curwin->w_arg_idx = old_arg_idx;
|
|
|
|
// like Vi: set the mark where the cursor is in the file.
|
|
|
|
else if (eap->cmdidx != CMD_argdo)
|
|
|
|
setmark('\'');
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":next", and commands that behave like it.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_next(exarg_T *eap)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
// check for changed buffer now, if this fails the argument list is not
|
|
|
|
// redefined.
|
|
|
|
if ( buf_hide(curbuf)
|
|
|
|
|| eap->cmdidx == CMD_snext
|
|
|
|
|| !check_changed(curbuf, CCGD_AW
|
|
|
|
| (eap->forceit ? CCGD_FORCEIT : 0)
|
|
|
|
| CCGD_EXCMD))
|
|
|
|
{
|
|
|
|
if (*eap->arg != NUL) // redefine file list
|
|
|
|
{
|
|
|
|
if (do_arglist(eap->arg, AL_SET, 0, TRUE) == FAIL)
|
|
|
|
return;
|
|
|
|
i = 0;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
i = curwin->w_arg_idx + (int)eap->line2;
|
|
|
|
do_argfile(eap, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-24 20:28:03 +00:00
|
|
|
/*
|
|
|
|
* ":argdedupe"
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_argdedupe(exarg_T *eap UNUSED)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int j;
|
|
|
|
|
|
|
|
for (i = 0; i < ARGCOUNT; ++i)
|
2022-11-12 17:00:31 +00:00
|
|
|
{
|
|
|
|
// Expand each argument to a full path to catch different paths leading
|
|
|
|
// to the same file.
|
|
|
|
char_u *firstFullname = FullName_save(ARGLIST[i].ae_fname, FALSE);
|
|
|
|
if (firstFullname == NULL)
|
|
|
|
return; // out of memory
|
|
|
|
|
2021-12-24 20:28:03 +00:00
|
|
|
for (j = i + 1; j < ARGCOUNT; ++j)
|
2022-11-12 17:00:31 +00:00
|
|
|
{
|
|
|
|
char_u *secondFullname = FullName_save(ARGLIST[j].ae_fname, FALSE);
|
|
|
|
if (secondFullname == NULL)
|
|
|
|
break; // out of memory
|
|
|
|
int areNamesDuplicate =
|
|
|
|
fnamecmp(firstFullname, secondFullname) == 0;
|
|
|
|
vim_free(secondFullname);
|
|
|
|
|
|
|
|
if (areNamesDuplicate)
|
2021-12-24 20:28:03 +00:00
|
|
|
{
|
2022-11-12 17:00:31 +00:00
|
|
|
// remove one duplicate argument
|
2021-12-24 20:28:03 +00:00
|
|
|
vim_free(ARGLIST[j].ae_fname);
|
|
|
|
mch_memmove(ARGLIST + j, ARGLIST + j + 1,
|
|
|
|
(ARGCOUNT - j - 1) * sizeof(aentry_T));
|
|
|
|
--ARGCOUNT;
|
|
|
|
|
|
|
|
if (curwin->w_arg_idx == j)
|
|
|
|
curwin->w_arg_idx = i;
|
|
|
|
else if (curwin->w_arg_idx > j)
|
|
|
|
--curwin->w_arg_idx;
|
|
|
|
|
|
|
|
--j;
|
|
|
|
}
|
2022-11-12 17:00:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
vim_free(firstFullname);
|
|
|
|
}
|
2021-12-24 20:28:03 +00:00
|
|
|
}
|
|
|
|
|
2019-08-17 14:38:55 +02:00
|
|
|
/*
|
|
|
|
* ":argedit"
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_argedit(exarg_T *eap)
|
|
|
|
{
|
|
|
|
int i = eap->addr_count ? (int)eap->line2 : curwin->w_arg_idx + 1;
|
|
|
|
// Whether curbuf will be reused, curbuf->b_ffname will be set.
|
|
|
|
int curbuf_is_reusable = curbuf_reusable();
|
|
|
|
|
|
|
|
if (do_arglist(eap->arg, AL_ADD, i, TRUE) == FAIL)
|
|
|
|
return;
|
|
|
|
maketitle();
|
|
|
|
|
|
|
|
if (curwin->w_arg_idx == 0
|
|
|
|
&& (curbuf->b_ml.ml_flags & ML_EMPTY)
|
|
|
|
&& (curbuf->b_ffname == NULL || curbuf_is_reusable))
|
|
|
|
i = 0;
|
|
|
|
// Edit the argument.
|
|
|
|
if (i < ARGCOUNT)
|
|
|
|
do_argfile(eap, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":argadd"
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_argadd(exarg_T *eap)
|
|
|
|
{
|
|
|
|
do_arglist(eap->arg, AL_ADD,
|
|
|
|
eap->addr_count > 0 ? (int)eap->line2 : curwin->w_arg_idx + 1,
|
|
|
|
FALSE);
|
|
|
|
maketitle();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":argdelete"
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_argdelete(exarg_T *eap)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
int n;
|
|
|
|
|
2021-01-28 14:24:55 +01:00
|
|
|
if (check_arglist_locked() == FAIL)
|
|
|
|
return;
|
|
|
|
|
2020-08-17 19:34:10 +02:00
|
|
|
if (eap->addr_count > 0 || *eap->arg == NUL)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2020-08-30 19:26:45 +02:00
|
|
|
// ":argdel" works like ":.argdel"
|
2020-08-17 19:34:10 +02:00
|
|
|
if (eap->addr_count == 0)
|
|
|
|
{
|
|
|
|
if (curwin->w_arg_idx >= ARGCOUNT)
|
|
|
|
{
|
2021-12-31 19:59:55 +00:00
|
|
|
emsg(_(e_no_argument_to_delete));
|
2020-08-17 19:34:10 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
eap->line1 = eap->line2 = curwin->w_arg_idx + 1;
|
|
|
|
}
|
|
|
|
else if (eap->line2 > ARGCOUNT)
|
|
|
|
// ":1,4argdel": Delete all arguments in the range.
|
2019-08-17 14:38:55 +02:00
|
|
|
eap->line2 = ARGCOUNT;
|
|
|
|
n = eap->line2 - eap->line1 + 1;
|
|
|
|
if (*eap->arg != NUL)
|
|
|
|
// Can't have both a range and an argument.
|
2021-12-31 22:49:24 +00:00
|
|
|
emsg(_(e_invalid_argument));
|
2019-08-17 14:38:55 +02:00
|
|
|
else if (n <= 0)
|
|
|
|
{
|
|
|
|
// Don't give an error for ":%argdel" if the list is empty.
|
|
|
|
if (eap->line1 != 1 || eap->line2 != 0)
|
2021-06-27 22:03:33 +02:00
|
|
|
emsg(_(e_invalid_range));
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i = eap->line1; i <= eap->line2; ++i)
|
|
|
|
vim_free(ARGLIST[i - 1].ae_fname);
|
|
|
|
mch_memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2,
|
|
|
|
(size_t)((ARGCOUNT - eap->line2) * sizeof(aentry_T)));
|
|
|
|
ALIST(curwin)->al_ga.ga_len -= n;
|
|
|
|
if (curwin->w_arg_idx >= eap->line2)
|
|
|
|
curwin->w_arg_idx -= n;
|
|
|
|
else if (curwin->w_arg_idx > eap->line1)
|
|
|
|
curwin->w_arg_idx = eap->line1;
|
|
|
|
if (ARGCOUNT == 0)
|
|
|
|
curwin->w_arg_idx = 0;
|
|
|
|
else if (curwin->w_arg_idx >= ARGCOUNT)
|
|
|
|
curwin->w_arg_idx = ARGCOUNT - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
do_arglist(eap->arg, AL_DEL, 0, FALSE);
|
|
|
|
maketitle();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Function given to ExpandGeneric() to obtain the possible arguments of the
|
|
|
|
* argedit and argdelete commands.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
get_arglist_name(expand_T *xp UNUSED, int idx)
|
|
|
|
{
|
|
|
|
if (idx >= ARGCOUNT)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return alist_name(&ARGLIST[idx]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the file name for an argument list entry.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
alist_name(aentry_T *aep)
|
|
|
|
{
|
|
|
|
buf_T *bp;
|
|
|
|
|
|
|
|
// Use the name from the associated buffer if it exists.
|
|
|
|
bp = buflist_findnr(aep->ae_fnum);
|
|
|
|
if (bp == NULL || bp->b_fname == NULL)
|
|
|
|
return aep->ae_fname;
|
|
|
|
return bp->b_fname;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2022-09-05 18:27:47 +01:00
|
|
|
* State used by the :all command to open all the files in the argument list in
|
|
|
|
* separate windows.
|
2019-08-17 14:38:55 +02:00
|
|
|
*/
|
2022-09-05 18:27:47 +01:00
|
|
|
typedef struct {
|
|
|
|
alist_T *alist; // argument list to be used
|
|
|
|
int had_tab;
|
|
|
|
int keep_tabs;
|
|
|
|
int forceit;
|
|
|
|
|
|
|
|
int use_firstwin; // use first window for arglist
|
2019-08-17 14:38:55 +02:00
|
|
|
char_u *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[]
|
2022-09-05 18:27:47 +01:00
|
|
|
win_T *new_curwin;
|
|
|
|
tabpage_T *new_curtab;
|
|
|
|
} arg_all_state_T;
|
2019-08-17 14:38:55 +02:00
|
|
|
|
2022-09-05 18:27:47 +01:00
|
|
|
/*
|
|
|
|
* 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)
|
|
|
|
{
|
|
|
|
win_T *wp;
|
|
|
|
win_T *wpnext;
|
|
|
|
tabpage_T *tpnext;
|
|
|
|
buf_T *buf;
|
|
|
|
int i;
|
|
|
|
win_T *old_curwin;
|
|
|
|
tabpage_T *old_curtab;
|
2019-08-17 14:38:55 +02:00
|
|
|
|
|
|
|
old_curwin = curwin;
|
|
|
|
old_curtab = curtab;
|
|
|
|
|
2022-09-05 18:27:47 +01:00
|
|
|
if (aall->had_tab > 0)
|
2019-08-17 14:38:55 +02:00
|
|
|
goto_tabpage_tp(first_tabpage, TRUE, TRUE);
|
patch 9.0.2168: Moving tabpages on :drop may cause an endless loop
Problem: Moving tabpages on :drop may cause an endless loop
Solution: Disallow moving tabpages on :drop when cleaning up the arglist
first
Moving tabpages during drop command may cause an endless loop
When executing a :tab drop command, Vim will close all windows not in
the argument list. This triggers various autocommands. If a user has
created an 'au Tabenter * :tabmove -' autocommand, this can cause Vim to
end up in an endless loop, when trying to iterate over all tabs (which
would trigger the tabmove autocommand, which will change the tpnext
pointer, etc).
So instead of blocking all autocommands before we actually try to edit
the given file, lets simply disallow to move tabpages around. Otherwise,
we may change the expected number of events triggered during a :drop
command, which users may rely on (there is actually a test, that expects
various TabLeave/TabEnter autocommands) and would therefore be a
backwards incompatible change.
Don't make this an error, as this could trigger several times during the
drop command, but silently ignore the :tabmove command in this case (and
it should in fact finally trigger successfully when loading the given
file in a new tab). So let's just be quiet here instead.
fixes: #13676
closes: #13686
Signed-off-by: Christian Brabandt <cb@256bit.org>
2023-12-16 13:55:32 +01:00
|
|
|
|
|
|
|
// moving tabpages around in an autocommand may cause an endless loop
|
|
|
|
tabpage_move_disallowed++;
|
2019-08-17 14:38:55 +02:00
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
tpnext = curtab->tp_next;
|
|
|
|
for (wp = firstwin; wp != NULL; wp = wpnext)
|
|
|
|
{
|
|
|
|
wpnext = wp->w_next;
|
|
|
|
buf = wp->w_buffer;
|
|
|
|
if (buf->b_ffname == NULL
|
2022-09-05 18:27:47 +01:00
|
|
|
|| (!aall->keep_tabs && (buf->b_nwindows > 1
|
2019-08-17 14:38:55 +02:00
|
|
|
|| wp->w_width != Columns)))
|
2022-09-05 18:27:47 +01:00
|
|
|
i = aall->opened_len;
|
2019-08-17 14:38:55 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// check if the buffer in this window is in the arglist
|
2022-09-05 18:27:47 +01:00
|
|
|
for (i = 0; i < aall->opened_len; ++i)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
if (i < aall->alist->al_ga.ga_len
|
|
|
|
&& (AARGLIST(aall->alist)[i].ae_fnum == buf->b_fnum
|
2022-09-18 13:06:41 +01:00
|
|
|
|| fullpathcmp(alist_name(
|
|
|
|
&AARGLIST(aall->alist)[i]),
|
2019-08-17 14:38:55 +02:00
|
|
|
buf->b_ffname, TRUE, TRUE) & FPC_SAME))
|
|
|
|
{
|
|
|
|
int weight = 1;
|
|
|
|
|
|
|
|
if (old_curtab == curtab)
|
|
|
|
{
|
|
|
|
++weight;
|
|
|
|
if (old_curwin == wp)
|
|
|
|
++weight;
|
|
|
|
}
|
|
|
|
|
2022-09-05 18:27:47 +01:00
|
|
|
if (weight > (int)aall->opened[i])
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
aall->opened[i] = (char_u)weight;
|
2019-08-17 14:38:55 +02:00
|
|
|
if (i == 0)
|
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
if (aall->new_curwin != NULL)
|
2022-09-18 13:06:41 +01:00
|
|
|
aall->new_curwin->w_arg_idx =
|
|
|
|
aall->opened_len;
|
2022-09-05 18:27:47 +01:00
|
|
|
aall->new_curwin = wp;
|
|
|
|
aall->new_curtab = curtab;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
}
|
2022-09-05 18:27:47 +01:00
|
|
|
else if (aall->keep_tabs)
|
|
|
|
i = aall->opened_len;
|
2019-08-17 14:38:55 +02:00
|
|
|
|
2022-09-05 18:27:47 +01:00
|
|
|
if (wp->w_alist != aall->alist)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
|
|
|
// Use the current argument list for all windows
|
|
|
|
// containing a file from it.
|
|
|
|
alist_unlink(wp->w_alist);
|
2022-09-05 18:27:47 +01:00
|
|
|
wp->w_alist = aall->alist;
|
2019-08-17 14:38:55 +02:00
|
|
|
++wp->w_alist->al_refcount;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wp->w_arg_idx = i;
|
|
|
|
|
2022-09-05 18:27:47 +01:00
|
|
|
if (i == aall->opened_len && !aall->keep_tabs)// close this window
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
if (buf_hide(buf) || aall->forceit || buf->b_nwindows > 1
|
2019-08-17 14:38:55 +02:00
|
|
|
|| !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))
|
|
|
|
{
|
|
|
|
bufref_T bufref;
|
|
|
|
|
|
|
|
set_bufref(&bufref, buf);
|
|
|
|
|
|
|
|
(void)autowrite(buf, FALSE);
|
|
|
|
|
|
|
|
// check if autocommands removed the window
|
|
|
|
if (!win_valid(wp) || !bufref_valid(&bufref))
|
|
|
|
{
|
|
|
|
wpnext = firstwin; // start all over...
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// don't close last window
|
|
|
|
if (ONE_WINDOW
|
2022-09-05 18:27:47 +01:00
|
|
|
&& (first_tabpage->tp_next == NULL
|
|
|
|
|| !aall->had_tab))
|
|
|
|
aall->use_firstwin = TRUE;
|
2019-08-17 14:38:55 +02:00
|
|
|
else
|
|
|
|
{
|
|
|
|
win_close(wp, !buf_hide(buf) && !bufIsChanged(buf));
|
|
|
|
|
|
|
|
// check if autocommands removed the next window
|
|
|
|
if (!win_valid(wpnext))
|
|
|
|
wpnext = firstwin; // start all over...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Without the ":tab" modifier only do the current tab page.
|
2022-09-05 18:27:47 +01:00
|
|
|
if (aall->had_tab == 0 || tpnext == NULL)
|
2019-08-17 14:38:55 +02:00
|
|
|
break;
|
|
|
|
|
|
|
|
// check if autocommands removed the next tab page
|
|
|
|
if (!valid_tabpage(tpnext))
|
|
|
|
tpnext = first_tabpage; // start all over...
|
|
|
|
|
|
|
|
goto_tabpage_tp(tpnext, TRUE, TRUE);
|
|
|
|
}
|
patch 9.0.2168: Moving tabpages on :drop may cause an endless loop
Problem: Moving tabpages on :drop may cause an endless loop
Solution: Disallow moving tabpages on :drop when cleaning up the arglist
first
Moving tabpages during drop command may cause an endless loop
When executing a :tab drop command, Vim will close all windows not in
the argument list. This triggers various autocommands. If a user has
created an 'au Tabenter * :tabmove -' autocommand, this can cause Vim to
end up in an endless loop, when trying to iterate over all tabs (which
would trigger the tabmove autocommand, which will change the tpnext
pointer, etc).
So instead of blocking all autocommands before we actually try to edit
the given file, lets simply disallow to move tabpages around. Otherwise,
we may change the expected number of events triggered during a :drop
command, which users may rely on (there is actually a test, that expects
various TabLeave/TabEnter autocommands) and would therefore be a
backwards incompatible change.
Don't make this an error, as this could trigger several times during the
drop command, but silently ignore the :tabmove command in this case (and
it should in fact finally trigger successfully when loading the given
file in a new tab). So let's just be quiet here instead.
fixes: #13676
closes: #13686
Signed-off-by: Christian Brabandt <cb@256bit.org>
2023-12-16 13:55:32 +01:00
|
|
|
tabpage_move_disallowed--;
|
2022-09-05 18:27:47 +01:00
|
|
|
}
|
2019-08-17 14:38:55 +02:00
|
|
|
|
2022-09-05 18:27:47 +01:00
|
|
|
/*
|
|
|
|
* Open upto "count" windows for the files in the argument list 'aall->alist'.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
arg_all_open_windows(arg_all_state_T *aall, int count)
|
|
|
|
{
|
|
|
|
win_T *wp;
|
|
|
|
int tab_drop_empty_window = FALSE;
|
|
|
|
int i;
|
|
|
|
int split_ret = OK;
|
|
|
|
int p_ea_save;
|
2019-08-17 14:38:55 +02:00
|
|
|
|
2020-01-13 20:54:51 +01:00
|
|
|
// ":tab drop file" should re-use an empty window to avoid "--remote-tab"
|
2019-08-17 14:38:55 +02:00
|
|
|
// leaving an empty tab page when executed locally.
|
2022-09-05 18:27:47 +01:00
|
|
|
if (aall->keep_tabs && BUFEMPTY() && curbuf->b_nwindows == 1
|
2019-08-17 14:38:55 +02:00
|
|
|
&& curbuf->b_ffname == NULL && !curbuf->b_changed)
|
2020-01-13 20:54:51 +01:00
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
aall->use_firstwin = TRUE;
|
2020-01-13 20:54:51 +01:00
|
|
|
tab_drop_empty_window = TRUE;
|
|
|
|
}
|
2019-08-17 14:38:55 +02:00
|
|
|
|
2020-01-13 20:54:51 +01:00
|
|
|
for (i = 0; i < count && !got_int; ++i)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
if (aall->alist == &global_alist && i == global_alist.al_ga.ga_len - 1)
|
2019-08-17 14:38:55 +02:00
|
|
|
arg_had_last = TRUE;
|
2022-09-05 18:27:47 +01:00
|
|
|
if (aall->opened[i] > 0)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
|
|
|
// Move the already present window to below the current window
|
|
|
|
if (curwin->w_arg_idx != i)
|
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
FOR_ALL_WINDOWS(wp)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
if (wp->w_arg_idx == i)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
if (aall->keep_tabs)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
aall->new_curwin = wp;
|
|
|
|
aall->new_curtab = curtab;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
2022-09-05 18:27:47 +01:00
|
|
|
else if (wp->w_frame->fr_parent
|
|
|
|
!= curwin->w_frame->fr_parent)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2021-12-31 19:59:55 +00:00
|
|
|
emsg(_(e_window_layout_changed_unexpectedly));
|
2019-08-17 14:38:55 +02:00
|
|
|
i = count;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else
|
2022-09-05 18:27:47 +01:00
|
|
|
win_move_after(wp, curwin);
|
2019-08-17 14:38:55 +02:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (split_ret == OK)
|
|
|
|
{
|
2020-01-13 20:54:51 +01:00
|
|
|
// trigger events for tab drop
|
|
|
|
if (tab_drop_empty_window && i == count - 1)
|
|
|
|
--autocmd_no_enter;
|
2022-09-05 18:27:47 +01:00
|
|
|
if (!aall->use_firstwin) // split current window
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
|
|
|
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;
|
|
|
|
if (split_ret == FAIL)
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else // first window: do autocmd for leaving this buffer
|
|
|
|
--autocmd_no_leave;
|
|
|
|
|
|
|
|
// edit file "i"
|
|
|
|
curwin->w_arg_idx = i;
|
|
|
|
if (i == 0)
|
|
|
|
{
|
2022-09-05 18:27:47 +01:00
|
|
|
aall->new_curwin = curwin;
|
|
|
|
aall->new_curtab = curtab;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
2022-09-05 18:27:47 +01:00
|
|
|
(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, curwin);
|
2020-01-13 20:54:51 +01:00
|
|
|
if (tab_drop_empty_window && i == count - 1)
|
|
|
|
++autocmd_no_enter;
|
2022-09-05 18:27:47 +01:00
|
|
|
if (aall->use_firstwin)
|
2019-08-17 14:38:55 +02:00
|
|
|
++autocmd_no_leave;
|
2022-09-05 18:27:47 +01:00
|
|
|
aall->use_firstwin = FALSE;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
ui_breakcheck();
|
|
|
|
|
|
|
|
// When ":tab" was used open a new tab for a new window repeatedly.
|
2022-09-05 18:27:47 +01:00
|
|
|
if (aall->had_tab > 0 && tabpage_index(NULL) <= p_tpm)
|
2020-10-24 20:49:43 +02:00
|
|
|
cmdmod.cmod_tab = 9999;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
2022-09-05 18:27:47 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* do_arg_all(): Open up to "count" windows, one for each argument.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
do_arg_all(
|
|
|
|
int count,
|
|
|
|
int forceit, // hide buffers in current windows
|
|
|
|
int keep_tabs) // keep current tabs, for ":tab drop file"
|
|
|
|
{
|
|
|
|
arg_all_state_T aall;
|
|
|
|
win_T *last_curwin;
|
|
|
|
tabpage_T *last_curtab;
|
|
|
|
int prev_arglist_locked = arglist_locked;
|
|
|
|
|
|
|
|
if (cmdwin_type != 0)
|
|
|
|
{
|
|
|
|
emsg(_(e_invalid_in_cmdline_window));
|
|
|
|
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();
|
|
|
|
|
|
|
|
aall.use_firstwin = FALSE;
|
|
|
|
aall.had_tab = cmdmod.cmod_tab;
|
|
|
|
aall.new_curwin = NULL;
|
|
|
|
aall.new_curtab = NULL;
|
|
|
|
aall.forceit = forceit;
|
|
|
|
aall.keep_tabs = keep_tabs;
|
|
|
|
aall.opened_len = ARGCOUNT;
|
|
|
|
aall.opened = alloc_clear(aall.opened_len);
|
|
|
|
if (aall.opened == NULL)
|
|
|
|
return;
|
|
|
|
|
|
|
|
// 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 being changed.
|
|
|
|
aall.alist = curwin->w_alist;
|
|
|
|
++aall.alist->al_refcount;
|
|
|
|
arglist_locked = TRUE;
|
|
|
|
|
|
|
|
#ifdef FEAT_GUI
|
|
|
|
need_mouse_correct = TRUE;
|
|
|
|
#endif
|
|
|
|
|
2023-04-20 18:07:57 +01:00
|
|
|
tabpage_T *new_lu_tp = curtab;
|
|
|
|
|
2022-09-05 18:27:47 +01:00
|
|
|
// 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);
|
2019-08-17 14:38:55 +02:00
|
|
|
|
|
|
|
// Remove the "lock" on the argument list.
|
2022-09-05 18:27:47 +01:00
|
|
|
alist_unlink(aall.alist);
|
2021-12-24 18:11:27 +00:00
|
|
|
arglist_locked = prev_arglist_locked;
|
2019-08-17 14:38:55 +02:00
|
|
|
|
|
|
|
--autocmd_no_enter;
|
|
|
|
|
|
|
|
// restore last referenced tabpage's curwin
|
2022-09-05 18:27:47 +01:00
|
|
|
if (last_curtab != aall.new_curtab)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
|
|
|
if (valid_tabpage(last_curtab))
|
|
|
|
goto_tabpage_tp(last_curtab, TRUE, TRUE);
|
|
|
|
if (win_valid(last_curwin))
|
|
|
|
win_enter(last_curwin, FALSE);
|
|
|
|
}
|
|
|
|
// to window with first arg
|
2022-09-05 18:27:47 +01:00
|
|
|
if (valid_tabpage(aall.new_curtab))
|
|
|
|
goto_tabpage_tp(aall.new_curtab, TRUE, TRUE);
|
2024-02-09 19:30:26 +01:00
|
|
|
|
|
|
|
// Now set the last used tabpage to where we started.
|
|
|
|
if (valid_tabpage(new_lu_tp))
|
|
|
|
lastused_tabpage = new_lu_tp;
|
|
|
|
|
2022-09-05 18:27:47 +01:00
|
|
|
if (win_valid(aall.new_curwin))
|
|
|
|
win_enter(aall.new_curwin, FALSE);
|
2019-08-17 14:38:55 +02:00
|
|
|
|
|
|
|
--autocmd_no_leave;
|
2022-09-05 18:27:47 +01:00
|
|
|
vim_free(aall.opened);
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* ":all" and ":sall".
|
|
|
|
* Also used for ":tab drop file ..." after setting the argument list.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ex_all(exarg_T *eap)
|
|
|
|
{
|
|
|
|
if (eap->addr_count == 0)
|
|
|
|
eap->line2 = 9999;
|
|
|
|
do_arg_all((int)eap->line2, eap->forceit, eap->cmdidx == CMD_drop);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Concatenate all files in the argument list, separated by spaces, and return
|
|
|
|
* it in one allocated string.
|
|
|
|
* Spaces and backslashes in the file names are escaped with a backslash.
|
|
|
|
* Returns NULL when out of memory.
|
|
|
|
*/
|
|
|
|
char_u *
|
|
|
|
arg_all(void)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
int idx;
|
|
|
|
char_u *retval = NULL;
|
|
|
|
char_u *p;
|
|
|
|
|
|
|
|
// Do this loop two times:
|
|
|
|
// first time: compute the total length
|
|
|
|
// second time: concatenate the names
|
|
|
|
for (;;)
|
|
|
|
{
|
|
|
|
len = 0;
|
|
|
|
for (idx = 0; idx < ARGCOUNT; ++idx)
|
|
|
|
{
|
|
|
|
p = alist_name(&ARGLIST[idx]);
|
2022-07-31 18:34:32 +01:00
|
|
|
if (p == NULL)
|
|
|
|
continue;
|
|
|
|
if (len > 0)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-07-31 18:34:32 +01:00
|
|
|
// insert a space in between names
|
|
|
|
if (retval != NULL)
|
|
|
|
retval[len] = ' ';
|
|
|
|
++len;
|
|
|
|
}
|
|
|
|
for ( ; *p != NUL; ++p)
|
|
|
|
{
|
|
|
|
if (*p == ' '
|
2019-08-17 14:38:55 +02:00
|
|
|
#ifndef BACKSLASH_IN_FILENAME
|
2022-07-31 18:34:32 +01:00
|
|
|
|| *p == '\\'
|
2019-08-17 14:38:55 +02:00
|
|
|
#endif
|
2022-07-31 18:34:32 +01:00
|
|
|
|| *p == '`')
|
|
|
|
{
|
|
|
|
// insert a backslash
|
2019-08-17 14:38:55 +02:00
|
|
|
if (retval != NULL)
|
2022-07-31 18:34:32 +01:00
|
|
|
retval[len] = '\\';
|
2019-08-17 14:38:55 +02:00
|
|
|
++len;
|
|
|
|
}
|
2022-07-31 18:34:32 +01:00
|
|
|
if (retval != NULL)
|
|
|
|
retval[len] = *p;
|
|
|
|
++len;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// second time: break here
|
|
|
|
if (retval != NULL)
|
|
|
|
{
|
|
|
|
retval[len] = NUL;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// allocate memory
|
|
|
|
retval = alloc(len + 1);
|
|
|
|
if (retval == NULL)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if defined(FEAT_EVAL) || defined(PROTO)
|
|
|
|
/*
|
|
|
|
* "argc([window id])" function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_argc(typval_T *argvars, typval_T *rettv)
|
|
|
|
{
|
|
|
|
win_T *wp;
|
|
|
|
|
2021-07-27 22:00:44 +02:00
|
|
|
if (in_vim9script() && check_for_opt_number_arg(argvars, 0) == FAIL)
|
|
|
|
return;
|
|
|
|
|
2019-08-17 14:38:55 +02:00
|
|
|
if (argvars[0].v_type == VAR_UNKNOWN)
|
|
|
|
// use the current window
|
|
|
|
rettv->vval.v_number = ARGCOUNT;
|
|
|
|
else if (argvars[0].v_type == VAR_NUMBER
|
|
|
|
&& tv_get_number(&argvars[0]) == -1)
|
|
|
|
// use the global argument list
|
|
|
|
rettv->vval.v_number = GARGCOUNT;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// use the argument list of the specified window
|
|
|
|
wp = find_win_by_nr_or_id(&argvars[0]);
|
|
|
|
if (wp != NULL)
|
|
|
|
rettv->vval.v_number = WARGCOUNT(wp);
|
|
|
|
else
|
|
|
|
rettv->vval.v_number = -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "argidx()" function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_argidx(typval_T *argvars UNUSED, typval_T *rettv)
|
|
|
|
{
|
|
|
|
rettv->vval.v_number = curwin->w_arg_idx;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "arglistid()" function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_arglistid(typval_T *argvars, typval_T *rettv)
|
|
|
|
{
|
|
|
|
win_T *wp;
|
|
|
|
|
2021-07-27 22:00:44 +02:00
|
|
|
if (in_vim9script()
|
|
|
|
&& (check_for_opt_number_arg(argvars, 0) == FAIL
|
|
|
|
|| (argvars[0].v_type != VAR_UNKNOWN
|
|
|
|
&& check_for_opt_number_arg(argvars, 1) == FAIL)))
|
|
|
|
return;
|
|
|
|
|
2019-08-17 14:38:55 +02:00
|
|
|
rettv->vval.v_number = -1;
|
|
|
|
wp = find_tabwin(&argvars[0], &argvars[1], NULL);
|
|
|
|
if (wp != NULL)
|
|
|
|
rettv->vval.v_number = wp->w_alist->id;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Get the argument list for a given window
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
get_arglist_as_rettv(aentry_T *arglist, int argcount, typval_T *rettv)
|
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
|
|
|
|
if (rettv_list_alloc(rettv) == OK && arglist != NULL)
|
|
|
|
for (idx = 0; idx < argcount; ++idx)
|
|
|
|
list_append_string(rettv->vval.v_list,
|
|
|
|
alist_name(&arglist[idx]), -1);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* "argv(nr)" function
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
f_argv(typval_T *argvars, typval_T *rettv)
|
|
|
|
{
|
|
|
|
int idx;
|
|
|
|
aentry_T *arglist = NULL;
|
|
|
|
int argcount = -1;
|
|
|
|
|
2021-07-27 22:00:44 +02:00
|
|
|
if (in_vim9script()
|
|
|
|
&& (check_for_opt_number_arg(argvars, 0) == FAIL
|
|
|
|
|| (argvars[0].v_type != VAR_UNKNOWN
|
|
|
|
&& check_for_opt_number_arg(argvars, 1) == FAIL)))
|
|
|
|
return;
|
|
|
|
|
2022-09-05 14:33:47 +01:00
|
|
|
if (argvars[0].v_type == VAR_UNKNOWN)
|
2019-08-17 14:38:55 +02:00
|
|
|
{
|
2022-09-05 14:33:47 +01:00
|
|
|
get_arglist_as_rettv(ARGLIST, ARGCOUNT, rettv);
|
|
|
|
return;
|
|
|
|
}
|
2019-08-17 14:38:55 +02:00
|
|
|
|
2022-09-05 14:33:47 +01:00
|
|
|
if (argvars[1].v_type == VAR_UNKNOWN)
|
|
|
|
{
|
|
|
|
arglist = ARGLIST;
|
|
|
|
argcount = ARGCOUNT;
|
|
|
|
}
|
|
|
|
else if (argvars[1].v_type == VAR_NUMBER
|
|
|
|
&& tv_get_number(&argvars[1]) == -1)
|
|
|
|
{
|
|
|
|
arglist = GARGLIST;
|
|
|
|
argcount = GARGCOUNT;
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
else
|
2022-09-05 14:33:47 +01:00
|
|
|
{
|
|
|
|
win_T *wp = find_win_by_nr_or_id(&argvars[1]);
|
|
|
|
|
|
|
|
if (wp != NULL)
|
|
|
|
{
|
|
|
|
// Use the argument list of the specified window
|
|
|
|
arglist = WARGLIST(wp);
|
|
|
|
argcount = WARGCOUNT(wp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
rettv->v_type = VAR_STRING;
|
|
|
|
rettv->vval.v_string = NULL;
|
|
|
|
idx = tv_get_number_chk(&argvars[0], NULL);
|
|
|
|
if (arglist != NULL && idx >= 0 && idx < argcount)
|
|
|
|
rettv->vval.v_string = vim_strsave(alist_name(&arglist[idx]));
|
|
|
|
else if (idx == -1)
|
|
|
|
get_arglist_as_rettv(arglist, argcount, rettv);
|
2019-08-17 14:38:55 +02:00
|
|
|
}
|
|
|
|
#endif
|