diff options
author | Bram Moolenaar <Bram@vim.org> | 2019-08-17 14:38:55 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2019-08-17 14:38:55 +0200 |
commit | 4ad62155a1015751a6645aaecd94b02c94c8934b (patch) | |
tree | 72d2dab4221986c394ca9f444dfe86cfabe51cfe | |
parent | 69cbbecf548f390197259ca30cfe147c3e59ce5a (diff) |
patch 8.1.1869: code for the argument list is spread outv8.1.1869
Problem: Code for the argument list is spread out.
Solution: Put argument list code in arglist.c. (Yegappan Lakshmanan,
closes #4819)
-rw-r--r-- | Filelist | 2 | ||||
-rw-r--r-- | src/Make_cyg_ming.mak | 1 | ||||
-rw-r--r-- | src/Make_morph.mak | 1 | ||||
-rw-r--r-- | src/Make_mvc.mak | 4 | ||||
-rw-r--r-- | src/Make_vms.mms | 49 | ||||
-rw-r--r-- | src/Makefile | 10 | ||||
-rw-r--r-- | src/README.md | 1 | ||||
-rw-r--r-- | src/arglist.c | 1320 | ||||
-rw-r--r-- | src/buffer.c | 301 | ||||
-rw-r--r-- | src/evalfunc.c | 114 | ||||
-rw-r--r-- | src/ex_cmds2.c | 649 | ||||
-rw-r--r-- | src/ex_docmd.c | 277 | ||||
-rw-r--r-- | src/proto.h | 1 | ||||
-rw-r--r-- | src/proto/arglist.pro | 32 | ||||
-rw-r--r-- | src/proto/buffer.pro | 2 | ||||
-rw-r--r-- | src/proto/ex_cmds2.pro | 14 | ||||
-rw-r--r-- | src/proto/ex_docmd.pro | 9 | ||||
-rw-r--r-- | src/version.c | 2 |
18 files changed, 1400 insertions, 1389 deletions
@@ -13,6 +13,7 @@ SRC_ALL = \ src/README.md \ src/alloc.h \ src/arabic.c \ + src/arglist.c \ src/ascii.h \ src/autocmd.c \ src/beval.c \ @@ -165,6 +166,7 @@ SRC_ALL = \ src/proto.h \ src/protodef.h \ src/proto/arabic.pro \ + src/proto/arglist.pro \ src/proto/autocmd.pro \ src/proto/beval.pro \ src/proto/blob.pro \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index c073b5f748..eb6d696868 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -703,6 +703,7 @@ GUIOBJ = $(OUTDIR)/gui.o $(OUTDIR)/gui_w32.o $(OUTDIR)/gui_beval.o CUIOBJ = $(OUTDIR)/iscygpty.o OBJ = \ $(OUTDIR)/arabic.o \ + $(OUTDIR)/arglist.o \ $(OUTDIR)/autocmd.o \ $(OUTDIR)/beval.o \ $(OUTDIR)/blob.o \ diff --git a/src/Make_morph.mak b/src/Make_morph.mak index 7616ce23b9..e478b307fb 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -25,6 +25,7 @@ RM = rm ${CC} ${CFLAGS} $< -o $@ SRC = arabic.c \ + arglist.c \ autocmd.c \ blowfish.c \ buffer.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 07ea52abdc..56dabe9be1 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -712,6 +712,7 @@ INCL = vim.h alloc.h ascii.h ex_cmds.h feature.h globals.h \ OBJ = \ $(OUTDIR)\arabic.obj \ + $(OUTDIR)\arglist.obj \ $(OUTDIR)\autocmd.obj \ $(OUTDIR)\beval.obj \ $(OUTDIR)\blob.obj \ @@ -1433,6 +1434,8 @@ $(NEW_TESTS): $(OUTDIR)/arabic.obj: $(OUTDIR) arabic.c $(INCL) +$(OUTDIR)/arglist.obj: $(OUTDIR) arglist.c $(INCL) + $(OUTDIR)/autocmd.obj: $(OUTDIR) autocmd.c $(INCL) $(OUTDIR)/beval.obj: $(OUTDIR) beval.c $(INCL) @@ -1744,6 +1747,7 @@ $(PATHDEF_SRC): Make_mvc.mak # End Custom Build proto.h: \ proto/arabic.pro \ + proto/arglist.pro \ proto/autocmd.pro \ proto/blob.pro \ proto/blowfish.pro \ diff --git a/src/Make_vms.mms b/src/Make_vms.mms index 4e55ad49e5..0504db7ab4 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -307,32 +307,34 @@ ALL_CFLAGS_VER = /def=($(MODEL_DEF)$(DEFS)$(DEBUG_DEF)$(PERL_DEF)$(PYTHON_DEF) - ALL_LIBS = $(LIBS) $(GUI_LIB_DIR) $(GUI_LIB) \ $(PERL_LIB) $(PYTHON_LIB) $(TCL_LIB) $(RUBY_LIB) -SRC = arabic.c autocmd.c beval.c blob.c blowfish.c buffer.c change.c charset.c \ - cmdhist.c crypt.c crypt_zip.c debugger.c dict.c diff.c digraph.c edit.c eval.c \ - evalfunc.c ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c ex_getln.c \ - if_cscope.c if_xcmdsrv.c fileio.c findfile.c fold.c getchar.c \ - hardcopy.c hashtab.c highlight.c indent.c insexpand.c json.c list.c \ - main.c map.c mark.c menu.c mbyte.c memfile.c memline.c message.c \ - misc1.c misc2.c move.c normal.c ops.c option.c popupmnu.c popupwin.c \ - profiler.c quickfix.c regexp.c search.c session.c sha256.c sign.c \ - spell.c spellfile.c syntax.c tag.c term.c termlib.c testing.c \ - textprop.c ui.c undo.c usercmd.c userfunc.c version.c viminfo.c \ - screen.c window.c os_unix.c os_vms.c pathdef.c \ +SRC = arabic.c arglist.c autocmd.c beval.c blob.c blowfish.c buffer.c \ + change.c charset.c cmdhist.c crypt.c crypt_zip.c debugger.c dict.c \ + diff.c digraph.c edit.c eval.c evalfunc.c ex_cmds.c ex_cmds2.c \ + ex_docmd.c ex_eval.c ex_getln.c if_cscope.c if_xcmdsrv.c fileio.c \ + findfile.c fold.c getchar.c hardcopy.c hashtab.c highlight.c indent.c \ + insexpand.c json.c list.c main.c map.c mark.c menu.c mbyte.c \ + memfile.c memline.c message.c misc1.c misc2.c move.c normal.c ops.c \ + option.c popupmnu.c popupwin.c profiler.c quickfix.c regexp.c \ + search.c session.c sha256.c sign.c spell.c spellfile.c syntax.c tag.c \ + term.c termlib.c testing.c textprop.c ui.c undo.c usercmd.c \ + userfunc.c version.c viminfo.c screen.c window.c os_unix.c os_vms.c \ + pathdef.c \ $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \ $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC) -OBJ = arabic.obj autocmd.obj beval.obj blob.obj blowfish.obj buffer.obj change.obj \ - charset.obj cmdhist.obj crypt.obj crypt_zip.obj debugger.obj dict.obj diff.obj \ - digraph.obj edit.obj eval.obj evalfunc.obj ex_cmds.obj ex_cmds2.obj \ - ex_docmd.obj ex_eval.obj ex_getln.obj if_cscope.obj if_xcmdsrv.obj \ - fileio.obj findfile.obj fold.obj getchar.obj hardcopy.obj hashtab.obj \ - highlight.obj indent.obj insexpand.obj json.obj list.obj main.obj \ - map.obj mark.obj menu.obj memfile.obj memline.obj message.obj \ - misc1.obj misc2.obj move.obj mbyte.obj normal.obj ops.obj option.obj \ - popupmnu.obj popupwin.obj profiler.obj quickfix.obj regexp.obj \ - search.obj session.obj sha256.obj sign.obj spell.obj spellfile.obj \ - syntax.obj tag.obj term.obj termlib.obj testing.obj textprop.obj \ - ui.obj undo.obj usercmd.obj userfunc.obj screen.obj version.obj \ +OBJ = arabic.obj arglist.obj autocmd.obj beval.obj blob.obj blowfish.obj \ + buffer.obj change.obj charset.obj cmdhist.obj crypt.obj crypt_zip.obj \ + debugger.obj dict.obj diff.obj digraph.obj edit.obj eval.obj \ + evalfunc.obj ex_cmds.obj ex_cmds2.obj ex_docmd.obj ex_eval.obj \ + ex_getln.obj if_cscope.obj if_xcmdsrv.obj fileio.obj findfile.obj \ + fold.obj getchar.obj hardcopy.obj hashtab.obj highlight.obj \ + indent.obj insexpand.obj json.obj list.obj main.obj map.obj mark.obj \ + menu.obj memfile.obj memline.obj message.obj misc1.obj misc2.obj \ + move.obj mbyte.obj normal.obj ops.obj option.obj popupmnu.obj \ + popupwin.obj profiler.obj quickfix.obj regexp.obj search.obj \ + session.obj sha256.obj sign.obj spell.obj spellfile.obj syntax.obj \ + tag.obj term.obj termlib.obj testing.obj textprop.obj ui.obj undo.obj \ + usercmd.obj userfunc.obj screen.obj version.obj \ viminfo.obj window.obj os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \ $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) @@ -505,6 +507,7 @@ ruby_env : .ENDIF arabic.obj : arabic.c vim.h +arglist.obj : arglist.c vim.h [.auto]config.h feature.h os_unix.h autocmd.obj : autocmd.c vim.h [.auto]config.h feature.h os_unix.h blowfish.obj : blowfish.c vim.h [.auto]config.h feature.h os_unix.h blob.obj : blob.c vim.h [.auto]config.h feature.h os_unix.h diff --git a/src/Makefile b/src/Makefile index 9eff349179..5697c28bdb 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1577,6 +1577,7 @@ include testdir/Make_all.mak BASIC_SRC = \ arabic.c \ + arglist.c \ autocmd.c \ beval.c \ blob.c \ @@ -1704,6 +1705,7 @@ LINT_SRC = $(BASIC_SRC) $(GUI_SRC) $(HANGULIN_SRC) \ OBJ_COMMON = \ objects/arabic.o \ + objects/arglist.o \ objects/autocmd.o \ objects/beval.o \ objects/buffer.o \ @@ -1844,6 +1846,7 @@ ALL_OBJ = $(OBJ_COMMON) \ PRO_AUTO = \ arabic.pro \ + arglist.pro \ autocmd.pro \ blowfish.pro \ buffer.pro \ @@ -2989,6 +2992,9 @@ $(ALL_OBJ): objects/.dirstamp objects/arabic.o: arabic.c $(CCC) -o $@ arabic.c +objects/arglist.o: arglist.c + $(CCC) -o $@ arglist.c + objects/autocmd.o: autocmd.c $(CCC) -o $@ autocmd.c @@ -3476,6 +3482,10 @@ objects/arabic.o: arabic.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h +objects/arglist.o: arglist.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h objects/autocmd.o: autocmd.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ diff --git a/src/README.md b/src/README.md index 2e774907ef..ffce497a5a 100644 --- a/src/README.md +++ b/src/README.md @@ -23,6 +23,7 @@ Most code can be found in a file with an obvious name (incomplete list): File name | Description --------------- | ----------- +arglist.c | handling argument list autocmd.c | autocommands blob.c | blob data type buffer.c | manipulating buffers (loaded files) diff --git a/src/arglist.c b/src/arglist.c new file mode 100644 index 0000000000..b8f2e2a6f2 --- /dev/null +++ b/src/arglist.c @@ -0,0 +1,1320 @@ +/* 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 + +/* + * Clear an argument list: free all file names and reset it to zero entries. + */ + void +alist_clear(alist_T *al) +{ + 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) +{ + ga_init2(&al->al_ga, (int)sizeof(aentry_T), 5); +} + +/* + * 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; + + // 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; + old_arg_files = ALLOC_MULT(char_u *, GARGCOUNT); + if (old_arg_files != NULL) + { + 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) + { + alist_set(&global_alist, new_arg_file_count, new_arg_files, + TRUE, fnum_list, fnum_len); + FreeWild(old_arg_count, old_arg_files); + } + } + 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; + static int recursive = 0; + + if (recursive) + { + emsg(_(e_au_recursive)); + return; + } + ++recursive; + + alist_clear(al); + if (ga_grow(&al->al_ga, count) == OK) + { + 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) + buf_set_name(fnum_list[i], files[i]); + + 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; + + --recursive; +} + +/* + * Add file "fname" to argument list "al". + * "fname" must have been allocated and "al" must have been checked for room. + */ + 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; +#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; +} + +#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) +{ + ga_init2(gap, (int)sizeof(char_u *), 20); + 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; +} + +#if defined(FEAT_QUICKFIX) || defined(FEAT_SYN_HL) || defined(PROTO) +/* + * 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, + fcountp, fnamesp, EW_FILE|EW_NOTFOUND); + else + i = gen_expand_wildcards(ga.ga_len, (char_u **)ga.ga_data, + fcountp, fnamesp, EW_FILE|EW_NOTFOUND); + + 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; + + if (ga_grow(&ALIST(curwin)->al_ga, count) == OK) + { + 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)); + 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); + } + 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]); +} + +/* + * "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; + char_u *p; + int match; + int arg_escaped = TRUE; + + // 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) + { + regmatch_T regmatch; + int didone; + + // 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 < new_ga.ga_len && !got_int; ++i) + { + p = ((char_u **)new_ga.ga_data)[i]; + p = file_pat_to_reg_pat(p, NULL, NULL, FALSE); + if (p == NULL) + break; + regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0); + if (regmatch.regprog == NULL) + { + vim_free(p); + break; + } + + didone = FALSE; + for (match = 0; match < ARGCOUNT; ++match) + if (vim_regexec(®match, alist_name(&ARGLIST[match]), + (colnr_T)0)) + { + 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_nomatch2), ((char_u **)new_ga.ga_data)[i]); + } + ga_clear(&new_ga); + } + 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) + { + emsg(_(e_nomatch)); + 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) +{ + do_arglist(str, AL_SET, 0, FALSE); +} + +/* + * 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) + { + alist_unlink(ALIST(curwin)); + if (eap->cmdidx == CMD_argglobal) + ALIST(curwin) = &global_alist; + else // eap->cmdidx == CMD_arglocal + alist_new(); + } + + 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) + { + char_u **items = ALLOC_MULT(char_u *, ARGCOUNT); + + if (items != NULL) + { + // 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); + } + } + } + else if (eap->cmdidx == CMD_arglocal) + { + garray_T *gap = &curwin->w_alist->al_ga; + + // ":argslocal": make a local copy of the global argument list. + if (ga_grow(gap, GARGCOUNT) == OK) + 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; + } + } +} + +/* + * ":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; + + if (ERROR_IF_POPUP_WINDOW) + return; + if (argn < 0 || argn >= ARGCOUNT) + { + if (ARGCOUNT <= 1) + emsg(_("E163: There is only one file to edit")); + else if (argn < 0) + emsg(_("E164: Cannot go before first file")); + else + emsg(_("E165: Cannot go beyond last file")); + } + else + { + setpcmark(); +#ifdef FEAT_GUI + need_mouse_correct = TRUE; +#endif + + // split window or create new tab page first + if (*eap->cmd == 's' || cmdmod.tab != 0) + { + 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)) + { + p = fix_fname(alist_name(&ARGLIST[argn])); + other = otherfile(p); + vim_free(p); + } + if ((!buf_hide(curbuf) || !other) + && check_changed(curbuf, CCGD_AW + | (other ? 0 : CCGD_MULTWIN) + | (eap->forceit ? CCGD_FORCEIT : 0) + | CCGD_EXCMD)) + return; + } + + 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('\''); + } +} + +/* + * ":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); + } +} + +/* + * ":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; +#ifdef FEAT_TITLE + maketitle(); +#endif + + 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); +#ifdef FEAT_TITLE + maketitle(); +#endif +} + +/* + * ":argdelete" + */ + void +ex_argdelete(exarg_T *eap) +{ + int i; + int n; + + if (eap->addr_count > 0) + { + // ":1,4argdel": Delete all arguments in the range. + if (eap->line2 > ARGCOUNT) + eap->line2 = ARGCOUNT; + n = eap->line2 - eap->line1 + 1; + if (*eap->arg != NUL) + // Can't have both a range and an argument. + emsg(_(e_invarg)); + else if (n <= 0) + { + // Don't give an error for ":%argdel" if the list is empty. + if (eap->line1 != 1 || eap->line2 != 0) + emsg(_(e_invrange)); + } + 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 if (*eap->arg == NUL) + emsg(_(e_argreq)); + else + do_arglist(eap->arg, AL_DEL, 0, FALSE); +#ifdef FEAT_TITLE + maketitle(); +#endif +} + +#if d |