updated for version 7.0041
diff --git a/src/eval.c b/src/eval.c
index 1b21a11..f2ce3f3 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -170,9 +170,10 @@
long ll_n1; /* First index for list */
long ll_n2; /* Second index for list range */
int ll_empty2; /* Second index is empty: [i:] */
- char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */
+ dictvar *ll_dict; /* The Dictionary or NULL */
dictitem *ll_di; /* The dictitem or NULL */
- dictitem **ll_pdi; /* field that points to found dictitem */
+ char_u *ll_newkey; /* New key for Dict in alloc. mem or NULL. */
+ dictitem **ll_pdi; /* di_next field pointing to found dictitem */
} lval;
@@ -183,11 +184,16 @@
static char *e_intern2 = N_("E685: Internal error: %s");
static char *e_listarg = N_("E686: Argument of %s must be a List");
static char *e_listdictarg = N_("E712: Argument of %s must be a List or Dictionaary");
-static char *e_emptykey = N_("E999: Empty key in Dictionary");
-static char *e_listreq = N_("E999: List required");
-static char *e_dictreq = N_("E999: Dictionary required");
+static char *e_emptykey = N_("E713: Empty key in Dictionary");
+static char *e_listreq = N_("E714: List required");
+static char *e_dictreq = N_("E715: Dictionary required");
static char *e_toomanyarg = N_("E118: Too many arguments for function: %s");
-static char *e_dictkey = N_("E999: key not found in Dictionary: %s");
+static char *e_dictkey = N_("E716: Key not present in Dictionary: %s");
+static char *e_funcexts = N_("E122: Function %s already exists, add ! to replace it");
+static char *e_funcdict = N_("E717: Dictionary entry already exists");
+static char *e_funcref = N_("E718: Funcref required");
+static char *e_dictrange = N_("E719: Cannot use [:] with a Dictionary");
+static char *e_letwrong = N_("E734: Wrong variable type for %s=");
/*
* All user-defined global variables are stored in "variables".
@@ -208,6 +214,10 @@
static int echo_attr = 0; /* attributes used for ":echo" */
+/* Values for trans_function_name() argument: */
+#define TFN_INT 1 /* internal function name OK */
+#define TFN_QUIET 2 /* no error messages */
+
/*
* Structure to hold info for a user function.
*/
@@ -225,6 +235,7 @@
garray_T lines; /* function lines */
scid_T script_ID; /* ID of script where function was defined,
used for s: variables */
+ int refcount; /* for numbered function: reference count */
};
/* function flags */
@@ -271,6 +282,17 @@
} forinfo;
/*
+ * Struct used by trans_function_name()
+ */
+typedef struct
+{
+ dictvar *fd_dict; /* Dictionary used */
+ char_u *fd_newkey; /* new key in "dict" */
+ dictitem *fd_di; /* Dictionary item used */
+ dictitem **fd_pdi; /* field that points to "fd_di" */
+} funcdict;
+
+/*
* Return the name of the executed function.
*/
char_u *
@@ -345,43 +367,44 @@
{
/*
* The order here must match the VV_ defines in vim.h!
+ * Initializing a union does not work, leave tv.vval empty to get zero's.
*/
- {VV_NAME("count"), {VAR_NUMBER, {NULL}}, VV_COMPAT+VV_RO},
- {VV_NAME("count1"), {VAR_NUMBER, {NULL}}, VV_RO},
- {VV_NAME("prevcount"), {VAR_NUMBER, {NULL}}, VV_RO},
- {VV_NAME("errmsg"), {VAR_STRING, {NULL}}, VV_COMPAT},
- {VV_NAME("warningmsg"), {VAR_STRING, {NULL}}, 0},
- {VV_NAME("statusmsg"), {VAR_STRING, {NULL}}, 0},
- {VV_NAME("shell_error"), {VAR_NUMBER, {NULL}}, VV_COMPAT+VV_RO},
- {VV_NAME("this_session"), {VAR_STRING, {NULL}}, VV_COMPAT},
- {VV_NAME("version"), {VAR_NUMBER, {VIM_VERSION_100}}, VV_COMPAT+VV_RO},
- {VV_NAME("lnum"), {VAR_NUMBER, {NULL}}, VV_RO_SBX},
- {VV_NAME("termresponse"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("fname"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("lang"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("lc_time"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("ctype"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("charconvert_from"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("charconvert_to"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("fname_in"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("fname_out"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("fname_new"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("fname_diff"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("cmdarg"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("foldstart"), {VAR_NUMBER, {NULL}}, VV_RO_SBX},
- {VV_NAME("foldend"), {VAR_NUMBER, {NULL}}, VV_RO_SBX},
- {VV_NAME("folddashes"), {VAR_STRING, {NULL}}, VV_RO_SBX},
- {VV_NAME("foldlevel"), {VAR_NUMBER, {NULL}}, VV_RO_SBX},
- {VV_NAME("progname"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("servername"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("dying"), {VAR_NUMBER, {NULL}}, VV_RO},
- {VV_NAME("exception"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("throwpoint"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("register"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("cmdbang"), {VAR_NUMBER, {NULL}}, VV_RO},
- {VV_NAME("insertmode"), {VAR_STRING, {NULL}}, VV_RO},
- {VV_NAME("val"), {VAR_UNKNOWN, {NULL}}, VV_RO},
- {VV_NAME("key"), {VAR_UNKNOWN, {NULL}}, VV_RO},
+ {VV_NAME("count"), {VAR_NUMBER}, VV_COMPAT+VV_RO},
+ {VV_NAME("count1"), {VAR_NUMBER}, VV_RO},
+ {VV_NAME("prevcount"), {VAR_NUMBER}, VV_RO},
+ {VV_NAME("errmsg"), {VAR_STRING}, VV_COMPAT},
+ {VV_NAME("warningmsg"), {VAR_STRING}, 0},
+ {VV_NAME("statusmsg"), {VAR_STRING}, 0},
+ {VV_NAME("shell_error"), {VAR_NUMBER}, VV_COMPAT+VV_RO},
+ {VV_NAME("this_session"), {VAR_STRING}, VV_COMPAT},
+ {VV_NAME("version"), {VAR_NUMBER}, VV_COMPAT+VV_RO},
+ {VV_NAME("lnum"), {VAR_NUMBER}, VV_RO_SBX},
+ {VV_NAME("termresponse"), {VAR_STRING}, VV_RO},
+ {VV_NAME("fname"), {VAR_STRING}, VV_RO},
+ {VV_NAME("lang"), {VAR_STRING}, VV_RO},
+ {VV_NAME("lc_time"), {VAR_STRING}, VV_RO},
+ {VV_NAME("ctype"), {VAR_STRING}, VV_RO},
+ {VV_NAME("charconvert_from"), {VAR_STRING}, VV_RO},
+ {VV_NAME("charconvert_to"), {VAR_STRING}, VV_RO},
+ {VV_NAME("fname_in"), {VAR_STRING}, VV_RO},
+ {VV_NAME("fname_out"), {VAR_STRING}, VV_RO},
+ {VV_NAME("fname_new"), {VAR_STRING}, VV_RO},
+ {VV_NAME("fname_diff"), {VAR_STRING}, VV_RO},
+ {VV_NAME("cmdarg"), {VAR_STRING}, VV_RO},
+ {VV_NAME("foldstart"), {VAR_NUMBER}, VV_RO_SBX},
+ {VV_NAME("foldend"), {VAR_NUMBER}, VV_RO_SBX},
+ {VV_NAME("folddashes"), {VAR_STRING}, VV_RO_SBX},
+ {VV_NAME("foldlevel"), {VAR_NUMBER}, VV_RO_SBX},
+ {VV_NAME("progname"), {VAR_STRING}, VV_RO},
+ {VV_NAME("servername"), {VAR_STRING}, VV_RO},
+ {VV_NAME("dying"), {VAR_NUMBER}, VV_RO},
+ {VV_NAME("exception"), {VAR_STRING}, VV_RO},
+ {VV_NAME("throwpoint"), {VAR_STRING}, VV_RO},
+ {VV_NAME("register"), {VAR_STRING}, VV_RO},
+ {VV_NAME("cmdbang"), {VAR_NUMBER}, VV_RO},
+ {VV_NAME("insertmode"), {VAR_STRING}, VV_RO},
+ {VV_NAME("val"), {VAR_UNKNOWN}, VV_RO},
+ {VV_NAME("key"), {VAR_UNKNOWN}, VV_RO},
};
/* shorthand */
@@ -409,6 +432,7 @@
static void listitem_remove __ARGS((listvar *l, listitem *item));
static long list_len __ARGS((listvar *l));
static int list_equal __ARGS((listvar *l1, listvar *l2, int ic));
+static int dict_equal __ARGS((dictvar *d1, dictvar *d2, int ic));
static int tv_equal __ARGS((typeval *tv1, typeval *tv2, int ic));
static int string_isa_number __ARGS((char_u *s));
static listitem *list_find __ARGS((listvar *l, long n));
@@ -431,6 +455,7 @@
static dictitem *dictitem_copy __ARGS((dictitem *org));
static void dictitem_free __ARGS((dictitem *item));
static void dict_add __ARGS((dictvar *d, dictitem *item));
+static long dict_len __ARGS((dictvar *d));
static dictitem *dict_find __ARGS((dictvar *d, char_u *key, int len, dictitem ***pdi));
static char_u *dict2string __ARGS((typeval *tv));
static int get_dict_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
@@ -640,13 +665,16 @@
static void copy_tv __ARGS((typeval *from, typeval *to));
static void item_copy __ARGS((typeval *from, typeval *to, int deep));
static char_u *find_option_end __ARGS((char_u **arg, int *opt_flags));
-static char_u *trans_function_name __ARGS((char_u **pp, int skip, int exists));
+static char_u *trans_function_name __ARGS((char_u **pp, int skip, int flags, funcdict *fd));
static int eval_fname_script __ARGS((char_u *p));
static int eval_fname_sid __ARGS((char_u *p));
static void list_func_head __ARGS((ufunc_T *fp, int indent));
static void cat_func_name __ARGS((char_u *buf, ufunc_T *fp));
static ufunc_T *find_func __ARGS((char_u *name));
static int function_exists __ARGS((char_u *name));
+static void func_free __ARGS((ufunc_T *fp));
+static void func_unref __ARGS((char_u *name));
+static void func_ref __ARGS((char_u *name));
static void call_user_func __ARGS((ufunc_T *fp, int argcount, typeval *argvars, typeval *rettv, linenr_T firstline, linenr_T lastline, dictvar *selfdict));
#define get_var_string(p) get_tv_string(&(p)->tv)
@@ -660,11 +688,12 @@
static char_u *skip_var_one __ARGS((char_u *arg));
static void list_all_vars __ARGS((void));
static char_u *list_arg_vars __ARGS((exarg_T *eap, char_u *arg));
-static char_u *ex_let_one __ARGS((char_u *arg, typeval *tv, int copy, char_u *endchars));
+static char_u *ex_let_one __ARGS((char_u *arg, typeval *tv, int copy, char_u *endchars, char_u *op));
static int check_changedtick __ARGS((char_u *arg));
static char_u *get_lval __ARGS((char_u *name, typeval *rettv, lval *lp, int unlet, int skip, int quiet));
static void clear_lval __ARGS((lval *lp));
-static void set_var_lval __ARGS((lval *lp, char_u *endp, typeval *rettv, int copy));
+static void set_var_lval __ARGS((lval *lp, char_u *endp, typeval *rettv, int copy, char_u *op));
+static int tv_op __ARGS((typeval *tv1, typeval *tv2, char_u *op));
static void list_add_watch __ARGS((listvar *l, listwatch *lw));
static void list_rem_watch __ARGS((listvar *l, listwatch *lwrem));
static void list_fix_watch __ARGS((listvar *l, listitem *item));
@@ -1170,6 +1199,9 @@
* ":let" list all variable values
* ":let var1 var2" list variable values
* ":let var = expr" assignment command.
+ * ":let var += expr" assignment command.
+ * ":let var -= expr" assignment command.
+ * ":let var .= expr" assignment command.
* ":let [var1, var2] = expr" unpack list.
*/
void
@@ -1182,6 +1214,7 @@
int i;
int var_count = 0;
int semicolon = 0;
+ char_u op[2];
expr = skip_var_list(arg, &var_count, &semicolon);
if (expr == NULL)
@@ -1201,6 +1234,13 @@
}
else
{
+ op[0] = '=';
+ op[1] = NUL;
+ if (expr > arg)
+ {
+ if (vim_strchr((char_u *)"+-.", expr[-1]) != NULL)
+ op[0] = expr[-1]; /* +=, -= or .= */
+ }
expr = skipwhite(expr + 1);
if (eap->skip)
@@ -1215,7 +1255,7 @@
else if (i != FAIL)
{
(void)ex_let_vars(eap->arg, &rettv, FALSE, semicolon, var_count,
- (char_u *)"=");
+ op);
clear_tv(&rettv);
}
}
@@ -1224,6 +1264,9 @@
/*
* Assign the typevalue "tv" to the variable or variables at "arg_start".
* Handles both "var" with any type and "[var, var; var]" with a list type.
+ * When "nextchars" is not NULL it points to a string with characters that
+ * must appear after the variable(s). Use "+", "-" or "." for add, subtract
+ * or concatenate.
* Returns OK or FAIL;
*/
static int
@@ -1233,7 +1276,7 @@
int copy; /* copy values from "tv", don't move */
int semicolon; /* from skip_var_list() */
int var_count; /* from skip_var_list() */
- char_u *nextchars; /* characters that must follow or NULL */
+ char_u *nextchars;
{
char_u *arg = arg_start;
listvar *l;
@@ -1246,7 +1289,7 @@
/*
* ":let var = expr" or ":for var in list"
*/
- if (ex_let_one(arg, tv, copy, nextchars) == NULL)
+ if (ex_let_one(arg, tv, copy, nextchars, nextchars) == NULL)
return FAIL;
return OK;
}
@@ -1277,7 +1320,7 @@
while (*arg != ']')
{
arg = skipwhite(arg + 1);
- arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]");
+ arg = ex_let_one(arg, &item->li_tv, TRUE, (char_u *)",;]", nextchars);
item = item->li_next;
if (arg == NULL)
return FAIL;
@@ -1300,7 +1343,8 @@
ltv.vval.v_list = l;
l->lv_refcount = 1;
- arg = ex_let_one(skipwhite(arg + 1), <v, FALSE, (char_u *)"]");
+ arg = ex_let_one(skipwhite(arg + 1), <v, FALSE,
+ (char_u *)"]", nextchars);
clear_tv(<v);
if (arg == NULL)
return FAIL;
@@ -1524,11 +1568,12 @@
* Returns NULL if there is an error.
*/
static char_u *
-ex_let_one(arg, tv, copy, endchars)
+ex_let_one(arg, tv, copy, endchars, op)
char_u *arg; /* points to variable name */
typeval *tv; /* value to assign to variable */
int copy; /* copy value from "tv" */
char_u *endchars; /* valid chars after variable name or NULL */
+ char_u *op; /* "+", "-", "." or NULL*/
{
int c1;
char_u *name;
@@ -1536,6 +1581,7 @@
char_u *arg_end = NULL;
int len;
int opt_flags;
+ char_u *tofree = NULL;
/*
* ":let $VAR = expr": Set environment variable.
@@ -1550,7 +1596,9 @@
EMSG2(_(e_invarg2), name - 1);
else
{
- if (endchars != NULL
+ if (op != NULL && (*op == '+' || *op == '-'))
+ EMSG2(_(e_letwrong), op);
+ else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg)) == NULL)
EMSG(_(e_letunexp));
else
@@ -1558,7 +1606,20 @@
c1 = name[len];
name[len] = NUL;
p = get_tv_string(tv);
- vim_setenv(name, p);
+ if (op != NULL && *op == '.')
+ {
+ int mustfree = FALSE;
+ char_u *s = vim_getenv(name, &mustfree);
+
+ if (s != NULL)
+ {
+ p = tofree = concat_str(s, p);
+ if (mustfree)
+ vim_free(s);
+ }
+ }
+ if (p != NULL)
+ vim_setenv(name, p);
if (STRICMP(name, "HOME") == 0)
init_homedir();
else if (didset_vim && STRICMP(name, "VIM") == 0)
@@ -1567,6 +1628,7 @@
didset_vimruntime = FALSE;
name[len] = c1;
arg_end = arg;
+ vim_free(tofree);
}
}
}
@@ -1585,12 +1647,45 @@
EMSG(_(e_letunexp));
else
{
+ long n;
+ int opt_type;
+ long numval;
+ char_u *stringval = NULL;
+ char_u *s;
+
c1 = *p;
*p = NUL;
- set_option_value(arg, get_tv_number(tv),
- get_tv_string(tv), opt_flags);
+
+ n = get_tv_number(tv);
+ s = get_tv_string(tv);
+ if (op != NULL && *op != '=')
+ {
+ opt_type = get_option_value(arg, &numval,
+ &stringval, opt_flags);
+ if ((opt_type == 1 && *op == '.')
+ || (opt_type == 0 && *op != '.'))
+ EMSG2(_(e_letwrong), op);
+ else
+ {
+ if (opt_type == 1) /* number */
+ {
+ if (*op == '+')
+ n = numval + n;
+ else
+ n = numval - n;
+ }
+ else if (opt_type == 0 && stringval != NULL) /* string */
+ {
+ s = concat_str(stringval, s);
+ vim_free(stringval);
+ stringval = s;
+ }
+ }
+ }
+ set_option_value(arg, n, s, opt_flags);
*p = c1;
arg_end = p;
+ vim_free(stringval);
}
}
@@ -1600,14 +1695,30 @@
else if (*arg == '@')
{
++arg;
- if (endchars != NULL
+ if (op != NULL && (*op == '+' || *op == '-'))
+ EMSG2(_(e_letwrong), op);
+ else if (endchars != NULL
&& vim_strchr(endchars, *skipwhite(arg + 1)) == NULL)
EMSG(_(e_letunexp));
else
{
- write_reg_contents(*arg == '@' ? '"' : *arg,
- get_tv_string(tv), -1, FALSE);
+ char_u *tofree = NULL;
+ char_u *s;
+
+ p = get_tv_string(tv);
+ if (op != NULL && *op == '.')
+ {
+ s = get_reg_contents(*arg == '@' ? '"' : *arg, FALSE);
+ if (s != NULL)
+ {
+ p = tofree = concat_str(s, p);
+ vim_free(s);
+ }
+ }
+ if (p != NULL)
+ write_reg_contents(*arg == '@' ? '"' : *arg, p, -1, FALSE);
arg_end = arg + 1;
+ vim_free(tofree);
}
}
@@ -1626,7 +1737,7 @@
EMSG(_(e_letunexp));
else
{
- set_var_lval(&lv, p, tv, copy);
+ set_var_lval(&lv, p, tv, copy, op);
arg_end = p;
}
}
@@ -1796,7 +1907,7 @@
if (lp->ll_tv->v_type == VAR_DICT)
{
if (!quiet)
- EMSG(_("E999: Cannot use [:] with a Dictionary"));
+ EMSG(_(e_dictrange));
if (!empty1)
clear_tv(&var1);
return NULL;
@@ -1857,16 +1968,16 @@
return NULL;
}
}
- lp->ll_di = dict_find(lp->ll_tv->vval.v_dict, key, len,
- &lp->ll_pdi);
+ lp->ll_list = NULL;
+ lp->ll_dict = lp->ll_tv->vval.v_dict;
+ lp->ll_di = dict_find(lp->ll_dict, key, len, &lp->ll_pdi);
if (lp->ll_di == NULL)
{
/* Key does not exist in dict: may need toadd it. */
if (*p == '[' || *p == '.' || unlet)
{
if (!quiet)
- EMSG2(_("E999: Key does not exist in Dictionary: %s"),
- key);
+ EMSG2(_(e_dictkey), key);
if (len == -1)
clear_tv(&var1);
return NULL;
@@ -1897,6 +2008,7 @@
lp->ll_n1 = get_tv_number(&var1);
clear_tv(&var1);
}
+ lp->ll_dict = NULL;
lp->ll_list = lp->ll_tv->vval.v_list;
lp->ll_li = list_find(lp->ll_list, lp->ll_n1);
if (lp->ll_li == NULL)
@@ -1962,13 +2074,15 @@
/*
* Set a variable that was parsed by get_lval().
* "endp" points to just after the parsed name.
+ * "op" is NULL, "+" for "+=", "-" for "-=", "." for ".=" or "=" for "=".
*/
static void
-set_var_lval(lp, endp, rettv, copy)
+set_var_lval(lp, endp, rettv, copy, op)
lval *lp;
char_u *endp;
typeval *rettv;
int copy;
+ char_u *op;
{
int cc;
listitem *ni;
@@ -1981,7 +2095,19 @@
{
cc = *endp;
*endp = NUL;
- set_var(lp->ll_name, rettv, copy);
+ if (op != NULL && *op != '=')
+ {
+ typeval tv;
+
+ if (get_var_tv(lp->ll_name, STRLEN(lp->ll_name), &tv) == OK)
+ {
+ if (tv_op(&tv, rettv, op) == OK)
+ set_var(lp->ll_name, &tv, FALSE);
+ clear_tv(&tv);
+ }
+ }
+ else
+ set_var(lp->ll_name, rettv, copy);
*endp = cc;
}
}
@@ -1992,8 +2118,13 @@
*/
for (ri = rettv->vval.v_list->lv_first; ri != NULL; )
{
- clear_tv(&lp->ll_li->li_tv);
- copy_tv(&ri->li_tv, &lp->ll_li->li_tv);
+ if (op != NULL && *op != '=')
+ tv_op(&lp->ll_li->li_tv, &ri->li_tv, op);
+ else
+ {
+ clear_tv(&lp->ll_li->li_tv);
+ copy_tv(&ri->li_tv, &lp->ll_li->li_tv);
+ }
ri = ri->li_next;
if (ri == NULL || (!lp->ll_empty2 && lp->ll_n2 == lp->ll_n1))
break;
@@ -2015,7 +2146,8 @@
}
if (ri != NULL)
EMSG(_("E710: List value has more items than target"));
- else if (lp->ll_empty2 ? (lp->ll_li != NULL && lp->ll_li->li_next != NULL)
+ else if (lp->ll_empty2
+ ? (lp->ll_li != NULL && lp->ll_li->li_next != NULL)
: lp->ll_n1 != lp->ll_n2)
EMSG(_("E711: List value has not enough items"));
}
@@ -2026,6 +2158,12 @@
*/
if (lp->ll_newkey != NULL)
{
+ if (op != NULL && *op != '=')
+ {
+ EMSG2(_(e_letwrong), op);
+ return;
+ }
+
/* Need to add an item to the Dictionary. */
di = dictitem_alloc();
if (di == NULL)
@@ -2035,6 +2173,11 @@
dict_add(lp->ll_tv->vval.v_dict, di);
lp->ll_tv = &di->di_tv;
}
+ else if (op != NULL && *op != '=')
+ {
+ tv_op(lp->ll_tv, rettv, op);
+ return;
+ }
else
clear_tv(lp->ll_tv);
@@ -2052,6 +2195,70 @@
}
/*
+ * Handle "tv1 += tv2", "tv1 -= tv2" and "tv1 .= tv2"
+ * Returns OK or FAIL.
+ */
+ static int
+tv_op(tv1, tv2, op)
+ typeval *tv1;
+ typeval *tv2;
+ char_u *op;
+{
+ long n;
+ char_u numbuf[NUMBUFLEN];
+ char_u *s;
+
+ /* Can't do anything with a Funcref or a Dict on the right. */
+ if (tv2->v_type != VAR_FUNC && tv2->v_type != VAR_DICT)
+ {
+ switch (tv1->v_type)
+ {
+ case VAR_DICT:
+ case VAR_FUNC:
+ break;
+
+ case VAR_LIST:
+ if (*op != '+' || tv2->v_type != VAR_LIST)
+ break;
+ /* List += List */
+ if (tv1->vval.v_list != NULL && tv2->vval.v_list != NULL)
+ list_extend(tv1->vval.v_list, tv2->vval.v_list, NULL);
+ return OK;
+
+ case VAR_NUMBER:
+ case VAR_STRING:
+ if (tv2->v_type == VAR_LIST)
+ break;
+ if (*op == '+' || *op == '-')
+ {
+ /* nr += nr or nr -= nr*/
+ n = get_tv_number(tv1);
+ if (*op == '+')
+ n += get_tv_number(tv2);
+ else
+ n -= get_tv_number(tv2);
+ clear_tv(tv1);
+ tv1->v_type = VAR_NUMBER;
+ tv1->vval.v_number = n;
+ }
+ else
+ {
+ /* str .= str */
+ s = get_tv_string(tv1);
+ s = concat_str(s, get_tv_string_buf(tv2, numbuf));
+ clear_tv(tv1);
+ tv1->v_type = VAR_STRING;
+ tv1->vval.v_string = s;
+ }
+ return OK;
+ }
+ }
+
+ EMSG2(_(e_letwrong), op);
+ return FAIL;
+}
+
+/*
* Add a watcher to a list.
*/
static void
@@ -2318,20 +2525,21 @@
{
char_u *arg = eap->arg;
char_u *startarg;
- char_u *alias;
char_u *name;
- typeval rettv;
+ char_u *tofree;
int len;
+ typeval rettv;
linenr_T lnum;
int doesrange;
int failed = FALSE;
- name = arg;
- len = get_func_len(&arg, &alias, !eap->skip);
- if (len == 0)
- goto end;
- if (alias != NULL)
- name = alias;
+ tofree = trans_function_name(&arg, eap->skip, TFN_INT, NULL);
+ if (tofree == NULL)
+ return;
+
+ /* If it is the name of a variable of type VAR_FUNC use its contents. */
+ len = STRLEN(tofree);
+ name = deref_func_name(tofree, &len);
startarg = arg;
rettv.v_type = VAR_UNKNOWN; /* clear_tv() uses this */
@@ -2363,8 +2571,8 @@
curwin->w_cursor.col = 0;
}
arg = startarg;
- if (get_func_tv(name, len, &rettv, &arg, eap->line1, eap->line2,
- &doesrange, !eap->skip, NULL) == FAIL)
+ if (get_func_tv(name, STRLEN(name), &rettv, &arg,
+ eap->line1, eap->line2, &doesrange, !eap->skip, NULL) == FAIL)
{
failed = TRUE;
break;
@@ -2395,8 +2603,7 @@
}
end:
- if (alias != NULL)
- vim_free(alias);
+ vim_free(tofree);
}
/*
@@ -3053,6 +3260,35 @@
}
}
+ else if (rettv->v_type == VAR_DICT || var2.v_type == VAR_DICT)
+ {
+ if (type_is)
+ {
+ n1 = (rettv->v_type == var2.v_type
+ && rettv->vval.v_dict == var2.vval.v_dict);
+ if (type == TYPE_NEQUAL)
+ n1 = !n1;
+ }
+ else if (rettv->v_type != var2.v_type
+ || (type != TYPE_EQUAL && type != TYPE_NEQUAL))
+ {
+ if (rettv->v_type != var2.v_type)
+ EMSG(_("E735: Can only compare Dictionary with Dictionary"));
+ else
+ EMSG(_("E736: Invalid operation for Dictionary"));
+ clear_tv(rettv);
+ clear_tv(&var2);
+ return FAIL;
+ }
+ else
+ {
+ /* Compare two Dictionaries for being equal or unequal. */
+ n1 = dict_equal(rettv->vval.v_dict, var2.vval.v_dict, ic);
+ if (type == TYPE_NEQUAL)
+ n1 = !n1;
+ }
+ }
+
else if (rettv->v_type == VAR_FUNC || var2.v_type == VAR_FUNC)
{
if (rettv->v_type != var2.v_type
@@ -3210,13 +3446,7 @@
{
s1 = get_tv_string_buf(rettv, buf1);
s2 = get_tv_string_buf(&var2, buf2);
- op = (int)STRLEN(s1);
- p = alloc((unsigned)(op + STRLEN(s2) + 1));
- if (p != NULL)
- {
- STRCPY(p, s1);
- STRCPY(p + op, s2);
- }
+ p = concat_str(s1, s2);
clear_tv(rettv);
rettv->v_type = VAR_STRING;
rettv->vval.v_string = p;
@@ -3518,6 +3748,8 @@
}
else if (evaluate)
ret = get_var_tv(s, len, rettv);
+ else
+ ret = OK;
}
if (alias != NULL)
@@ -3782,7 +4014,7 @@
case VAR_DICT:
if (range)
{
- EMSG(_("E999: Using range with Dictionary"));
+ EMSG(_(e_dictrange));
if (len == -1)
clear_tv(&var1);
return FAIL;
@@ -3795,7 +4027,7 @@
key = get_tv_string(&var1);
if (*key == NUL)
{
- EMSG(_("E999: Empty key for Dictionary"));
+ EMSG(_(e_emptykey));
clear_tv(&var1);
return FAIL;
}
@@ -3804,7 +4036,7 @@
item = dict_find(rettv->vval.v_dict, key, (int)len, NULL);
if (item == NULL)
- EMSG2(_("E999: Key not found in Dictionary: %s"), key);
+ EMSG2(_(e_dictkey), key);
if (len == -1)
clear_tv(&var1);
if (item == NULL)
@@ -4271,6 +4503,9 @@
{
listitem *item1, *item2;
+ if (list_len(l1) != list_len(l2))
+ return FALSE;
+
for (item1 = l1->lv_first, item2 = l2->lv_first;
item1 != NULL && item2 != NULL;
item1 = item1->li_next, item2 = item2->li_next)
@@ -4280,6 +4515,31 @@
}
/*
+ * Return TRUE when two dictionaries have exactly the same key/values.
+ */
+ static int
+dict_equal(d1, d2, ic)
+ dictvar *d1;
+ dictvar *d2;
+ int ic; /* ignore case for strings */
+{
+ dictitem *item1, *item2;
+
+ if (dict_len(d1) != dict_len(d2))
+ return FALSE;
+
+ for (item1 = d1->dv_first; item1 != NULL; item1 = item1->di_next)
+ {
+ item2 = dict_find(d2, item1->di_key, -1, NULL);
+ if (item2 == NULL)
+ return FALSE;
+ if (!tv_equal(&item1->di_tv, &item2->di_tv, ic))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/*
* Return TRUE if "tv1" and "tv2" have the same value.
* Compares the items just like "==" would compare them.
*/
@@ -4994,7 +5254,7 @@
goto failret;
if (**arg != ':')
{
- EMSG2(_("E999: Missing colon in Dictionary: %s"), *arg);
+ EMSG2(_("E720: Missing colon in Dictionary: %s"), *arg);
clear_tv(&tv);
goto failret;
}
@@ -5021,7 +5281,7 @@
item = dict_find(d, key, -1, NULL);
if (item != NULL)
{
- EMSG(_("E999: Duplicate key in Dictionary"));
+ EMSG(_("E721: Duplicate key in Dictionary"));
vim_free(key);
clear_tv(&tv);
goto failret;
@@ -5041,7 +5301,7 @@
break;
if (**arg != ',')
{
- EMSG2(_("E999: Missing comma in Dictionary: %s"), *arg);
+ EMSG2(_("E722: Missing comma in Dictionary: %s"), *arg);
goto failret;
}
*arg = skipwhite(*arg + 1);
@@ -5049,7 +5309,7 @@
if (**arg != '}')
{
- EMSG2(_("E999: Missing end of Dictionary '}': %s"), *arg);
+ EMSG2(_("E723: Missing end of Dictionary '}': %s"), *arg);
failret:
if (evaluate)
dict_free(d);
@@ -5067,7 +5327,6 @@
return OK;
}
-
/*
* Return a string with the string representation of a variable.
* If the memory is allocated "tofree" is set to it, otherwise NULL.
@@ -5086,7 +5345,7 @@
if (recurse >= VAR_MAXNEST)
{
- EMSG(_("E999: variable nested too deep for displaying"));
+ EMSG(_("E724: variable nested too deep for displaying"));
*tofree = NULL;
return NULL;
}
@@ -5735,7 +5994,10 @@
call_user_func(fp, argcount, argvars, rettv,
firstline, lastline,
(fp->flags & FC_DICT) ? selfdict : NULL);
- --fp->calls;
+ if (--fp->calls <= 0 && isdigit(*fp->name))
+ /* Function was unreferenced while being used, free it
+ * now. */
+ func_free(fp);
restoreRedobuff();
restore_search_patterns();
error = ERROR_NONE;
@@ -5800,7 +6062,7 @@
name);
break;
case ERROR_DICT:
- EMSG2(_("E999: Calling dict function without Dictionary: %s"),
+ EMSG2(_("E725: Calling dict function without Dictionary: %s"),
name);
break;
}
@@ -9944,9 +10206,9 @@
rettv->vval.v_number = 0;
if (stride == 0)
- EMSG(_("E999: Stride is zero"));
+ EMSG(_("E726: Stride is zero"));
else if (stride > 0 ? end < start : end > start)
- EMSG(_("E999: Start past end"));
+ EMSG(_("E727: Start past end"));
else
{
l = list_alloc();
@@ -10440,7 +10702,7 @@
remain = vim_strsave(q - 1);
else
{
- cpy = vim_strnsave(q-1, STRLEN(q-1)+STRLEN(remain));
+ cpy = vim_strnsave(q-1, STRLEN(q-1) + STRLEN(remain));
if (cpy != NULL)
{
STRCAT(cpy, remain);
@@ -10514,10 +10776,9 @@
|| vim_ispathsep(p[2]))))))
{
/* Prepend "./". */
- cpy = vim_strnsave((char_u *)"./", 2 + STRLEN(p));
+ cpy = concat_str((char_u *)"./", p);
if (cpy != NULL)
{
- STRCAT(cpy, p);
vim_free(p);
p = cpy;
}
@@ -12603,6 +12864,10 @@
char_u *val;
int len; /* length of "val" to use or -1 (whole string) */
{
+ /* Need to do this (at least) once, since we can't initialize a union.
+ * Will always be invoked when "v:progname" is set. */
+ vimvars[VV_VERSION].vv_nr = VIM_VERSION_100;
+
vim_free(vimvars[idx].vv_str);
if (val == NULL)
vimvars[idx].vv_str = NULL;
@@ -12830,13 +13095,18 @@
{
switch (varp->v_type)
{
- case VAR_STRING:
case VAR_FUNC:
+ func_unref(varp->vval.v_string);
+ /*FALLTHROUGH*/
+ case VAR_STRING:
vim_free(varp->vval.v_string);
break;
case VAR_LIST:
list_unref(varp->vval.v_list);
break;
+ case VAR_DICT:
+ dict_unref(varp->vval.v_dict);
+ break;
default:
break;
}
@@ -12855,8 +13125,10 @@
{
switch (varp->v_type)
{
- case VAR_STRING:
case VAR_FUNC:
+ func_unref(varp->vval.v_string);
+ /*FALLTHROUGH*/
+ case VAR_STRING:
vim_free(varp->vval.v_string);
varp->vval.v_string = NULL;
break;
@@ -12914,7 +13186,11 @@
case VAR_LIST:
EMSG(_("E703: Using a List as a number"));
break;
+ case VAR_DICT:
+ EMSG(_("E728: Using a Dictionary as a number"));
+ break;
default:
+ EMSG2(_(e_intern2), "get_tv_number()");
break;
}
return n;
@@ -12969,13 +13245,13 @@
sprintf((char *)buf, "%ld", (long)varp->vval.v_number);
return buf;
case VAR_FUNC:
- EMSG(_("E999: using Funcref as a String"));
+ EMSG(_("E729: using Funcref as a String"));
break;
case VAR_LIST:
- EMSG(_("E999: using List as a String"));
+ EMSG(_("E730: using List as a String"));
break;
case VAR_DICT:
- EMSG(_("E999: using Dictionary as a String"));
+ EMSG(_("E731: using Dictionary as a String"));
break;
case VAR_STRING:
if (varp->vval.v_string != NULL)
@@ -13361,7 +13637,11 @@
if (from->vval.v_string == NULL)
to->vval.v_string = NULL;
else
+ {
to->vval.v_string = vim_strsave(from->vval.v_string);
+ if (from->v_type == VAR_FUNC)
+ func_ref(to->vval.v_string);
+ }
break;
case VAR_LIST:
if (from->vval.v_list == NULL)
@@ -13679,8 +13959,10 @@
int indent;
int nesting;
char_u *skip_until = NULL;
- static char_u e_funcexts[] = N_("E122: Function %s already exists, add ! to replace it");
VAR v;
+ funcdict fudi;
+ static int func_nr = 0; /* number for nameless function */
+ int paren;
/*
* ":function" without argument: list functions.
@@ -13689,14 +13971,30 @@
{
if (!eap->skip)
for (fp = firstfunc; fp != NULL && !got_int; fp = fp->next)
- list_func_head(fp, FALSE);
+ if (!isdigit(*fp->name))
+ list_func_head(fp, FALSE);
eap->nextcmd = check_nextcmd(eap->arg);
return;
}
+ /*
+ * Get the function name. There are these situations:
+ * func normal function name
+ * "name" == func, "fudi.fd_dict" == NULL
+ * dict.func new dictionary entry
+ * "name" == NULL, "fudi.fd_dict" set,
+ * "fudi.fd_di" == NULL, "fudi.fd_newkey" == func
+ * dict.func existing dict entry with a Funcref
+ * "name" == fname, "fudi.fd_dict" set,
+ * "fudi.fd_di" set, "fudi.fd_newkey" == NULL
+ * dict.func existing dict entry that's not a Funcref
+ * "name" == NULL, "fudi.fd_dict" set,
+ * "fudi.fd_di" set, "fudi.fd_newkey" == NULL
+ */
p = eap->arg;
- name = trans_function_name(&p, eap->skip, FALSE);
- if (name == NULL && !eap->skip)
+ name = trans_function_name(&p, eap->skip, 0, &fudi);
+ paren = (vim_strchr(p, '(') != NULL);
+ if (name == NULL && (fudi.fd_dict == NULL || !paren) && !eap->skip)
{
/*
* Return on an invalid expression in braces, unless the expression
@@ -13704,7 +14002,10 @@
* interrupt, or an exception.
*/
if (!aborting())
+ {
+ vim_free(fudi.fd_newkey);
return;
+ }
else
eap->skip = TRUE;
}
@@ -13716,12 +14017,12 @@
/*
* ":function func" with only function name: list function.
*/
- if (vim_strchr(p, '(') == NULL)
+ if (!paren)
{
if (!ends_excmd(*skipwhite(p)))
{
EMSG(_(e_trailing));
- goto erret_name;
+ goto ret_free;
}
eap->nextcmd = check_nextcmd(p);
if (eap->nextcmd != NULL)
@@ -13753,7 +14054,7 @@
else
EMSG2(_("E123: Undefined function: %s"), name);
}
- goto erret_name;
+ goto ret_free;
}
/*
@@ -13765,7 +14066,7 @@
if (!eap->skip)
{
EMSG2(_("E124: Missing '(': %s"), eap->arg);
- goto erret_name;
+ goto ret_free;
}
/* attempt to continue by skipping some text */
if (vim_strchr(p, '(') != NULL)
@@ -13859,8 +14160,13 @@
/* Check if the function already exists, don't let the user type the
* whole function before telling him it doesn't work! For a script we
* need to skip the body to be able to find what follows. */
- if (!eap->skip && !eap->forceit && find_func(name) != NULL)
- EMSG2(_(e_funcexts), name);
+ if (!eap->skip && !eap->forceit)
+ {
+ if (fudi.fd_dict != NULL && fudi.fd_newkey == NULL)
+ EMSG(_(e_funcdict));
+ else if (name != NULL && find_func(name) != NULL)
+ EMSG2(_(e_funcexts), name);
+ }
msg_putchar('\n'); /* don't overwrite the function name */
cmdline_row = msg_row;
@@ -13925,7 +14231,7 @@
p += eval_fname_script(p);
if (ASCII_ISALPHA(*p))
{
- vim_free(trans_function_name(&p, TRUE, FALSE));
+ vim_free(trans_function_name(&p, TRUE, 0, NULL));
if (*skipwhite(p) == '(')
{
++nesting;
@@ -13981,40 +14287,90 @@
/*
* If there are no errors, add the function
*/
- v = find_var(name, FALSE);
- if (v != NULL && v->tv.v_type == VAR_FUNC)
+ if (fudi.fd_dict == NULL)
{
- EMSG2(_("E707: Function name conflicts with variable: %s"), name);
- goto erret;
- }
+ v = find_var(name, FALSE);
+ if (v != NULL && v->tv.v_type == VAR_FUNC)
+ {
+ EMSG2(_("E707: Function name conflicts with variable: %s"), name);
+ goto erret;
+ }
- fp = find_func(name);
- if (fp != NULL)
- {
- if (!eap->forceit)
+ fp = find_func(name);
+ if (fp != NULL)
{
- EMSG2(_(e_funcexts), name);
- goto erret;
+ if (!eap->forceit)
+ {
+ EMSG2(_(e_funcexts), name);
+ goto erret;
+ }
+ if (fp->calls > 0)
+ {
+ EMSG2(_("E127: Cannot redefine function %s: It is in use"),
+ name);
+ goto erret;
+ }
+ /* redefine existing function */
+ ga_clear_strings(&(fp->args));
+ ga_clear_strings(&(fp->lines));
+ vim_free(name);
+ name = NULL;
}
- if (fp->calls)
- {
- EMSG2(_("E127: Cannot redefine function %s: It is in use"), name);
- goto erret;
- }
- /* redefine existing function */
- ga_clear_strings(&(fp->args));
- ga_clear_strings(&(fp->lines));
- vim_free(name);
}
else
{
+ char numbuf[20];
+
+ fp = NULL;
+ if (fudi.fd_newkey == NULL && !eap->forceit)
+ {
+ EMSG(_(e_funcdict));
+ goto erret;
+ }
+
+ /* Give the function a sequential number. Can only be used with a
+ * Funcref! */
+ vim_free(name);
+ sprintf(numbuf, "%d", ++func_nr);
+ name = vim_strsave((char_u *)numbuf);
+ if (name == NULL)
+ goto erret;
+ }
+
+ if (fp == NULL)
+ {
fp = (ufunc_T *)alloc((unsigned)sizeof(ufunc_T));
if (fp == NULL)
goto erret;
+
+ if (fudi.fd_dict != NULL)
+ {
+ if (fudi.fd_di == NULL)
+ {
+ /* add new dict entry */
+ fudi.fd_di = dictitem_alloc();
+ if (fudi.fd_di == NULL)
+ {
+ vim_free(fp);
+ goto erret;
+ }
+ fudi.fd_di->di_key = fudi.fd_newkey;
+ fudi.fd_newkey = NULL;
+ dict_add(fudi.fd_dict, fudi.fd_di);
+ }
+ else
+ /* overwrite existing dict entry */
+ clear_tv(&fudi.fd_di->di_tv);
+ fudi.fd_di->di_tv.v_type = VAR_FUNC;
+ fudi.fd_di->di_tv.vval.v_string = vim_strsave(name);
+ fp->refcount = 1;
+ }
+
/* insert the new function in the function list */
+ fp->name = name;
+ name = NULL;
fp->next = firstfunc;
firstfunc = fp;
- fp->name = name;
}
fp->args = newargs;
fp->lines = newlines;
@@ -14022,15 +14378,14 @@
fp->flags = flags;
fp->calls = 0;
fp->script_ID = current_SID;
- did_emsg |= saved_did_emsg;
- vim_free(skip_until);
- return;
+ goto ret_free;
erret:
- vim_free(skip_until);
ga_clear_strings(&newargs);
ga_clear_strings(&newlines);
-erret_name:
+ret_free:
+ vim_free(skip_until);
+ vim_free(fudi.fd_newkey);
vim_free(name);
did_emsg |= saved_did_emsg;
}
@@ -14038,14 +14393,17 @@
/*
* Get a function name, translating "<SID>" and "<SNR>".
* Returns the function name in allocated memory, or NULL for failure.
+ * flags:
+ * TFN_INT: internal function name OK
+ * TFN_QUIET: be quiet
* Advances "pp" to just after the function name (if no error).
*/
static char_u *
-trans_function_name(pp, skip, exists)
+trans_function_name(pp, skip, flags, fdp)
char_u **pp;
int skip; /* only find the end, don't evaluate */
- int exists; /* TRUE for exists(): internal function name
- OK and be quiet. */
+ int flags;
+ funcdict *fdp; /* return: info about dictionary used */
{
char_u *name = NULL;
char_u *start;
@@ -14053,12 +14411,10 @@
int lead;
char_u sid_buf[20];
int len;
-#if 0
- char_u *expr_start, *expr_end;
- char_u *temp_string = NULL;
-#else
lval lv;
-#endif
+
+ if (fdp != NULL)
+ vim_memset(fdp, 0, sizeof(funcdict));
/* A name starting with "<SID>" or "<SNR>" is local to a script. */
start = *pp;
@@ -14066,8 +14422,7 @@
if (lead > 0)
start += lead;
-#if 1
- end = get_lval(start, NULL, &lv, FALSE, skip, exists);
+ end = get_lval(start, NULL, &lv, FALSE, skip, flags & TFN_QUIET);
if (end == start)
{
if (!skip)
@@ -14093,7 +14448,14 @@
if (lv.ll_tv != NULL)
{
- /* TODO: When defining a function accept a Dict here. */
+ if (fdp != NULL)
+ {
+ fdp->fd_dict = lv.ll_dict;
+ fdp->fd_newkey = lv.ll_newkey;
+ lv.ll_newkey = NULL;
+ fdp->fd_di = lv.ll_di;
+ fdp->fd_pdi = lv.ll_pdi;
+ }
if (lv.ll_tv->v_type == VAR_FUNC && lv.ll_tv->vval.v_string != NULL)
{
name = vim_strsave(lv.ll_tv->vval.v_string);
@@ -14101,8 +14463,11 @@
}
else
{
- if (!skip && !exists)
- EMSG(_("E999: Funcref required"));
+ if (!skip && !(flags & TFN_QUIET)
+ && (fdp == NULL || lv.ll_dict == NULL))
+ EMSG(_(e_funcref));
+ else
+ *pp = end;
name = NULL;
}
goto theend;
@@ -14141,7 +14506,7 @@
lead += (int)STRLEN(sid_buf);
}
}
- else if (!exists && !ASCII_ISUPPER(*lv.ll_name))
+ else if (!(flags & TFN_INT) && !ASCII_ISUPPER(*lv.ll_name))
{
EMSG2(_("E128: Function name must start with a capital: %s"),
lv.ll_name);
@@ -14166,85 +14531,6 @@
theend:
clear_lval(&lv);
return name;
-#endif
-
-#if 0
-
- end = find_name_end(start, &expr_start, &expr_end, FALSE);
- if (end == start)
- {
- if (!skip)
- EMSG(_("E129: Function name required"));
- return NULL;
- }
- if (expr_start != NULL && !skip)
- {
- /* expand magic curlies */
- temp_string = make_expanded_name(start, expr_start, expr_end, end);
- if (temp_string == NULL)
- {
- /*
- * Report an invalid expression in braces, unless the expression
- * evaluation has been cancelled due to an aborting error, an
- * interrupt, or an exception.
- */
- if (!aborting())
- EMSG2(_(e_invarg2), start);
- else
- *pp = end;
- return NULL;
- }
- start = temp_string;
- len = (int)STRLEN(temp_string);
- }
- else
- len = (int)(end - start);
-
- /*
- * Copy the function name to allocated memory.
- * Accept <SID>name() inside a script, translate into <SNR>123_name().
- * Accept <SNR>123_name() outside a script.
- */
- if (skip)
- lead = 0; /* do nothing */
- else if (lead > 0)
- {
- lead = 3;
- if (eval_fname_sid(*pp)) /* If it's "<SID>" */
- {
- if (current_SID <= 0)
- {
- EMSG(_(e_usingsid));
- return NULL;
- }
- sprintf((char *)sid_buf, "%ld_", (long)current_SID);
- lead += (int)STRLEN(sid_buf);
- }
- }
- else if (!exists && !ASCII_ISUPPER(*start))
- {
- EMSG2(_("E128: Function name must start with a capital: %s"), start);
- return NULL;
- }
- name = alloc((unsigned)(len + lead + 1));
- if (name != NULL)
- {
- if (lead > 0)
- {
- name[0] = K_SPECIAL;
- name[1] = KS_EXTRA;
- name[2] = (int)KE_SNR;
- if (eval_fname_sid(*pp)) /* If it's "<SID>" */
- STRCPY(name + 3, sid_buf);
- }
- mch_memmove(name + lead, start, (size_t)len);
- name[len + lead] = NUL;
- }
- *pp = end;
-
- vim_free(temp_string);
- return name;
-#endif
}
/*
@@ -14338,7 +14624,7 @@
char_u *p = name;
int n = FALSE;
- p = trans_function_name(&p, FALSE, TRUE);
+ p = trans_function_name(&p, FALSE, TFN_INT|TFN_QUIET, NULL);
if (p != NULL)
{
if (ASCII_ISUPPER(*p) || p[0] == K_SPECIAL)
@@ -14412,14 +14698,20 @@
ex_delfunction(eap)
exarg_T *eap;
{
- ufunc_T *fp = NULL, *pfp;
+ ufunc_T *fp = NULL;
char_u *p;
char_u *name;
+ funcdict fudi;
p = eap->arg;
- name = trans_function_name(&p, eap->skip, FALSE);
+ name = trans_function_name(&p, eap->skip, 0, &fudi);
+ vim_free(fudi.fd_newkey);
if (name == NULL)
+ {
+ if (fudi.fd_dict != NULL && !eap->skip)
+ EMSG(_(e_funcref));
return;
+ }
if (!ends_excmd(*skipwhite(p)))
{
vim_free(name);
@@ -14441,30 +14733,94 @@
EMSG2(_("E130: Undefined function: %s"), eap->arg);
return;
}
- if (fp->calls)
+ if (fp->calls > 0)
{
EMSG2(_("E131: Cannot delete function %s: It is in use"), eap->arg);
return;
}
- /* clear this function */
- vim_free(fp->name);
- ga_clear_strings(&(fp->args));
- ga_clear_strings(&(fp->lines));
-
- /* remove the function from the function list */
- if (firstfunc == fp)
- firstfunc = fp->next;
- else
+ if (fudi.fd_dict != NULL)
{
- for (pfp = firstfunc; pfp != NULL; pfp = pfp->next)
- if (pfp->next == fp)
- {
- pfp->next = fp->next;
- break;
- }
+ /* Delete the dict item that refers to the function, it will
+ * invoke func_unref() and possibly delete the function. */
+ *fudi.fd_pdi = fudi.fd_di->di_next;
+ dictitem_free(fudi.fd_di);
}
- vim_free(fp);
+ else
+ func_free(fp);
+ }
+}
+
+/*
+ * Free a function and remove it from the list of functions.
+ */
+ static void
+func_free(fp)
+ ufunc_T *fp;
+{
+ ufunc_T *pfp;
+
+ /* clear this function */
+ vim_free(fp->name);
+ ga_clear_strings(&(fp->args));
+ ga_clear_strings(&(fp->lines));
+
+ /* remove the function from the function list */
+ if (firstfunc == fp)
+ firstfunc = fp->next;
+ else
+ {
+ for (pfp = firstfunc; pfp != NULL; pfp = pfp->next)
+ if (pfp->next == fp)
+ {
+ pfp->next = fp->next;
+ break;
+ }
+ }
+ vim_free(fp);
+}
+
+/*
+ * Unreference a Function: decrement the reference count and free it when it
+ * becomes zero. Only for numbered functions.
+ */
+ static void
+func_unref(name)
+ char_u *name;
+{
+ ufunc_T *fp;
+
+ if (name != NULL && isdigit(*name))
+ {
+ fp = find_func(name);
+ if (fp == NULL)
+ EMSG2(_(e_intern2), "func_unref()");
+ else if (--fp->refcount <= 0)
+ {
+ /* Only delete it when it's not being used. Otherwise it's done
+ * when "calls" becomes zero. */
+ if (fp->calls == 0)
+ func_free(fp);
+ }
+ }
+}
+
+/*
+ * Count a reference to a Function.
+ */
+ static void
+func_ref(name)
+ char_u *name;
+{
+ ufunc_T *fp;
+
+ if (name != NULL && isdigit(*name))
+ {
+ fp = find_func(name);
+ if (fp == NULL)
+ EMSG2(_(e_intern2), "func_ref()");
+ else
+ ++fp->refcount;
}
}