# HG changeset patch # User Bram Moolenaar # Date 1567347303 -7200 # Node ID 4d63d47d87ef4bce7f31f6aa16878abfdcbbaeb4 # Parent 14be395d672c303faf5fc96bd998e62bcb9e1879 patch 8.1.1957: more code can be moved to evalvars.c Commit: https://github.com/vim/vim/commit/da6c03342117fb7f4a8110bd9e8627b612a05a64 Author: Bram Moolenaar Date: Sun Sep 1 16:01:30 2019 +0200 patch 8.1.1957: more code can be moved to evalvars.c Problem: More code can be moved to evalvars.c. Solution: Move code to where it fits better. (Yegappan Lakshmanan, closes #4883) diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -38,9 +38,6 @@ static int current_copyID = 0; static int echo_attr = 0; /* attributes used for ":echo" */ -/* The names of packages that once were loaded are remembered. */ -static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; - /* * Info used by a ":for" loop. */ @@ -156,8 +153,8 @@ eval_clear(void) free_scriptnames(); free_locales(); - /* autoloaded script names */ - ga_clear_strings(&ga_loaded); + // autoloaded script names + free_autoload_scriptnames(); // unreferenced lists and dicts (void)garbage_collect(FALSE); @@ -167,240 +164,6 @@ eval_clear(void) } #endif -static lval_T *redir_lval = NULL; -#define EVALCMD_BUSY (redir_lval == (lval_T *)&redir_lval) -static garray_T redir_ga; /* only valid when redir_lval is not NULL */ -static char_u *redir_endp = NULL; -static char_u *redir_varname = NULL; - -/* - * Start recording command output to a variable - * When "append" is TRUE append to an existing variable. - * Returns OK if successfully completed the setup. FAIL otherwise. - */ - int -var_redir_start(char_u *name, int append) -{ - int save_emsg; - int err; - typval_T tv; - - /* Catch a bad name early. */ - if (!eval_isnamec1(*name)) - { - emsg(_(e_invarg)); - return FAIL; - } - - /* Make a copy of the name, it is used in redir_lval until redir ends. */ - redir_varname = vim_strsave(name); - if (redir_varname == NULL) - return FAIL; - - redir_lval = ALLOC_CLEAR_ONE(lval_T); - if (redir_lval == NULL) - { - var_redir_stop(); - return FAIL; - } - - /* The output is stored in growarray "redir_ga" until redirection ends. */ - ga_init2(&redir_ga, (int)sizeof(char), 500); - - /* Parse the variable name (can be a dict or list entry). */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0, - FNE_CHECK_START); - if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL) - { - clear_lval(redir_lval); - if (redir_endp != NULL && *redir_endp != NUL) - /* Trailing characters are present after the variable name */ - emsg(_(e_trailing)); - else - emsg(_(e_invarg)); - redir_endp = NULL; /* don't store a value, only cleanup */ - var_redir_stop(); - return FAIL; - } - - /* check if we can write to the variable: set it to or append an empty - * string */ - save_emsg = did_emsg; - did_emsg = FALSE; - tv.v_type = VAR_STRING; - tv.vval.v_string = (char_u *)""; - if (append) - set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"."); - else - set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"="); - clear_lval(redir_lval); - err = did_emsg; - did_emsg |= save_emsg; - if (err) - { - redir_endp = NULL; /* don't store a value, only cleanup */ - var_redir_stop(); - return FAIL; - } - - return OK; -} - -/* - * Append "value[value_len]" to the variable set by var_redir_start(). - * The actual appending is postponed until redirection ends, because the value - * appended may in fact be the string we write to, changing it may cause freed - * memory to be used: - * :redir => foo - * :let foo - * :redir END - */ - void -var_redir_str(char_u *value, int value_len) -{ - int len; - - if (redir_lval == NULL) - return; - - if (value_len == -1) - len = (int)STRLEN(value); /* Append the entire string */ - else - len = value_len; /* Append only "value_len" characters */ - - if (ga_grow(&redir_ga, len) == OK) - { - mch_memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, len); - redir_ga.ga_len += len; - } - else - var_redir_stop(); -} - -/* - * Stop redirecting command output to a variable. - * Frees the allocated memory. - */ - void -var_redir_stop(void) -{ - typval_T tv; - - if (EVALCMD_BUSY) - { - redir_lval = NULL; - return; - } - - if (redir_lval != NULL) - { - /* If there was no error: assign the text to the variable. */ - if (redir_endp != NULL) - { - ga_append(&redir_ga, NUL); /* Append the trailing NUL. */ - tv.v_type = VAR_STRING; - tv.vval.v_string = redir_ga.ga_data; - /* Call get_lval() again, if it's inside a Dict or List it may - * have changed. */ - redir_endp = get_lval(redir_varname, NULL, redir_lval, - FALSE, FALSE, 0, FNE_CHECK_START); - if (redir_endp != NULL && redir_lval->ll_name != NULL) - set_var_lval(redir_lval, redir_endp, &tv, FALSE, FALSE, - (char_u *)"."); - clear_lval(redir_lval); - } - - /* free the collected output */ - VIM_CLEAR(redir_ga.ga_data); - - VIM_CLEAR(redir_lval); - } - VIM_CLEAR(redir_varname); -} - - int -eval_charconvert( - char_u *enc_from, - char_u *enc_to, - char_u *fname_from, - char_u *fname_to) -{ - int err = FALSE; - - set_vim_var_string(VV_CC_FROM, enc_from, -1); - set_vim_var_string(VV_CC_TO, enc_to, -1); - set_vim_var_string(VV_FNAME_IN, fname_from, -1); - set_vim_var_string(VV_FNAME_OUT, fname_to, -1); - if (eval_to_bool(p_ccv, &err, NULL, FALSE)) - err = TRUE; - set_vim_var_string(VV_CC_FROM, NULL, -1); - set_vim_var_string(VV_CC_TO, NULL, -1); - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_FNAME_OUT, NULL, -1); - - if (err) - return FAIL; - return OK; -} - -# if defined(FEAT_POSTSCRIPT) || defined(PROTO) - int -eval_printexpr(char_u *fname, char_u *args) -{ - int err = FALSE; - - set_vim_var_string(VV_FNAME_IN, fname, -1); - set_vim_var_string(VV_CMDARG, args, -1); - if (eval_to_bool(p_pexpr, &err, NULL, FALSE)) - err = TRUE; - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_CMDARG, NULL, -1); - - if (err) - { - mch_remove(fname); - return FAIL; - } - return OK; -} -# endif - -# if defined(FEAT_DIFF) || defined(PROTO) - void -eval_diff( - char_u *origfile, - char_u *newfile, - char_u *outfile) -{ - int err = FALSE; - - set_vim_var_string(VV_FNAME_IN, origfile, -1); - set_vim_var_string(VV_FNAME_NEW, newfile, -1); - set_vim_var_string(VV_FNAME_OUT, outfile, -1); - (void)eval_to_bool(p_dex, &err, NULL, FALSE); - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_FNAME_NEW, NULL, -1); - set_vim_var_string(VV_FNAME_OUT, NULL, -1); -} - - void -eval_patch( - char_u *origfile, - char_u *difffile, - char_u *outfile) -{ - int err; - - set_vim_var_string(VV_FNAME_IN, origfile, -1); - set_vim_var_string(VV_FNAME_DIFF, difffile, -1); - set_vim_var_string(VV_FNAME_OUT, outfile, -1); - (void)eval_to_bool(p_pex, &err, NULL, FALSE); - set_vim_var_string(VV_FNAME_IN, NULL, -1); - set_vim_var_string(VV_FNAME_DIFF, NULL, -1); - set_vim_var_string(VV_FNAME_OUT, NULL, -1); -} -# endif - /* * Top level evaluation function, returning a boolean. * Sets "error" to TRUE if there was an error. @@ -671,65 +434,6 @@ eval_to_number(char_u *expr) return retval; } -#if defined(FEAT_SPELL) || defined(PROTO) -/* - * Evaluate an expression to a list with suggestions. - * For the "expr:" part of 'spellsuggest'. - * Returns NULL when there is an error. - */ - list_T * -eval_spell_expr(char_u *badword, char_u *expr) -{ - typval_T save_val; - typval_T rettv; - list_T *list = NULL; - char_u *p = skipwhite(expr); - - /* Set "v:val" to the bad word. */ - prepare_vimvar(VV_VAL, &save_val); - set_vim_var_string(VV_VAL, badword, -1); - if (p_verbose == 0) - ++emsg_off; - - if (eval1(&p, &rettv, TRUE) == OK) - { - if (rettv.v_type != VAR_LIST) - clear_tv(&rettv); - else - list = rettv.vval.v_list; - } - - if (p_verbose == 0) - --emsg_off; - clear_tv(get_vim_var_tv(VV_VAL)); - restore_vimvar(VV_VAL, &save_val); - - return list; -} - -/* - * "list" is supposed to contain two items: a word and a number. Return the - * word in "pp" and the number as the return value. - * Return -1 if anything isn't right. - * Used to get the good word and score from the eval_spell_expr() result. - */ - int -get_spellword(list_T *list, char_u **pp) -{ - listitem_T *li; - - li = list->lv_first; - if (li == NULL) - return -1; - *pp = tv_get_string(&li->li_tv); - - li = li->li_next; - if (li == NULL) - return -1; - return (int)tv_get_number(&li->li_tv); -} -#endif - /* * Top level evaluation function. * Returns an allocated typval_T with the result. @@ -1153,7 +857,7 @@ get_lval( if (lp->ll_di == NULL) { // Can't add "v:" or "a:" variable. - if (lp->ll_dict == &vimvardict + if (lp->ll_dict == get_vimvar_dict() || &lp->ll_dict->dv_hashtab == get_funccal_args_ht()) { semsg(_(e_illvar), name); @@ -1921,31 +1625,6 @@ set_context_for_expression( xp->xp_pattern = arg; } -#if (defined(FEAT_MENU) && defined(FEAT_MULTI_LANG)) || defined(PROTO) -/* - * Delete all "menutrans_" variables. - */ - void -del_menutrans_vars(void) -{ - hashitem_T *hi; - int todo; - - hash_lock(&globvarht); - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi) - { - if (!HASHITEM_EMPTY(hi)) - { - --todo; - if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0) - delete_var(&globvarht, hi); - } - } - hash_unlock(&globvarht); -} -#endif - /* * Return TRUE if "pat" matches "text". * Does not use 'cpo' and always uses 'magic'. @@ -4215,7 +3894,7 @@ garbage_collect(int testing) abort = abort || set_ref_in_item(&tp->tp_winvar.di_tv, copyID, NULL, NULL); /* global variables */ - abort = abort || set_ref_in_ht(&globvarht, copyID, NULL); + abort = abort || garbage_collect_globvars(copyID); /* function-local variables */ abort = abort || set_ref_in_call_stack(copyID); @@ -4630,20 +4309,6 @@ set_ref_in_item( return abort; } - static char * -get_var_special_name(int nr) -{ - switch (nr) - { - case VVAL_FALSE: return "v:false"; - case VVAL_TRUE: return "v:true"; - case VVAL_NONE: return "v:none"; - case VVAL_NULL: return "v:null"; - } - internal_error("get_var_special_name()"); - return "42"; -} - /* * Return a string with the string representation of a variable. * If the memory is allocated "tofree" is set to it, otherwise NULL. @@ -6204,112 +5869,6 @@ item_copy( } /* - * 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( - typval_T *argvars, - typval_T *rettv, - int inputdialog, - int secret) -{ - char_u *prompt = tv_get_string_chk(&argvars[0]); - char_u *p = NULL; - int c; - char_u buf[NUMBUFLEN]; - int cmd_silent_save = cmd_silent; - char_u *defstr = (char_u *)""; - int xp_type = EXPAND_NOTHING; - char_u *xp_arg = NULL; - - rettv->v_type = VAR_STRING; - rettv->vval.v_string = NULL; - -#ifdef NO_CONSOLE_INPUT - /* While starting up, there is no place to enter text. When running tests - * with --not-a-term we assume feedkeys() will be used. */ - if (no_console_input() && !is_not_a_term()) - return; -#endif - - cmd_silent = FALSE; /* Want to see the prompt. */ - if (prompt != NULL) - { - /* Only the part of the message after the last NL is considered as - * prompt for the command line */ - p = vim_strrchr(prompt, '\n'); - if (p == NULL) - p = prompt; - else - { - ++p; - c = *p; - *p = NUL; - msg_start(); - msg_clr_eos(); - msg_puts_attr((char *)prompt, echo_attr); - msg_didout = FALSE; - msg_starthere(); - *p = c; - } - cmdline_row = msg_row; - - if (argvars[1].v_type != VAR_UNKNOWN) - { - defstr = tv_get_string_buf_chk(&argvars[1], buf); - if (defstr != NULL) - stuffReadbuffSpec(defstr); - - if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) - { - char_u *xp_name; - int xp_namelen; - long argt; - - /* input() with a third argument: completion */ - rettv->vval.v_string = NULL; - - xp_name = tv_get_string_buf_chk(&argvars[2], buf); - if (xp_name == NULL) - return; - - xp_namelen = (int)STRLEN(xp_name); - - if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, - &xp_arg) == FAIL) - return; - } - } - - if (defstr != NULL) - { - 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); - ex_normal_busy = save_ex_normal_busy; - } - if (inputdialog && rettv->vval.v_string == NULL - && argvars[1].v_type != VAR_UNKNOWN - && argvars[2].v_type != VAR_UNKNOWN) - rettv->vval.v_string = vim_strsave(tv_get_string_buf( - &argvars[2], buf)); - - vim_free(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; -} - -/* * ":echo expr1 ..." print each argument separated with a space, add a * newline at the end. * ":echon expr1 ..." print each argument plain. @@ -6425,6 +5984,15 @@ ex_echohl(exarg_T *eap) } /* + * 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 @@ -6551,78 +6119,6 @@ find_option_end(char_u **arg, int *opt_f } /* - * Return the autoload script name for a function or variable name. - * Returns NULL when out of memory. - * Caller must make sure that "name" contains AUTOLOAD_CHAR. - */ - char_u * -autoload_name(char_u *name) -{ - char_u *p, *q = NULL; - char_u *scriptname; - - // Get the script file name: replace '#' with '/', append ".vim". - scriptname = alloc(STRLEN(name) + 14); - if (scriptname == NULL) - return NULL; - STRCPY(scriptname, "autoload/"); - STRCAT(scriptname, name); - for (p = scriptname + 9; (p = vim_strchr(p, AUTOLOAD_CHAR)) != NULL; - q = p, ++p) - *p = '/'; - STRCPY(q, ".vim"); - return scriptname; -} - -/* - * If "name" has a package name try autoloading the script for it. - * Return TRUE if a package was loaded. - */ - int -script_autoload( - char_u *name, - int reload) /* load script again when already loaded */ -{ - char_u *p; - char_u *scriptname, *tofree; - int ret = FALSE; - int i; - - /* If there is no '#' after name[0] there is no package name. */ - p = vim_strchr(name, AUTOLOAD_CHAR); - if (p == NULL || p == name) - return FALSE; - - tofree = scriptname = autoload_name(name); - if (scriptname == NULL) - return FALSE; - - /* Find the name in the list of previously loaded package names. Skip - * "autoload/", it's always the same. */ - for (i = 0; i < ga_loaded.ga_len; ++i) - if (STRCMP(((char_u **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) - break; - if (!reload && i < ga_loaded.ga_len) - ret = FALSE; /* was loaded already */ - else - { - /* Remember the name if it wasn't loaded already. */ - if (i == ga_loaded.ga_len && ga_grow(&ga_loaded, 1) == OK) - { - ((char_u **)ga_loaded.ga_data)[ga_loaded.ga_len++] = scriptname; - tofree = NULL; - } - - /* Try loading the package from $VIMRUNTIME/autoload/.vim */ - if (source_runtime(scriptname, 0) == OK) - ret = TRUE; - } - - vim_free(tofree); - return ret; -} - -/* * Display script name where an item was last set. * Should only be invoked when 'verbose' is non-zero. */ @@ -7739,6 +7235,8 @@ filter_map(typval_T *argvars, typval_T * prepare_vimvar(VV_KEY, &save_key); if (argvars[0].v_type == VAR_DICT) { + set_vim_var_type(VV_KEY, VAR_STRING); + ht = &d->dv_hashtab; hash_lock(ht); todo = (int)ht->ht_used; diff --git a/src/evalvars.c b/src/evalvars.c --- a/src/evalvars.c +++ b/src/evalvars.c @@ -18,6 +18,8 @@ static char *e_letunexp = N_("E18: Unexpected characters in :let"); static dictitem_T globvars_var; // variable used for g: +static dict_T globvardict; // Dictionary with g: variables +#define globvarht globvardict.dv_hashtab /* * Old Vim variables such as "v:version" are also available without the "v:". @@ -154,6 +156,7 @@ static struct vimvar #define vv_tv vv_di.di_tv static dictitem_T vimvars_var; // variable used for v: +static dict_T vimvardict; // Dictionary with v: variables #define vimvarht vimvardict.dv_hashtab // for VIM_VERSION_ defines @@ -185,6 +188,7 @@ static void ex_unletlock(exarg_T *eap, c static int do_unlet_var(lval_T *lp, char_u *name_end, int forceit); static int do_lock_var(lval_T *lp, char_u *name_end, int deep, int lock); static void item_lock(typval_T *tv, int deep, int lock); +static void delete_var(hashtab_T *ht, hashitem_T *hi); static void list_one_var(dictitem_T *v, char *prefix, int *first); static void list_one_var_a(char *prefix, char_u *name, int type, char_u *string, int *first); @@ -296,6 +300,12 @@ evalvars_clear(void) #endif int +garbage_collect_globvars(int copyID) +{ + return set_ref_in_ht(&globvarht, copyID, NULL); +} + + int garbage_collect_vimvars(int copyID) { return set_ref_in_ht(&vimvarht, copyID, NULL); @@ -335,6 +345,148 @@ set_internal_string_var(char_u *name, ch } } + int +eval_charconvert( + char_u *enc_from, + char_u *enc_to, + char_u *fname_from, + char_u *fname_to) +{ + int err = FALSE; + + set_vim_var_string(VV_CC_FROM, enc_from, -1); + set_vim_var_string(VV_CC_TO, enc_to, -1); + set_vim_var_string(VV_FNAME_IN, fname_from, -1); + set_vim_var_string(VV_FNAME_OUT, fname_to, -1); + if (eval_to_bool(p_ccv, &err, NULL, FALSE)) + err = TRUE; + set_vim_var_string(VV_CC_FROM, NULL, -1); + set_vim_var_string(VV_CC_TO, NULL, -1); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); + + if (err) + return FAIL; + return OK; +} + +# if defined(FEAT_POSTSCRIPT) || defined(PROTO) + int +eval_printexpr(char_u *fname, char_u *args) +{ + int err = FALSE; + + set_vim_var_string(VV_FNAME_IN, fname, -1); + set_vim_var_string(VV_CMDARG, args, -1); + if (eval_to_bool(p_pexpr, &err, NULL, FALSE)) + err = TRUE; + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_CMDARG, NULL, -1); + + if (err) + { + mch_remove(fname); + return FAIL; + } + return OK; +} +# endif + +# if defined(FEAT_DIFF) || defined(PROTO) + void +eval_diff( + char_u *origfile, + char_u *newfile, + char_u *outfile) +{ + int err = FALSE; + + set_vim_var_string(VV_FNAME_IN, origfile, -1); + set_vim_var_string(VV_FNAME_NEW, newfile, -1); + set_vim_var_string(VV_FNAME_OUT, outfile, -1); + (void)eval_to_bool(p_dex, &err, NULL, FALSE); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_NEW, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); +} + + void +eval_patch( + char_u *origfile, + char_u *difffile, + char_u *outfile) +{ + int err; + + set_vim_var_string(VV_FNAME_IN, origfile, -1); + set_vim_var_string(VV_FNAME_DIFF, difffile, -1); + set_vim_var_string(VV_FNAME_OUT, outfile, -1); + (void)eval_to_bool(p_pex, &err, NULL, FALSE); + set_vim_var_string(VV_FNAME_IN, NULL, -1); + set_vim_var_string(VV_FNAME_DIFF, NULL, -1); + set_vim_var_string(VV_FNAME_OUT, NULL, -1); +} +# endif + +#if defined(FEAT_SPELL) || defined(PROTO) +/* + * Evaluate an expression to a list with suggestions. + * For the "expr:" part of 'spellsuggest'. + * Returns NULL when there is an error. + */ + list_T * +eval_spell_expr(char_u *badword, char_u *expr) +{ + typval_T save_val; + typval_T rettv; + list_T *list = NULL; + char_u *p = skipwhite(expr); + + // Set "v:val" to the bad word. + prepare_vimvar(VV_VAL, &save_val); + set_vim_var_string(VV_VAL, badword, -1); + if (p_verbose == 0) + ++emsg_off; + + if (eval1(&p, &rettv, TRUE) == OK) + { + if (rettv.v_type != VAR_LIST) + clear_tv(&rettv); + else + list = rettv.vval.v_list; + } + + if (p_verbose == 0) + --emsg_off; + clear_tv(get_vim_var_tv(VV_VAL)); + restore_vimvar(VV_VAL, &save_val); + + return list; +} + +/* + * "list" is supposed to contain two items: a word and a number. Return the + * word in "pp" and the number as the return value. + * Return -1 if anything isn't right. + * Used to get the good word and score from the eval_spell_expr() result. + */ + int +get_spellword(list_T *list, char_u **pp) +{ + listitem_T *li; + + li = list->lv_first; + if (li == NULL) + return -1; + *pp = tv_get_string(&li->li_tv); + + li = li->li_next; + if (li == NULL) + return -1; + return (int)tv_get_number(&li->li_tv); +} +#endif + /* * Prepare v: variable "idx" to be used. * Save the current typeval in "save_tv". @@ -1569,6 +1721,31 @@ item_lock(typval_T *tv, int deep, int lo --recurse; } +#if (defined(FEAT_MENU) && defined(FEAT_MULTI_LANG)) || defined(PROTO) +/* + * Delete all "menutrans_" variables. + */ + void +del_menutrans_vars(void) +{ + hashitem_T *hi; + int todo; + + hash_lock(&globvarht); + todo = (int)globvarht.ht_used; + for (hi = globvarht.ht_array; todo > 0 && !got_int; ++hi) + { + if (!HASHITEM_EMPTY(hi)) + { + --todo; + if (STRNCMP(HI2DI(hi)->di_key, "menutrans_", 10) == 0) + delete_var(&globvarht, hi); + } + } + hash_unlock(&globvarht); +} +#endif + /* * Local string buffer for the next two functions to store a variable name * with its prefix. Allocated in cat_prefix_varname(), freed later in @@ -1688,6 +1865,47 @@ get_user_var_name(expand_T *xp, int idx) return NULL; } + char * +get_var_special_name(int nr) +{ + switch (nr) + { + case VVAL_FALSE: return "v:false"; + case VVAL_TRUE: return "v:true"; + case VVAL_NONE: return "v:none"; + case VVAL_NULL: return "v:null"; + } + internal_error("get_var_special_name()"); + return "42"; +} + +/* + * Returns the global variable dictionary + */ + dict_T * +get_globvar_dict(void) +{ + return &globvardict; +} + +/* + * Returns the global variable hash table + */ + hashtab_T * +get_globvar_ht(void) +{ + return &globvarht; +} + +/* + * Returns the v: variable dictionary + */ + dict_T * +get_vimvar_dict(void) +{ + return &vimvardict; +} + /* * Set type of v: variable to "type". */ @@ -2321,7 +2539,7 @@ vars_clear_ext(hashtab_T *ht, int free_v * Delete a variable from hashtab "ht" at item "hi". * Clear the variable value and free the dictitem. */ - void + static void delete_var(hashtab_T *ht, hashitem_T *hi) { dictitem_T *di = HI2DI(hi); @@ -2853,6 +3071,157 @@ var_exists(char_u *var) return n; } +static lval_T *redir_lval = NULL; +#define EVALCMD_BUSY (redir_lval == (lval_T *)&redir_lval) +static garray_T redir_ga; // only valid when redir_lval is not NULL +static char_u *redir_endp = NULL; +static char_u *redir_varname = NULL; + +/* + * Start recording command output to a variable + * When "append" is TRUE append to an existing variable. + * Returns OK if successfully completed the setup. FAIL otherwise. + */ + int +var_redir_start(char_u *name, int append) +{ + int save_emsg; + int err; + typval_T tv; + + // Catch a bad name early. + if (!eval_isnamec1(*name)) + { + emsg(_(e_invarg)); + return FAIL; + } + + // Make a copy of the name, it is used in redir_lval until redir ends. + redir_varname = vim_strsave(name); + if (redir_varname == NULL) + return FAIL; + + redir_lval = ALLOC_CLEAR_ONE(lval_T); + if (redir_lval == NULL) + { + var_redir_stop(); + return FAIL; + } + + // The output is stored in growarray "redir_ga" until redirection ends. + ga_init2(&redir_ga, (int)sizeof(char), 500); + + // Parse the variable name (can be a dict or list entry). + redir_endp = get_lval(redir_varname, NULL, redir_lval, FALSE, FALSE, 0, + FNE_CHECK_START); + if (redir_endp == NULL || redir_lval->ll_name == NULL || *redir_endp != NUL) + { + clear_lval(redir_lval); + if (redir_endp != NULL && *redir_endp != NUL) + // Trailing characters are present after the variable name + emsg(_(e_trailing)); + else + emsg(_(e_invarg)); + redir_endp = NULL; // don't store a value, only cleanup + var_redir_stop(); + return FAIL; + } + + // check if we can write to the variable: set it to or append an empty + // string + save_emsg = did_emsg; + did_emsg = FALSE; + tv.v_type = VAR_STRING; + tv.vval.v_string = (char_u *)""; + if (append) + set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"."); + else + set_var_lval(redir_lval, redir_endp, &tv, TRUE, FALSE, (char_u *)"="); + clear_lval(redir_lval); + err = did_emsg; + did_emsg |= save_emsg; + if (err) + { + redir_endp = NULL; // don't store a value, only cleanup + var_redir_stop(); + return FAIL; + } + + return OK; +} + +/* + * Append "value[value_len]" to the variable set by var_redir_start(). + * The actual appending is postponed until redirection ends, because the value + * appended may in fact be the string we write to, changing it may cause freed + * memory to be used: + * :redir => foo + * :let foo + * :redir END + */ + void +var_redir_str(char_u *value, int value_len) +{ + int len; + + if (redir_lval == NULL) + return; + + if (value_len == -1) + len = (int)STRLEN(value); // Append the entire string + else + len = value_len; // Append only "value_len" characters + + if (ga_grow(&redir_ga, len) == OK) + { + mch_memmove((char *)redir_ga.ga_data + redir_ga.ga_len, value, len); + redir_ga.ga_len += len; + } + else + var_redir_stop(); +} + +/* + * Stop redirecting command output to a variable. + * Frees the allocated memory. + */ + void +var_redir_stop(void) +{ + typval_T tv; + + if (EVALCMD_BUSY) + { + redir_lval = NULL; + return; + } + + if (redir_lval != NULL) + { + // If there was no error: assign the text to the variable. + if (redir_endp != NULL) + { + ga_append(&redir_ga, NUL); // Append the trailing NUL. + tv.v_type = VAR_STRING; + tv.vval.v_string = redir_ga.ga_data; + // Call get_lval() again, if it's inside a Dict or List it may + // have changed. + redir_endp = get_lval(redir_varname, NULL, redir_lval, + FALSE, FALSE, 0, FNE_CHECK_START); + if (redir_endp != NULL && redir_lval->ll_name != NULL) + set_var_lval(redir_lval, redir_endp, &tv, FALSE, FALSE, + (char_u *)"."); + clear_lval(redir_lval); + } + + // free the collected output + VIM_CLEAR(redir_ga.ga_data); + + VIM_CLEAR(redir_lval); + } + VIM_CLEAR(redir_varname); +} + /* * "gettabvar()" function */ diff --git a/src/ex_getln.c b/src/ex_getln.c --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -4317,3 +4317,111 @@ script_get(exarg_T *eap, char_u *cmd) return (char_u *)ga.ga_data; } + +#if defined(FEAT_EVAL) || defined(PROTO) +/* + * 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( + typval_T *argvars, + typval_T *rettv, + int inputdialog, + int secret) +{ + char_u *prompt = tv_get_string_chk(&argvars[0]); + char_u *p = NULL; + int c; + char_u buf[NUMBUFLEN]; + int cmd_silent_save = cmd_silent; + char_u *defstr = (char_u *)""; + int xp_type = EXPAND_NOTHING; + char_u *xp_arg = NULL; + + rettv->v_type = VAR_STRING; + rettv->vval.v_string = NULL; + +#ifdef NO_CONSOLE_INPUT + // While starting up, there is no place to enter text. When running tests + // with --not-a-term we assume feedkeys() will be used. + if (no_console_input() && !is_not_a_term()) + return; +#endif + + cmd_silent = FALSE; // Want to see the prompt. + if (prompt != NULL) + { + // Only the part of the message after the last NL is considered as + // prompt for the command line + p = vim_strrchr(prompt, '\n'); + if (p == NULL) + p = prompt; + else + { + ++p; + c = *p; + *p = NUL; + msg_start(); + msg_clr_eos(); + msg_puts_attr((char *)prompt, get_echo_attr()); + msg_didout = FALSE; + msg_starthere(); + *p = c; + } + cmdline_row = msg_row; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + defstr = tv_get_string_buf_chk(&argvars[1], buf); + if (defstr != NULL) + stuffReadbuffSpec(defstr); + + if (!inputdialog && argvars[2].v_type != VAR_UNKNOWN) + { + char_u *xp_name; + int xp_namelen; + long argt; + + // input() with a third argument: completion + rettv->vval.v_string = NULL; + + xp_name = tv_get_string_buf_chk(&argvars[2], buf); + if (xp_name == NULL) + return; + + xp_namelen = (int)STRLEN(xp_name); + + if (parse_compl_arg(xp_name, xp_namelen, &xp_type, &argt, + &xp_arg) == FAIL) + return; + } + } + + if (defstr != NULL) + { + 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); + ex_normal_busy = save_ex_normal_busy; + } + if (inputdialog && rettv->vval.v_string == NULL + && argvars[1].v_type != VAR_UNKNOWN + && argvars[2].v_type != VAR_UNKNOWN) + rettv->vval.v_string = vim_strsave(tv_get_string_buf( + &argvars[2], buf)); + + vim_free(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; +} +#endif diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -191,9 +191,6 @@ EXTERN int emsg_skip INIT(= 0); // d EXTERN int emsg_severe INIT(= FALSE); // use message of next of several // emsg() calls for throw EXTERN int did_endif INIT(= FALSE); // just had ":endif" -EXTERN dict_T vimvardict; // Dictionary with v: variables -EXTERN dict_T globvardict; // Dictionary with g: variables -#define globvarht globvardict.dv_hashtab #endif EXTERN int did_emsg; // set by emsg() when the message // is displayed or thrown diff --git a/src/if_py_both.h b/src/if_py_both.h --- a/src/if_py_both.h +++ b/src/if_py_both.h @@ -6847,8 +6847,8 @@ populate_module(PyObject *m) return -1; ADD_OBJECT(m, "error", VimError); - ADD_CHECKED_OBJECT(m, "vars", NEW_DICTIONARY(&globvardict)); - ADD_CHECKED_OBJECT(m, "vvars", NEW_DICTIONARY(&vimvardict)); + ADD_CHECKED_OBJECT(m, "vars", NEW_DICTIONARY(get_globvar_dict())); + ADD_CHECKED_OBJECT(m, "vvars", NEW_DICTIONARY(get_vimvar_dict())); ADD_CHECKED_OBJECT(m, "options", OptionsNew(SREQ_GLOBAL, NULL, dummy_check, NULL)); diff --git a/src/proto/eval.pro b/src/proto/eval.pro --- a/src/proto/eval.pro +++ b/src/proto/eval.pro @@ -3,13 +3,6 @@ varnumber_T num_divide(varnumber_T n1, v varnumber_T num_modulus(varnumber_T n1, varnumber_T n2); void eval_init(void); void eval_clear(void); -int var_redir_start(char_u *name, int append); -void var_redir_str(char_u *value, int value_len); -void var_redir_stop(void); -int eval_charconvert(char_u *enc_from, char_u *enc_to, char_u *fname_from, char_u *fname_to); -int eval_printexpr(char_u *fname, char_u *args); -void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile); -void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile); int eval_to_bool(char_u *arg, int *error, char_u **nextcmd, int skip); int eval_expr_typval(typval_T *expr, typval_T *argv, int argc, typval_T *rettv); int eval_expr_to_bool(typval_T *expr, int *error); @@ -33,7 +26,6 @@ void *eval_for_line(char_u *arg, int *er int next_for_item(void *fi_void, char_u *arg); void free_for_info(void *fi_void); void set_context_for_expression(expand_T *xp, char_u *arg, cmdidx_T cmdidx); -void del_menutrans_vars(void); int pattern_match(char_u *pat, char_u *text, int ic); int eval0(char_u *arg, typval_T *rettv, char_u **nextcmd, int evaluate); int eval1(char_u **arg, typval_T *rettv, int evaluate); @@ -76,13 +68,11 @@ char_u *tv_get_string_chk(typval_T *varp char_u *tv_get_string_buf_chk(typval_T *varp, char_u *buf); void copy_tv(typval_T *from, typval_T *to); int item_copy(typval_T *from, typval_T *to, int deep, int copyID); -void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog, int secret); void ex_echo(exarg_T *eap); void ex_echohl(exarg_T *eap); +int get_echo_attr(void); void ex_execute(exarg_T *eap); char_u *find_option_end(char_u **arg, int *opt_flags); -char_u *autoload_name(char_u *name); -int script_autoload(char_u *name, int reload); void last_set_msg(sctx_T script_ctx); int typval_compare(typval_T *typ1, typval_T *typ2, exptype_T type, int type_is, int ic); char_u *typval_tostring(typval_T *arg); diff --git a/src/proto/evalvars.pro b/src/proto/evalvars.pro --- a/src/proto/evalvars.pro +++ b/src/proto/evalvars.pro @@ -1,9 +1,14 @@ /* evalvars.c */ void evalvars_init(void); void evalvars_clear(void); +int garbage_collect_globvars(int copyID); int garbage_collect_vimvars(int copyID); int garbage_collect_scriptvars(int copyID); void set_internal_string_var(char_u *name, char_u *value); +int eval_charconvert(char_u *enc_from, char_u *enc_to, char_u *fname_from, char_u *fname_to); +int eval_printexpr(char_u *fname, char_u *args); +void eval_diff(char_u *origfile, char_u *newfile, char_u *outfile); +void eval_patch(char_u *origfile, char_u *difffile, char_u *outfile); void prepare_vimvar(int idx, typval_T *save_tv); void restore_vimvar(int idx, typval_T *save_tv); void ex_let(exarg_T *eap); @@ -14,7 +19,12 @@ void list_hashtable_vars(hashtab_T *ht, void ex_unlet(exarg_T *eap); void ex_lockvar(exarg_T *eap); int do_unlet(char_u *name, int forceit); +void del_menutrans_vars(void); char_u *get_user_var_name(expand_T *xp, int idx); +char *get_var_special_name(int nr); +dict_T *get_globvar_dict(void); +hashtab_T *get_globvar_ht(void); +dict_T *get_vimvar_dict(void); void set_vim_var_type(int idx, vartype_T type); void set_vim_var_nr(int idx, varnumber_T val); typval_T *get_vim_var_tv(int idx); @@ -44,7 +54,6 @@ void init_var_dict(dict_T *dict, dictite void unref_var_dict(dict_T *dict); void vars_clear(hashtab_T *ht); void vars_clear_ext(hashtab_T *ht, int free_val); -void delete_var(hashtab_T *ht, hashitem_T *hi); void set_var(char_u *name, typval_T *tv, int copy); void set_var_const(char_u *name, typval_T *tv, int copy, int is_const); int var_check_ro(int flags, char_u *name, int use_gettext); @@ -55,6 +64,9 @@ int valid_varname(char_u *varname); void reset_v_option_vars(void); void assert_error(garray_T *gap); int var_exists(char_u *var); +int var_redir_start(char_u *name, int append); +void var_redir_str(char_u *value, int value_len); +void var_redir_stop(void); void f_gettabvar(typval_T *argvars, typval_T *rettv); void f_gettabwinvar(typval_T *argvars, typval_T *rettv); void f_getwinvar(typval_T *argvars, typval_T *rettv); diff --git a/src/proto/ex_getln.pro b/src/proto/ex_getln.pro --- a/src/proto/ex_getln.pro +++ b/src/proto/ex_getln.pro @@ -35,4 +35,5 @@ int get_cmdline_type(void); int get_cmdline_firstc(void); int get_list_range(char_u **str, int *num1, int *num2); char_u *script_get(exarg_T *eap, char_u *cmd); +void get_user_input(typval_T *argvars, typval_T *rettv, int inputdialog, int secret); /* vim: set ft=c : */ diff --git a/src/proto/scriptfile.pro b/src/proto/scriptfile.pro --- a/src/proto/scriptfile.pro +++ b/src/proto/scriptfile.pro @@ -20,6 +20,7 @@ void ex_scriptnames(exarg_T *eap); void scriptnames_slash_adjust(void); char_u *get_scriptname(scid_T id); void free_scriptnames(void); +void free_autoload_scriptnames(void); linenr_T get_sourced_lnum(char_u *(*fgetline)(int, void *, int, int), void *cookie); char_u *getsourceline(int c, void *cookie, int indent, int do_concat); void ex_scriptencoding(exarg_T *eap); @@ -27,4 +28,6 @@ void ex_scriptversion(exarg_T *eap); void ex_finish(exarg_T *eap); void do_finish(exarg_T *eap, int reanimate); int source_finished(char_u *(*fgetline)(int, void *, int, int), void *cookie); +char_u *autoload_name(char_u *name); +int script_autoload(char_u *name, int reload); /* vim: set ft=c : */ diff --git a/src/scriptfile.c b/src/scriptfile.c --- a/src/scriptfile.c +++ b/src/scriptfile.c @@ -13,6 +13,11 @@ #include "vim.h" +#if defined(FEAT_EVAL) || defined(PROTO) +// The names of packages that once were loaded are remembered. +static garray_T ga_loaded = {0, 0, sizeof(char_u *), 4, NULL}; +#endif + /* * ":runtime [what] {name}" */ @@ -1334,6 +1339,12 @@ free_scriptnames(void) vim_free(SCRIPT_ITEM(i).sn_name); ga_clear(&script_items); } + + void +free_autoload_scriptnames(void) +{ + ga_clear_strings(&ga_loaded); +} # endif #endif @@ -1690,4 +1701,76 @@ source_finished( && ((struct source_cookie *)getline_cookie( fgetline, cookie))->finished); } + +/* + * Return the autoload script name for a function or variable name. + * Returns NULL when out of memory. + * Caller must make sure that "name" contains AUTOLOAD_CHAR. + */ + char_u * +autoload_name(char_u *name) +{ + char_u *p, *q = NULL; + char_u *scriptname; + + // Get the script file name: replace '#' with '/', append ".vim". + scriptname = alloc(STRLEN(name) + 14); + if (scriptname == NULL) + return NULL; + STRCPY(scriptname, "autoload/"); + STRCAT(scriptname, name); + for (p = scriptname + 9; (p = vim_strchr(p, AUTOLOAD_CHAR)) != NULL; + q = p, ++p) + *p = '/'; + STRCPY(q, ".vim"); + return scriptname; +} + +/* + * If "name" has a package name try autoloading the script for it. + * Return TRUE if a package was loaded. + */ + int +script_autoload( + char_u *name, + int reload) // load script again when already loaded +{ + char_u *p; + char_u *scriptname, *tofree; + int ret = FALSE; + int i; + + // If there is no '#' after name[0] there is no package name. + p = vim_strchr(name, AUTOLOAD_CHAR); + if (p == NULL || p == name) + return FALSE; + + tofree = scriptname = autoload_name(name); + if (scriptname == NULL) + return FALSE; + + // Find the name in the list of previously loaded package names. Skip + // "autoload/", it's always the same. + for (i = 0; i < ga_loaded.ga_len; ++i) + if (STRCMP(((char_u **)ga_loaded.ga_data)[i] + 9, scriptname + 9) == 0) + break; + if (!reload && i < ga_loaded.ga_len) + ret = FALSE; // was loaded already + else + { + // Remember the name if it wasn't loaded already. + if (i == ga_loaded.ga_len && ga_grow(&ga_loaded, 1) == OK) + { + ((char_u **)ga_loaded.ga_data)[ga_loaded.ga_len++] = scriptname; + tofree = NULL; + } + + // Try loading the package from $VIMRUNTIME/autoload/.vim + if (source_runtime(scriptname, 0) == OK) + ret = TRUE; + } + + vim_free(tofree); + return ret; +} #endif diff --git a/src/session.c b/src/session.c --- a/src/session.c +++ b/src/session.c @@ -500,13 +500,14 @@ put_view( static int store_session_globals(FILE *fd) { + hashtab_T *gvht = get_globvar_ht(); hashitem_T *hi; dictitem_T *this_var; int todo; char_u *p, *t; - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0; ++hi) + todo = (int)gvht->ht_used; + for (hi = gvht->ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) { diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -762,6 +762,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 1957, +/**/ 1956, /**/ 1955, diff --git a/src/viminfo.c b/src/viminfo.c --- a/src/viminfo.c +++ b/src/viminfo.c @@ -1277,6 +1277,7 @@ read_viminfo_varlist(vir_T *virp, int wr static void write_viminfo_varlist(FILE *fp) { + hashtab_T *gvht = get_globvar_ht(); hashitem_T *hi; dictitem_T *this_var; int todo; @@ -1290,8 +1291,8 @@ write_viminfo_varlist(FILE *fp) fputs(_("\n# global variables:\n"), fp); - todo = (int)globvarht.ht_used; - for (hi = globvarht.ht_array; todo > 0; ++hi) + todo = (int)gvht->ht_used; + for (hi = gvht->ht_array; todo > 0; ++hi) { if (!HASHITEM_EMPTY(hi)) {