changeset 80:d2796c60ca6f v7.0032

updated for version 7.0032
author vimboss
date Thu, 06 Jan 2005 23:28:25 +0000
parents e918d3e340a4
children bf3f798c35c6
files runtime/doc/todo.txt src/eval.c
diffstat 2 files changed, 588 insertions(+), 149 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 7.0aa.  Last change: 2005 Jan 05
+*todo.txt*      For Vim version 7.0aa.  Last change: 2005 Jan 06
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -31,26 +31,7 @@ be worked on, but only if you sponsor Vi
 -------------------- Known bugs and current work -----------------------
 
 List data type:
-- When removing items from the condition stack may free cs_fors.
-- don't copy the list, use a list-watcher to adjust the item pointer when it's
-  deleted.
-- "for a in list"
-    Make copy of the list to avoid trouble when it changes.  As one big block?
-- "for [a, b] in [[1, 2], [3, 4]]"
-- support list generator: items are obtained with a function by index.
-    "range(1, 400, 2)" creates one.
-- == (same value) and "is" (same list)
-- add many functions:
-    call(func, list)		call function
-    keys(list)			list of all indexes 0 - (len(list) - 1)
-    repeat(list, count)		return list concatenated count times
-    concat(list1, list2)	return list1 and list2 concatenated
-    extend(list1, list2)	concatenate list2 to list 1
-    extend(list1, list2, idx)	prepend list2 before idx in list1
-    count(list, item)		nr of times item appears in list
-    index(list, item)		lowest index of item in list
-    pop(list[, idx])		removes item at idx (default: last)
-    pop(list, idx1, idx2)	removes items idx1 to idx2, returns them
+- add more functions:
     reverse(list)		reverses order
     sort(list[, func])		sort; func compares items
     getval(list, idx[, default])	get value at idx or default
@@ -59,14 +40,14 @@ List data type:
     str2list()			parse string to list in several ways: white
 				separated, [] form, etc.
 Fix the error numbers E999 in eval.c.
-
-Function reference: Define a nameless (numbered) function and assign
-it to a Funcref variable.
-    :function Myfunc = (arg)
-    :endfunc
+- Cache the length of a List?
 
 Use 'ignorecase' for ":vimgrep"?
 
+When allocating a new variable, a search is done for an empty entry.  May
+waste a lot of time if there isn't one.  Keep an index of available entry,
+none available, or unknown.
+
 patch for QuickFixCmdPre and QuickFixCmdPost autocommands. (Ciaran McCreesh,
 2005 Jan 1)
 
@@ -101,7 +82,6 @@ PLANNED FOR VERSION 7.0:
 -   new DATA TYPES:
 	- None?  (or use empty string?)
 	- dictionary
-	Check old patch from Robert Webb for array support.
     Add type checking?  See ~/vim/ideas.txt.
 -   Add SPELLCHECKER, with easy to add support for many languages.
     8   Add spell checking.  Use "ispell -a" somehow.
--- a/src/eval.c
+++ b/src/eval.c
@@ -320,13 +320,19 @@ static void list_free __ARGS((listvar *l
 static listitem *listitem_alloc __ARGS((void));
 static void listitem_free __ARGS((listitem *item));
 static long list_len __ARGS((listvar *l));
+static int list_equal __ARGS((listvar *l1, listvar *l2, int ic));
+static int tv_equal __ARGS((typeval *tv1, typeval *tv2, int ic));
 static listitem *list_find __ARGS((listvar *l, long n));
+static listitem *list_find_ext __ARGS((listvar *l, long *ip));
 static void list_append __ARGS((listvar *l, listitem *item));
 static int list_append_tv __ARGS((listvar *l, typeval *tv));
+static int list_insert_tv __ARGS((listvar *l, typeval *tv, listitem *item));
+static int list_extend __ARGS((listvar	*l1, listvar *l2, listitem *bef));
+static int list_concat __ARGS((listvar *l1, listvar *l2, typeval *tv));
 static listvar *list_copy __ARGS((listvar *orig, int deep));
-static listitem *list_getrem __ARGS((listvar *l, long n));
+static void list_getrem __ARGS((listvar *l, listitem *item, listitem *item2));
 static char_u *list2string __ARGS((typeval *tv));
-static char_u *tv2string __ARGS((typeval *tv, char_u **tofree));
+static char_u *tv2string __ARGS((typeval *tv, char_u **tofree, char_u *numbuf));
 static int get_env_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
 static int find_internal_func __ARGS((char_u *name));
 static char_u *deref_func_name __ARGS((char_u *name, int *lenp));
@@ -348,11 +354,13 @@ static void f_bufnr __ARGS((typeval *arg
 static void f_bufwinnr __ARGS((typeval *argvars, typeval *rettv));
 static void f_byte2line __ARGS((typeval *argvars, typeval *rettv));
 static void f_byteidx __ARGS((typeval *argvars, typeval *rettv));
+static void f_call __ARGS((typeval *argvars, typeval *rettv));
 static void f_char2nr __ARGS((typeval *argvars, typeval *rettv));
 static void f_cindent __ARGS((typeval *argvars, typeval *rettv));
 static void f_col __ARGS((typeval *argvars, typeval *rettv));
 static void f_confirm __ARGS((typeval *argvars, typeval *rettv));
 static void f_copy __ARGS((typeval *argvars, typeval *rettv));
+static void f_count __ARGS((typeval *argvars, typeval *rettv));
 static void f_cscope_connection __ARGS((typeval *argvars, typeval *rettv));
 static void f_cursor __ARGS((typeval *argsvars, typeval *rettv));
 static void f_deepcopy __ARGS((typeval *argvars, typeval *rettv));
@@ -365,6 +373,7 @@ static void f_eventhandler __ARGS((typev
 static void f_executable __ARGS((typeval *argvars, typeval *rettv));
 static void f_exists __ARGS((typeval *argvars, typeval *rettv));
 static void f_expand __ARGS((typeval *argvars, typeval *rettv));
+static void f_extend __ARGS((typeval *argvars, typeval *rettv));
 static void f_filereadable __ARGS((typeval *argvars, typeval *rettv));
 static void f_filewritable __ARGS((typeval *argvars, typeval *rettv));
 static void f_finddir __ARGS((typeval *argvars, typeval *rettv));
@@ -411,6 +420,7 @@ static void f_iconv __ARGS((typeval *arg
 static void f_indent __ARGS((typeval *argvars, typeval *rettv));
 static void f_insert __ARGS((typeval *argvars, typeval *rettv));
 static void f_isdirectory __ARGS((typeval *argvars, typeval *rettv));
+static void f_index __ARGS((typeval *argvars, typeval *rettv));
 static void f_input __ARGS((typeval *argvars, typeval *rettv));
 static void f_inputdialog __ARGS((typeval *argvars, typeval *rettv));
 static void f_inputrestore __ARGS((typeval *argvars, typeval *rettv));
@@ -440,6 +450,7 @@ static void f_setcmdpos __ARGS((typeval 
 static void f_setwinvar __ARGS((typeval *argvars, typeval *rettv));
 static void f_remove __ARGS((typeval *argvars, typeval *rettv));
 static void f_rename __ARGS((typeval *argvars, typeval *rettv));
+static void f_repeat __ARGS((typeval *argvars, typeval *rettv));
 static void f_resolve __ARGS((typeval *argvars, typeval *rettv));
 static void f_search __ARGS((typeval *argvars, typeval *rettv));
 static void f_searchpair __ARGS((typeval *argvars, typeval *rettv));
@@ -449,7 +460,6 @@ static void f_remote_foreground __ARGS((
 static void f_remote_peek __ARGS((typeval *argvars, typeval *rettv));
 static void f_remote_read __ARGS((typeval *argvars, typeval *rettv));
 static void f_remote_send __ARGS((typeval *argvars, typeval *rettv));
-static void f_repeat __ARGS((typeval *argvars, typeval *rettv));
 static void f_server2client __ARGS((typeval *argvars, typeval *rettv));
 static void f_serverlist __ARGS((typeval *argvars, typeval *rettv));
 static void f_setline __ARGS((typeval *argvars, typeval *rettv));
@@ -2448,6 +2458,8 @@ eval3(arg, rettv, evaluate)
  *	var1 >= var2
  *	var1 < var2
  *	var1 <= var2
+ *	var1 is var2
+ *	var1 isnot var2
  *
  * "arg" must point to the first non-white of the expression.
  * "arg" is advanced to the next non-white after the recognized expression.
@@ -2464,6 +2476,7 @@ eval4(arg, rettv, evaluate)
     char_u	*p;
     int		i;
     exptype_T	type = TYPE_UNKNOWN;
+    int		type_is = FALSE;    /* TRUE for "is" and "isnot" */
     int		len = 2;
     long	n1, n2;
     char_u	*s1, *s2;
@@ -2507,6 +2520,17 @@ eval4(arg, rettv, evaluate)
 		    else
 			type = TYPE_SEQUAL;
 		    break;
+	case 'i':   if (p[1] == 's')
+		    {
+			if (p[2] == 'n' && p[3] == 'o' && p[4] == 't')
+			    len = 5;
+			if (!vim_isIDc(p[len]))
+			{
+			    type = len == 2 ? TYPE_EQUAL : TYPE_NEQUAL;
+			    type_is = TRUE;
+			}
+		    }
+		    break;
     }
 
     /*
@@ -2542,11 +2566,73 @@ eval4(arg, rettv, evaluate)
 
 	if (evaluate)
 	{
+	    if (type_is && rettv->v_type != var2.v_type)
+	    {
+		/* For "is" a different type always means FALSE, for "notis"
+		 * it means TRUE. */
+		n1 = (type == TYPE_NEQUAL);
+	    }
+	    else if (rettv->v_type == VAR_LIST || var2.v_type == VAR_LIST)
+	    {
+		if (type_is)
+		{
+		    n1 = (rettv->v_type == var2.v_type
+				   && rettv->vval.v_list == var2.vval.v_list);
+		    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(_("E999: Can only compare List with List"));
+		    else
+			EMSG(_("E999: Invalid operation for Lists"));
+		    clear_tv(rettv);
+		    clear_tv(&var2);
+		    return FAIL;
+		}
+		else
+		{
+		    /* Compare two Lists for being equal or unequal. */
+		    n1 = list_equal(rettv->vval.v_list, var2.vval.v_list, 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
+			|| (type != TYPE_EQUAL && type != TYPE_NEQUAL))
+		{
+		    if (rettv->v_type != var2.v_type)
+			EMSG(_("E999: Can only compare Funcref with Funcref"));
+		    else
+			EMSG(_("E999: Invalid operation for Funcrefs"));
+		    clear_tv(rettv);
+		    clear_tv(&var2);
+		    return FAIL;
+		}
+		else
+		{
+		    /* Compare two Funcrefs for being equal or unequal. */
+		    if (rettv->vval.v_string == NULL
+						|| var2.vval.v_string == NULL)
+			n1 = FALSE;
+		    else
+			n1 = STRCMP(rettv->vval.v_string,
+						     var2.vval.v_string) == 0;
+		    if (type == TYPE_NEQUAL)
+			n1 = !n1;
+		}
+	    }
+
 	    /*
 	     * If one of the two variables is a number, compare as a number.
 	     * When using "=~" or "!~", always compare as string.
 	     */
-	    if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER)
+	    else if ((rettv->v_type == VAR_NUMBER || var2.v_type == VAR_NUMBER)
 		    && type != TYPE_MATCH && type != TYPE_NOMATCH)
 	    {
 		n1 = get_tv_number(rettv);
@@ -2631,6 +2717,7 @@ eval5(arg, rettv, evaluate)
     int		evaluate;
 {
     typeval	var2;
+    typeval	var3;
     int		op;
     long	n1, n2;
     char_u	*s1, *s2;
@@ -2682,6 +2769,19 @@ eval5(arg, rettv, evaluate)
 		rettv->v_type = VAR_STRING;
 		rettv->vval.v_string = p;
 	    }
+	    else if (rettv->v_type == VAR_LIST && var2.v_type == VAR_LIST)
+	    {
+		/* concatenate Lists */
+		if (list_concat(rettv->vval.v_list, var2.vval.v_list,
+							       &var3) == FAIL)
+		{
+		    clear_tv(rettv);
+		    clear_tv(&var2);
+		    return FAIL;
+		}
+		clear_tv(rettv);
+		*rettv = var3;
+	    }
 	    else
 	    {
 		n1 = get_tv_number(rettv);
@@ -3589,6 +3689,66 @@ list_len(l)
 }
 
 /*
+ * Return TRUE when two lists have exactly the same values.
+ */
+    static int
+list_equal(l1, l2, ic)
+    listvar	*l1;
+    listvar	*l2;
+    int		ic;	/* ignore case for strings */
+{
+    listitem	*item1, *item2;
+
+    for (item1 = l1->lv_first, item2 = l2->lv_first;
+	    item1 != NULL && item2 != NULL;
+			       item1 = item1->li_next, item2 = item2->li_next)
+	if (!tv_equal(&item1->li_tv, &item2->li_tv, ic))
+	    return FALSE;
+    return item1 == NULL && item2 == NULL;
+}
+
+/*
+ * Return TRUE if "tv1" and "tv2" have the same value.
+ * Compares the items just like "==" would compare them.
+ */
+    static int
+tv_equal(tv1, tv2, ic)
+    typeval *tv1;
+    typeval *tv2;
+    int	    ic;	    /* ignore case */
+{
+    char_u	buf1[NUMBUFLEN], buf2[NUMBUFLEN];
+
+    if (tv1->v_type == VAR_LIST || tv2->v_type == VAR_LIST)
+    {
+	/* recursive! */
+	if (tv1->v_type != tv2->v_type
+		   || !list_equal(tv1->vval.v_list, tv2->vval.v_list, ic))
+	    return FALSE;
+    }
+    else if (tv1->v_type == VAR_FUNC || tv2->v_type == VAR_FUNC)
+    {
+	if (tv1->v_type != tv2->v_type
+		|| tv1->vval.v_string == NULL
+		|| tv2->vval.v_string == NULL
+		|| STRCMP(tv1->vval.v_string, tv2->vval.v_string) != 0)
+	    return FALSE;
+    }
+    else if (tv1->v_type == VAR_NUMBER || tv2->v_type == VAR_NUMBER)
+    {
+	if (get_tv_number(tv1) != get_tv_number(tv2))
+	    return FALSE;
+    }
+    else if (!ic && STRCMP(get_tv_string_buf(tv1, buf1),
+				       get_tv_string_buf(tv2, buf2)) != 0)
+	return FALSE;
+    else if (ic && STRICMP(get_tv_string_buf(tv1, buf1),
+				       get_tv_string_buf(tv2, buf2)) != 0)
+	return FALSE;
+    return TRUE;
+}
+
+/*
  * Locate item with index "n" in list "l" and return it.
  * A negative index is counted from the end; -1 is the last item.
  * Returns NULL when "n" is out of range.
@@ -3621,6 +3781,39 @@ list_find(l, n)
 }
 
 /*
+ * Like list_find(), but also find an item just past the end.
+ * "*ip" is the item to find.
+ * When found "*ip" is set to zero, when not found "*ip" is non-zero.
+ * Returns NULL when item not found or item is just past the end.
+ */
+    static listitem *
+list_find_ext(l, ip)
+    listvar	*l;
+    long	*ip;
+{
+    long	n;
+    listitem	*item;
+
+    if (*ip < 0)
+    {
+	/* Count from the end: -1 is before last item. */
+	item = l->lv_last;
+	for (n = *ip + 1; n < 0 && item != NULL; ++n)
+	    item = item->li_prev;
+	if (item == NULL)
+	    n = 1;	/* error! */
+    }
+    else
+    {
+	item = l->lv_first;
+	for (n = *ip; n > 0 && item != NULL; --n)
+	    item = item->li_next;
+    }
+    *ip = n;
+    return item;
+}
+
+/*
  * Append item "item" to the end of list "l".
  */
     static void
@@ -3646,6 +3839,7 @@ list_append(l, item)
 
 /*
  * Append typeval "tv" to the end of list "l".
+ * Return FAIL when out of memory.
  */
     static int
 list_append_tv(l, tv)
@@ -3662,6 +3856,81 @@ list_append_tv(l, tv)
 }
 
 /*
+ * Insert typeval "tv" in list "l" before "item".
+ * If "item" is NULL append at the end.
+ * Return FAIL when out of memory.
+ */
+    static int
+list_insert_tv(l, tv, item)
+    listvar	*l;
+    typeval	*tv;
+    listitem	*item;
+{
+    listitem	*ni = listitem_alloc();
+
+    if (ni == NULL)
+	return FAIL;
+    copy_tv(tv, &ni->li_tv);
+    if (item == NULL)
+	/* Append new item at end of list. */
+	list_append(l, ni);
+    else
+    {
+	/* Insert new item before existing item. */
+	ni->li_prev = item->li_prev;
+	ni->li_next = item;
+	if (item->li_prev == NULL)
+	    l->lv_first = ni;
+	else
+	    item->li_prev->li_next = ni;
+	item->li_prev = ni;
+    }
+    return OK;
+}
+
+/*
+ * Extend "l1" with "l2".
+ * If "bef" is NULL append at the end, otherwise insert before this item.
+ * Returns FAIL when out of memory.
+ */
+    static int
+list_extend(l1, l2, bef)
+    listvar	*l1;
+    listvar	*l2;
+    listitem	*bef;
+{
+    listitem	*item;
+
+    for (item = l2->lv_first; item != NULL; item = item->li_next)
+	if (list_insert_tv(l1, &item->li_tv, bef) == FAIL)
+	    return FAIL;
+    return OK;
+}
+
+/*
+ * Concatenate lists "l1" and "l2" into a new list, stored in "tv".
+ * Return FAIL when out of memory.
+ */
+    static int
+list_concat(l1, l2, tv)
+    listvar	*l1;
+    listvar	*l2;
+    typeval	*tv;
+{
+    listvar	*l;
+
+    /* make a copy of the first list. */
+    l = list_copy(l1, FALSE);
+    if (l == NULL)
+	return FAIL;
+    tv->v_type = VAR_LIST;
+    tv->vval.v_list = l;
+
+    /* append all items from the second list */
+    return list_extend(l, l2, NULL);
+}
+
+/*
  * Make a copy of list "l".  Shallow if "deep" is FALSE.
  * The refcount of the new list is set to 1.
  * Returns NULL when out of memory.
@@ -3716,30 +3985,32 @@ list_copy(orig, deep)
 }
 
 /*
- * Remove item with index "n" from list "l" and return it.
- * Returns NULL when "n" is out of range.
- */
-    static listitem *
-list_getrem(l, n)
+ * Remove items "item" to "item2" from list "l".
+ */
+    static void
+list_getrem(l, item, item2)
     listvar	*l;
-    long	n;
-{
     listitem	*item;
-
-    item = list_find(l, n);
-    if (item != NULL)
-    {
-	list_fix_watch(l, item);	/* notify watchers */
-	if (item->li_next == NULL)
-	    l->lv_last = item->li_prev;
-	else
-	    item->li_next->li_prev = item->li_prev;
-	if (item->li_prev == NULL)
-	    l->lv_first = item->li_next;
-	else
-	    item->li_prev->li_next = item->li_next;
-    }
-    return item;
+    listitem	*item2;
+{
+    listitem	*ip;
+
+    /* notify watchers */
+    for (ip = item; ip != NULL; ip = ip->li_next)
+    {
+	list_fix_watch(l, ip);
+	if (ip == item2)
+	    break;
+    }
+
+    if (item2->li_next == NULL)
+	l->lv_last = item->li_prev;
+    else
+	item2->li_next->li_prev = item->li_prev;
+    if (item->li_prev == NULL)
+	l->lv_first = item2->li_next;
+    else
+	item->li_prev->li_next = item2->li_next;
 }
 
 /*
@@ -3755,6 +4026,7 @@ list2string(tv)
     int		first = TRUE;
     char_u	*tofree;
     char_u	*s;
+    char_u	numbuf[NUMBUFLEN];
 
     if (tv->vval.v_list == NULL)
 	return NULL;
@@ -3768,7 +4040,7 @@ list2string(tv)
 	else
 	    ga_concat(&ga, (char_u *)", ");
 
-	s = tv2string(&item->li_tv, &tofree);
+	s = tv2string(&item->li_tv, &tofree, numbuf);
 	if (s != NULL)
 	    ga_concat(&ga, s);
 	vim_free(tofree);
@@ -3782,14 +4054,14 @@ list2string(tv)
 /*
  * Return a string with the string representation of a variable.
  * If the memory is allocated "tofree" is set to it, otherwise NULL.
- * Can only be used once before the value is used, it may call
- * get_var_string().
+ * "numbuf" is used for a number.
  * May return NULL;
  */
     static char_u *
-tv2string(tv, tofree)
+tv2string(tv, tofree, numbuf)
     typeval	*tv;
     char_u	**tofree;
+    char_u	*numbuf;
 {
     switch (tv->v_type)
     {
@@ -3806,7 +4078,7 @@ tv2string(tv, tofree)
 	    EMSG2(_(e_intern2), "tv2string()");
     }
     *tofree = NULL;
-    return get_tv_string(tv);
+    return get_tv_string_buf(tv, numbuf);
 }
 
 /*
@@ -3888,11 +4160,13 @@ static struct fst
     {"bufwinnr",	1, 1, f_bufwinnr},
     {"byte2line",	1, 1, f_byte2line},
     {"byteidx",		2, 2, f_byteidx},
+    {"call",		2, 2, f_call},
     {"char2nr",		1, 1, f_char2nr},
     {"cindent",		1, 1, f_cindent},
     {"col",		1, 1, f_col},
     {"confirm",		1, 4, f_confirm},
     {"copy",		1, 1, f_copy},
+    {"count",		2, 3, f_count},
     {"cscope_connection",0,3, f_cscope_connection},
     {"cursor",		2, 2, f_cursor},
     {"deepcopy",	1, 1, f_deepcopy},
@@ -3905,6 +4179,7 @@ static struct fst
     {"executable",	1, 1, f_executable},
     {"exists",		1, 1, f_exists},
     {"expand",		1, 2, f_expand},
+    {"extend",		2, 3, f_extend},
     {"file_readable",	1, 1, f_filereadable},	/* obsolete */
     {"filereadable",	1, 1, f_filereadable},
     {"filewritable",	1, 1, f_filewritable},
@@ -3950,6 +4225,7 @@ static struct fst
     {"hostname",	0, 0, f_hostname},
     {"iconv",		3, 3, f_iconv},
     {"indent",		1, 1, f_indent},
+    {"index",		2, 3, f_index},
     {"input",		1, 2, f_input},
     {"inputdialog",	1, 3, f_inputdialog},
     {"inputrestore",	0, 0, f_inputrestore},
@@ -3979,7 +4255,7 @@ static struct fst
     {"remote_peek",	1, 2, f_remote_peek},
     {"remote_read",	1, 1, f_remote_read},
     {"remote_send",	2, 3, f_remote_send},
-    {"remove",		2, 2, f_remove},
+    {"remove",		2, 3, f_remove},
     {"rename",		2, 2, f_rename},
     {"repeat",		2, 2, f_repeat},
     {"resolve",		1, 1, f_resolve},
@@ -4402,13 +4678,17 @@ f_append(argvars, rettv)
     typeval	*rettv;
 {
     long	lnum;
+    listvar	*l;
 
     rettv->vval.v_number = 1; /* Default: Failed */
     if (argvars[0].v_type == VAR_LIST)
     {
-	if (argvars[0].vval.v_list != NULL
-		&& list_append_tv(argvars[0].vval.v_list, &argvars[1]) == OK)
+	l = argvars[0].vval.v_list;
+	if (l != NULL && list_append_tv(l, &argvars[1]) == OK)
+	{
+	    ++l->lv_refcount;
 	    copy_tv(&argvars[0], rettv);
+	}
     }
     else
     {
@@ -4762,6 +5042,55 @@ f_byteidx(argvars, rettv)
 }
 
 /*
+ * "call(func, arglist)" function
+ */
+    static void
+f_call(argvars, rettv)
+    typeval	*argvars;
+    typeval	*rettv;
+{
+    char_u	*func;
+    typeval	argv[MAX_FUNC_ARGS];
+    int		argc = 0;
+    listitem	*item;
+    int		dummy;
+
+    rettv->vval.v_number = 0;
+    if (argvars[1].v_type != VAR_LIST)
+    {
+	EMSG(_(e_listreq));
+	return;
+    }
+    if (argvars[1].vval.v_list == NULL)
+	return;
+
+    if (argvars[0].v_type == VAR_FUNC)
+	func = argvars[0].vval.v_string;
+    else
+	func = get_tv_string(&argvars[0]);
+
+    for (item = argvars[1].vval.v_list->lv_first; item != NULL;
+							 item = item->li_next)
+    {
+	if (argc == MAX_FUNC_ARGS)
+	{
+	    EMSG(_("E999: Too many arguments"));
+	    break;
+	}
+	/* Make a copy of each argument (is this really needed?) */
+	copy_tv(&item->li_tv, &argv[argc++]);
+    }
+
+    if (item == NULL)
+	(void)call_func(func, STRLEN(func), rettv, argc, argv,
+		  curwin->w_cursor.lnum, curwin->w_cursor.lnum, &dummy, TRUE);
+
+    /* Free the arguments. */
+    while (argc > 0)
+	clear_tv(&argv[--argc]);
+}
+
+/*
  * "char2nr(string)" function
  */
     static void
@@ -4924,6 +5253,33 @@ f_copy(argvars, rettv)
 }
 
 /*
+ * "count()" function
+ */
+    static void
+f_count(argvars, rettv)
+    typeval	*argvars;
+    typeval	*rettv;
+{
+    listitem	*li;
+    long	n = 0;
+    int		ic = FALSE;
+
+    if (argvars[0].v_type != VAR_LIST)
+	EMSG(_(e_listreq));
+    else if (argvars[0].vval.v_list != NULL)
+    {
+	if (argvars[2].v_type != VAR_UNKNOWN)
+	    ic = get_tv_number(&argvars[2]);
+
+	for (li = argvars[0].vval.v_list->lv_first; li != NULL;
+							     li = li->li_next)
+	    if (tv_equal(&li->li_tv, &argvars[1], ic))
+		++n;
+    }
+    rettv->vval.v_number = n;
+}
+
+/*
  * "cscope_connection([{num} , {dbpath} [, {prepend}]])" function
  *
  * Checks the existence of a cscope connection.
@@ -5263,6 +5619,48 @@ f_expand(argvars, rettv)
 }
 
 /*
+ * "extend(list, list [, idx])" function
+ */
+    static void
+f_extend(argvars, rettv)
+    typeval	*argvars;
+    typeval	*rettv;
+{
+    long	before;
+    long	n;
+    listitem	*item;
+    listvar	*l1, *l2;
+
+    rettv->vval.v_number = 0;
+    if (argvars[0].v_type != VAR_LIST || argvars[1].v_type != VAR_LIST)
+    {
+	EMSG(_(e_listreq));
+	return;
+    }
+    l1 = argvars[0].vval.v_list;
+    l2 = argvars[1].vval.v_list;
+    if (l1 != NULL && l2 != NULL)
+    {
+	if (argvars[2].v_type != VAR_UNKNOWN)
+	{
+	    n = before = get_tv_number(&argvars[2]);
+	    item = list_find_ext(l1, &n);
+	    if (n != 0)
+	    {
+		EMSGN(_(e_listidx), before);
+		return;
+	    }
+	}
+	else
+	    item = NULL;
+	list_extend(l1, l2, item);
+
+	++l1->lv_refcount;
+	copy_tv(&argvars[0], rettv);
+    }
+}
+
+/*
  * "filereadable()" function
  */
     static void
@@ -6916,6 +7314,40 @@ f_indent(argvars, rettv)
 	rettv->vval.v_number = -1;
 }
 
+/*
+ * "index()" function
+ */
+    static void
+f_index(argvars, rettv)
+    typeval	*argvars;
+    typeval	*rettv;
+{
+    listvar	*l;
+    listitem	*item;
+    long	idx = 0;
+    int		ic = FALSE;
+
+    rettv->vval.v_number = -1;
+    if (argvars[0].v_type != VAR_LIST)
+    {
+	EMSG(_(e_listreq));
+	return;
+    }
+    l = argvars[0].vval.v_list;
+    if (l != NULL)
+    {
+	if (argvars[2].v_type != VAR_UNKNOWN)
+	    ic = get_tv_number(&argvars[2]);
+
+	for (item = l->lv_first; item != NULL; item = item->li_next, ++idx)
+	    if (tv_equal(&item->li_tv, &argvars[1], ic))
+	    {
+		rettv->vval.v_number = idx;
+		break;
+	    }
+    }
+}
+
 static int inputsecret_flag = 0;
 
 /*
@@ -7093,7 +7525,6 @@ f_insert(argvars, rettv)
     long	before = 0;
     long	n;
     listitem	*item;
-    listitem	*ni;
     listvar	*l;
 
     if (argvars[0].v_type != VAR_LIST)
@@ -7103,46 +7534,15 @@ f_insert(argvars, rettv)
 	if (argvars[2].v_type != VAR_UNKNOWN)
 	    before = get_tv_number(&argvars[2]);
 
-	if (before < 0)
-	{
-	    /* Count from the end: -1 is before last item. */
-	    item = l->lv_last;
-	    for (n = before + 1; n < 0 && item != NULL; ++n)
-		item = item->li_prev;
-	    if (item == NULL)
-		n = 1;	/* report an error, don't append */
-	}
-	else
-	{
-	    /* Can't use list_find() here, we allow one past the end. */
-	    item = l->lv_first;
-	    for (n = before; n > 0 && item != NULL; --n)
-		item = item->li_next;
-	}
+	n = before;
+	item = list_find_ext(l, &n);
 	if (n > 0)
 	    EMSGN(_(e_listidx), before);
 	else
 	{
-	    ni = listitem_alloc();
-	    if (ni != NULL)
-	    {
-		copy_tv(&argvars[1], &ni->li_tv);
-		if (item == NULL)
-		    /* Append new item at end of list. */
-		    list_append(l, ni);
-		else
-		{
-		    /* Insert new item before existing item. */
-		    ni->li_prev = item->li_prev;
-		    ni->li_next = item;
-		    if (item->li_prev == NULL)
-			l->lv_first = ni;
-		    else
-			item->li_prev->li_next = ni;
-		    item->li_prev = ni;
-		}
-		copy_tv(&argvars[0], rettv);
-	    }
+	    list_insert_tv(l, &argvars[1], item);
+	    ++l->lv_refcount;
+	    copy_tv(&argvars[0], rettv);
 	}
     }
 }
@@ -7595,7 +7995,7 @@ f_nr2char(argvars, rettv)
 }
 
 /*
- * "remove({list}, {idx})" function
+ * "remove({list}, {idx} [, {end}])" function
  */
     static void
 f_remove(argvars, rettv)
@@ -7603,21 +8003,59 @@ f_remove(argvars, rettv)
     typeval	*rettv;
 {
     listvar	*l;
-    listitem	*item;
+    listitem	*item, *item2;
+    listitem	*li;
     long	idx;
-
+    long	end;
+
+    rettv->vval.v_number = 0;
     if (argvars[0].v_type != VAR_LIST)
 	EMSG(_("E999: First argument of remove() must be a list"));
     else if ((l = argvars[0].vval.v_list) != NULL)
     {
 	idx = get_tv_number(&argvars[1]);
-	item = list_getrem(l, idx);
+	item = list_find(l, idx);
 	if (item == NULL)
 	    EMSGN(_(e_listidx), idx);
 	else
 	{
-	    *rettv = item->li_tv;
-	    vim_free(item);
+	    if (argvars[2].v_type == VAR_UNKNOWN)
+	    {
+		/* Remove one item, return its value. */
+		list_getrem(l, item, item);
+		*rettv = item->li_tv;
+		vim_free(item);
+	    }
+	    else
+	    {
+		/* Remove range of items, return list with values. */
+		end = get_tv_number(&argvars[2]);
+		item2 = list_find(l, end);
+		if (item2 == NULL)
+		    EMSGN(_(e_listidx), end);
+		else
+		{
+		    for (li = item; li != item2 && li != NULL; li = li->li_next)
+			;
+		    if (li == NULL)  /* didn't find "item2" after "item" */
+			EMSG(_(e_invrange));
+		    else
+		    {
+			list_getrem(l, item, item2);
+			l = list_alloc();
+			if (l != NULL)
+			{
+			    rettv->v_type = VAR_LIST;
+			    rettv->vval.v_list = l;
+			    l->lv_first = item;
+			    l->lv_last = item2;
+			    l->lv_refcount = 1;
+			    item->li_prev = NULL;
+			    item2->li_next = NULL;
+			}
+		    }
+		}
+	    }
 	}
     }
 }
@@ -7640,6 +8078,60 @@ f_rename(argvars, rettv)
 }
 
 /*
+ * "repeat()" function
+ */
+/*ARGSUSED*/
+    static void
+f_repeat(argvars, rettv)
+    typeval	*argvars;
+    typeval	*rettv;
+{
+    char_u	*p;
+    int		n;
+    int		slen;
+    int		len;
+    char_u	*r;
+    int		i;
+    listvar	*l;
+
+    n = get_tv_number(&argvars[1]);
+    if (argvars[0].v_type == VAR_LIST)
+    {
+	l = list_alloc();
+	if (l != NULL && argvars[0].vval.v_list != NULL)
+	{
+	    l->lv_refcount = 1;
+	    while (n-- > 0)
+		if (list_extend(l, argvars[0].vval.v_list, NULL) == FAIL)
+		    break;
+	}
+	rettv->v_type = VAR_LIST;
+	rettv->vval.v_list = l;
+    }
+    else
+    {
+	p = get_tv_string(&argvars[0]);
+	rettv->v_type = VAR_STRING;
+	rettv->vval.v_string = NULL;
+
+	slen = (int)STRLEN(p);
+	len = slen * n;
+	if (len <= 0)
+	    return;
+
+	r = alloc(len + 1);
+	if (r != NULL)
+	{
+	    for (i = 0; i < n; i++)
+		mch_memmove(r + i * slen, p, (size_t)slen);
+	    r[len] = NUL;
+	}
+
+	rettv->vval.v_string = r;
+    }
+}
+
+/*
  * "resolve()" function
  */
     static void
@@ -8637,45 +9129,6 @@ f_remote_foreground(argvars, rettv)
 #endif
 }
 
-/*
- * "repeat()" function
- */
-/*ARGSUSED*/
-    static void
-f_repeat(argvars, rettv)
-    typeval	*argvars;
-    typeval	*rettv;
-{
-    char_u	*p;
-    int		n;
-    int		slen;
-    int		len;
-    char_u	*r;
-    int		i;
-
-    p = get_tv_string(&argvars[0]);
-    n = get_tv_number(&argvars[1]);
-
-    rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = NULL;
-
-    slen = (int)STRLEN(p);
-    len = slen * n;
-
-    if (len <= 0)
-        return;
-
-    r = alloc(len + 1);
-    if (r != NULL)
-    {
-        for (i = 0; i < n; i++)
-	    mch_memmove(r + i * slen, p, (size_t)slen);
-        r[len] = NUL;
-    }
-
-    rettv->vval.v_string = r;
-}
-
 #ifdef HAVE_STRFTIME
 /*
  * "strftime({format}[, {time}])" function
@@ -8804,9 +9257,10 @@ f_string(argvars, rettv)
     typeval	*rettv;
 {
     char_u	*tofree;
+    char_u	numbuf[NUMBUFLEN];
 
     rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = tv2string(&argvars[0], &tofree);
+    rettv->vval.v_string = tv2string(&argvars[0], &tofree, numbuf);
     if (tofree == NULL)
 	rettv->vval.v_string = vim_strsave(rettv->vval.v_string);
 }
@@ -10475,8 +10929,9 @@ list_one_var(v, prefix)
 {
     char_u	*tofree;
     char_u	*s;
-
-    s = tv2string(&v->tv, &tofree);
+    char_u	numbuf[NUMBUFLEN];
+
+    s = tv2string(&v->tv, &tofree, numbuf);
     list_one_var_a(prefix, v->v_name, v->tv.v_type,
 						s == NULL ? (char_u *)"" : s);
     vim_free(tofree);
@@ -10699,6 +11154,7 @@ ex_echo(eap)
     char_u	*p;
     int		needclr = TRUE;
     int		atstart = TRUE;
+    char_u	numbuf[NUMBUFLEN];
 
     if (eap->skip)
 	++emsg_skip;
@@ -10728,7 +11184,8 @@ ex_echo(eap)
 	    }
 	    else if (eap->cmdidx == CMD_echo)
 		msg_puts_attr((char_u *)" ", echo_attr);
-	    for (p = tv2string(&rettv, &tofree); *p != NUL && !got_int; ++p)
+	    for (p = tv2string(&rettv, &tofree, numbuf);
+						   *p != NUL && !got_int; ++p)
 		if (*p == '\n' || *p == '\r' || *p == TAB)
 		{
 		    if (*p != TAB && needclr)
@@ -11938,11 +12395,12 @@ get_return_cmd(rettv)
 {
     char_u	*s;
     char_u	*tofree = NULL;
+    char_u	numbuf[NUMBUFLEN];
 
     if (rettv == NULL)
 	s = (char_u *)"";
     else
-	s = tv2string((typeval *)rettv, &tofree);
+	s = tv2string((typeval *)rettv, &tofree, numbuf);
 
     STRCPY(IObuff, ":return ");
     STRNCPY(IObuff + 8, s, IOSIZE - 8);
@@ -12112,6 +12570,7 @@ write_viminfo_varlist(fp)
     int		i;
     char	*s;
     char_u	*tofree;
+    char_u	numbuf[NUMBUFLEN];
 
     if (find_viminfo_parameter('!') == NULL)
 	return;
@@ -12130,7 +12589,7 @@ write_viminfo_varlist(fp)
 		default: continue;
 	    }
 	    fprintf(fp, "!%s\t%s\t", this_var->v_name, s);
-	    viminfo_writestring(fp, tv2string(&this_var->tv, &tofree));
+	    viminfo_writestring(fp, tv2string(&this_var->tv, &tofree, numbuf));
 	    vim_free(tofree);
 	}
     }