diff options
author | zeertzjq <zeertzjq@outlook.com> | 2024-08-03 07:42:52 +0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-02 23:42:52 +0000 |
commit | 383f6934720a203d093c762cbd5362092110f35f (patch) | |
tree | 00c2b4ff33f60e4b30247fb6fba197f9d5b9b3c3 | |
parent | e7f8349a2eec01dda531d93fecb8df920b042d9f (diff) | |
download | rneovim-383f6934720a203d093c762cbd5362092110f35f.tar.gz rneovim-383f6934720a203d093c762cbd5362092110f35f.tar.bz2 rneovim-383f6934720a203d093c762cbd5362092110f35f.zip |
refactor: move some functions out of eval.c (#29964)
- common_function() has always been in evalfunc.c in Vim
- return_register() has always been in evalfunc.c in Vim
- get_user_input() was moved to ex_getln.c in Vim 8.1.1957
- tv_get_lnum_buf() was moved to typval.c in Vim 8.2.0847
-rw-r--r-- | src/nvim/decoration_defs.h | 1 | ||||
-rw-r--r-- | src/nvim/eval.c | 375 | ||||
-rw-r--r-- | src/nvim/eval/funcs.c | 209 | ||||
-rw-r--r-- | src/nvim/eval/typval.c | 23 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 146 | ||||
-rw-r--r-- | src/nvim/tag.c | 4 |
6 files changed, 378 insertions, 380 deletions
diff --git a/src/nvim/decoration_defs.h b/src/nvim/decoration_defs.h index c9475257b1..49dc4f9168 100644 --- a/src/nvim/decoration_defs.h +++ b/src/nvim/decoration_defs.h @@ -56,6 +56,7 @@ typedef struct { } DecorHighlightInline; #define DECOR_HIGHLIGHT_INLINE_INIT { 0, DECOR_PRIORITY_BASE, 0, 0 } + typedef struct { uint16_t flags; DecorPriority priority; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index 007b16568f..0180d37ad8 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -39,13 +39,10 @@ #include "nvim/ex_cmds.h" #include "nvim/ex_docmd.h" #include "nvim/ex_eval.h" -#include "nvim/ex_getln.h" #include "nvim/garray.h" #include "nvim/garray_defs.h" -#include "nvim/getchar.h" #include "nvim/gettext_defs.h" #include "nvim/globals.h" -#include "nvim/grid_defs.h" #include "nvim/hashtab.h" #include "nvim/highlight_group.h" #include "nvim/insexpand.h" @@ -74,7 +71,6 @@ #include "nvim/os/os.h" #include "nvim/os/os_defs.h" #include "nvim/os/shell.h" -#include "nvim/os/stdpaths_defs.h" #include "nvim/path.h" #include "nvim/pos_defs.h" #include "nvim/profile.h" @@ -87,10 +83,6 @@ #include "nvim/strings.h" #include "nvim/tag.h" #include "nvim/types_defs.h" -#include "nvim/ui.h" -#include "nvim/ui_compositor.h" -#include "nvim/ui_defs.h" -#include "nvim/usercmd.h" #include "nvim/version.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -5890,317 +5882,6 @@ void f_foreach(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) filter_map(argvars, rettv, FILTERMAP_FOREACH); } -/// "function()" function -/// "funcref()" function -void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) -{ - char *s; - char *name; - bool use_string = false; - partial_T *arg_pt = NULL; - char *trans_name = NULL; - - if (argvars[0].v_type == VAR_FUNC) { - // function(MyFunc, [arg], dict) - s = argvars[0].vval.v_string; - } else if (argvars[0].v_type == VAR_PARTIAL - && argvars[0].vval.v_partial != NULL) { - // function(dict.MyFunc, [arg]) - arg_pt = argvars[0].vval.v_partial; - s = partial_name(arg_pt); - // TODO(bfredl): do the entire nlua_is_table_from_lua dance - } else { - // function('MyFunc', [arg], dict) - s = (char *)tv_get_string(&argvars[0]); - use_string = true; - } - - if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) { - name = s; - trans_name = save_function_name(&name, false, - TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL); - if (*name != NUL) { - s = NULL; - } - } - if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) - || (is_funcref && trans_name == NULL)) { - semsg(_(e_invarg2), (use_string ? tv_get_string(&argvars[0]) : s)); - // Don't check an autoload name for existence here. - } else if (trans_name != NULL - && (is_funcref - ? find_func(trans_name) == NULL - : !translated_function_exists(trans_name))) { - semsg(_("E700: Unknown function: %s"), s); - } else { - int dict_idx = 0; - int arg_idx = 0; - list_T *list = NULL; - if (strncmp(s, "s:", 2) == 0 || strncmp(s, "<SID>", 5) == 0) { - // Expand s: and <SID> into <SNR>nr_, so that the function can - // also be called from another script. Using trans_function_name() - // would also work, but some plugins depend on the name being - // printable text. - name = get_scriptlocal_funcname(s); - } else { - name = xstrdup(s); - } - - if (argvars[1].v_type != VAR_UNKNOWN) { - if (argvars[2].v_type != VAR_UNKNOWN) { - // function(name, [args], dict) - arg_idx = 1; - dict_idx = 2; - } else if (argvars[1].v_type == VAR_DICT) { - // function(name, dict) - dict_idx = 1; - } else { - // function(name, [args]) - arg_idx = 1; - } - if (dict_idx > 0) { - if (tv_check_for_dict_arg(argvars, dict_idx) == FAIL) { - xfree(name); - goto theend; - } - if (argvars[dict_idx].vval.v_dict == NULL) { - dict_idx = 0; - } - } - if (arg_idx > 0) { - if (argvars[arg_idx].v_type != VAR_LIST) { - emsg(_("E923: Second argument of function() must be " - "a list or a dict")); - xfree(name); - goto theend; - } - list = argvars[arg_idx].vval.v_list; - if (tv_list_len(list) == 0) { - arg_idx = 0; - } else if (tv_list_len(list) > MAX_FUNC_ARGS) { - emsg_funcname(e_toomanyarg, s); - xfree(name); - goto theend; - } - } - } - if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) { - partial_T *const pt = xcalloc(1, sizeof(*pt)); - - // result is a VAR_PARTIAL - if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) { - const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); - const int lv_len = tv_list_len(list); - - pt->pt_argc = arg_len + lv_len; - pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * (size_t)pt->pt_argc); - int i = 0; - for (; i < arg_len; i++) { - tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); - } - if (lv_len > 0) { - TV_LIST_ITER(list, li, { - tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); - }); - } - } - - // For "function(dict.func, [], dict)" and "func" is a partial - // use "dict". That is backwards compatible. - if (dict_idx > 0) { - // The dict is bound explicitly, pt_auto is false - pt->pt_dict = argvars[dict_idx].vval.v_dict; - (pt->pt_dict->dv_refcount)++; - } else if (arg_pt != NULL) { - // If the dict was bound automatically the result is also - // bound automatically. - pt->pt_dict = arg_pt->pt_dict; - pt->pt_auto = arg_pt->pt_auto; - if (pt->pt_dict != NULL) { - (pt->pt_dict->dv_refcount)++; - } - } - - pt->pt_refcount = 1; - if (arg_pt != NULL && arg_pt->pt_func != NULL) { - pt->pt_func = arg_pt->pt_func; - func_ptr_ref(pt->pt_func); - xfree(name); - } else if (is_funcref) { - pt->pt_func = find_func(trans_name); - func_ptr_ref(pt->pt_func); - xfree(name); - } else { - pt->pt_name = name; - func_ref(name); - } - - rettv->v_type = VAR_PARTIAL; - rettv->vval.v_partial = pt; - } else { - // result is a VAR_FUNC - rettv->v_type = VAR_FUNC; - rettv->vval.v_string = name; - func_ref(name); - } - } -theend: - xfree(trans_name); -} - -/// Get the line number from Vimscript object -/// -/// @note Unlike tv_get_lnum(), this one supports only "$" special string. -/// -/// @param[in] tv Object to get value from. Is expected to be a number or -/// a special string "$". -/// @param[in] buf Buffer to take last line number from in case tv is "$". May -/// be NULL, in this case "$" results in zero return. -/// -/// @return Line number or 0 in case of error. -linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf) - FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT -{ - if (tv->v_type == VAR_STRING - && tv->vval.v_string != NULL - && tv->vval.v_string[0] == '$' - && tv->vval.v_string[1] == NUL - && buf != NULL) { - return buf->b_ml.ml_line_count; - } - return (linenr_T)tv_get_number_chk(tv, NULL); -} - -/// This function is used by f_input() and f_inputdialog() functions. The third -/// argument to f_input() specifies the type of completion to use at the -/// prompt. The third argument to f_inputdialog() specifies the value to return -/// when the user cancels the prompt. -void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog, - const bool secret) - FUNC_ATTR_NONNULL_ALL -{ - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - - const char *prompt; - const char *defstr = ""; - typval_T *cancelreturn = NULL; - typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE; - const char *xp_name = NULL; - Callback input_callback = { .type = kCallbackNone }; - char prompt_buf[NUMBUFLEN]; - char defstr_buf[NUMBUFLEN]; - char cancelreturn_buf[NUMBUFLEN]; - char xp_name_buf[NUMBUFLEN]; - char def[1] = { 0 }; - if (argvars[0].v_type == VAR_DICT) { - if (argvars[1].v_type != VAR_UNKNOWN) { - emsg(_("E5050: {opts} must be the only argument")); - return; - } - dict_T *const dict = argvars[0].vval.v_dict; - prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); - if (prompt == NULL) { - return; - } - defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, ""); - if (defstr == NULL) { - return; - } - dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn")); - if (cancelreturn_di != NULL) { - cancelreturn = &cancelreturn_di->di_tv; - } - xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"), - xp_name_buf, def); - if (xp_name == NULL) { // error - return; - } - if (xp_name == def) { // default to NULL - xp_name = NULL; - } - if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) { - return; - } - } else { - prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); - if (prompt == NULL) { - return; - } - if (argvars[1].v_type != VAR_UNKNOWN) { - defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf); - if (defstr == NULL) { - return; - } - if (argvars[2].v_type != VAR_UNKNOWN) { - const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf); - if (strarg2 == NULL) { - return; - } - if (inputdialog) { - cancelreturn_strarg2.v_type = VAR_STRING; - cancelreturn_strarg2.vval.v_string = (char *)strarg2; - cancelreturn = &cancelreturn_strarg2; - } else { - xp_name = strarg2; - } - } - } - } - - int xp_type = EXPAND_NOTHING; - char *xp_arg = NULL; - if (xp_name != NULL) { - // input() with a third argument: completion - const int xp_namelen = (int)strlen(xp_name); - - uint32_t argt = 0; - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, - &argt, &xp_arg) == FAIL) { - return; - } - } - - const bool cmd_silent_save = cmd_silent; - - cmd_silent = false; // Want to see the prompt. - // Only the part of the message after the last NL is considered as - // prompt for the command line, unlsess cmdline is externalized - const char *p = prompt; - if (!ui_has(kUICmdline)) { - const char *lastnl = strrchr(prompt, '\n'); - if (lastnl != NULL) { - p = lastnl + 1; - msg_start(); - msg_clr_eos(); - msg_puts_len(prompt, p - prompt, echo_attr); - msg_didout = false; - msg_starthere(); - } - } - cmdline_row = msg_row; - - stuffReadbuffSpec(defstr); - - const int save_ex_normal_busy = ex_normal_busy; - ex_normal_busy = 0; - rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, echo_attr, xp_type, xp_arg, - input_callback); - ex_normal_busy = save_ex_normal_busy; - callback_free(&input_callback); - - if (rettv->vval.v_string == NULL && cancelreturn != NULL) { - tv_copy(cancelreturn, rettv); - } - - xfree(xp_arg); - - // Since the user typed this, no need to wait for return. - need_wait_return = false; - msg_didout = false; - cmd_silent = cmd_silent_save; -} - /// Builds a process argument vector from a Vimscript object (typval_T). /// /// @param[in] cmd_tv Vimscript object @@ -6269,56 +5950,6 @@ char **tv_to_argv(typval_T *cmd_tv, const char **cmd, bool *executable) return argv; } -void return_register(int regname, typval_T *rettv) -{ - char buf[2] = { (char)regname, 0 }; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = xstrdup(buf); -} - -void screenchar_adjust(ScreenGrid **grid, int *row, int *col) -{ - // TODO(bfredl): this is a hack for legacy tests which use screenchar() - // to check printed messages on the screen (but not floats etc - // as these are not legacy features). If the compositor is refactored to - // have its own buffer, this should just read from it instead. - msg_scroll_flush(); - - *grid = ui_comp_get_grid_at_coord(*row, *col); - - // Make `row` and `col` relative to the grid - *row -= (*grid)->comp_row; - *col -= (*grid)->comp_col; -} - -/// "stdpath()" helper for list results -void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) - FUNC_ATTR_NONNULL_ALL -{ - list_T *const list = tv_list_alloc(kListLenShouldKnow); - rettv->v_type = VAR_LIST; - rettv->vval.v_list = list; - tv_list_ref(list); - char *const dirs = stdpaths_get_xdg_var(xdg); - if (dirs == NULL) { - return; - } - const void *iter = NULL; - const char *appname = get_appname(); - do { - size_t dir_len; - const char *dir; - iter = vim_env_iter(ENV_SEPCHAR, dirs, iter, &dir, &dir_len); - if (dir != NULL && dir_len > 0) { - char *dir_with_nvim = xmemdupz(dir, dir_len); - dir_with_nvim = concat_fnames_realloc(dir_with_nvim, appname, true); - tv_list_append_allocated_string(list, dir_with_nvim); - } - } while (iter != NULL); - xfree(dirs); -} - static list_T *string_to_list(const char *str, size_t len, const bool keepempty) { if (!keepempty && str[len - 1] == NL) { @@ -8522,6 +8153,12 @@ void ex_echohl(exarg_T *eap) echo_attr = syn_name2attr(eap->arg); } +/// Returns the :echo attribute +int get_echo_attr(void) +{ + return echo_attr; +} + /// ":execute expr1 ..." execute the result of an expression. /// ":echomsg expr1 ..." Print a message /// ":echoerr expr1 ..." Print an error diff --git a/src/nvim/eval/funcs.c b/src/nvim/eval/funcs.c index bd977523c6..d2f6af4d9e 100644 --- a/src/nvim/eval/funcs.c +++ b/src/nvim/eval/funcs.c @@ -134,6 +134,7 @@ #include "nvim/tag.h" #include "nvim/types_defs.h" #include "nvim/ui.h" +#include "nvim/ui_compositor.h" #include "nvim/version.h" #include "nvim/vim_defs.h" #include "nvim/window.h" @@ -2272,6 +2273,164 @@ static void f_foreground(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { } +/// "function()" function +/// "funcref()" function +static void common_function(typval_T *argvars, typval_T *rettv, bool is_funcref) +{ + char *s; + char *name; + bool use_string = false; + partial_T *arg_pt = NULL; + char *trans_name = NULL; + + if (argvars[0].v_type == VAR_FUNC) { + // function(MyFunc, [arg], dict) + s = argvars[0].vval.v_string; + } else if (argvars[0].v_type == VAR_PARTIAL + && argvars[0].vval.v_partial != NULL) { + // function(dict.MyFunc, [arg]) + arg_pt = argvars[0].vval.v_partial; + s = partial_name(arg_pt); + // TODO(bfredl): do the entire nlua_is_table_from_lua dance + } else { + // function('MyFunc', [arg], dict) + s = (char *)tv_get_string(&argvars[0]); + use_string = true; + } + + if ((use_string && vim_strchr(s, AUTOLOAD_CHAR) == NULL) || is_funcref) { + name = s; + trans_name = save_function_name(&name, false, + TFN_INT | TFN_QUIET | TFN_NO_AUTOLOAD | TFN_NO_DEREF, NULL); + if (*name != NUL) { + s = NULL; + } + } + if (s == NULL || *s == NUL || (use_string && ascii_isdigit(*s)) + || (is_funcref && trans_name == NULL)) { + semsg(_(e_invarg2), (use_string ? tv_get_string(&argvars[0]) : s)); + // Don't check an autoload name for existence here. + } else if (trans_name != NULL + && (is_funcref + ? find_func(trans_name) == NULL + : !translated_function_exists(trans_name))) { + semsg(_("E700: Unknown function: %s"), s); + } else { + int dict_idx = 0; + int arg_idx = 0; + list_T *list = NULL; + if (strncmp(s, "s:", 2) == 0 || strncmp(s, "<SID>", 5) == 0) { + // Expand s: and <SID> into <SNR>nr_, so that the function can + // also be called from another script. Using trans_function_name() + // would also work, but some plugins depend on the name being + // printable text. + name = get_scriptlocal_funcname(s); + } else { + name = xstrdup(s); + } + + if (argvars[1].v_type != VAR_UNKNOWN) { + if (argvars[2].v_type != VAR_UNKNOWN) { + // function(name, [args], dict) + arg_idx = 1; + dict_idx = 2; + } else if (argvars[1].v_type == VAR_DICT) { + // function(name, dict) + dict_idx = 1; + } else { + // function(name, [args]) + arg_idx = 1; + } + if (dict_idx > 0) { + if (tv_check_for_dict_arg(argvars, dict_idx) == FAIL) { + xfree(name); + goto theend; + } + if (argvars[dict_idx].vval.v_dict == NULL) { + dict_idx = 0; + } + } + if (arg_idx > 0) { + if (argvars[arg_idx].v_type != VAR_LIST) { + emsg(_("E923: Second argument of function() must be " + "a list or a dict")); + xfree(name); + goto theend; + } + list = argvars[arg_idx].vval.v_list; + if (tv_list_len(list) == 0) { + arg_idx = 0; + } else if (tv_list_len(list) > MAX_FUNC_ARGS) { + emsg_funcname(e_toomanyarg, s); + xfree(name); + goto theend; + } + } + } + if (dict_idx > 0 || arg_idx > 0 || arg_pt != NULL || is_funcref) { + partial_T *const pt = xcalloc(1, sizeof(*pt)); + + // result is a VAR_PARTIAL + if (arg_idx > 0 || (arg_pt != NULL && arg_pt->pt_argc > 0)) { + const int arg_len = (arg_pt == NULL ? 0 : arg_pt->pt_argc); + const int lv_len = tv_list_len(list); + + pt->pt_argc = arg_len + lv_len; + pt->pt_argv = xmalloc(sizeof(pt->pt_argv[0]) * (size_t)pt->pt_argc); + int i = 0; + for (; i < arg_len; i++) { + tv_copy(&arg_pt->pt_argv[i], &pt->pt_argv[i]); + } + if (lv_len > 0) { + TV_LIST_ITER(list, li, { + tv_copy(TV_LIST_ITEM_TV(li), &pt->pt_argv[i++]); + }); + } + } + + // For "function(dict.func, [], dict)" and "func" is a partial + // use "dict". That is backwards compatible. + if (dict_idx > 0) { + // The dict is bound explicitly, pt_auto is false + pt->pt_dict = argvars[dict_idx].vval.v_dict; + (pt->pt_dict->dv_refcount)++; + } else if (arg_pt != NULL) { + // If the dict was bound automatically the result is also + // bound automatically. + pt->pt_dict = arg_pt->pt_dict; + pt->pt_auto = arg_pt->pt_auto; + if (pt->pt_dict != NULL) { + (pt->pt_dict->dv_refcount)++; + } + } + + pt->pt_refcount = 1; + if (arg_pt != NULL && arg_pt->pt_func != NULL) { + pt->pt_func = arg_pt->pt_func; + func_ptr_ref(pt->pt_func); + xfree(name); + } else if (is_funcref) { + pt->pt_func = find_func(trans_name); + func_ptr_ref(pt->pt_func); + xfree(name); + } else { + pt->pt_name = name; + func_ref(name); + } + + rettv->v_type = VAR_PARTIAL; + rettv->vval.v_partial = pt; + } else { + // result is a VAR_FUNC + rettv->v_type = VAR_FUNC; + rettv->vval.v_string = name; + func_ref(name); + } + } +theend: + xfree(trans_name); +} + static void f_funcref(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { common_function(argvars, rettv, true); @@ -6414,6 +6573,14 @@ static void f_getreginfo(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } +static void return_register(int regname, typval_T *rettv) +{ + char buf[2] = { (char)regname, 0 }; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = xstrdup(buf); +} + /// "reg_executing()" function static void f_reg_executing(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -7439,6 +7606,21 @@ static void f_rpcstop(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } } +static void screenchar_adjust(ScreenGrid **grid, int *row, int *col) +{ + // TODO(bfredl): this is a hack for legacy tests which use screenchar() + // to check printed messages on the screen (but not floats etc + // as these are not legacy features). If the compositor is refactored to + // have its own buffer, this should just read from it instead. + msg_scroll_flush(); + + *grid = ui_comp_get_grid_at_coord(*row, *col); + + // Make `row` and `col` relative to the grid + *row -= (*grid)->comp_row; + *col -= (*grid)->comp_col; +} + /// "screenattr()" function static void f_screenattr(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { @@ -8625,6 +8807,33 @@ theend: p_cpo = save_cpo; } +/// "stdpath()" helper for list results +static void get_xdg_var_list(const XDGVarType xdg, typval_T *rettv) + FUNC_ATTR_NONNULL_ALL +{ + list_T *const list = tv_list_alloc(kListLenShouldKnow); + rettv->v_type = VAR_LIST; + rettv->vval.v_list = list; + tv_list_ref(list); + char *const dirs = stdpaths_get_xdg_var(xdg); + if (dirs == NULL) { + return; + } + const void *iter = NULL; + const char *appname = get_appname(); + do { + size_t dir_len; + const char *dir; + iter = vim_env_iter(ENV_SEPCHAR, dirs, iter, &dir, &dir_len); + if (dir != NULL && dir_len > 0) { + char *dir_with_nvim = xmemdupz(dir, dir_len); + dir_with_nvim = concat_fnames_realloc(dir_with_nvim, appname, true); + tv_list_append_allocated_string(list, dir_with_nvim); + } + } while (iter != NULL); + xfree(dirs); +} + /// "stdpath(type)" function static void f_stdpath(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { diff --git a/src/nvim/eval/typval.c b/src/nvim/eval/typval.c index 4458dba27d..c00abe452c 100644 --- a/src/nvim/eval/typval.c +++ b/src/nvim/eval/typval.c @@ -4208,6 +4208,29 @@ linenr_T tv_get_lnum(const typval_T *const tv) return lnum; } +/// Get the line number from Vimscript object +/// +/// @note Unlike tv_get_lnum(), this one supports only "$" special string. +/// +/// @param[in] tv Object to get value from. Is expected to be a number or +/// a special string "$". +/// @param[in] buf Buffer to take last line number from in case tv is "$". May +/// be NULL, in this case "$" results in zero return. +/// +/// @return Line number or 0 in case of error. +linenr_T tv_get_lnum_buf(const typval_T *const tv, const buf_T *const buf) + FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT +{ + if (tv->v_type == VAR_STRING + && tv->vval.v_string != NULL + && tv->vval.v_string[0] == '$' + && tv->vval.v_string[1] == NUL + && buf != NULL) { + return buf->b_ml.ml_line_count; + } + return (linenr_T)tv_get_number_chk(tv, NULL); +} + /// Get the floating-point value of a Vimscript object /// /// Raises an error if object is not number or floating-point. diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index c8ca6f0e4e..8a34e03d91 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -267,6 +267,18 @@ static void init_incsearch_state(incsearch_state_T *s) save_viewstate(curwin, &s->old_viewstate); } +static void set_search_match(pos_T *t) +{ + // First move cursor to end of match, then to the start. This + // moves the whole match onto the screen when 'nowrap' is set. + t->lnum += search_match_lines; + t->col = search_match_endcol; + if (t->lnum > curbuf->b_ml.ml_line_count) { + t->lnum = curbuf->b_ml.ml_line_count; + coladvance(curwin, MAXCOL); + } +} + // Return true when 'incsearch' highlighting is to be done. // Sets search_first_line and search_last_line to the address range. static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_state_T *s, @@ -4622,14 +4634,132 @@ char *script_get(exarg_T *const eap, size_t *const lenp) return (char *)ga.ga_data; } -static void set_search_match(pos_T *t) +/// This function is used by f_input() and f_inputdialog() functions. The third +/// argument to f_input() specifies the type of completion to use at the +/// prompt. The third argument to f_inputdialog() specifies the value to return +/// when the user cancels the prompt. +void get_user_input(const typval_T *const argvars, typval_T *const rettv, const bool inputdialog, + const bool secret) + FUNC_ATTR_NONNULL_ALL { - // First move cursor to end of match, then to the start. This - // moves the whole match onto the screen when 'nowrap' is set. - t->lnum += search_match_lines; - t->col = search_match_endcol; - if (t->lnum > curbuf->b_ml.ml_line_count) { - t->lnum = curbuf->b_ml.ml_line_count; - coladvance(curwin, MAXCOL); + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + + const char *prompt; + const char *defstr = ""; + typval_T *cancelreturn = NULL; + typval_T cancelreturn_strarg2 = TV_INITIAL_VALUE; + const char *xp_name = NULL; + Callback input_callback = { .type = kCallbackNone }; + char prompt_buf[NUMBUFLEN]; + char defstr_buf[NUMBUFLEN]; + char cancelreturn_buf[NUMBUFLEN]; + char xp_name_buf[NUMBUFLEN]; + char def[1] = { 0 }; + if (argvars[0].v_type == VAR_DICT) { + if (argvars[1].v_type != VAR_UNKNOWN) { + emsg(_("E5050: {opts} must be the only argument")); + return; + } + dict_T *const dict = argvars[0].vval.v_dict; + prompt = tv_dict_get_string_buf_chk(dict, S_LEN("prompt"), prompt_buf, ""); + if (prompt == NULL) { + return; + } + defstr = tv_dict_get_string_buf_chk(dict, S_LEN("default"), defstr_buf, ""); + if (defstr == NULL) { + return; + } + dictitem_T *cancelreturn_di = tv_dict_find(dict, S_LEN("cancelreturn")); + if (cancelreturn_di != NULL) { + cancelreturn = &cancelreturn_di->di_tv; + } + xp_name = tv_dict_get_string_buf_chk(dict, S_LEN("completion"), + xp_name_buf, def); + if (xp_name == NULL) { // error + return; + } + if (xp_name == def) { // default to NULL + xp_name = NULL; + } + if (!tv_dict_get_callback(dict, S_LEN("highlight"), &input_callback)) { + return; + } + } else { + prompt = tv_get_string_buf_chk(&argvars[0], prompt_buf); + if (prompt == NULL) { + return; + } + if (argvars[1].v_type != VAR_UNKNOWN) { + defstr = tv_get_string_buf_chk(&argvars[1], defstr_buf); + if (defstr == NULL) { + return; + } + if (argvars[2].v_type != VAR_UNKNOWN) { + const char *const strarg2 = tv_get_string_buf_chk(&argvars[2], cancelreturn_buf); + if (strarg2 == NULL) { + return; + } + if (inputdialog) { + cancelreturn_strarg2.v_type = VAR_STRING; + cancelreturn_strarg2.vval.v_string = (char *)strarg2; + cancelreturn = &cancelreturn_strarg2; + } else { + xp_name = strarg2; + } + } + } + } + + int xp_type = EXPAND_NOTHING; + char *xp_arg = NULL; + if (xp_name != NULL) { + // input() with a third argument: completion + const int xp_namelen = (int)strlen(xp_name); + + uint32_t argt = 0; + if (parse_compl_arg(xp_name, xp_namelen, &xp_type, + &argt, &xp_arg) == FAIL) { + return; + } + } + + const bool cmd_silent_save = cmd_silent; + + cmd_silent = false; // Want to see the prompt. + // Only the part of the message after the last NL is considered as + // prompt for the command line, unlsess cmdline is externalized + const char *p = prompt; + if (!ui_has(kUICmdline)) { + const char *lastnl = strrchr(prompt, '\n'); + if (lastnl != NULL) { + p = lastnl + 1; + msg_start(); + msg_clr_eos(); + msg_puts_len(prompt, p - prompt, get_echo_attr()); + msg_didout = false; + msg_starthere(); + } } + cmdline_row = msg_row; + + stuffReadbuffSpec(defstr); + + const int save_ex_normal_busy = ex_normal_busy; + ex_normal_busy = 0; + rettv->vval.v_string = getcmdline_prompt(secret ? NUL : '@', p, get_echo_attr(), + xp_type, xp_arg, input_callback); + ex_normal_busy = save_ex_normal_busy; + callback_free(&input_callback); + + if (rettv->vval.v_string == NULL && cancelreturn != NULL) { + tv_copy(cancelreturn, rettv); + } + + xfree(xp_arg); + + // Since the user typed this, no need to wait for return. + need_wait_return = false; + msg_didout = false; + cmd_silent = cmd_silent_save; } diff --git a/src/nvim/tag.c b/src/nvim/tag.c index 01200e43e0..7a0c1cd810 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -3152,9 +3152,7 @@ static int find_extra(char **pp) return FAIL; } -// -// Free a single entry in a tag stack -// +/// Free a single entry in a tag stack void tagstack_clear_entry(taggy_T *item) { XFREE_CLEAR(item->tagname); |