diff options
Diffstat (limited to 'src/nvim/ex_cmds2.c')
-rw-r--r-- | src/nvim/ex_cmds2.c | 1867 |
1 files changed, 6 insertions, 1861 deletions
diff --git a/src/nvim/ex_cmds2.c b/src/nvim/ex_cmds2.c index 730a2f1b69..54315a6417 100644 --- a/src/nvim/ex_cmds2.c +++ b/src/nvim/ex_cmds2.c @@ -11,30 +11,27 @@ #include <stdbool.h> #include <string.h> +#include "nvim/arglist.h" #include "nvim/ascii.h" +#include "nvim/autocmd.h" #include "nvim/globals.h" #include "nvim/vim.h" #ifdef HAVE_LOCALE_H # include <locale.h> #endif -#include "nvim/api/private/defs.h" -#include "nvim/api/private/helpers.h" #include "nvim/buffer.h" #include "nvim/change.h" #include "nvim/charset.h" -#include "nvim/debugger.h" -#include "nvim/eval/userfunc.h" +#include "nvim/eval.h" #include "nvim/eval/vars.h" #include "nvim/ex_cmds.h" #include "nvim/ex_cmds2.h" +#include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" #include "nvim/ex_getln.h" #include "nvim/fileio.h" -#include "nvim/garray.h" -#include "nvim/lua/executor.h" #include "nvim/mark.h" #include "nvim/mbyte.h" -#include "nvim/memline.h" #include "nvim/memory.h" #include "nvim/message.h" #include "nvim/move.h" @@ -42,101 +39,19 @@ #include "nvim/ops.h" #include "nvim/option.h" #include "nvim/os/fs_defs.h" -#include "nvim/os/input.h" #include "nvim/os/shell.h" #include "nvim/os_unix.h" #include "nvim/path.h" -#include "nvim/profile.h" #include "nvim/quickfix.h" -#include "nvim/regexp.h" +#include "nvim/runtime.h" #include "nvim/strings.h" #include "nvim/undo.h" -#include "nvim/version.h" #include "nvim/window.h" -/// Growarray to store info about already sourced scripts. -static garray_T script_items = { 0, 0, sizeof(scriptitem_T), 4, NULL }; -#define SCRIPT_ITEM(id) (((scriptitem_T *)script_items.ga_data)[(id) - 1]) - -// Struct used in sn_prl_ga for every line of a script. -typedef struct sn_prl_S { - int snp_count; ///< nr of times line was executed - proftime_T sn_prl_total; ///< time spent in a line + children - proftime_T sn_prl_self; ///< time spent in a line itself -} sn_prl_T; - -/// Structure used to store info for each sourced file. -/// It is shared between do_source() and getsourceline(). -/// This is required, because it needs to be handed to do_cmdline() and -/// sourcing can be done recursively. -struct source_cookie { - FILE *fp; ///< opened file for sourcing - char *nextline; ///< if not NULL: line that was read ahead - linenr_T sourcing_lnum; ///< line number of the source file - int finished; ///< ":finish" used -#if defined(USE_CRNL) - int fileformat; ///< EOL_UNKNOWN, EOL_UNIX or EOL_DOS - bool error; ///< true if LF found after CR-LF -#endif - linenr_T breakpoint; ///< next line with breakpoint or zero - char *fname; ///< name of sourced file - int dbg_tick; ///< debug_tick when breakpoint was set - int level; ///< top nesting level of sourced file - vimconv_T conv; ///< type of conversion -}; - -#define PRL_ITEM(si, idx) (((sn_prl_T *)(si)->sn_prl_ga.ga_data)[(idx)]) - #ifdef INCLUDE_GENERATED_DECLARATIONS # include "ex_cmds2.c.generated.h" #endif -static char *profile_fname = NULL; - -/// ":profile cmd args" -void ex_profile(exarg_T *eap) -{ - static proftime_T pause_time; - - char *e; - int len; - - e = (char *)skiptowhite((char_u *)eap->arg); - len = (int)(e - eap->arg); - e = skipwhite(e); - - if (len == 5 && STRNCMP(eap->arg, "start", 5) == 0 && *e != NUL) { - xfree(profile_fname); - profile_fname = (char *)expand_env_save_opt((char_u *)e, true); - do_profiling = PROF_YES; - profile_set_wait(profile_zero()); - set_vim_var_nr(VV_PROFILING, 1L); - } else if (do_profiling == PROF_NONE) { - emsg(_("E750: First use \":profile start {fname}\"")); - } else if (STRCMP(eap->arg, "stop") == 0) { - profile_dump(); - do_profiling = PROF_NONE; - set_vim_var_nr(VV_PROFILING, 0L); - profile_reset(); - } else if (STRCMP(eap->arg, "pause") == 0) { - if (do_profiling == PROF_YES) { - pause_time = profile_start(); - } - do_profiling = PROF_PAUSED; - } else if (STRCMP(eap->arg, "continue") == 0) { - if (do_profiling == PROF_PAUSED) { - pause_time = profile_end(pause_time); - profile_set_wait(profile_add(profile_get_wait(), pause_time)); - } - do_profiling = PROF_YES; - } else if (STRCMP(eap->arg, "dump") == 0) { - profile_dump(); - } else { - // The rest is similar to ":breakadd". - ex_breakadd(eap); - } -} - void ex_ruby(exarg_T *eap) { script_host_execute("ruby", eap); @@ -182,273 +97,6 @@ void ex_perldo(exarg_T *eap) script_host_do_range("perl", eap); } -// Command line expansion for :profile. -static enum { - PEXP_SUBCMD, ///< expand :profile sub-commands - PEXP_FUNC, ///< expand :profile func {funcname} -} pexpand_what; - -static char *pexpand_cmds[] = { - "continue", - "dump", - "file", - "func", - "pause", - "start", - "stop", - NULL -}; - -/// Function given to ExpandGeneric() to obtain the profile command -/// specific expansion. -char *get_profile_name(expand_T *xp, int idx) - FUNC_ATTR_PURE -{ - switch (pexpand_what) { - case PEXP_SUBCMD: - return pexpand_cmds[idx]; - // case PEXP_FUNC: TODO - default: - return NULL; - } -} - -/// Handle command line completion for :profile command. -void set_context_in_profile_cmd(expand_T *xp, const char *arg) -{ - // Default: expand subcommands. - xp->xp_context = EXPAND_PROFILE; - pexpand_what = PEXP_SUBCMD; - xp->xp_pattern = (char *)arg; - - char_u *const end_subcmd = skiptowhite((const char_u *)arg); - if (*end_subcmd == NUL) { - return; - } - - if ((const char *)end_subcmd - arg == 5 && strncmp(arg, "start", 5) == 0) { - xp->xp_context = EXPAND_FILES; - xp->xp_pattern = skipwhite((char *)end_subcmd); - return; - } - - // TODO(tarruda): expand function names after "func" - xp->xp_context = EXPAND_NOTHING; -} - -/// Dump the profiling info. -void profile_dump(void) -{ - FILE *fd; - - if (profile_fname != NULL) { - fd = os_fopen(profile_fname, "w"); - if (fd == NULL) { - semsg(_(e_notopen), profile_fname); - } else { - script_dump_profile(fd); - func_dump_profile(fd); - fclose(fd); - } - } -} - -/// Reset all profiling information. -static void profile_reset(void) -{ - // Reset sourced files. - for (int id = 1; id <= script_items.ga_len; id++) { - scriptitem_T *si = &SCRIPT_ITEM(id); - if (si->sn_prof_on) { - si->sn_prof_on = false; - si->sn_pr_force = false; - si->sn_pr_child = profile_zero(); - si->sn_pr_nest = 0; - si->sn_pr_count = 0; - si->sn_pr_total = profile_zero(); - si->sn_pr_self = profile_zero(); - si->sn_pr_start = profile_zero(); - si->sn_pr_children = profile_zero(); - ga_clear(&si->sn_prl_ga); - si->sn_prl_start = profile_zero(); - si->sn_prl_children = profile_zero(); - si->sn_prl_wait = profile_zero(); - si->sn_prl_idx = -1; - si->sn_prl_execed = 0; - } - } - - // Reset functions. - size_t n = func_hashtab.ht_used; - hashitem_T *hi = func_hashtab.ht_array; - - for (; n > (size_t)0; hi++) { - if (!HASHITEM_EMPTY(hi)) { - n--; - ufunc_T *uf = HI2UF(hi); - if (uf->uf_prof_initialized) { - uf->uf_profiling = 0; - uf->uf_tm_count = 0; - uf->uf_tm_total = profile_zero(); - uf->uf_tm_self = profile_zero(); - uf->uf_tm_children = profile_zero(); - - for (int i = 0; i < uf->uf_lines.ga_len; i++) { - uf->uf_tml_count[i] = 0; - uf->uf_tml_total[i] = uf->uf_tml_self[i] = 0; - } - - uf->uf_tml_start = profile_zero(); - uf->uf_tml_children = profile_zero(); - uf->uf_tml_wait = profile_zero(); - uf->uf_tml_idx = -1; - uf->uf_tml_execed = 0; - } - } - } - - XFREE_CLEAR(profile_fname); -} - -/// Start profiling a script. -static void profile_init(scriptitem_T *si) -{ - si->sn_pr_count = 0; - si->sn_pr_total = profile_zero(); - si->sn_pr_self = profile_zero(); - - ga_init(&si->sn_prl_ga, sizeof(sn_prl_T), 100); - si->sn_prl_idx = -1; - si->sn_prof_on = true; - si->sn_pr_nest = 0; -} - -/// Save time when starting to invoke another script or function. -/// -/// @param tm place to store wait time -void script_prof_save(proftime_T *tm) -{ - scriptitem_T *si; - - if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) { - si = &SCRIPT_ITEM(current_sctx.sc_sid); - if (si->sn_prof_on && si->sn_pr_nest++ == 0) { - si->sn_pr_child = profile_start(); - } - } - *tm = profile_get_wait(); -} - -/// Count time spent in children after invoking another script or function. -void script_prof_restore(proftime_T *tm) -{ - scriptitem_T *si; - - if (current_sctx.sc_sid > 0 && current_sctx.sc_sid <= script_items.ga_len) { - si = &SCRIPT_ITEM(current_sctx.sc_sid); - if (si->sn_prof_on && --si->sn_pr_nest == 0) { - si->sn_pr_child = profile_end(si->sn_pr_child); - // don't count wait time - si->sn_pr_child = profile_sub_wait(*tm, si->sn_pr_child); - si->sn_pr_children = profile_add(si->sn_pr_children, si->sn_pr_child); - si->sn_prl_children = profile_add(si->sn_prl_children, si->sn_pr_child); - } - } -} - -static proftime_T inchar_time; - -/// Called when starting to wait for the user to type a character. -void prof_inchar_enter(void) -{ - inchar_time = profile_start(); -} - -/// Called when finished waiting for the user to type a character. -void prof_inchar_exit(void) -{ - inchar_time = profile_end(inchar_time); - profile_set_wait(profile_add(profile_get_wait(), inchar_time)); -} - -/// Dump the profiling results for all scripts in file "fd". -static void script_dump_profile(FILE *fd) -{ - scriptitem_T *si; - FILE *sfd; - sn_prl_T *pp; - - for (int id = 1; id <= script_items.ga_len; id++) { - si = &SCRIPT_ITEM(id); - if (si->sn_prof_on) { - fprintf(fd, "SCRIPT %s\n", si->sn_name); - if (si->sn_pr_count == 1) { - fprintf(fd, "Sourced 1 time\n"); - } else { - fprintf(fd, "Sourced %d times\n", si->sn_pr_count); - } - fprintf(fd, "Total time: %s\n", profile_msg(si->sn_pr_total)); - fprintf(fd, " Self time: %s\n", profile_msg(si->sn_pr_self)); - fprintf(fd, "\n"); - fprintf(fd, "count total (s) self (s)\n"); - - sfd = os_fopen((char *)si->sn_name, "r"); - if (sfd == NULL) { - fprintf(fd, "Cannot open file!\n"); - } else { - // Keep going till the end of file, so that trailing - // continuation lines are listed. - for (int i = 0;; i++) { - if (vim_fgets(IObuff, IOSIZE, sfd)) { - break; - } - // When a line has been truncated, append NL, taking care - // of multi-byte characters . - if (IObuff[IOSIZE - 2] != NUL && IObuff[IOSIZE - 2] != NL) { - int n = IOSIZE - 2; - - // Move to the first byte of this char. - // utf_head_off() doesn't work, because it checks - // for a truncated character. - while (n > 0 && (IObuff[n] & 0xc0) == 0x80) { - n--; - } - - IObuff[n] = NL; - IObuff[n + 1] = NUL; - } - if (i < si->sn_prl_ga.ga_len - && (pp = &PRL_ITEM(si, i))->snp_count > 0) { - fprintf(fd, "%5d ", pp->snp_count); - if (profile_equal(pp->sn_prl_total, pp->sn_prl_self)) { - fprintf(fd, " "); - } else { - fprintf(fd, "%s ", profile_msg(pp->sn_prl_total)); - } - fprintf(fd, "%s ", profile_msg(pp->sn_prl_self)); - } else { - fprintf(fd, " "); - } - fprintf(fd, "%s", IObuff); - } - fclose(sfd); - } - fprintf(fd, "\n"); - } - } -} - -/// @return true when a function defined in the current script should be -/// profiled. -bool prof_def_func(void) - FUNC_ATTR_PURE -{ - if (current_sctx.sc_sid > 0) { - return SCRIPT_ITEM(current_sctx.sc_sid).sn_pr_force; - } - return false; -} - /// If 'autowrite' option set, try to write the file. /// Careful: autocommands may make "buf" invalid! /// @@ -796,483 +444,6 @@ int buf_write_all(buf_T *buf, int forceit) return retval; } -/// Code to handle the argument list. - -#define AL_SET 1 -#define AL_ADD 2 -#define AL_DEL 3 - -/// 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 *do_one_arg(char *str) -{ - char *p; - bool 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((char_u *)str)) { - *p++ = *str++; - *p++ = *str; - } else { - // An item ends at a space not in backticks - if (!inbacktick && ascii_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 void get_arglist(garray_T *gap, char *str, int escaped) -{ - ga_init(gap, (int)sizeof(char_u *), 20); - while (*str != NUL) { - GA_APPEND(char *, gap, str); - - // If str is escaped, don't handle backslashes or spaces - if (!escaped) { - return; - } - - // Isolate one argument, change it in-place, put a NUL after it. - str = do_one_arg(str); - } -} - -/// 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 ***fnamesp, bool wig) -{ - garray_T ga; - int i; - - get_arglist(&ga, (char *)str, true); - - if (wig) { - i = expand_wildcards(ga.ga_len, ga.ga_data, - fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD); - } else { - i = gen_expand_wildcards(ga.ga_len, ga.ga_data, - fcountp, fnamesp, EW_FILE|EW_NOTFOUND|EW_NOTWILD); - } - - ga_clear(&ga); - return i; -} - -/// @param str -/// @param what -/// AL_SET: Redefine the argument list to 'str'. -/// AL_ADD: add files in 'str' to the argument list after "after". -/// AL_DEL: remove files in 'str' from the argument list. -/// @param after -/// 0 means before first one -/// @param will_edit will edit added argument -/// -/// @return FAIL for failure, OK otherwise. -static int do_arglist(char *str, int what, int after, bool will_edit) - FUNC_ATTR_NONNULL_ALL -{ - garray_T new_ga; - int exp_count; - char **exp_files; - char *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". - get_arglist(&new_ga, str, arg_escaped); - - if (what == AL_DEL) { - regmatch_T regmatch; - bool 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 (int i = 0; i < new_ga.ga_len && !got_int; i++) { - p = ((char **)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) { - xfree(p); - break; - } - - didone = false; - for (match = 0; match < ARGCOUNT; match++) { - if (vim_regexec(®match, alist_name(&ARGLIST[match]), (colnr_T)0)) { - didone = true; - xfree(ARGLIST[match].ae_fname); - memmove(ARGLIST + match, ARGLIST + match + 1, - (size_t)(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); - xfree(p); - if (!didone) { - semsg(_(e_nomatch2), ((char_u **)new_ga.ga_data)[i]); - } - } - ga_clear(&new_ga); - } else { - int i = expand_wildcards(new_ga.ga_len, 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); - xfree(exp_files); - } else { - assert(what == AL_SET); - alist_set(ALIST(curwin), exp_count, exp_files, will_edit, NULL, 0); - } - } - - alist_check_arg_idx(); - - return OK; -} - -/// Check the validity of the arg_idx for each other window. -static void alist_check_arg_idx(void) -{ - FOR_ALL_TAB_WINDOWS(tp, win) { - if (win->w_alist == curwin->w_alist) { - check_arg_idx(win); - } - } -} - -/// @return true if window "win" is editing the file at the current argument -/// index. -static bool 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 - || !(path_full_compare(alist_name(&WARGLIST(win)[win->w_arg_idx]), - win->w_buffer->b_ffname, true, - true) & kEqualFiles)))); -} - -/// 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 - && (path_full_compare(alist_name(&GARGLIST[GARGCOUNT - 1]), - win->w_buffer->b_ffname, true, true) - & kEqualFiles)))) { - 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) -{ - 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 **items = xmalloc(sizeof(char_u *) * (size_t)ARGCOUNT); - // Overwrite the command, for a short list there is no scrolling - // required and no wait_return(). - gotocmdline(true); - for (int i = 0; i < ARGCOUNT; i++) { - items[i] = alist_name(&ARGLIST[i]); - } - list_in_columns((char_u **)items, ARGCOUNT, curwin->w_arg_idx); - xfree(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. - ga_grow(gap, GARGCOUNT); - for (int 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 = (int)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 *p; - int old_arg_idx = curwin->w_arg_idx; - - 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(); - - // split window or create new tab page first - if (*eap->cmd == 's' || cmdmod.cmod_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); - xfree(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; - } else if (eap->cmdidx != CMD_argdo) { - // like Vi: set the mark where the cursor is in the file. - 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. - bool 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) -{ - if (eap->addr_count > 0 || *eap->arg == NUL) { - // ":argdel" works like ":.argdel" - if (eap->addr_count == 0) { - if (curwin->w_arg_idx >= ARGCOUNT) { - emsg(_("E610: No argument to delete")); - return; - } - eap->line1 = eap->line2 = curwin->w_arg_idx + 1; - } else if (eap->line2 > ARGCOUNT) { - // ":1,4argdel": Delete all arguments in the range. - eap->line2 = ARGCOUNT; - } - linenr_T 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 (linenr_T i = eap->line1; i <= eap->line2; i++) { - xfree(ARGLIST[i - 1].ae_fname); - } - memmove(ARGLIST + eap->line1 - 1, ARGLIST + eap->line2, - (size_t)(ARGCOUNT - eap->line2) * sizeof(aentry_T)); - ALIST(curwin)->al_ga.ga_len -= (int)n; - if (curwin->w_arg_idx >= eap->line2) { - curwin->w_arg_idx -= (int)n; - } else if (curwin->w_arg_idx > eap->line1) { - curwin->w_arg_idx = (int)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(); -} - /// ":argdo", ":windo", ":bufdo", ":tabdo", ":cdo", ":ldo", ":cfdo" and ":lfdo" void ex_listdo(exarg_T *eap) { @@ -1524,51 +695,6 @@ void ex_listdo(exarg_T *eap) } } -/// 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. -/// -/// @param after: where to add: 0 = before first one -/// @param will_edit will edit adding argument -static void alist_add_list(int count, char **files, int after, bool will_edit) - FUNC_ATTR_NONNULL_ALL -{ - int old_argcount = ARGCOUNT; - ga_grow(&ALIST(curwin)->al_ga, count); - { - if (after < 0) { - after = 0; - } - if (after > ARGCOUNT) { - after = ARGCOUNT; - } - if (after < ARGCOUNT) { - memmove(&(ARGLIST[after + count]), &(ARGLIST[after]), - (size_t)(ARGCOUNT - after) * sizeof(aentry_T)); - } - for (int i = 0; i < count; i++) { - const int flags = BLN_LISTED | (will_edit ? BLN_CURBUF : 0); - ARGLIST[after + i].ae_fname = (char_u *)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; - } -} - -// Function given to ExpandGeneric() to obtain the possible arguments of the -// argedit and argdelete commands. -char *get_arglist_name(expand_T *xp FUNC_ATTR_UNUSED, int idx) -{ - if (idx >= ARGCOUNT) { - return NULL; - } - return alist_name(&ARGLIST[idx]); -} - /// ":compiler[!] {name}" void ex_compiler(exarg_T *eap) { @@ -1632,987 +758,6 @@ void ex_compiler(exarg_T *eap) } } -/// ":options" -void ex_options(exarg_T *eap) -{ - char buf[500]; - bool multi_mods = 0; - - buf[0] = NUL; - (void)add_win_cmd_modifers(buf, &cmdmod, &multi_mods); - - os_setenv("OPTWIN_CMD", buf, 1); - cmd_source(SYS_OPTWIN_FILE, NULL); -} - -/// ":source [{fname}]" -void ex_source(exarg_T *eap) -{ - cmd_source(eap->arg, eap); -} - -static void cmd_source(char *fname, exarg_T *eap) -{ - if (eap != NULL && *fname == NUL) { - cmd_source_buffer(eap); - } else if (eap != NULL && eap->forceit) { - // ":source!": read Normal mode commands - // Need to execute the commands directly. This is required at least - // for: - // - ":g" command busy - // - after ":argdo", ":windo" or ":bufdo" - // - another command follows - // - inside a loop - openscript((char_u *)fname, global_busy || listcmd_busy || eap->nextcmd != NULL - || eap->cstack->cs_idx >= 0); - - // ":source" read ex commands - } else if (do_source(fname, false, DOSO_NONE) == FAIL) { - semsg(_(e_notopen), fname); - } -} - -/// Concatenate VimL line if it starts with a line continuation into a growarray -/// (excluding the continuation chars and leading whitespace) -/// -/// @note Growsize of the growarray may be changed to speed up concatenations! -/// -/// @param ga the growarray to append to -/// @param init_growsize the starting growsize value of the growarray -/// @param p pointer to the beginning of the line to consider -/// @param len the length of this line -/// -/// @return true if this line did begin with a continuation (the next line -/// should also be considered, if it exists); false otherwise -static bool concat_continued_line(garray_T *const ga, const int init_growsize, - const char_u *const p, size_t len) - FUNC_ATTR_NONNULL_ALL -{ - const char *const line = (char *)skipwhite_len(p, len); - len -= (size_t)((char_u *)line - p); - // Skip lines starting with '\" ', concat lines starting with '\' - if (len >= 3 && STRNCMP(line, "\"\\ ", 3) == 0) { - return true; - } else if (len == 0 || line[0] != '\\') { - return false; - } - if (ga->ga_len > init_growsize) { - ga_set_growsize(ga, MIN(ga->ga_len, 8000)); - } - ga_concat_len(ga, line + 1, len - 1); - return true; -} - -typedef struct { - linenr_T curr_lnum; - const linenr_T final_lnum; -} GetBufferLineCookie; - -/// ":source" and associated commands. -/// -/// @return address holding the next breakpoint line for a source cookie -linenr_T *source_breakpoint(void *cookie) -{ - return &((struct source_cookie *)cookie)->breakpoint; -} - -/// @return the address holding the debug tick for a source cookie. -int *source_dbg_tick(void *cookie) -{ - return &((struct source_cookie *)cookie)->dbg_tick; -} - -/// @return the nesting level for a source cookie. -int source_level(void *cookie) - FUNC_ATTR_PURE -{ - return ((struct source_cookie *)cookie)->level; -} - -/// Special function to open a file without handle inheritance. -/// If possible the handle is closed on exec(). -static FILE *fopen_noinh_readbin(char *filename) -{ -#ifdef WIN32 - int fd_tmp = os_open(filename, O_RDONLY | O_BINARY | O_NOINHERIT, 0); -#else - int fd_tmp = os_open(filename, O_RDONLY, 0); -#endif - - if (fd_tmp < 0) { - return NULL; - } - - (void)os_set_cloexec(fd_tmp); - - return fdopen(fd_tmp, READBIN); -} - -typedef struct { - char *buf; - size_t offset; -} GetStrLineCookie; - -/// Get one full line from a sourced string (in-memory, no file). -/// Called by do_cmdline() when it's called from do_source_str(). -/// -/// @return pointer to allocated line, or NULL for end-of-file or -/// some error. -static char *get_str_line(int c, void *cookie, int indent, bool do_concat) -{ - GetStrLineCookie *p = cookie; - if (STRLEN(p->buf) <= p->offset) { - return NULL; - } - const char *line = p->buf + p->offset; - const char *eol = (char *)skip_to_newline((char_u *)line); - garray_T ga; - ga_init(&ga, sizeof(char_u), 400); - ga_concat_len(&ga, line, (size_t)(eol - line)); - if (do_concat && vim_strchr(p_cpo, CPO_CONCAT) == NULL) { - while (eol[0] != NUL) { - line = eol + 1; - const char_u *const next_eol = skip_to_newline((char_u *)line); - if (!concat_continued_line(&ga, 400, (char_u *)line, (size_t)(next_eol - (char_u *)line))) { - break; - } - eol = (char *)next_eol; - } - } - ga_append(&ga, NUL); - p->offset = (size_t)(eol - p->buf) + 1; - return ga.ga_data; -} - -/// Create a new script item and allocate script-local vars. @see new_script_vars -/// -/// @param name File name of the script. NULL for anonymous :source. -/// @param[out] sid_out SID of the new item. -/// -/// @return pointer to the created script item. -scriptitem_T *new_script_item(char *const name, scid_T *const sid_out) -{ - static scid_T last_current_SID = 0; - const scid_T sid = ++last_current_SID; - if (sid_out != NULL) { - *sid_out = sid; - } - ga_grow(&script_items, sid - script_items.ga_len); - while (script_items.ga_len < sid) { - script_items.ga_len++; - SCRIPT_ITEM(script_items.ga_len).sn_name = NULL; - SCRIPT_ITEM(script_items.ga_len).sn_prof_on = false; - } - SCRIPT_ITEM(sid).sn_name = (char_u *)name; - new_script_vars(sid); // Allocate the local script variables to use for this script. - return &SCRIPT_ITEM(sid); -} - -static int source_using_linegetter(void *cookie, LineGetter fgetline, const char *traceback_name) -{ - char *save_sourcing_name = sourcing_name; - linenr_T save_sourcing_lnum = sourcing_lnum; - char sourcing_name_buf[256]; - if (save_sourcing_name == NULL) { - sourcing_name = (char *)traceback_name; - } else { - snprintf((char *)sourcing_name_buf, sizeof(sourcing_name_buf), - "%s called at %s:%" PRIdLINENR, traceback_name, save_sourcing_name, - save_sourcing_lnum); - sourcing_name = sourcing_name_buf; // -V507 reassigned below, before return. - } - sourcing_lnum = 0; - - const sctx_T save_current_sctx = current_sctx; - if (current_sctx.sc_sid != SID_LUA) { - current_sctx.sc_sid = SID_STR; - } - current_sctx.sc_seq = 0; - current_sctx.sc_lnum = save_sourcing_lnum; - funccal_entry_T entry; - save_funccal(&entry); - int retval = do_cmdline(NULL, fgetline, cookie, - DOCMD_VERBOSE | DOCMD_NOWAIT | DOCMD_REPEAT); - sourcing_lnum = save_sourcing_lnum; - sourcing_name = save_sourcing_name; - current_sctx = save_current_sctx; - restore_funccal(); - return retval; -} - -static void cmd_source_buffer(const exarg_T *const eap) - FUNC_ATTR_NONNULL_ALL -{ - if (curbuf == NULL) { - return; - } - garray_T ga; - ga_init(&ga, sizeof(char_u), 400); - const linenr_T final_lnum = eap->line2; - // Copy the contents to be executed. - for (linenr_T curr_lnum = eap->line1; curr_lnum <= final_lnum; curr_lnum++) { - // Adjust growsize to current length to speed up concatenating many lines. - if (ga.ga_len > 400) { - ga_set_growsize(&ga, MIN(ga.ga_len, 8000)); - } - ga_concat(&ga, (char *)ml_get(curr_lnum)); - ga_append(&ga, NL); - } - ((char_u *)ga.ga_data)[ga.ga_len - 1] = NUL; - const GetStrLineCookie cookie = { - .buf = ga.ga_data, - .offset = 0, - }; - if (curbuf->b_fname - && path_with_extension((const char *)curbuf->b_fname, "lua")) { - nlua_source_using_linegetter(get_str_line, (void *)&cookie, - ":source (no file)"); - } else { - source_using_linegetter((void *)&cookie, get_str_line, - ":source (no file)"); - } - ga_clear(&ga); -} - -/// Executes lines in `src` as Ex commands. -/// -/// @see do_source() -int do_source_str(const char *cmd, const char *traceback_name) -{ - GetStrLineCookie cookie = { - .buf = (char *)cmd, - .offset = 0, - }; - return source_using_linegetter((void *)&cookie, get_str_line, traceback_name); -} - -/// When fname is a 'lua' file nlua_exec_file() is invoked to source it. -/// Otherwise reads the file `fname` and executes its lines as Ex commands. -/// -/// This function may be called recursively! -/// -/// @see do_source_str -/// -/// @param fname -/// @param check_other check for .vimrc and _vimrc -/// @param is_vimrc DOSO_ value -/// -/// @return FAIL if file could not be opened, OK otherwise -int do_source(char *fname, int check_other, int is_vimrc) -{ - struct source_cookie cookie; - char *save_sourcing_name; - linenr_T save_sourcing_lnum; - char *p; - char *fname_exp; - uint8_t *firstline = NULL; - int retval = FAIL; - int save_debug_break_level = debug_break_level; - scriptitem_T *si = NULL; - proftime_T wait_start; - bool trigger_source_post = false; - - p = expand_env_save(fname); - if (p == NULL) { - return retval; - } - fname_exp = fix_fname(p); - xfree(p); - if (fname_exp == NULL) { - return retval; - } - if (os_isdir((char_u *)fname_exp)) { - smsg(_("Cannot source a directory: \"%s\""), fname); - goto theend; - } - - // Apply SourceCmd autocommands, they should get the file and source it. - if (has_autocmd(EVENT_SOURCECMD, fname_exp, NULL) - && apply_autocmds(EVENT_SOURCECMD, fname_exp, fname_exp, - false, curbuf)) { - retval = aborting() ? FAIL : OK; - if (retval == OK) { - // Apply SourcePost autocommands. - apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf); - } - goto theend; - } - - // Apply SourcePre autocommands, they may get the file. - apply_autocmds(EVENT_SOURCEPRE, fname_exp, fname_exp, false, curbuf); - - cookie.fp = fopen_noinh_readbin(fname_exp); - if (cookie.fp == NULL && check_other) { - // Try again, replacing file name ".vimrc" by "_vimrc" or vice versa, - // and ".exrc" by "_exrc" or vice versa. - p = path_tail(fname_exp); - if ((*p == '.' || *p == '_') - && (STRICMP(p + 1, "nvimrc") == 0 || STRICMP(p + 1, "exrc") == 0)) { - *p = (*p == '_') ? '.' : '_'; - cookie.fp = fopen_noinh_readbin(fname_exp); - } - } - - if (cookie.fp == NULL) { - if (p_verbose > 1) { - verbose_enter(); - if (sourcing_name == NULL) { - smsg(_("could not source \"%s\""), fname); - } else { - smsg(_("line %" PRId64 ": could not source \"%s\""), - (int64_t)sourcing_lnum, fname); - } - verbose_leave(); - } - goto theend; - } - - // The file exists. - // - In verbose mode, give a message. - // - For a vimrc file, may want to call vimrc_found(). - if (p_verbose > 1) { - verbose_enter(); - if (sourcing_name == NULL) { - smsg(_("sourcing \"%s\""), fname); - } else { - smsg(_("line %" PRId64 ": sourcing \"%s\""), - (int64_t)sourcing_lnum, fname); - } - verbose_leave(); - } - if (is_vimrc == DOSO_VIMRC) { - vimrc_found(fname_exp, "MYVIMRC"); - } - -#ifdef USE_CRNL - // If no automatic file format: Set default to CR-NL. - if (*p_ffs == NUL) { - cookie.fileformat = EOL_DOS; - } else { - cookie.fileformat = EOL_UNKNOWN; - } - cookie.error = false; -#endif - - cookie.nextline = NULL; - cookie.sourcing_lnum = 0; - cookie.finished = false; - - // Check if this script has a breakpoint. - cookie.breakpoint = dbg_find_breakpoint(true, (char_u *)fname_exp, (linenr_T)0); - cookie.fname = fname_exp; - cookie.dbg_tick = debug_tick; - - cookie.level = ex_nesting_level; - - // Keep the sourcing name/lnum, for recursive calls. - save_sourcing_name = sourcing_name; - sourcing_name = fname_exp; - save_sourcing_lnum = sourcing_lnum; - sourcing_lnum = 0; - - // start measuring script load time if --startuptime was passed and - // time_fd was successfully opened afterwards. - proftime_T rel_time; - proftime_T start_time; - FILE * const l_time_fd = time_fd; - if (l_time_fd != NULL) { - time_push(&rel_time, &start_time); - } - - const int l_do_profiling = do_profiling; - if (l_do_profiling == PROF_YES) { - prof_child_enter(&wait_start); // entering a child now - } - - // Don't use local function variables, if called from a function. - // Also starts profiling timer for nested script. - funccal_entry_T funccalp_entry; - save_funccal(&funccalp_entry); - - const sctx_T save_current_sctx = current_sctx; - si = get_current_script_id((char_u *)fname_exp, ¤t_sctx); - - if (l_do_profiling == PROF_YES) { - bool forceit = false; - - // Check if we do profiling for this script. - if (!si->sn_prof_on && has_profiling(true, si->sn_name, &forceit)) { - profile_init(si); - si->sn_pr_force = forceit; - } - if (si->sn_prof_on) { - si->sn_pr_count++; - si->sn_pr_start = profile_start(); - si->sn_pr_children = profile_zero(); - } - } - - cookie.conv.vc_type = CONV_NONE; // no conversion - - // Read the first line so we can check for a UTF-8 BOM. - firstline = (uint8_t *)getsourceline(0, (void *)&cookie, 0, true); - if (firstline != NULL && STRLEN(firstline) >= 3 && firstline[0] == 0xef - && firstline[1] == 0xbb && firstline[2] == 0xbf) { - // Found BOM; setup conversion, skip over BOM and recode the line. - convert_setup(&cookie.conv, (char_u *)"utf-8", p_enc); - p = (char *)string_convert(&cookie.conv, (char_u *)firstline + 3, NULL); - if (p == NULL) { - p = xstrdup((char *)firstline + 3); - } - xfree(firstline); - firstline = (uint8_t *)p; - } - - if (path_with_extension((const char *)fname_exp, "lua")) { - const sctx_T current_sctx_backup = current_sctx; - const linenr_T sourcing_lnum_backup = sourcing_lnum; - current_sctx.sc_sid = SID_LUA; - current_sctx.sc_lnum = 0; - sourcing_lnum = 0; - // Source the file as lua - nlua_exec_file((const char *)fname_exp); - current_sctx = current_sctx_backup; - sourcing_lnum = sourcing_lnum_backup; - } else { - // Call do_cmdline, which will call getsourceline() to get the lines. - do_cmdline((char *)firstline, getsourceline, (void *)&cookie, - DOCMD_VERBOSE|DOCMD_NOWAIT|DOCMD_REPEAT); - } - retval = OK; - - if (l_do_profiling == PROF_YES) { - // Get "si" again, "script_items" may have been reallocated. - si = &SCRIPT_ITEM(current_sctx.sc_sid); - if (si->sn_prof_on) { - si->sn_pr_start = profile_end(si->sn_pr_start); - si->sn_pr_start = profile_sub_wait(wait_start, si->sn_pr_start); - si->sn_pr_total = profile_add(si->sn_pr_total, si->sn_pr_start); - si->sn_pr_self = profile_self(si->sn_pr_self, si->sn_pr_start, - si->sn_pr_children); - } - } - - if (got_int) { - emsg(_(e_interr)); - } - sourcing_name = save_sourcing_name; - sourcing_lnum = save_sourcing_lnum; - if (p_verbose > 1) { - verbose_enter(); - smsg(_("finished sourcing %s"), fname); - if (sourcing_name != NULL) { - smsg(_("continuing in %s"), sourcing_name); - } - verbose_leave(); - } - - if (l_time_fd != NULL) { - vim_snprintf((char *)IObuff, IOSIZE, "sourcing %s", fname); - time_msg((char *)IObuff, &start_time); - time_pop(rel_time); - } - - if (!got_int) { - trigger_source_post = true; - } - - // After a "finish" in debug mode, need to break at first command of next - // sourced file. - if (save_debug_break_level > ex_nesting_level - && debug_break_level == ex_nesting_level) { - debug_break_level++; - } - - current_sctx = save_current_sctx; - restore_funccal(); - if (l_do_profiling == PROF_YES) { - prof_child_exit(&wait_start); // leaving a child now - } - fclose(cookie.fp); - xfree(cookie.nextline); - xfree(firstline); - convert_setup(&cookie.conv, NULL, NULL); - - if (trigger_source_post) { - apply_autocmds(EVENT_SOURCEPOST, fname_exp, fname_exp, false, curbuf); - } - -theend: - xfree(fname_exp); - return retval; -} - -/// Check if fname was sourced before to finds its SID. -/// If it's new, generate a new SID. -/// -/// @param[in] fname file path of script -/// @param[out] ret_sctx sctx of this script -scriptitem_T *get_current_script_id(char_u *fname, sctx_T *ret_sctx) -{ - static int last_current_SID_seq = 0; - - sctx_T script_sctx = { .sc_seq = ++last_current_SID_seq, - .sc_lnum = 0, - .sc_sid = 0 }; - scriptitem_T *si = NULL; - - assert(script_items.ga_len >= 0); - for (script_sctx.sc_sid = script_items.ga_len; script_sctx.sc_sid > 0; script_sctx.sc_sid--) { - // We used to check inode here, but that doesn't work: - // - If a script is edited and written, it may get a different - // inode number, even though to the user it is the same script. - // - If a script is deleted and another script is written, with a - // different name, the inode may be re-used. - si = &SCRIPT_ITEM(script_sctx.sc_sid); - if (si->sn_name != NULL && FNAMECMP(si->sn_name, fname) == 0) { - // Found it! - break; - } - } - if (script_sctx.sc_sid == 0) { - si = new_script_item((char *)vim_strsave(fname), &script_sctx.sc_sid); - } - if (ret_sctx != NULL) { - *ret_sctx = script_sctx; - } - - return si; -} - -/// ":scriptnames" -void ex_scriptnames(exarg_T *eap) -{ - if (eap->addr_count > 0) { - // :script {scriptId}: edit the script - if (eap->line2 < 1 || eap->line2 > script_items.ga_len) { - emsg(_(e_invarg)); - } else { - eap->arg = (char *)SCRIPT_ITEM(eap->line2).sn_name; - do_exedit(eap, NULL); - } - return; - } - - for (int i = 1; i <= script_items.ga_len && !got_int; i++) { - if (SCRIPT_ITEM(i).sn_name != NULL) { - home_replace(NULL, (char *)SCRIPT_ITEM(i).sn_name, (char *)NameBuff, MAXPATHL, true); - vim_snprintf((char *)IObuff, IOSIZE, "%3d: %s", i, NameBuff); - if (!message_filtered(IObuff)) { - msg_putchar('\n'); - msg_outtrans((char *)IObuff); - line_breakcheck(); - } - } - } -} - -#if defined(BACKSLASH_IN_FILENAME) -/// Fix slashes in the list of script names for 'shellslash'. -void scriptnames_slash_adjust(void) -{ - for (int i = 1; i <= script_items.ga_len; i++) { - if (SCRIPT_ITEM(i).sn_name != NULL) { - slash_adjust(SCRIPT_ITEM(i).sn_name); - } - } -} - -#endif - -/// Get a pointer to a script name. Used for ":verbose set". -/// Message appended to "Last set from " -char_u *get_scriptname(LastSet last_set, bool *should_free) -{ - *should_free = false; - - switch (last_set.script_ctx.sc_sid) { - case SID_MODELINE: - return (char_u *)_("modeline"); - case SID_CMDARG: - return (char_u *)_("--cmd argument"); - case SID_CARG: - return (char_u *)_("-c argument"); - case SID_ENV: - return (char_u *)_("environment variable"); - case SID_ERROR: - return (char_u *)_("error handler"); - case SID_WINLAYOUT: - return (char_u *)_("changed window size"); - case SID_LUA: - return (char_u *)_("Lua"); - case SID_API_CLIENT: - snprintf((char *)IObuff, IOSIZE, _("API client (channel id %" PRIu64 ")"), last_set.channel_id); - return IObuff; - case SID_STR: - return (char_u *)_("anonymous :source"); - default: { - char *const sname = (char *)SCRIPT_ITEM(last_set.script_ctx.sc_sid).sn_name; - if (sname == NULL) { - snprintf((char *)IObuff, IOSIZE, _("anonymous :source (script id %d)"), - last_set.script_ctx.sc_sid); - return IObuff; - } - - *should_free = true; - return (char_u *)home_replace_save(NULL, sname); - } - } -} - -#if defined(EXITFREE) -void free_scriptnames(void) -{ - profile_reset(); - -# define FREE_SCRIPTNAME(item) xfree((item)->sn_name) - GA_DEEP_CLEAR(&script_items, scriptitem_T, FREE_SCRIPTNAME); -} -#endif - -linenr_T get_sourced_lnum(LineGetter fgetline, void *cookie) - FUNC_ATTR_PURE -{ - return fgetline == getsourceline - ? ((struct source_cookie *)cookie)->sourcing_lnum - : sourcing_lnum; -} - -/// Get one full line from a sourced file. -/// Called by do_cmdline() when it's called from do_source(). -/// -/// @return pointer to the line in allocated memory, or NULL for end-of-file or -/// some error. -char *getsourceline(int c, void *cookie, int indent, bool do_concat) -{ - struct source_cookie *sp = (struct source_cookie *)cookie; - char *line; - char *p; - - // If breakpoints have been added/deleted need to check for it. - if (sp->dbg_tick < debug_tick) { - sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, sourcing_lnum); - sp->dbg_tick = debug_tick; - } - if (do_profiling == PROF_YES) { - script_line_end(); - } - // Set the current sourcing line number. - sourcing_lnum = sp->sourcing_lnum + 1; - // Get current line. If there is a read-ahead line, use it, otherwise get - // one now. - if (sp->finished) { - line = NULL; - } else if (sp->nextline == NULL) { - line = get_one_sourceline(sp); - } else { - line = sp->nextline; - sp->nextline = NULL; - sp->sourcing_lnum++; - } - if (line != NULL && do_profiling == PROF_YES) { - script_line_start(); - } - - // Only concatenate lines starting with a \ when 'cpoptions' doesn't - // contain the 'C' flag. - if (line != NULL && do_concat && (vim_strchr(p_cpo, CPO_CONCAT) == NULL)) { - // compensate for the one line read-ahead - sp->sourcing_lnum--; - - // Get the next line and concatenate it when it starts with a - // backslash. We always need to read the next line, keep it in - // sp->nextline. - // Also check for a comment in between continuation lines: "\ . - sp->nextline = get_one_sourceline(sp); - if (sp->nextline != NULL - && (*(p = skipwhite(sp->nextline)) == '\\' - || (p[0] == '"' && p[1] == '\\' && p[2] == ' '))) { - garray_T ga; - - ga_init(&ga, (int)sizeof(char_u), 400); - ga_concat(&ga, line); - while (sp->nextline != NULL - && concat_continued_line(&ga, 400, (char_u *)sp->nextline, - STRLEN(sp->nextline))) { - xfree(sp->nextline); - sp->nextline = get_one_sourceline(sp); - } - ga_append(&ga, NUL); - xfree(line); - line = ga.ga_data; - } - } - - if (line != NULL && sp->conv.vc_type != CONV_NONE) { - char *s; - - // Convert the encoding of the script line. - s = (char *)string_convert(&sp->conv, (char_u *)line, NULL); - if (s != NULL) { - xfree(line); - line = s; - } - } - - // Did we encounter a breakpoint? - if (sp->breakpoint != 0 && sp->breakpoint <= sourcing_lnum) { - dbg_breakpoint((char_u *)sp->fname, sourcing_lnum); - // Find next breakpoint. - sp->breakpoint = dbg_find_breakpoint(true, (char_u *)sp->fname, sourcing_lnum); - sp->dbg_tick = debug_tick; - } - - return line; -} - -static char *get_one_sourceline(struct source_cookie *sp) -{ - garray_T ga; - int len; - int c; - char *buf; -#ifdef USE_CRNL - int has_cr; // CR-LF found -#endif - bool have_read = false; - - // use a growarray to store the sourced line - ga_init(&ga, 1, 250); - - // Loop until there is a finished line (or end-of-file). - sp->sourcing_lnum++; - for (;;) { - // make room to read at least 120 (more) characters - ga_grow(&ga, 120); - buf = ga.ga_data; - -retry: - errno = 0; - if (fgets(buf + ga.ga_len, ga.ga_maxlen - ga.ga_len, - sp->fp) == NULL) { - if (errno == EINTR) { - goto retry; - } - - break; - } - len = ga.ga_len + (int)STRLEN(buf + ga.ga_len); -#ifdef USE_CRNL - // Ignore a trailing CTRL-Z, when in Dos mode. Only recognize the - // CTRL-Z by its own, or after a NL. - if ((len == 1 || (len >= 2 && buf[len - 2] == '\n')) - && sp->fileformat == EOL_DOS - && buf[len - 1] == Ctrl_Z) { - buf[len - 1] = NUL; - break; - } -#endif - - have_read = true; - ga.ga_len = len; - - // If the line was longer than the buffer, read more. - if (ga.ga_maxlen - ga.ga_len == 1 && buf[len - 1] != '\n') { - continue; - } - - if (len >= 1 && buf[len - 1] == '\n') { // remove trailing NL -#ifdef USE_CRNL - has_cr = (len >= 2 && buf[len - 2] == '\r'); - if (sp->fileformat == EOL_UNKNOWN) { - if (has_cr) { - sp->fileformat = EOL_DOS; - } else { - sp->fileformat = EOL_UNIX; - } - } - - if (sp->fileformat == EOL_DOS) { - if (has_cr) { // replace trailing CR - buf[len - 2] = '\n'; - len--; - ga.ga_len--; - } else { // lines like ":map xx yy^M" will have failed - if (!sp->error) { - msg_source(HL_ATTR(HLF_W)); - emsg(_("W15: Warning: Wrong line separator, ^M may be missing")); - } - sp->error = true; - sp->fileformat = EOL_UNIX; - } - } -#endif - // The '\n' is escaped if there is an odd number of ^V's just - // before it, first set "c" just before the 'V's and then check - // len&c parities (is faster than ((len-c)%2 == 0)) -- Acevedo - for (c = len - 2; c >= 0 && buf[c] == Ctrl_V; c--) {} - if ((len & 1) != (c & 1)) { // escaped NL, read more - sp->sourcing_lnum++; - continue; - } - - buf[len - 1] = NUL; // remove the NL - } - - // Check for ^C here now and then, so recursive :so can be broken. - line_breakcheck(); - break; - } - - if (have_read) { - return ga.ga_data; - } - - xfree(ga.ga_data); - return NULL; -} - -/// Called when starting to read a script line. -/// "sourcing_lnum" must be correct! -/// When skipping lines it may not actually be executed, but we won't find out -/// until later and we need to store the time now. -void script_line_start(void) -{ - scriptitem_T *si; - sn_prl_T *pp; - - if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) { - return; - } - si = &SCRIPT_ITEM(current_sctx.sc_sid); - if (si->sn_prof_on && sourcing_lnum >= 1) { - // Grow the array before starting the timer, so that the time spent - // here isn't counted. - (void)ga_grow(&si->sn_prl_ga, sourcing_lnum - si->sn_prl_ga.ga_len); - si->sn_prl_idx = sourcing_lnum - 1; - while (si->sn_prl_ga.ga_len <= si->sn_prl_idx - && si->sn_prl_ga.ga_len < si->sn_prl_ga.ga_maxlen) { - // Zero counters for a line that was not used before. - pp = &PRL_ITEM(si, si->sn_prl_ga.ga_len); - pp->snp_count = 0; - pp->sn_prl_total = profile_zero(); - pp->sn_prl_self = profile_zero(); - si->sn_prl_ga.ga_len++; - } - si->sn_prl_execed = false; - si->sn_prl_start = profile_start(); - si->sn_prl_children = profile_zero(); - si->sn_prl_wait = profile_get_wait(); - } -} - -/// Called when actually executing a function line. -void script_line_exec(void) -{ - scriptitem_T *si; - - if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) { - return; - } - si = &SCRIPT_ITEM(current_sctx.sc_sid); - if (si->sn_prof_on && si->sn_prl_idx >= 0) { - si->sn_prl_execed = true; - } -} - -/// Called when done with a function line. -void script_line_end(void) -{ - scriptitem_T *si; - sn_prl_T *pp; - - if (current_sctx.sc_sid <= 0 || current_sctx.sc_sid > script_items.ga_len) { - return; - } - si = &SCRIPT_ITEM(current_sctx.sc_sid); - if (si->sn_prof_on && si->sn_prl_idx >= 0 - && si->sn_prl_idx < si->sn_prl_ga.ga_len) { - if (si->sn_prl_execed) { - pp = &PRL_ITEM(si, si->sn_prl_idx); - pp->snp_count++; - si->sn_prl_start = profile_end(si->sn_prl_start); - si->sn_prl_start = profile_sub_wait(si->sn_prl_wait, si->sn_prl_start); - pp->sn_prl_total = profile_add(pp->sn_prl_total, si->sn_prl_start); - pp->sn_prl_self = profile_self(pp->sn_prl_self, si->sn_prl_start, - si->sn_prl_children); - } - si->sn_prl_idx = -1; - } -} - -/// ":scriptencoding": Set encoding conversion for a sourced script. -/// Without the multi-byte feature it's simply ignored. -void ex_scriptencoding(exarg_T *eap) -{ - struct source_cookie *sp; - char *name; - - if (!getline_equal(eap->getline, eap->cookie, getsourceline)) { - emsg(_("E167: :scriptencoding used outside of a sourced file")); - return; - } - - if (*eap->arg != NUL) { - name = (char *)enc_canonize((char_u *)eap->arg); - } else { - name = eap->arg; - } - - // Setup for conversion from the specified encoding to 'encoding'. - sp = (struct source_cookie *)getline_cookie(eap->getline, eap->cookie); - convert_setup(&sp->conv, (char_u *)name, p_enc); - - if (name != eap->arg) { - xfree(name); - } -} - -/// ":finish": Mark a sourced file as finished. -void ex_finish(exarg_T *eap) -{ - if (getline_equal(eap->getline, eap->cookie, getsourceline)) { - do_finish(eap, false); - } else { - emsg(_("E168: :finish used outside of a sourced file")); - } -} - -/// Mark a sourced file as finished. Possibly makes the ":finish" pending. -/// Also called for a pending finish at the ":endtry" or after returning from -/// an extra do_cmdline(). "reanimate" is used in the latter case. -void do_finish(exarg_T *eap, int reanimate) -{ - int idx; - - if (reanimate) { - ((struct source_cookie *)getline_cookie(eap->getline, - eap->cookie))->finished = false; - } - - // Cleanup (and deactivate) conditionals, but stop when a try conditional - // not in its finally clause (which then is to be executed next) is found. - // In this case, make the ":finish" pending for execution at the ":endtry". - // Otherwise, finish normally. - idx = cleanup_conditionals(eap->cstack, 0, true); - if (idx >= 0) { - eap->cstack->cs_pending[idx] = CSTP_FINISH; - report_make_pending(CSTP_FINISH, NULL); - } else { - ((struct source_cookie *)getline_cookie(eap->getline, - eap->cookie))->finished = true; - } -} - -/// @return true when a sourced file had the ":finish" command: Don't give error -/// message for missing ":endif". -/// false when not sourcing a file. -bool source_finished(LineGetter fgetline, void *cookie) -{ - return getline_equal(fgetline, cookie, getsourceline) - && ((struct source_cookie *)getline_cookie(fgetline, cookie))->finished; -} - /// ":checktime [buffer]" void ex_checktime(exarg_T *eap) { @@ -3007,7 +1152,7 @@ void ex_drop(exarg_T *eap) // and mostly only one file is dropped. // This also ignores wildcards, since it is very unlikely the user is // editing a file name with a wildcard character. - do_arglist(eap->arg, AL_SET, 0, false); + set_arglist(eap->arg); // Expanding wildcards may result in an empty argument list. E.g. when // editing "foo.pyc" and ".pyc" is in 'wildignore'. Assume that we |