]> granicus.if.org Git - vim/commitdiff
updated for version 7.0038 v7.0038
authorBram Moolenaar <Bram@vim.org>
Fri, 14 Jan 2005 21:53:12 +0000 (21:53 +0000)
committerBram Moolenaar <Bram@vim.org>
Fri, 14 Jan 2005 21:53:12 +0000 (21:53 +0000)
runtime/doc/todo.txt
runtime/doc/version7.txt
runtime/keymap/polish-slash_cp1250.vim
runtime/tools/tcltags [deleted file]
src/eval.c
src/ex_cmds.c
src/gui_gtk_x11.c

index 0add158cee35e5aaa0babd0474069775ac588a2d..13f4ad1c459872c90ca437dde0d733732b2aa4e5 100644 (file)
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 7.0aa.  Last change: 2005 Jan 11
+*todo.txt*      For Vim version 7.0aa.  Last change: 2005 Jan 14
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -30,13 +30,42 @@ be worked on, but only if you sponsor Vim development.  See |sponsor|.
                                                        *known-bugs*
 -------------------- Known bugs and current work -----------------------
 
-Fixes in ex_getln.c also in Vim 6.3.
+"daw" doesn't delete white space before single word in line. (Jens Paulus)
+
+Dictionary:
+- filter() for dictionaries: :call filter(dict, '& =~ "bla.*"')
+    & for key, &v for value?
+- functions:
+       len(dict)               nr of items
+       copy(dict)              make a copy
+       deepcopy(dict)          make a copy recursively
+       has_key(dict, key)      whether key is present
+       getval(dict, key [, default])
+       extend(dict1, dict2)    add items from dict2 to dict1
+- Using "self" in function with "dict" attribute.  Also allow l:self.
+    Such a function must be called from a dictionary entry.
+  Third argument of call() passes dictionary:
+       :let res = call(function("DictFunc"), arglist, mydict)
+- Define nameless function: ":function dict.key(arg)"
+- ":delfunc dict.key".
+- ":unlet dict.key" "unlet list[idx]"
+
+Mention Rsync command on runtime.php page:
+    rsync -avzcP --delete --exclude="dos" --delete-excluded ftp.nluug.nl::Vim/runtime/ vim63-runtime
 
 List type:
+- Make second index in list[i:j] exclusive, like Python?
+    Marcus Aurelius: yes
 - Add List functions to version7.txt.
 - Add List functions to overview of funtions in user manual.
 - Explain Lists in the user manual?
 
+Add +=, -= and .= assignments.
+
+netrw plugin:
+- provide :Explore and :Sexplore like the old file explorer?
+- alignment of long listing isn't very good.
+
 Better way to get temp file for tcltags and vimspell scripts. (javier Pena)
 Possibly leave out code for temp directory.
 
@@ -124,6 +153,10 @@ PLANNED FOR VERSION 7.0:
     - Check Readline for its completion interface.
     - Use ctags for other languages.  Writing a file could trigger running
       ctags, merging the tags of the changed file.
+    Also see "Visual Assist" http://www.wholetomato.com/products:
+    - Put the list of choices right under the place where they would be
+      inserted.
+    - Pre-expand abbreviations, show which abbrevs would match?
 -   UNDO TREE: keep all states of the text, don't delete undo info.
     When making a change, instead of clearing any future undo (thus redo)
     info, make a new branch.
@@ -144,6 +177,7 @@ PLANNED FOR VERSION 7.0:
     extension: ".filename.un~" (like swapfile but "un~" instead of "swp").
 7   Support WINDOW TABS.  Works like several pages, each with their own
     split windows.  Patch for GTK 1.2 passed on by Christian Michon, 2004 Jan 6.
+    Don't forget to provide an "X" to close a tab.
     Also for the console!
     In Emacs these are called frames.  Could also call them "pages".
     Use "1gt" - "99gt" to switch to a tab?
@@ -244,6 +278,10 @@ List data type performance:
 - Cache the last used index?
 - Use blocks of items, so that finding an item by index is faster?
 
+Dictionary data type performance:
+- Use a hash to locate items
+
+
 Awaiting updated patches:
     --- awaiting updated patch ---
     7   Add patch from Wall for this one ( ~/Mail/oldmail/wall/in.00019 ):
index ccf9b037fb130561ee9a0e638035c1ebe0553253..9fa874746aadb0c43d79fc5f15c1f8e7de5c6b65 100644 (file)
@@ -1,4 +1,4 @@
-*version7.txt*  For Vim version 7.0aa.  Last change: 2005 Jan 11
+*version7.txt*  For Vim version 7.0aa.  Last change: 2005 Jan 14
 
 
                  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -51,6 +51,10 @@ buffer is still modified compared to the original file.  And when undoing
 all changes the file would actually be marked modified.  It does mean that
 ":quit" fails now.
 
+In a |literal-string| a single quote can be doubled to get one.
+":echo 'a''b'" would result in "a b", but now that two quotes stand for one it
+results in "a'b".
+
 
 Minor incompatibilities:
 
@@ -76,9 +80,9 @@ Win32: The effect of the <F10> key depended on 'winaltkeys'.  Now it depends
 on whether <F10> has been mapped or not.  This allows mapping <F10> without
 changing 'winaltkeys'.
 
-When using CTRL-A on "08" it became "018", which is illogical.  Now it becomes
-"9".  The leading zero(s) is(are) removed to avoid the number becoming octal
-after incrementing "009" to "010".
+When 'octal' is in 'nrformats' and using CTRL-A on "08" it became "018", which
+is illogical.  Now it becomes "9".  The leading zero(s) is(are) removed to
+avoid the number becoming octal after incrementing "009" to "010".
 
 When 'encoding' is set to a Unicode encoding, the value for 'fileencodings'
 now includes "default" before "latin1".  This means that for files with 8-bit
@@ -106,10 +110,6 @@ The |string()| function can be used to get a string representation of a
 variable.  Works for Numbers, Strings and composites of them.  Then |eval()|
 can be used to turn the string back into the variable value.
 
-The |sharp-string| has been added as a convenient way to put an expression in
-a string.  Examples: >
-       :let l = filter(mylist, # & =~ '^\A'#)
-
 
 KDE support                                            *new-KDE*
 -----------
@@ -392,6 +392,8 @@ a bit less dependent on the X11 font management.
 
 When a register is empty it is not stored in the viminfo file.
 
+Removed the tcltags script, it's obsolete.
+
 ==============================================================================
 COMPILE TIME CHANGES                                   *compile-changes-7*
 
@@ -662,4 +664,7 @@ mode. (Peter Winters)
 
 "2daw" didn't work at end of file if the last word is a single character.
 
+Completion for ":next a'<Tab>" put a backslash before single quote, but it was
+not removed when editing a file.  Now halve backslashes in save_patterns().
+
  vim:tw=78:ts=8:ft=help:norl:
index 3aa3202bf4fa4c422fabad4d2ca121d3b55a540c..df1f67a21170f0d803fa38daa073465acae21ea0 100644 (file)
@@ -1,6 +1,8 @@
 " Polish letters keymap for cp1250
 " Maintainer:  HS6_06  <hs6_06@o2.pl>
-" Last Changed:        2005 Jan 9
+" Last Changed:        2005 Jan 12
+" Current version: 1.0.2
+" History: see polish-slash.vim
 
 " This keymap adds the special Polish letters 
 " to an existing Latin keyboard.
@@ -9,7 +11,7 @@
 "   instead of AltGr+{acelnosxz} you ve to use "/" followed by {acelnosxz}
 
 " short keymap name for statusline
-let b:keymap_name = "polish-slash-cp1250"
+let b:keymap_name = "PL-slash-WIN"
 
 scriptencoding latin1
 
diff --git a/runtime/tools/tcltags b/runtime/tools/tcltags
deleted file mode 100755 (executable)
index 9524152..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-#!/bin/sh
-# vim:ts=4:
-# Generates a tag file for TCL code. Slow, but gets the job done.
-#
-# Written by Darren Hiebert <darren@hiebert.com>
-
-program_name=`basename $0`
-program_version="0.3"
-program_author="Darren Hiebert"
-author_email="darren@hiebert.com"
-tmp_tagfile=/tmp/${program_name}.$$
-
-usage="\
-Usage: $program_name [-au] [-{f|o} tagfile] [--format=n] file(s)
-  -a          append to current tag file
-  -f tagfile  specify output tag file name (default=tags)
-  -o          alternative for -f
-  -u          unsorted
-  --format=n  specify tag file format (default=2)
-  --help      print this help message
-"
-
-# defaults
-#
-append=0
-format=2
-sorted=1
-tagfile=tags
-filelist=
-
-# read options
-#
-getparam()
-{
-       if [ -n "$1" ]; then
-               # set variable to word passed in
-               echo "$2='$1'; opt="
-       else
-               # set variable to next word on command line
-               echo "$2="'$1'"; shift"
-       fi
-}
-
-finished=0
-while [ $# -gt 0  -a  $finished -eq 0 ]
-do
-    case "$1" in
-               --*)
-                       opt=`echo "$1" | cut -c 3-`
-                       shift
-                       opt_name=`echo "$opt" | awk -F= '{print $1}'`
-                       opt_value=`echo "$opt" | awk -F= '{print $2}'`
-                       case "$opt_name" in
-                               format) case "$opt_value" in
-                                                       1|2) format=$opt_value;;
-                                                       *) echo "--$opt: unsupported value" >&2; exit 1;;
-                                               esac
-                                               ;;
-                               help)   echo "$usage"; exit 0;;
-                               *)              echo "$opt_name: unsupported option" >&2; exit 1;;
-                       esac
-                       ;;
-               -*)
-                       # chop off leading '-'
-                       opt=`echo "$1" | cut -c 2-`
-                       shift
-                       while [ -n "$opt" ]
-                       do
-                               opt_char=`echo "$opt" | cut -c 1`
-                               opt=`echo "$opt" | cut -c 2-`
-                               case "$opt_char" in
-                                       a) append=1;;
-                                       u) sorted=0;;
-                                       o|f) eval `getparam "$opt" tagfile`;;
-                                       *) echo "$opt: unsupported option" >&2; exit 1;;
-                               esac
-                       done
-                       ;;
-               *) filelist="$*"; break;;
-    esac
-done
-
-if [ -z "$filelist" ] ;then
-    echo "$usage" >&2; exit 1
-fi
-
-# awk program for generating tags
-#
-ext_flags=""
-ttype=""
-if [ $format -eq 2 ] ;then
-    ext_flags=';\"     %s'
-    ttype=", type"
-fi
-awkprg='
-function trim_comment(string) {
-    comment = index(string, "#")
-    if (comment != 0)
-       string = substr(string, 0, comment-1)
-    return string
-}
-function maketag(tagname, pattern, type, line_end) {
-    gsub(/\\/, "\\\\", pattern)
-    gsub(/\//, "\\/", pattern)
-    if (line_end)
-       terminator="$"
-    else
-       terminator=""
-    printf("%s\t%s\t/^%s%s/'"$ext_flags"'\n", \
-               tagname, FILENAME, pattern, terminator'"$ttype"')
-}
-$1 == "proc"  &&  $3 ~ /^{/  {
-    pattern = substr($0, 0, index($0, "{"))
-    maketag($2, pattern, "f", 0)
-}
-/^set[ \t]/  &&  $2 !~ /\(/ {
-    pattern = substr($0, 0, index($0, $2) + length($2))
-    maketag($2, pattern, "v", 0)
-}
-/^array[ \t]*set[ \t]/  &&  $3 !~ /\(/ {
-    pattern = substr($0, 0, index($0, $3) + length($3))
-    maketag($3, pattern, "v", 0)
-}'
-
-# add or correct the pseudo tags
-#
-if [ "$tagfile" != "-" ] ;then
-    if [ $append -eq 1 ]; then
-       # ensure existing sort flag is correct
-       sed -e "/^!_TAG_FILE_SORTED/s/  [01]    /       $sorted /" \
-           -e "/^!_TAG_FILE_FORMAT/s/  1       /       $format /" \
-           $tagfile > $tmp_tagfile
-    else
-       echo -ne "\
-!_TAG_FILE_FORMAT      $format /extended format; --format=1 will not append ;\" to lines/
-!_TAG_FILE_SORTED      $sorted /0=unsorted, 1=sorted/
-!_TAG_PROGRAM_AUTHOR   $program_author /$author_email/
-!_TAG_PROGRAM_NAME     $program_name   //
-!_TAG_PROGRAM_VERSION  $program_version        //
-" > $tmp_tagfile
-    fi
-fi
-
-# generate tags
-#
-awk "$awkprg" $filelist >> $tmp_tagfile
-
-if [ $sorted -eq 1 ] ;then
-    sort -u -o $tmp_tagfile $tmp_tagfile
-fi
-
-if [ "$tagfile" = '-' ] ;then
-    cat $tmp_tagfile
-else
-    cp $tmp_tagfile $tagfile
-fi
-rm $tmp_tagfile
-
-exit 0
index 54215198d21d4ee5d974a3f7a379dc2569d574ae..100bc80a991ddb28cef9b2934810f25df8dba532 100644 (file)
@@ -47,6 +47,7 @@ typedef struct
        varnumber_T     v_number;       /* number value */
        char_u          *v_string;      /* string value (can be NULL!) */
        struct listvar_S *v_list;       /* list value (can be NULL!) */
+       struct dictvar_S *v_dict;       /* dict value (can be NULL!) */
     }          vval;
 } typeval;
 
@@ -56,6 +57,7 @@ typedef struct
 #define VAR_STRING  2  /* "v_string" is used */
 #define VAR_FUNC    3  /* "v_string" is function name */
 #define VAR_LIST    4  /* "v_list" is used */
+#define VAR_DICT    5  /* "v_dict" is used */
 
 /*
  * Structure to hold an internal variable with a name.
@@ -104,12 +106,43 @@ struct listvar_S
 typedef struct listvar_S listvar;
 
 #define VAR_LIST_MAXNEST 100   /* maximum nesting of lists */
+
+/*
+ * Structure to hold an item of a Dictionary.
+ */
+struct dictitem_S
+{
+    struct dictitem_S  *di_next;       /* next item in list */
+    char_u             *di_key;        /* key string */
+    typeval            di_tv;          /* type and value of the variable */
+};
+
+typedef struct dictitem_S dictitem;
+
+/*
+ * Structure to hold info about a Dictionary.
+ */
+struct dictvar_S
+{
+    int                dv_refcount;    /* reference count */
+    dictitem   *dv_first;      /* first item, NULL if none */
+};
+
+typedef struct dictvar_S dictvar;
+
+
 static char *e_letunexp        = N_("E18: Unexpected characters in :let");
 static char *e_listidx = N_("E684: list index out of range: %ld");
 static char *e_undefvar = N_("E121: Undefined variable: %s");
 static char *e_missbrac = N_("E111: Missing ']'");
 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_listarg = N_("E686: Argument of %s must be a List");
+static char *e_listdictarg = N_("E999: 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_toomanyarg = N_("E118: Too many arguments for function: %s");
+static char *e_dictkey = N_("E999: key not found in Dictionary: %s");
 
 /*
  * All user-defined global variables are stored in "variables".
@@ -316,7 +349,6 @@ static int eval_index __ARGS((char_u **arg, typeval *rettv, int evaluate));
 static int get_option_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
 static int get_string_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
 static int get_lit_string_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
-static int get_sharp_string_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
 static int get_list_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
 static listvar *list_alloc __ARGS((void));
 static void list_unref __ARGS((listvar *l));
@@ -340,6 +372,16 @@ static void list_getrem __ARGS((listvar *l, listitem *item, listitem *item2));
 static char_u *list2string __ARGS((typeval *tv));
 static void list_join __ARGS((garray_T *gap, listvar *l, char_u *sep, int echo));
 
+static dictvar *dict_alloc __ARGS((void));
+static void dict_unref __ARGS((dictvar *d));
+static void dict_free __ARGS((dictvar *d));
+static dictitem *dictitem_alloc __ARGS((void));
+static void dictitem_free __ARGS((dictitem *item));
+static void dict_add __ARGS((dictvar *d, dictitem *item));
+static dictitem *dict_find __ARGS((dictvar *d, char_u *key, int len));
+static char_u *dict2string __ARGS((typeval *tv));
+static int get_dict_tv __ARGS((char_u **arg, typeval *rettv, int evaluate));
+
 static char_u *echo_string __ARGS((typeval *tv, char_u **tofree, char_u *numbuf));
 static char_u *tv2string __ARGS((typeval *tv, char_u **tofree, char_u *numbuf));
 static char_u *string_quote __ARGS((char_u *str, int function));
@@ -438,7 +480,9 @@ static void f_inputsave __ARGS((typeval *argvars, typeval *rettv));
 static void f_inputsecret __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_items __ARGS((typeval *argvars, typeval *rettv));
 static void f_join __ARGS((typeval *argvars, typeval *rettv));
+static void f_keys __ARGS((typeval *argvars, typeval *rettv));
 static void f_last_buffer_nr __ARGS((typeval *argvars, typeval *rettv));
 static void f_len __ARGS((typeval *argvars, typeval *rettv));
 static void f_libcall __ARGS((typeval *argvars, typeval *rettv));
@@ -459,6 +503,7 @@ static void f_mode __ARGS((typeval *argvars, typeval *rettv));
 static void f_nextnonblank __ARGS((typeval *argvars, typeval *rettv));
 static void f_nr2char __ARGS((typeval *argvars, typeval *rettv));
 static void f_prevnonblank __ARGS((typeval *argvars, typeval *rettv));
+static void f_range __ARGS((typeval *argvars, typeval *rettv));
 static void f_remote_expr __ARGS((typeval *argvars, typeval *rettv));
 static void f_remote_foreground __ARGS((typeval *argvars, typeval *rettv));
 static void f_remote_peek __ARGS((typeval *argvars, typeval *rettv));
@@ -501,6 +546,7 @@ static void f_tolower __ARGS((typeval *argvars, typeval *rettv));
 static void f_toupper __ARGS((typeval *argvars, typeval *rettv));
 static void f_tr __ARGS((typeval *argvars, typeval *rettv));
 static void f_type __ARGS((typeval *argvars, typeval *rettv));
+static void f_values __ARGS((typeval *argvars, typeval *rettv));
 static void f_virtcol __ARGS((typeval *argvars, typeval *rettv));
 static void f_visualmode __ARGS((typeval *argvars, typeval *rettv));
 static void f_winbufnr __ARGS((typeval *argvars, typeval *rettv));
@@ -561,8 +607,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 int check_changedtick __ARGS((char_u *arg));
 static char_u *set_var_idx __ARGS((char_u *name, char_u *ip, typeval *rettv, int copy, char_u *endchars));
 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));
+static int do_unlet_var __ARGS((char_u *name, int forceit));
 
 /*
  * Set an internal variable to a string value. Creates the variable if it does
@@ -1529,15 +1579,12 @@ ex_let_one(arg, tv, copy, endchars)
            if (!aborting())
                EMSG2(_(e_invarg2), arg);
        }
-       else if (*p == '[')
+       else if (*p == '[' || *p == '.')
            arg_end = set_var_idx(arg, p, tv, copy, endchars);
        else if (endchars != NULL
                               && vim_strchr(endchars, *skipwhite(p)) == NULL)
            EMSG(_(e_letunexp));
-       else if (STRNCMP(arg, "b:changedtick", 13) == 0
-                                           && !eval_isnamec(arg[13]))
-           EMSG2(_(e_readonlyvar), arg);
-       else
+       else if (!check_changedtick(arg))
        {
            c1 = *p;
            *p = NUL;
@@ -1555,9 +1602,25 @@ ex_let_one(arg, tv, copy, endchars)
     return arg_end;
 }
 
+/*
+ * If "arg" is equal to "b:changedtick" give an error and return TRUE.
+ */
+    static int
+check_changedtick(arg)
+    char_u     *arg;
+{
+    if (STRNCMP(arg, "b:changedtick", 13) == 0 && !eval_isnamec(arg[13]))
+    {
+       EMSG2(_(e_readonlyvar), arg);
+       return TRUE;
+    }
+    return FALSE;
+}
+
 /*
  * Set a variable with an index: "name[expr]", "name[expr:expr]",
- * "name[expr][expr]", etc.  Only works if "name" is an existing List.
+ * "name[expr][expr]", "name.key", "name.key[expr]" etc.
+ * Only works if "name" is an existing List or Dictionary.
  * "ip" points to the first '['.
  * Returns a pointer to just after the last used ']'; NULL for error.
  */
@@ -1577,11 +1640,15 @@ set_var_idx(name, ip, rettv, copy, endchars)
     int                range = FALSE;
     typeval    *tv;
     long       n1 = 0, n2 = 0;
-    int                empty1, empty2 = FALSE;
-    listitem   *item = NULL;
+    int                empty1 = FALSE, empty2 = FALSE;
+    listitem   *li = NULL;
     listitem   *ni;
     listitem   *ri;
     listvar    *l = NULL;
+    dictitem   *di;
+    char_u     *key = NULL;
+    char_u     *newkey = NULL;
+    int                len;
 
     c1 = *ip;
     *ip = NUL;
@@ -1593,11 +1660,12 @@ set_var_idx(name, ip, rettv, copy, endchars)
        return NULL;
 
     tv = &v->tv;
-    for (p = ip; *p == '['; p = skipwhite(p + 1))
+    for (p = ip; *p == '[' || (*p == '.' && tv->v_type == VAR_DICT); )
     {
-       if (tv->v_type != VAR_LIST || tv->vval.v_list == NULL)
+       if (!(tv->v_type == VAR_LIST && tv->vval.v_list != NULL)
+               && !(tv->v_type == VAR_DICT && tv->vval.v_dict != NULL))
        {
-           EMSG(_("E689: Can only index a List"));
+           EMSG(_("E689: Can only index a List or Dictionary"));
            p = NULL;
            break;
        }
@@ -1608,120 +1676,191 @@ set_var_idx(name, ip, rettv, copy, endchars)
            break;
        }
 
-       /* Get the index [expr] or the first index [expr: ]. */
-       p = skipwhite(p + 1);
-       if (*p == ':')
-           empty1 = TRUE;
-       else
+       len = -1;
+       if (*p == '.')
        {
-           empty1 = FALSE;
-           if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */
+           key = p + 1;
+           for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len)
+               ;
+           if (len == 0)
            {
+               EMSG(_(e_emptykey));
                p = NULL;
                break;
            }
+           p = key + len;
        }
-
-       /* Optionally get the second index [ :expr]. */
-       if (*p == ':')
+       else
        {
-           if (rettv->v_type != VAR_LIST || rettv->vval.v_list == NULL)
-           {
-               EMSG(_("E709: [:] requires a List value"));
-               p = NULL;
-               if (!empty1)
-                   clear_tv(&var1);
-               break;
-           }
+           /* Get the index [expr] or the first index [expr: ]. */
            p = skipwhite(p + 1);
-           if (*p == ']')
-               empty2 = TRUE;
+           if (*p == ':')
+               empty1 = TRUE;
            else
            {
-               empty2 = FALSE;
-               if (eval1(&p, &var2, TRUE) == FAIL)     /* recursive! */
+               empty1 = FALSE;
+               if (eval1(&p, &var1, TRUE) == FAIL)     /* recursive! */
                {
+                   p = NULL;
+                   break;
+               }
+           }
+
+           /* Optionally get the second index [ :expr]. */
+           if (*p == ':')
+           {
+               if (tv->v_type == VAR_DICT)
+               {
+                   EMSG(_("E999: Cannot use [:] with a Dictionary"));
+                   p = NULL;
+                   if (!empty1)
+                       clear_tv(&var1);
+                   break;
+               }
+               if (rettv->v_type != VAR_LIST || rettv->vval.v_list == NULL)
+               {
+                   EMSG(_("E709: [:] requires a List value"));
                    p = NULL;
                    if (!empty1)
                        clear_tv(&var1);
                    break;
                }
+               p = skipwhite(p + 1);
+               if (*p == ']')
+                   empty2 = TRUE;
+               else
+               {
+                   empty2 = FALSE;
+                   if (eval1(&p, &var2, TRUE) == FAIL) /* recursive! */
+                   {
+                       p = NULL;
+                       if (!empty1)
+                           clear_tv(&var1);
+                       break;
+                   }
+               }
+               range = TRUE;
            }
-           range = TRUE;
+           else
+               range = FALSE;
+
+           if (*p != ']')
+           {
+               EMSG(_(e_missbrac));
+               if (!empty1)
+                   clear_tv(&var1);
+               if (range && !empty2)
+                   clear_tv(&var2);
+               p = NULL;
+               break;
+           }
+
+           /* Skip to past ']'. */
+           ++p;
        }
-       else
-           range = FALSE;
 
-       if (*p != ']')
+       if (tv->v_type == VAR_DICT)
        {
-           EMSG(_(e_missbrac));
-           if (!empty1)
+           if (len == -1)
+           {
+               key = get_tv_string(&var1);
+               if (*key == NUL)
+               {
+                   EMSG(_(e_emptykey));
+                   clear_tv(&var1);
+                   p = NULL;
+                   break;
+               }
+           }
+           di = dict_find(tv->vval.v_dict, key, len);
+           if (di == NULL)
+           {
+               /* Key does not exist in dict: may need toadd it. */
+               if (*p == '[' || *p == '.')
+               {
+                   EMSG2(_("E999: Key does not exist in Dictionary: %s"), key);
+                   p = NULL;
+                   if (len == -1)
+                       clear_tv(&var1);
+                   break;
+               }
+               if (len == -1)
+                   newkey = vim_strsave(key);
+               else
+                   newkey = vim_strnsave(key, len);
+               if (len == -1)
+                   clear_tv(&var1);
+               if (newkey == NULL)
+                   p = NULL;
+               break;
+           }
+           if (len == -1)
                clear_tv(&var1);
-           if (range && !empty2)
-               clear_tv(&var2);
-           p = NULL;
-           break;
+           tv = &di->di_tv;
        }
-
-       /*
-        * Get the number and item for the only or first index.
-        */
-       if (empty1)
-           n1 = 0;
        else
        {
-           n1 = get_tv_number(&var1);
-           clear_tv(&var1);
-       }
-       l = tv->vval.v_list;
-       item = list_find(l, n1);
-       if (item == NULL)
-       {
-           EMSGN(_(e_listidx), n1);
-           p = NULL;
+           /*
+            * Get the number and item for the only or first index of the List.
+            */
+           if (empty1)
+               n1 = 0;
+           else
+           {
+               n1 = get_tv_number(&var1);
+               clear_tv(&var1);
+           }
+           l = tv->vval.v_list;
+           li = list_find(l, n1);
+           if (li == NULL)
+           {
+               EMSGN(_(e_listidx), n1);
+               p = NULL;
+               if (range && !empty2)
+                   clear_tv(&var2);
+               break;
+           }
+
+           /*
+            * May need to find the item or absolute index for the second
+            * index of a range.
+            * When no index given: "empty2" is TRUE.
+            * Otherwise "n2" is set to the second index.
+            */
            if (range && !empty2)
+           {
+               n2 = get_tv_number(&var2);
                clear_tv(&var2);
-           break;
-       }
+               if (n2 < 0)
+               {
+                   ni = list_find(l, n2);
+                   if (ni == NULL)
+                   {
+                       EMSGN(_(e_listidx), n2);
+                       p = NULL;
+                       break;
+                   }
+                   n2 = list_idx_of_item(l, ni);
+               }
 
-       /*
-        * May need to find the item or absolute index for the second index of
-        * a range.
-        * When no index given: "empty2" is TRUE.
-        * Otherwise "n2" is set to the second index.
-        */
-       if (range && !empty2)
-       {
-           n2 = get_tv_number(&var2);
-           clear_tv(&var2);
-           if (n2 < 0)
-           {
-               ni = list_find(l, n2);
-               if (ni == NULL)
+               /* Check that n2 isn't before n1. */
+               if (n1 < 0)
+                   n1 = list_idx_of_item(l, li);
+               if (n2 < n1)
                {
                    EMSGN(_(e_listidx), n2);
                    p = NULL;
                    break;
                }
-               n2 = list_idx_of_item(l, ni);
            }
 
-           /* Check that n2 isn't before n1. */
-           if (n1 < 0)
-               n1 = list_idx_of_item(l, item);
-           if (n2 < n1)
-           {
-               EMSGN(_(e_listidx), n2);
-               p = NULL;
-               break;
-           }
+           tv = &li->li_tv;
        }
-
-       tv = &item->li_tv;
     }
 
     if (p != NULL)
     {
+       p = skipwhite(p);
        if (endchars != NULL && vim_strchr(endchars, *p) == NULL)
        {
            EMSG(_(e_letunexp));
@@ -1734,12 +1873,12 @@ set_var_idx(name, ip, rettv, copy, endchars)
             */
            for (ri = rettv->vval.v_list->lv_first; ri != NULL; )
            {
-               clear_tv(&item->li_tv);
-               copy_tv(&ri->li_tv, &item->li_tv);
+               clear_tv(&li->li_tv);
+               copy_tv(&ri->li_tv, &li->li_tv);
                ri = ri->li_next;
                if (ri == NULL || (!empty2 && n2 == n1))
                    break;
-               if (item->li_next == NULL)
+               if (li->li_next == NULL)
                {
                    /* Need to add an empty item. */
                    ni = listitem_alloc();
@@ -1752,29 +1891,49 @@ set_var_idx(name, ip, rettv, copy, endchars)
                    ni->li_tv.vval.v_number = 0;
                    list_append(l, ni);
                }
-               item = item->li_next;
+               li = li->li_next;
                ++n1;
            }
            if (ri != NULL)
                EMSG(_("E710: List value has more items than target"));
-           else if (empty2 ? item != NULL && item->li_next != NULL : n1 != n2)
+           else if (empty2 ? li != NULL && li->li_next != NULL : n1 != n2)
                EMSG(_("E711: List value has not enough items"));
        }
        else
        {
+           if (newkey != NULL)
+           {
+               /* Need to add the item to the dictionary. */
+               di = dictitem_alloc();
+               if (di == NULL)
+                   p = NULL;
+               else
+               {
+                   di->di_key = newkey;
+                   newkey = NULL;
+                   dict_add(tv->vval.v_dict, di);
+                   tv = &di->di_tv;
+               }
+           }
+           else
+               clear_tv(tv);
+
            /*
             * Assign the value to the variable or list item.
             */
-           clear_tv(tv);
-           if (copy)
-               copy_tv(rettv, tv);
-           else
+           if (p != NULL)
            {
-               *tv = *rettv;
-               init_tv(rettv);
+               if (copy)
+                   copy_tv(rettv, tv);
+               else
+               {
+                   *tv = *rettv;
+                   init_tv(rettv);
+               }
            }
        }
     }
+    vim_free(newkey);
     return p;
 }
 
@@ -2004,17 +2163,11 @@ set_context_for_expression(xp, arg, cmdidx)
            }
            else if (c == '\'')     /* literal string */
            {
+               /* Trick: '' is like stopping and starting a literal string. */
                while ((c = *++xp->xp_pattern) != NUL && c != '\'')
                    /* skip */ ;
                xp->xp_context = EXPAND_NOTHING;
            }
-           else if (c == '#')      /* sharp string */
-           {
-               /* Trick: ## is like stopping and starting a sharp string. */
-               while ((c = *++xp->xp_pattern) != NUL && c != '#')
-                   /* skip */ ;
-               xp->xp_context = EXPAND_NOTHING;
-           }
            else if (c == '|')
            {
                if (xp->xp_pattern[1] == '|')
@@ -2149,7 +2302,7 @@ ex_unlet(eap)
     do
     {
        /* Find the end of the name. */
-       name_end = find_name_end(arg, &expr_start, &expr_end, FALSE);
+       name_end = find_name_end(arg, &expr_start, &expr_end, TRUE);
 
        if (!vim_iswhite(*name_end) && !ends_excmd(*name_end))
        {
@@ -2183,11 +2336,8 @@ ex_unlet(eap)
                }
                else
                {
-                   if (do_unlet(temp_string) == FAIL && !eap->forceit)
-                   {
-                       EMSG2(_("E108: No such variable: \"%s\""), temp_string);
+                   if (do_unlet_var(temp_string, eap->forceit) == FAIL)
                        error = TRUE;
-                   }
                    vim_free(temp_string);
                }
            }
@@ -2195,13 +2345,8 @@ ex_unlet(eap)
            {
                cc = *name_end;
                *name_end = NUL;
-
-               if (do_unlet(arg) == FAIL && !eap->forceit)
-               {
-                   EMSG2(_("E108: No such variable: \"%s\""), arg);
+               if (do_unlet_var(arg, eap->forceit) == FAIL)
                    error = TRUE;
-               }
-
                *name_end = cc;
            }
        }
@@ -2211,6 +2356,21 @@ ex_unlet(eap)
     eap->nextcmd = check_nextcmd(arg);
 }
 
+    static int
+do_unlet_var(name, forceit)
+    char_u     *name;
+    int                forceit;
+{
+    if (check_changedtick(name))
+       return FAIL;
+    if (do_unlet(name) == FAIL && !forceit)
+    {
+       EMSG2(_("E108: No such variable: \"%s\""), name);
+       return FAIL;
+    }
+    return OK;
+}
+
 /*
  * "unlet" a variable.  Return OK if it existed, FAIL if not.
  */
@@ -3061,7 +3221,8 @@ eval6(arg, rettv, evaluate)
  *  ! in front         logical NOT
  *  - in front         unary minus
  *  + in front         unary plus (ignored)
- *  trailing []                subscript in String
+ *  trailing []                subscript in String or List
+ *  trailing .name     entry in Dictionary
  *
  * "arg" must point to the first non-white of the expression.
  * "arg" is advanced to the next non-white after the recognized expression.
@@ -3127,21 +3288,21 @@ eval7(arg, rettv, evaluate)
                break;
 
     /*
-     * Literal string constant: 'string'.
+     * Literal string constant: 'str''ing'.
      */
     case '\'': ret = get_lit_string_tv(arg, rettv, evaluate);
                break;
 
     /*
-     * Sharp string constant: #str##ing#.
+     * List: [expr, expr]
      */
-    case '#':  ret = get_sharp_string_tv(arg, rettv, evaluate);
+    case '[':  ret = get_list_tv(arg, rettv, evaluate);
                break;
 
     /*
-     * List: [expr, expr]
+     * Dictionary: {key: val, key: val}
      */
-    case '[':  ret = get_list_tv(arg, rettv, evaluate);
+    case '{':  ret = get_dict_tv(arg, rettv, evaluate);
                break;
 
     /*
@@ -3191,53 +3352,60 @@ eval7(arg, rettv, evaluate)
                }
                break;
 
-    /*
-     * Must be a variable or function name then.
-     */
-    default:   s = *arg;
-               len = get_func_len(arg, &alias, evaluate);
-               if (alias != NULL)
-                   s = alias;
+    default:   ret = NOTDONE;
+               break;
+    }
 
-               if (len == 0)
-                   ret = FAIL;
-               else
+    if (ret == NOTDONE)
+    {
+       /*
+        * Must be a variable or function name.
+        * Can also be a curly-braces kind of name: {expr}.
+        */
+       s = *arg;
+       len = get_func_len(arg, &alias, evaluate);
+       if (alias != NULL)
+           s = alias;
+
+       if (len == 0)
+           ret = FAIL;
+       else
+       {
+           if (**arg == '(')           /* recursive! */
+           {
+               /* If "s" is the name of a variable of type VAR_FUNC
+                * use its contents. */
+               s = deref_func_name(s, &len);
+
+               /* Invoke the function. */
+               ret = get_func_tv(s, len, rettv, arg,
+                         curwin->w_cursor.lnum, curwin->w_cursor.lnum,
+                         &len, evaluate);
+               /* Stop the expression evaluation when immediately
+                * aborting on error, or when an interrupt occurred or
+                * an exception was thrown but not caught. */
+               if (aborting())
                {
-                   if (**arg == '(')           /* recursive! */
-                   {
-                       /* If "s" is the name of a variable of type VAR_FUNC
-                        * use its contents. */
-                       s = deref_func_name(s, &len);
-
-                       /* Invoke the function. */
-                       ret = get_func_tv(s, len, rettv, arg,
-                                 curwin->w_cursor.lnum, curwin->w_cursor.lnum,
-                                 &len, evaluate);
-                       /* Stop the expression evaluation when immediately
-                        * aborting on error, or when an interrupt occurred or
-                        * an exception was thrown but not caught. */
-                       if (aborting())
-                       {
-                           if (ret == OK)
-                               clear_tv(rettv);
-                           ret = FAIL;
-                       }
-                   }
-                   else if (evaluate)
-                       ret = get_var_tv(s, len, rettv);
+                   if (ret == OK)
+                       clear_tv(rettv);
+                   ret = FAIL;
                }
+           }
+           else if (evaluate)
+               ret = get_var_tv(s, len, rettv);
+       }
 
-               if (alias != NULL)
-                   vim_free(alias);
-
-               break;
+       if (alias != NULL)
+           vim_free(alias);
     }
+
     *arg = skipwhite(*arg);
 
     /*
-     * Handle expr[expr] and expr[expr:expr] subscript.
+     * Handle expr[expr], expr[expr:expr] subscript and .name lookup.
      */
-    while (**arg == '[' && ret == OK)
+    while ((**arg == '[' || (**arg == '.' && rettv->v_type == VAR_DICT))
+                                   && !vim_iswhite(*(*arg - 1)) && ret == OK)
     {
        if (eval_index(arg, rettv, evaluate) == FAIL)
        {
@@ -3282,9 +3450,10 @@ eval_index(arg, rettv, evaluate)
     int                empty1 = FALSE, empty2 = FALSE;
     typeval    var1, var2;
     long       n1, n2 = 0;
-    long       len;
-    int                range;
+    long       len = -1;
+    int                range = FALSE;
     char_u     *s;
+    char_u     *key = NULL;
 
     if (rettv->v_type == VAR_FUNC)
     {
@@ -3292,48 +3461,63 @@ eval_index(arg, rettv, evaluate)
        return FAIL;
     }
 
-    /*
-     * Get the (first) variable from inside the [].
-     */
-    *arg = skipwhite(*arg + 1);
-    if (**arg == ':')
-       empty1 = TRUE;
-    else if (eval1(arg, &var1, evaluate) == FAIL)      /* recursive! */
-       return FAIL;
-
-    /*
-     * Get the second variable from inside the [:].
-     */
-    if (**arg == ':')
+    if (**arg == '.')
+    {
+       /*
+        * dict.name
+        */
+       key = *arg + 1;
+       for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len)
+           ;
+       if (len == 0)
+           return FAIL;
+       *arg = skipwhite(key + len);
+    }
+    else
     {
-       range = TRUE;
+       /*
+        * something[idx]
+        *
+        * Get the (first) variable from inside the [].
+        */
        *arg = skipwhite(*arg + 1);
-       if (**arg == ']')
-           empty2 = TRUE;
-       else if (eval1(arg, &var2, evaluate) == FAIL)   /* recursive! */
+       if (**arg == ':')
+           empty1 = TRUE;
+       else if (eval1(arg, &var1, evaluate) == FAIL)   /* recursive! */
+           return FAIL;
+
+       /*
+        * Get the second variable from inside the [:].
+        */
+       if (**arg == ':')
+       {
+           range = TRUE;
+           *arg = skipwhite(*arg + 1);
+           if (**arg == ']')
+               empty2 = TRUE;
+           else if (eval1(arg, &var2, evaluate) == FAIL)       /* recursive! */
+           {
+               clear_tv(&var1);
+               return FAIL;
+           }
+       }
+
+       /* Check for the ']'. */
+       if (**arg != ']')
        {
+           EMSG(_(e_missbrac));
            clear_tv(&var1);
+           if (range)
+               clear_tv(&var2);
            return FAIL;
        }
-    }
-    else
-       range = FALSE;
-
-    /* Check for the ']'. */
-    if (**arg != ']')
-    {
-       EMSG(_(e_missbrac));
-       clear_tv(&var1);
-       if (range)
-           clear_tv(&var2);
-       return FAIL;
+       *arg = skipwhite(*arg + 1);     /* skip the ']' */
     }
 
     if (evaluate)
     {
-       if (empty1)
-           n1 = 0;
-       else
+       n1 = 0;
+       if (!empty1 && rettv->v_type != VAR_DICT)
        {
            n1 = get_tv_number(&var1);
            clear_tv(&var1);
@@ -3405,7 +3589,7 @@ eval_index(arg, rettv, evaluate)
 
                    if (n2 < 0)
                        n2 = len + n2;
-                   if (!empty2 && (n2 < 0 || n2 >= len || n2 < n1))
+                   if (!empty2 && (n2 < 0 || n2 >= len || n2 + 1 < n1))
                    {
                        EMSGN(_(e_listidx), n2);
                        return FAIL;
@@ -3436,10 +3620,46 @@ eval_index(arg, rettv, evaluate)
                    *rettv = var1;
                }
                break;
+
+           case VAR_DICT:
+               if (range)
+               {
+                   EMSG(_("E999: Using range with Dictionary"));
+                   if (len == -1)
+                       clear_tv(&var1);
+                   return FAIL;
+               }
+               {
+                   dictitem    *item;
+
+                   if (len == -1)
+                   {
+                       key = get_tv_string(&var1);
+                       if (*key == NUL)
+                       {
+                           EMSG(_("E999: Empty key for Dictionary"));
+                           clear_tv(&var1);
+                           return FAIL;
+                       }
+                   }
+
+                   item = dict_find(rettv->vval.v_dict, key, (int)len);
+
+                   if (item == NULL)
+                       EMSG2(_("E999: Key not found in Dictionary: %s"), key);
+                   if (len == -1)
+                       clear_tv(&var1);
+                   if (item == NULL)
+                       return FAIL;
+
+                   copy_tv(&item->di_tv, &var1);
+                   clear_tv(rettv);
+                   *rettv = var1;
+               }
+               break;
        }
     }
 
-    *arg = skipwhite(*arg + 1);        /* skip the ']' */
     return OK;
 }
 
@@ -3536,7 +3756,6 @@ get_string_tv(arg, rettv, evaluate)
 {
     char_u     *p;
     char_u     *name;
-    int                i;
     int                extra = 0;
 
     /*
@@ -3574,20 +3793,21 @@ get_string_tv(arg, rettv, evaluate)
     name = alloc((unsigned)(p - *arg + extra));
     if (name == NULL)
        return FAIL;
+    rettv->v_type = VAR_STRING;
+    rettv->vval.v_string = name;
 
-    i = 0;
-    for (p = *arg + 1; *p != NUL && *p != '"'; ++p)
+    for (p = *arg + 1; *p != NUL && *p != '"'; )
     {
        if (*p == '\\')
        {
            switch (*++p)
            {
-               case 'b': name[i++] = BS; break;
-               case 'e': name[i++] = ESC; break;
-               case 'f': name[i++] = FF; break;
-               case 'n': name[i++] = NL; break;
-               case 'r': name[i++] = CAR; break;
-               case 't': name[i++] = TAB; break;
+               case 'b': *name++ = BS; ++p; break;
+               case 'e': *name++ = ESC; ++p; break;
+               case 'f': *name++ = FF; ++p; break;
+               case 'n': *name++ = NL; ++p; break;
+               case 'r': *name++ = CAR; ++p; break;
+               case 't': *name++ = TAB; ++p; break;
 
                case 'X': /* hex: "\x1", "\x12" */
                case 'x':
@@ -3608,17 +3828,16 @@ get_string_tv(arg, rettv, evaluate)
                                  ++p;
                                  nr = (nr << 4) + hex2nr(*p);
                              }
+                             ++p;
 #ifdef FEAT_MBYTE
                              /* For "\u" store the number according to
                               * 'encoding'. */
                              if (c != 'X')
-                                 i += (*mb_char2bytes)(nr, name + i);
+                                 name += (*mb_char2bytes)(nr, name);
                              else
 #endif
-                                 name[i++] = nr;
+                                 *name++ = nr;
                          }
-                         else
-                             name[i++] = *p;
                          break;
 
                          /* octal: "\1", "\12", "\123" */
@@ -3629,59 +3848,41 @@ get_string_tv(arg, rettv, evaluate)
                case '4':
                case '5':
                case '6':
-               case '7': name[i] = *p - '0';
-                         if (p[1] >= '0' && p[1] <= '7')
+               case '7': *name = *p++ - '0';
+                         if (*p >= '0' && *p <= '7')
                          {
-                             ++p;
-                             name[i] = (name[i] << 3) + *p - '0';
-                             if (p[1] >= '0' && p[1] <= '7')
-                             {
-                                 ++p;
-                                 name[i] = (name[i] << 3) + *p - '0';
-                             }
+                             *name = (*name << 3) + *p++ - '0';
+                             if (*p >= '0' && *p <= '7')
+                                 *name = (*name << 3) + *p++ - '0';
                          }
-                         ++i;
+                         ++name;
                          break;
 
                            /* Special key, e.g.: "\<C-W>" */
-               case '<': extra = trans_special(&p, name + i, TRUE);
+               case '<': extra = trans_special(&p, name, TRUE);
                          if (extra != 0)
                          {
-                             i += extra;
-                             --p;
+                             name += extra;
                              break;
                          }
                          /* FALLTHROUGH */
 
-               default:  name[i++] = *p;
+               default:  MB_COPY_CHAR(p, name);
                          break;
            }
        }
        else
-           name[i++] = *p;
+           MB_COPY_CHAR(p, name);
 
-#ifdef FEAT_MBYTE
-       /* For a multi-byte character copy the bytes after the first one. */
-       if (has_mbyte)
-       {
-           int l = (*mb_ptr2len_check)(p);
-
-           while (--l > 0)
-               name[i++] = *++p;
-       }
-#endif
     }
-    name[i] = NUL;
+    *name = NUL;
     *arg = p + 1;
 
-    rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = name;
-
     return OK;
 }
 
 /*
- * Allocate a variable for an backtick-string constant.
+ * Allocate a variable for a 'str''ing' constant.
  * Return OK or FAIL.
  */
     static int
@@ -3689,74 +3890,32 @@ get_lit_string_tv(arg, rettv, evaluate)
     char_u     **arg;
     typeval    *rettv;
     int                evaluate;
-{
-    char_u     *p;
-    char_u     *name;
-
-    /*
-     * Find the end of the string.
-     */
-    p = vim_strchr(*arg + 1, '\'');
-    if (p == NULL)
-    {
-       EMSG2(_("E115: Missing quote: %s"), *arg);
-       return FAIL;
-    }
-
-    if (evaluate)
-    {
-       /*
-        * Copy the string into allocated memory.
-        */
-       name = vim_strnsave(*arg + 1, (int)(p - (*arg + 1)));
-       if (name == NULL)
-           return FAIL;
-
-       rettv->v_type = VAR_STRING;
-       rettv->vval.v_string = name;
-    }
-
-    *arg = p + 1;
-
-    return OK;
-}
-
-/*
- * Allocate a variable for a #string# constant.
- * Return OK or FAIL.
- */
-    static int
-get_sharp_string_tv(arg, rettv, evaluate)
-    char_u     **arg;
-    typeval    *rettv;
-    int                evaluate;
 {
     char_u     *p;
     char_u     *str;
-    int                i;
     int                reduce = 0;
 
     /*
-     * Find the end of the string, skipping ##.
+     * Find the end of the string, skipping ''.
      */
     for (p = *arg + 1; *p != NUL; mb_ptr_adv(p))
     {
-       if (*p == '#')
+       if (*p == '\'')
        {
-           if (p[1] != '#')
+           if (p[1] != '\'')
                break;
            ++reduce;
            ++p;
        }
     }
 
-    if (*p != '#')
+    if (*p != '\'')
     {
-       EMSG2(_("E999: Missing #: %s"), *arg);
+       EMSG2(_("E115: Missing quote: %s"), *arg);
        return FAIL;
     }
 
-    /* If only parsing, set *arg and return here */
+    /* If only parsing return after setting "*arg" */
     if (!evaluate)
     {
        *arg = p + 1;
@@ -3764,40 +3923,27 @@ get_sharp_string_tv(arg, rettv, evaluate)
     }
 
     /*
-     * Copy the string into allocated memory, handling ## to # reduction.
+     * Copy the string into allocated memory, handling '' to ' reduction.
      */
     str = alloc((unsigned)((p - *arg) - reduce));
     if (str == NULL)
        return FAIL;
+    rettv->v_type = VAR_STRING;
+    rettv->vval.v_string = str;
 
-    i = 0;
-    for (p = *arg + 1; *p != NUL; ++p)
+    for (p = *arg + 1; *p != NUL; )
     {
-       if (*p == '#')
+       if (*p == '\'')
        {
-           if (p[1] != '#')
+           if (p[1] != '\'')
                break;
            ++p;
        }
-       str[i++] = *p;
-
-#ifdef FEAT_MBYTE
-       /* For a multi-byte character copy the bytes after the first one. */
-       if (has_mbyte)
-       {
-           int l = (*mb_ptr2len_check)(p);
-
-           while (--l > 0)
-               str[i++] = *++p;
-       }
-#endif
+       MB_COPY_CHAR(p, str);
     }
-    str[i] = NUL;
+    *str = NUL;
     *arg = p + 1;
 
-    rettv->v_type = VAR_STRING;
-    rettv->vval.v_string = str;
-
     return OK;
 }
 
@@ -3841,7 +3987,7 @@ get_list_tv(arg, rettv, evaluate)
            break;
        if (**arg != ',')
        {
-           EMSG2(_("E696: Missing comma in list: %s"), *arg);
+           EMSG2(_("E696: Missing comma in List: %s"), *arg);
            goto failret;
        }
        *arg = skipwhite(*arg + 1);
@@ -3849,7 +3995,7 @@ get_list_tv(arg, rettv, evaluate)
 
     if (**arg != ']')
     {
-       EMSG2(_("E697: Missing end of list ']': %s"), *arg);
+       EMSG2(_("E697: Missing end of List ']': %s"), *arg);
 failret:
        if (evaluate)
            list_free(l);
@@ -4312,59 +4458,367 @@ list_getrem(l, item, item2)
 }
 
 /*
- * Return an allocated string with the string representation of a list.
- * May return NULL.
+ * Return an allocated string with the string representation of a list.
+ * May return NULL.
+ */
+    static char_u *
+list2string(tv)
+    typeval    *tv;
+{
+    garray_T   ga;
+
+    if (tv->vval.v_list == NULL)
+       return NULL;
+    ga_init2(&ga, (int)sizeof(char), 80);
+    ga_append(&ga, '[');
+    list_join(&ga, tv->vval.v_list, (char_u *)", ", FALSE);
+    ga_append(&ga, ']');
+    ga_append(&ga, NUL);
+    return (char_u *)ga.ga_data;
+}
+
+/*
+ * Join list "l" into a string in "*gap", using separator "sep".
+ * When "echo" is TRUE use String as echoed, otherwise as inside a List.
+ */
+    static void
+list_join(gap, l, sep, echo)
+    garray_T   *gap;
+    listvar    *l;
+    char_u     *sep;
+    int                echo;
+{
+    int                first = TRUE;
+    char_u     *tofree;
+    char_u     numbuf[NUMBUFLEN];
+    listitem   *item;
+    char_u     *s;
+
+    for (item = l->lv_first; item != NULL; item = item->li_next)
+    {
+       if (first)
+           first = FALSE;
+       else
+           ga_concat(gap, sep);
+
+       if (echo)
+           s = echo_string(&item->li_tv, &tofree, numbuf);
+       else
+           s = tv2string(&item->li_tv, &tofree, numbuf);
+       if (s != NULL)
+           ga_concat(gap, s);
+       vim_free(tofree);
+    }
+}
+
+/*
+ * Allocate an empty header for a dictionary.
+ */
+    static dictvar *
+dict_alloc()
+{
+    return (dictvar *)alloc_clear(sizeof(dictvar));
+}
+
+/*
+ * Unreference a Dictionary: decrement the reference count and free it when it
+ * becomes zero.
+ */
+    static void
+dict_unref(d)
+    dictvar *d;
+{
+    if (d != NULL && --d->dv_refcount <= 0)
+       dict_free(d);
+}
+
+/*
+ * Free a Dictionary, including all items it contains.
+ * Ignores the reference count.
+ */
+    static void
+dict_free(d)
+    dictvar *d;
+{
+    dictitem *item;
+    dictitem *next;
+
+    for (item = d->dv_first; item != NULL; item = next)
+    {
+       next = item->di_next;
+       dictitem_free(item);
+    }
+    vim_free(d);
+}
+
+/*
+ * Allocate a Dictionary item.
+ */
+    static dictitem *
+dictitem_alloc()
+{
+    return (dictitem *)alloc(sizeof(dictitem));
+}
+
+/*
+ * Free a dict item.  Also clears the value.
+ */
+    static void
+dictitem_free(item)
+    dictitem *item;
+{
+    vim_free(item->di_key);
+    clear_tv(&item->di_tv);
+    vim_free(item);
+}
+
+/*
+ * Add item "item" to Dictionary "d".
+ */
+    static void
+dict_add(d, item)
+    dictvar    *d;
+    dictitem   *item;
+{
+    item->di_next = d->dv_first;
+    d->dv_first = item;
+}
+
+#if 0  /* not currently used */
+static void dict_set_item __ARGS((dictvar *d, int type, char *key, void *val));
+
+/*
+ * Add an item to Dictionary "d" with type "type", key "key" and value "val".
+ * If it already exists it is overwritten.
+ * The key and value are copied to allocated memory.
+ */
+    static void
+dict_set_item(d, type, key, val)
+    dictvar    *d;
+    int                type;
+    char       *key;
+    void       *val;
+{
+    dictitem   *di;
+    char_u     *dkey;
+
+    di = dict_find(d, (char_u *)key, -1);
+    if (di == NULL)
+    {
+       dkey = vim_strsave((char_u *)key);
+       if (dkey != NULL)
+       {
+           di = dictitem_alloc();
+           if (di == NULL)
+               vim_free(dkey);
+           else
+               di->di_key = dkey;
+       }
+    }
+    else
+       clear_tv(&di->di_tv);
+
+    if (di != NULL)
+    {
+       di->di_tv.v_type = type;
+       switch (type)
+       {
+           case VAR_NUMBER:
+               di->di_tv.vval.v_number = (varnumber_T)val;
+               break;
+           case VAR_FUNC:
+           case VAR_STRING:
+               di->di_tv.vval.v_string = vim_strsave((char_u *)val);
+               break;
+           default:
+               EMSG2(_(e_intern2), "dict_set_item()");
+       }
+       dict_add(d, di);
+    }
+}
+#endif
+
+/*
+ * Find item "key[len]" in Dictionary "d".
+ * If "len" is negative use strlen(key).
+ * Returns NULL when not found.
  */
-    static char_u *
-list2string(tv)
-    typeval    *tv;
+    static dictitem *
+dict_find(d, key, len)
+    dictvar    *d;
+    char_u     *key;
+    int                len;
 {
-    garray_T   ga;
+    static dictitem *di;
 
-    if (tv->vval.v_list == NULL)
-       return NULL;
-    ga_init2(&ga, (int)sizeof(char), 80);
-    ga_append(&ga, '[');
-    list_join(&ga, tv->vval.v_list, (char_u *)", ", FALSE);
-    ga_append(&ga, ']');
-    ga_append(&ga, NUL);
-    return (char_u *)ga.ga_data;
+    for (di = d->dv_first; di != NULL; di = di->di_next)
+       if (len < 0
+               ? STRCMP(di->di_key, key) == 0
+               : STRNCMP(di->di_key, key, len) == 0 && di->di_key[len] == NUL)
+           return di;
+    return NULL;
 }
 
 /*
- * Join list "l" into a string in "*gap", using separator "sep".
- * When "echo" is TRUE use String as echoed, otherwise as inside a List.
+ * Return an allocated string with the string representation of a Dictionary.
+ * May return NULL.
  */
-    static void
-list_join(gap, l, sep, echo)
-    garray_T   *gap;
-    listvar    *l;
-    char_u     *sep;
-    int                echo;
+    static char_u *
+dict2string(tv)
+    typeval    *tv;
 {
+    garray_T   ga;
     int                first = TRUE;
     char_u     *tofree;
     char_u     numbuf[NUMBUFLEN];
-    listitem   *item;
+    dictitem   *item;
     char_u     *s;
 
-    for (item = l->lv_first; item != NULL; item = item->li_next)
+    if (tv->vval.v_dict == NULL)
+       return NULL;
+    ga_init2(&ga, (int)sizeof(char), 80);
+    ga_append(&ga, '{');
+
+    for (item = tv->vval.v_dict->dv_first; item != NULL; item = item->di_next)
     {
        if (first)
            first = FALSE;
        else
-           ga_concat(gap, sep);
+           ga_concat(&ga, (char_u *)", ");
 
-       if (echo)
-           s = echo_string(&item->li_tv, &tofree, numbuf);
-       else
-           s = tv2string(&item->li_tv, &tofree, numbuf);
+       tofree = string_quote(item->di_key, FALSE);
+       if (tofree != NULL)
+       {
+           ga_concat(&ga, tofree);
+           vim_free(tofree);
+       }
+       ga_concat(&ga, (char_u *)": ");
+       s = tv2string(&item->di_tv, &tofree, numbuf);
        if (s != NULL)
-           ga_concat(gap, s);
+           ga_concat(&ga, s);
        vim_free(tofree);
     }
+
+    ga_append(&ga, '}');
+    ga_append(&ga, NUL);
+    return (char_u *)ga.ga_data;
+}
+
+/*
+ * Allocate a variable for a Dictionary and fill it from "*arg".
+ * Return OK or FAIL.  Returns NOTDONE for {expr}.
+ */
+    static int
+get_dict_tv(arg, rettv, evaluate)
+    char_u     **arg;
+    typeval    *rettv;
+    int                evaluate;
+{
+    dictvar    *d = NULL;
+    typeval    tv;
+    char_u     *key;
+    dictitem   *item;
+    char_u     *start = skipwhite(*arg + 1);
+
+    /*
+     * First check if it's not a curly-braces thing: {expr}.
+     * Must do this without evaluating, otherwise a function may be called
+     * twice.  Unfortunately this means we need to call eval1() twice for the
+     * first item.
+     */
+    if (eval1(&start, &tv, FALSE) == FAIL)     /* recursive! */
+       return FAIL;
+    if (*start == '}')
+       return NOTDONE;
+
+    if (evaluate)
+    {
+       d = dict_alloc();
+       if (d == NULL)
+           return FAIL;
+    }
+
+    *arg = skipwhite(*arg + 1);
+    while (**arg != '}' && **arg != NUL)
+    {
+       if (eval1(arg, &tv, evaluate) == FAIL)  /* recursive! */
+           goto failret;
+       if (**arg != ':')
+       {
+           EMSG2(_("E999: Missing colon in Dictionary: %s"), *arg);
+           clear_tv(&tv);
+           goto failret;
+       }
+       key = get_tv_string(&tv);
+       if (*key == NUL)
+       {
+           EMSG(_(e_emptykey));
+           clear_tv(&tv);
+           goto failret;
+       }
+       key = vim_strsave(key);
+       clear_tv(&tv);
+       if (key == NULL)
+           goto failret;
+
+       *arg = skipwhite(*arg + 1);
+       if (eval1(arg, &tv, evaluate) == FAIL)  /* recursive! */
+       {
+           vim_free(key);
+           goto failret;
+       }
+       if (evaluate)
+       {
+           item = dict_find(d, key, -1);
+           if (item != NULL)
+           {
+               EMSG(_("E999: Duplicate key in Dictionary"));
+               vim_free(key);
+               clear_tv(&tv);
+               goto failret;
+           }
+           item = dictitem_alloc();
+           if (item == NULL)
+               vim_free(key);
+           else
+           {
+               item->di_key = key;
+               item->di_tv = tv;
+               dict_add(d, item);
+           }
+       }
+
+       if (**arg == '}')
+           break;
+       if (**arg != ',')
+       {
+           EMSG2(_("E999: Missing comma in Dictionary: %s"), *arg);
+           goto failret;
+       }
+       *arg = skipwhite(*arg + 1);
+    }
+
+    if (**arg != '}')
+    {
+       EMSG2(_("E999: Missing end of Dictionary '}': %s"), *arg);
+failret:
+       if (evaluate)
+           dict_free(d);
+       return FAIL;
+    }
+
+    *arg = skipwhite(*arg + 1);
+    if (evaluate)
+    {
+       rettv->v_type = VAR_DICT;
+       rettv->vval.v_dict = d;
+       ++d->dv_refcount;
+    }
+
+    return OK;
 }
 
+
 /*
  * Return a string with the string representation of a variable.
  * If the memory is allocated "tofree" is set to it, otherwise NULL.
@@ -4386,6 +4840,9 @@ echo_string(tv, tofree, numbuf)
        case VAR_LIST:
            *tofree = list2string(tv);
            return *tofree;
+       case VAR_DICT:
+           *tofree = dict2string(tv);
+           return *tofree;
        case VAR_STRING:
        case VAR_NUMBER:
            break;
@@ -4422,6 +4879,9 @@ tv2string(tv, tofree, numbuf)
        case VAR_LIST:
            *tofree = list2string(tv);
            return *tofree;
+       case VAR_DICT:
+           *tofree = dict2string(tv);
+           return *tofree;
        default:
            EMSG2(_(e_intern2), "tv2string()");
     }
@@ -4430,8 +4890,8 @@ tv2string(tv, tofree, numbuf)
 }
 
 /*
- * Return a string in # quotes, doubling # characters.
- * If "function" is TRUE make it function(#string#).
+ * Return a string in ' quotes, doubling ' characters.
+ * If "function" is TRUE make it function('string').
  */
     static char_u *
 string_quote(str, function)
@@ -4442,35 +4902,25 @@ string_quote(str, function)
     char_u     *p, *r, *s;
 
     for (p = str; *p != NUL; mb_ptr_adv(p))
-       if (*p == '#')
+       if (*p == '\'')
            ++len;
     s = r = alloc(len);
     if (r != NULL)
     {
        if (function)
        {
-           STRCPY(r, "function(#");
+           STRCPY(r, "function('");
            r += 10;
        }
        else
-           *r++ = '#';
-       for (p = str; *p != NUL; ++p)
+           *r++ = '\'';
+       for (p = str; *p != NUL; )
        {
-           if (*p == '#')
-               *r++ = '#';
-           *r++ = *p;
-#ifdef FEAT_MBYTE
-           /* For a multi-byte character copy the bytes after the first one. */
-           if (has_mbyte)
-           {
-               int     l = (*mb_ptr2len_check)(p);
-
-               while (--l > 0)
-                   *r++ = *++p;
-           }
-#endif
+           if (*p == '\'')
+               *r++ = '\'';
+           MB_COPY_CHAR(p, r);
        }
-       *r++ = '#';
+       *r++ = '\'';
        if (function)
            *r++ = ')';
        *r++ = NUL;
@@ -4635,7 +5085,9 @@ static struct fst
     {"inputsecret",    1, 2, f_inputsecret},
     {"insert",         2, 3, f_insert},
     {"isdirectory",    1, 1, f_isdirectory},
+    {"items",          1, 1, f_items},
     {"join",           1, 2, f_join},
+    {"keys",           1, 1, f_keys},
     {"last_buffer_nr", 0, 0, f_last_buffer_nr},/* obsolete */
     {"len",            1, 1, f_len},
     {"libcall",                3, 3, f_libcall},
@@ -4656,6 +5108,7 @@ static struct fst
     {"nextnonblank",   1, 1, f_nextnonblank},
     {"nr2char",                1, 1, f_nr2char},
     {"prevnonblank",   1, 1, f_prevnonblank},
+    {"range",          1, 3, f_range},
     {"remote_expr",    2, 3, f_remote_expr},
     {"remote_foreground", 1, 1, f_remote_foreground},
     {"remote_peek",    1, 2, f_remote_peek},
@@ -4698,6 +5151,7 @@ static struct fst
     {"toupper",                1, 1, f_toupper},
     {"tr",             3, 3, f_tr},
     {"type",           1, 1, f_type},
+    {"values",         1, 1, f_values},
     {"virtcol",                1, 1, f_virtcol},
     {"visualmode",     0, 1, f_visualmode},
     {"winbufnr",       1, 1, f_winbufnr},
@@ -4905,12 +5359,6 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline,
     int                evaluate;
 {
     int                ret = FAIL;
-    static char *errors[] =
-               {N_("E117: Unknown function: %s"),
-                N_("E118: Too many arguments for function: %s"),
-                N_("E119: Not enough arguments for function: %s"),
-                N_("E120: Using <SID> not in a script context: %s"),
-               };
 #define ERROR_UNKNOWN  0
 #define ERROR_TOOMANY  1
 #define ERROR_TOOFEW   2
@@ -5063,8 +5511,26 @@ call_func(name, len, rettv, argcount, argvars, firstline, lastline,
      * Report an error unless the argument evaluation or function call has been
      * cancelled due to an aborting error, an interrupt, or an exception.
      */
-    if (error < ERROR_NONE && !aborting())
-       EMSG2((char_u *)_(errors[error]), name);
+    if (!aborting())
+    {
+       switch (error)
+       {
+           case ERROR_UNKNOWN:
+                   EMSG2(_("E117: Unknown function: %s"), name);
+                   break;
+           case ERROR_TOOMANY:
+                   EMSG2(_(e_toomanyarg), name);
+                   break;
+           case ERROR_TOOFEW:
+                   EMSG2(_("E119: Not enough arguments for function: %s"),
+                                                                       name);
+                   break;
+           case ERROR_SCRIPT:
+                   EMSG2(_("E120: Using <SID> not in a script context: %s"),
+                                                                       name);
+                   break;
+       }
+    }
 
     name[len] = cc;
     if (fname != name && fname != fname_buf)
@@ -8208,6 +8674,97 @@ f_isdirectory(argvars, rettv)
     rettv->vval.v_number = mch_isdir(get_tv_string(&argvars[0]));
 }
 
+static void dict_list __ARGS((typeval *argvars, typeval *rettv, int what));
+
+/*
+ * Turn a dict into a list:
+ * "what" == 0: list of keys
+ * "what" == 1: list of values
+ * "what" == 2: list of items
+ */
+    static void
+dict_list(argvars, rettv, what)
+    typeval    *argvars;
+    typeval    *rettv;
+    int                what;
+{
+    listvar    *l;
+    listvar    *l2;
+    dictitem   *di;
+    listitem   *li;
+    listitem   *li2;
+
+    rettv->vval.v_number = 0;
+    if (argvars[0].v_type != VAR_DICT)
+    {
+       EMSG(_(e_dictreq));
+       return;
+    }
+    if (argvars[0].vval.v_dict == NULL)
+       return;
+
+    l = list_alloc();
+    if (l == NULL)
+       return;
+    rettv->v_type = VAR_LIST;
+    rettv->vval.v_list = l;
+    ++l->lv_refcount;
+
+    for (di = argvars[0].vval.v_dict->dv_first; di != NULL; di = di->di_next)
+    {
+       li = listitem_alloc();
+       if (li == NULL)
+           break;
+       list_append(l, li);
+
+       if (what == 0)
+       {
+           /* keys() */
+           li->li_tv.v_type = VAR_STRING;
+           li->li_tv.vval.v_string = vim_strsave(di->di_key);
+       }
+       else if (what == 1)
+       {
+           /* values() */
+           copy_tv(&di->di_tv, &li->li_tv);
+       }
+       else
+       {
+           /* items() */
+           l2 = list_alloc();
+           li->li_tv.v_type = VAR_LIST;
+           li->li_tv.vval.v_list = l2;
+           if (l2 == NULL)
+               break;
+           ++l2->lv_refcount;
+
+           li2 = listitem_alloc();
+           if (li2 == NULL)
+               break;
+           list_append(l2, li2);
+           li2->li_tv.v_type = VAR_STRING;
+           li2->li_tv.vval.v_string = vim_strsave(di->di_key);
+
+           li2 = listitem_alloc();
+           if (li2 == NULL)
+               break;
+           list_append(l2, li2);
+           copy_tv(&di->di_tv, &li2->li_tv);
+       }
+    }
+}
+
+/*
+ * "items(dict)" function
+ */
+    static void
+f_items(argvars, rettv)
+    typeval    *argvars;
+    typeval    *rettv;
+{
+    dict_list(argvars, rettv, 2);
+}
+
 /*
  * "join()" function
  */
@@ -8240,6 +8797,17 @@ f_join(argvars, rettv)
     rettv->vval.v_string = (char_u *)ga.ga_data;
 }
 
+/*
+ * "keys()" function
+ */
+    static void
+f_keys(argvars, rettv)
+    typeval    *argvars;
+    typeval    *rettv;
+{
+    dict_list(argvars, rettv, 0);
+}
+
 /*
  * "last_buffer_nr()" function.
  */
@@ -8859,6 +9427,61 @@ f_prevnonblank(argvars, rettv)
     rettv->vval.v_number = lnum;
 }
 
+/*
+ * "range()" function
+ */
+    static void
+f_range(argvars, rettv)
+    typeval    *argvars;
+    typeval    *rettv;
+{
+    long       start;
+    long       end;
+    long       stride = 1;
+    long       i;
+    listvar    *l;
+    listitem   *li;
+
+    start = get_tv_number(&argvars[0]);
+    if (argvars[1].v_type == VAR_UNKNOWN)
+    {
+       end = start - 1;
+       start = 0;
+    }
+    else
+    {
+       end = get_tv_number(&argvars[1]);
+       if (argvars[2].v_type != VAR_UNKNOWN)
+           stride = get_tv_number(&argvars[2]);
+    }
+
+    rettv->vval.v_number = 0;
+    if (stride == 0)
+       EMSG(_("E999: Stride is zero"));
+    else if (stride > 0 ? end < start : end > start)
+       EMSG(_("E999: Start past end"));
+    else
+    {
+       l = list_alloc();
+       if (l != NULL)
+       {
+           rettv->v_type = VAR_LIST;
+           rettv->vval.v_list = l;
+           ++l->lv_refcount;
+
+           for (i = start; stride > 0 ? i <= end : i >= end; i += stride)
+           {
+               li = listitem_alloc();
+               if (li == NULL)
+                   break;
+               li->li_tv.v_type = VAR_NUMBER;
+               li->li_tv.vval.v_number = i;
+               list_append(l, li);
+           }
+       }
+    }
+}
+
 #if defined(FEAT_CLIENTSERVER) && defined(FEAT_X11)
 static void make_connection __ARGS((void));
 static int check_connection __ARGS((void));
@@ -9086,7 +9709,7 @@ f_remote_send(argvars, rettv)
 }
 
 /*
- * "remove({list}, {idx} [, {end}])" function
+ * "remove()" function
  */
     static void
 f_remove(argvars, rettv)
@@ -9098,10 +9721,32 @@ f_remove(argvars, rettv)
     listitem   *li;
     long       idx;
     long       end;
+    char_u     *key;
+    dictvar    *d;
+    dictitem   *di, **pdi;
 
     rettv->vval.v_number = 0;
-    if (argvars[0].v_type != VAR_LIST)
-       EMSG2(_(e_listarg), "remove()");
+    if (argvars[0].v_type == VAR_DICT)
+    {
+       if (argvars[2].v_type != VAR_UNKNOWN)
+           EMSG2(_(e_toomanyarg), "remove()");
+       else if ((d = argvars[0].vval.v_dict) != NULL)
+       {
+           key = get_tv_string(&argvars[1]);
+           pdi = &d->dv_first;
+           for (di = d->dv_first; di != NULL; pdi = &di->di_next, di = *pdi)
+               if (STRCMP(di->di_key, key) == 0)
+               {
+                   *pdi = di->di_next;
+                   dictitem_free(di);
+                   break;
+               }
+           if (di == NULL)
+               EMSG2(_(e_dictkey), key);
+       }
+    }
+    else if (argvars[0].v_type != VAR_LIST)
+       EMSG2(_(e_listdictarg), "remove()");
     else if ((l = argvars[0].vval.v_list) != NULL)
     {
        idx = get_tv_number(&argvars[1]);
@@ -10929,6 +11574,17 @@ f_type(argvars, rettv)
     rettv->vval.v_number = n;
 }
 
+/*
+ * "values(dict)" function
+ */
+    static void
+f_values(argvars, rettv)
+    typeval    *argvars;
+    typeval    *rettv;
+{
+    dict_list(argvars, rettv, 1);
+}
+
 /*
  * "virtcol(string)" function
  */
@@ -11325,7 +11981,7 @@ find_name_end(arg, expr_start, expr_end, incl_br)
     char_u     *arg;
     char_u     **expr_start;
     char_u     **expr_end;
-    int                incl_br;        /* Include [] indexes */
+    int                incl_br;        /* Include [] indexes and .name */
 {
     int                mb_nest = 0;
     int                br_nest = 0;
@@ -11339,7 +11995,7 @@ find_name_end(arg, expr_start, expr_end, incl_br)
 
     for (p = arg; *p != NUL
                    && (eval_isnamec(*p)
-                       || (*p == '[' && incl_br)
+                       || (incl_br && (*p == '[' || *p == '.'))
                        || mb_nest != 0
                        || br_nest != 0); ++p)
     {
@@ -11720,6 +12376,9 @@ clear_tv(varp)
            case VAR_LIST:
                list_unref(varp->vval.v_list);
                break;
+           case VAR_DICT:
+               dict_unref(varp->vval.v_dict);
+               break;
            case VAR_NUMBER:
                varp->vval.v_number = 0;
                break;
@@ -11823,10 +12482,13 @@ get_tv_string_buf(varp, buf)
            sprintf((char *)buf, "%ld", (long)varp->vval.v_number);
            return buf;
        case VAR_FUNC:
-           EMSG(_("E99: using Funcref as a String"));
+           EMSG(_("E999: using Funcref as a String"));
            break;
        case VAR_LIST:
-           EMSG(_("E99: using List as a String"));
+           EMSG(_("E999: using List as a String"));
+           break;
+       case VAR_DICT:
+           EMSG(_("E999: using Dictionary as a String"));
            break;
        case VAR_STRING:
            if (varp->vval.v_string != NULL)
@@ -12088,6 +12750,12 @@ list_one_var_a(prefix, name, type, string)
        if (*string == '[')
            ++string;
     }
+    else if (type == VAR_DICT)
+    {
+       msg_putchar('{');
+       if (*string == '{')
+           ++string;
+    }
     else
        msg_putchar(' ');
 
@@ -12239,6 +12907,15 @@ copy_tv(from, to)
                ++to->vval.v_list->lv_refcount;
            }
            break;
+       case VAR_DICT:
+           if (from->vval.v_dict == NULL)
+               to->vval.v_dict = NULL;
+           else
+           {
+               to->vval.v_dict = from->vval.v_dict;
+               ++to->vval.v_dict->dv_refcount;
+           }
+           break;
        default:
            EMSG2(_(e_intern2), "copy_tv()");
            break;
index 7bcdaf2e914f725ed33a35b34dd2e31694f4ad7d..14f7580134ddf50c1da26ce27fc882f6b71b4710 100644 (file)
@@ -905,6 +905,9 @@ do_filter(line1, line2, eap, cmd, do_in, do_out)
            curbuf->b_op_end.lnum -= linecount;         /* adjust '] */
            write_lnum_adjust(-linecount);              /* adjust last line
                                                           for next write */
+#ifdef FEAT_FOLDING
+           foldUpdate(curwin, curbuf->b_op_start.lnum, curbuf->b_op_end.lnum);
+#endif
        }
        else
        {
@@ -914,6 +917,7 @@ do_filter(line1, line2, eap, cmd, do_in, do_out)
            curwin->w_cursor.lnum = curbuf->b_op_end.lnum;
            linecount = curbuf->b_op_end.lnum - curbuf->b_op_start.lnum + 1;
        }
+
        beginline(BL_WHITE | BL_FIX);       /* cursor on first non-blank */
        --no_wait_return;
 
index 0ff685aeab0693e90b3b0ae6e852ccdcbab1cae1..518a4b1f803a634c9b9e4c23c66d2bab610547fe 100644 (file)
@@ -4240,7 +4240,7 @@ gui_mch_font_dialog(char_u *oldval)
                GTK_FONT_SELECTION_DIALOG(dialog), (const char *)oldname);
 
        if (oldname != oldval)
-           vim_free(oldval);
+           vim_free(oldname);
     }
 
     response = gtk_dialog_run(GTK_DIALOG(dialog));