diff options
Diffstat (limited to 'src/nvim/ex_docmd.c')
-rw-r--r-- | src/nvim/ex_docmd.c | 232 |
1 files changed, 207 insertions, 25 deletions
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 293aaac036..f5ecedf827 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5165,6 +5165,177 @@ static void ex_wrongmodifier(exarg_T *eap) eap->errmsg = _(e_invcmd); } +/// callback function for 'findfunc' +static Callback ffu_cb; + +static Callback *get_findfunc_callback(void) +{ + return *curbuf->b_p_ffu != NUL ? &curbuf->b_ffu_cb : &ffu_cb; +} + +/// Call 'findfunc' to obtain a list of file names. +static list_T *call_findfunc(char *pat, BoolVarValue cmdcomplete) +{ + const sctx_T saved_sctx = current_sctx; + + typval_T args[3]; + args[0].v_type = VAR_STRING; + args[0].vval.v_string = pat; + args[1].v_type = VAR_BOOL; + args[1].vval.v_bool = cmdcomplete; + args[2].v_type = VAR_UNKNOWN; + + // Lock the text to prevent weird things from happening. Also disallow + // switching to another window, it should not be needed and may end up in + // Insert mode in another buffer. + textlock++; + + sctx_T *ctx = get_option_sctx(kOptFindfunc); + if (ctx != NULL) { + current_sctx = *ctx; + } + + Callback *cb = get_findfunc_callback(); + typval_T rettv; + int retval = callback_call(cb, 2, args, &rettv); + + current_sctx = saved_sctx; + + textlock--; + + list_T *retlist = NULL; + + if (retval == OK) { + if (rettv.v_type == VAR_LIST) { + retlist = tv_list_copy(NULL, rettv.vval.v_list, false, get_copyID()); + } else { + emsg(_(e_invalid_return_type_from_findfunc)); + } + + tv_clear(&rettv); + } + + return retlist; +} + +/// Find file names matching "pat" using 'findfunc' and return it in "files". +/// Used for expanding the :find, :sfind and :tabfind command argument. +/// Returns OK on success and FAIL otherwise. +int expand_findfunc(char *pat, char ***files, int *numMatches) +{ + *numMatches = 0; + *files = NULL; + + list_T *l = call_findfunc(pat, kBoolVarTrue); + if (l == NULL) { + return FAIL; + } + + int len = tv_list_len(l); + if (len == 0) { // empty List + return FAIL; + } + + *files = xmalloc(sizeof(char *) * (size_t)len); + + // Copy all the List items + int idx = 0; + TV_LIST_ITER_CONST(l, li, { + if (TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) { + (*files)[idx] = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string); + idx++; + } + }); + + *numMatches = idx; + tv_list_free(l); + + return OK; +} + +/// Use 'findfunc' to find file 'findarg'. The 'count' argument is used to find +/// the n'th matching file. +static char *findfunc_find_file(char *findarg, size_t findarg_len, int count) +{ + char *ret_fname = NULL; + + const char cc = findarg[findarg_len]; + findarg[findarg_len] = NUL; + + list_T *fname_list = call_findfunc(findarg, kBoolVarFalse); + int fname_count = tv_list_len(fname_list); + + if (fname_count == 0) { + semsg(_(e_cant_find_file_str_in_path), findarg); + } else { + if (count > fname_count) { + semsg(_(e_no_more_file_str_found_in_path), findarg); + } else { + listitem_T *li = tv_list_find(fname_list, count - 1); + if (li != NULL && TV_LIST_ITEM_TV(li)->v_type == VAR_STRING) { + ret_fname = xstrdup(TV_LIST_ITEM_TV(li)->vval.v_string); + } + } + } + + if (fname_list != NULL) { + tv_list_free(fname_list); + } + + findarg[findarg_len] = cc; + + return ret_fname; +} + +/// Process the 'findfunc' option value. +/// Returns NULL on success and an error message on failure. +const char *did_set_findfunc(optset_T *args) +{ + buf_T *buf = (buf_T *)args->os_buf; + int retval; + + if (args->os_flags & OPT_LOCAL) { + // buffer-local option set + retval = option_set_callback_func(buf->b_p_ffu, &buf->b_ffu_cb); + } else { + // global option set + retval = option_set_callback_func(p_ffu, &ffu_cb); + // when using :set, free the local callback + if (!(args->os_flags & OPT_GLOBAL)) { + callback_free(&buf->b_ffu_cb); + } + } + + if (retval == FAIL) { + return e_invarg; + } + + // If the option value starts with <SID> or s:, then replace that with + // the script identifier. + char **varp = (char **)args->os_varp; + char *name = get_scriptlocal_funcname(*varp); + if (name != NULL) { + free_string_option(*varp); + *varp = name; + } + + return NULL; +} + +void free_findfunc_option(void) +{ + callback_free(&ffu_cb); +} + +/// Mark the global 'findfunc' callback with "copyID" so that it is not +/// garbage collected. +bool set_ref_in_findfunc(int copyID) +{ + bool abort = false; + abort = set_ref_in_callback(&ffu_cb, copyID, NULL, NULL); + return abort; +} + /// :sview [+command] file split window with new file, read-only /// :split [[+command] file] split window with current or new file /// :vsplit [[+command] file] split window vertically with current or new file @@ -5196,13 +5367,17 @@ void ex_splitview(exarg_T *eap) } if (eap->cmdidx == CMD_sfind || eap->cmdidx == CMD_tabfind) { - char *file_to_find = NULL; - char *search_ctx = NULL; - fname = find_file_in_path(eap->arg, strlen(eap->arg), - FNAME_MESS, true, curbuf->b_ffname, - &file_to_find, &search_ctx); - xfree(file_to_find); - vim_findfile_cleanup(search_ctx); + if (*get_findfunc() != NUL) { + fname = findfunc_find_file(eap->arg, strlen(eap->arg), + eap->addr_count > 0 ? eap->line2 : 1); + } else { + char *file_to_find = NULL; + char *search_ctx = NULL; + fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true, + curbuf->b_ffname, &file_to_find, &search_ctx); + xfree(file_to_find); + vim_findfile_cleanup(search_ctx); + } if (fname == NULL) { goto theend; } @@ -5326,12 +5501,14 @@ static void ex_tabs(exarg_T *eap) msg_putchar('\n'); vim_snprintf(IObuff, IOSIZE, _("Tab page %d"), tabcount++); - msg_outtrans(IObuff, HL_ATTR(HLF_T)); + msg_outtrans(IObuff, HLF_T, false); os_breakcheck(); FOR_ALL_WINDOWS_IN_TAB(wp, tp) { if (got_int) { break; + } else if (!wp->w_config.focusable) { + continue; } msg_putchar('\n'); @@ -5344,7 +5521,7 @@ static void ex_tabs(exarg_T *eap) } else { home_replace(wp->w_buffer, wp->w_buffer->b_fname, IObuff, IOSIZE, true); } - msg_outtrans(IObuff, 0); + msg_outtrans(IObuff, 0, false); os_breakcheck(); } } @@ -5398,23 +5575,28 @@ static void ex_find(exarg_T *eap) return; } - char *file_to_find = NULL; - char *search_ctx = NULL; - char *fname = find_file_in_path(eap->arg, strlen(eap->arg), - FNAME_MESS, true, curbuf->b_ffname, - &file_to_find, &search_ctx); - if (eap->addr_count > 0) { - // Repeat finding the file "count" times. This matters when it appears - // several times in the path. - linenr_T count = eap->line2; - while (fname != NULL && --count > 0) { - xfree(fname); - fname = find_file_in_path(NULL, 0, FNAME_MESS, false, curbuf->b_ffname, - &file_to_find, &search_ctx); + char *fname = NULL; + if (*get_findfunc() != NUL) { + fname = findfunc_find_file(eap->arg, strlen(eap->arg), + eap->addr_count > 0 ? eap->line2 : 1); + } else { + char *file_to_find = NULL; + char *search_ctx = NULL; + fname = find_file_in_path(eap->arg, strlen(eap->arg), FNAME_MESS, true, + curbuf->b_ffname, &file_to_find, &search_ctx); + if (eap->addr_count > 0) { + // Repeat finding the file "count" times. This matters when it appears + // several times in the path. + linenr_T count = eap->line2; + while (fname != NULL && --count > 0) { + xfree(fname); + fname = find_file_in_path(NULL, 0, FNAME_MESS, false, + curbuf->b_ffname, &file_to_find, &search_ctx); + } } + xfree(file_to_find); + vim_findfile_cleanup(search_ctx); } - xfree(file_to_find); - vim_findfile_cleanup(search_ctx); if (fname == NULL) { return; @@ -7596,7 +7778,7 @@ void verify_command(char *cmd) if (strcmp("smile", cmd) != 0) { return; // acceptable non-existing command } - int a = HL_ATTR(HLF_E); + int a = HLF_E; msg(" #xxn` #xnxx` ,+x@##@Mz;` .xxx" "xxxxxxnz+, znnnnnnnnnnnnnnnn.", a); msg(" n###z x####` :x##########W+` ,###" |