# HG changeset patch # User vimboss # Date 1105739592 0 # Node ID 1f3902f3eb5c20e7872c42cdee9c91e2b25d3f47 # Parent 04f2e519ab18c9de57bbd67e4e9e671499eedaa4 updated for version 7.0038 diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt --- 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 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 Vi *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 ): diff --git a/runtime/doc/version7.txt b/runtime/doc/version7.txt --- a/runtime/doc/version7.txt +++ b/runtime/doc/version7.txt @@ -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 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 key depen on whether has been mapped or not. This allows mapping 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 g 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 man 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'" 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: diff --git a/runtime/keymap/polish-slash_cp1250.vim b/runtime/keymap/polish-slash_cp1250.vim --- a/runtime/keymap/polish-slash_cp1250.vim +++ b/runtime/keymap/polish-slash_cp1250.vim @@ -1,6 +1,8 @@ " Polish letters keymap for cp1250 " Maintainer: HS6_06 -" 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 --- a/runtime/tools/tcltags +++ /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 - -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 diff --git a/src/eval.c b/src/eval.c --- a/src/eval.c +++ b/src/eval.c @@ -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 **a 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 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 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 *argv 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 *a 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 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; @@ -1556,8 +1603,24 @@ ex_let_one(arg, tv, copy, endchars) } /* + * 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, endch 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, endch return NULL; tv = &v->tv; - for (p = ip; *p == '['; p = skipwhite(p + 1)) - { - if (tv->v_type != VAR_LIST || tv->vval.v_list == NULL) - { - EMSG(_("E689: Can only index a List")); + for (p = ip; *p == '[' || (*p == '.' && tv->v_type == VAR_DICT); ) + { + 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 or Dictionary")); p = NULL; break; } @@ -1608,120 +1676,191 @@ set_var_idx(name, ip, rettv, copy, endch break; } - /* Get the index [expr] or the first index [expr: ]. */ - p = skipwhite(p + 1); - if (*p == ':') - empty1 = TRUE; - else - { - empty1 = FALSE; - if (eval1(&p, &var1, TRUE) == FAIL) /* recursive! */ - { + len = -1; + if (*p == '.') + { + key = p + 1; + for (len = 0; ASCII_ISALNUM(key[len]) || key[len] == '_'; ++len) + ; + if (len == 0) + { + EMSG(_(e_emptykey)); p = NULL; break; } - } - - /* Optionally get the second index [ :expr]. */ - if (*p == ':') - { - 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 = key + len; + } + else + { + /* 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; } - } - range = TRUE; - } - else - range = FALSE; - - if (*p != ']') - { - EMSG(_(e_missbrac)); - if (!empty1) + 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; + } + 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; + } + + if (tv->v_type == VAR_DICT) + { + 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); + tv = &di->di_tv; + } + else + { + /* + * 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); - p = NULL; - break; - } - - /* - * 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; - 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); - if (n2 < 0) - { - ni = list_find(l, n2); - if (ni == NULL) + 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); + } + + /* 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 = &item->li_tv; + } + + tv = &li->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, endch */ 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, endch 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 - { - *tv = *rettv; - init_tv(rettv); - } - } - } + if (p != NULL) + { + 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, cmdi } 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,24 +3288,24 @@ 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#. - */ - case '#': ret = get_sharp_string_tv(arg, rettv, evaluate); - break; - - /* * List: [expr, expr] */ case '[': ret = get_list_tv(arg, rettv, evaluate); break; /* + * Dictionary: {key: val, key: val} + */ + case '{': ret = get_dict_tv(arg, rettv, evaluate); + break; + + /* * Option value: &name or map() item "&". */ case '&': if (!ASCII_ISALPHA(*(*arg + 1))) @@ -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; - - if (len == 0) + default: ret = NOTDONE; + break; + } + + 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 (ret == OK) + clear_tv(rettv); 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 (ret == OK) - clear_tv(rettv); - ret = FAIL; - } - } - else if (evaluate) - ret = get_var_tv(s, len, rettv); - } - - if (alias != NULL) - vim_free(alias); - - break; - } + } + } + else if (evaluate) + ret = get_var_tv(s, len, rettv); + } + + 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 == ':') - { - range = TRUE; + 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 + { + /* + * 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! */ - { - clear_tv(&var1); + if (**arg == ':') + empty1 = TRUE; + else if (eval1(arg, &var1, evaluate) == FAIL) /* recursive! */ return FAIL; - } - } - else - range = FALSE; - - /* Check for the ']'. */ - if (**arg != ']') - { - EMSG(_(e_missbrac)); - clear_tv(&var1); - if (range) - clear_tv(&var2); - 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; + } + *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; - } - } - - *arg = skipwhite(*arg + 1); /* skip the ']' */ + + 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; + } + } + 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; - - i = 0; - for (p = *arg + 1; *p != NUL && *p != '"'; ++p) + rettv->v_type = VAR_STRING; + rettv->vval.v_string = name; + + 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.: "\" */ - 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; - -#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; + MB_COPY_CHAR(p, name); + + } + *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 @@ -3691,72 +3892,30 @@ get_lit_string_tv(arg, rettv, evaluate) int evaluate; { char_u *p; - char_u *name; + char_u *str; + int reduce = 0; /* - * Find the end of the string. + * Find the end of the string, skipping ''. */ - p = vim_strchr(*arg + 1, '\''); - if (p == NULL) + for (p = *arg + 1; *p != NUL; mb_ptr_adv(p)) + { + if (*p == '\'') + { + if (p[1] != '\'') + break; + ++reduce; + ++p; + } + } + + if (*p != '\'') { 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 ##. - */ - for (p = *arg + 1; *p != NUL; mb_ptr_adv(p)) - { - if (*p == '#') - { - if (p[1] != '#') - break; - ++reduce; - ++p; - } - } - - if (*p != '#') - { - EMSG2(_("E999: Missing #: %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; - - i = 0; - for (p = *arg + 1; *p != NUL; ++p) - { - if (*p == '#') - { - if (p[1] != '#') + rettv->v_type = VAR_STRING; + rettv->vval.v_string = str; + + for (p = *arg + 1; *p != NUL; ) + { + if (*p == '\'') + { + 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 - } - str[i] = NUL; + MB_COPY_CHAR(p, str); + } + *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); @@ -4366,6 +4512,314 @@ list_join(gap, l, sep, echo) } /* + * 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 dictitem * +dict_find(d, key, len) + dictvar *d; + char_u *key; + int len; +{ + static dictitem *di; + + 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; +} + +/* + * Return an allocated string with the string representation of a Dictionary. + * May return NULL. + */ + static char_u * +dict2string(tv) + typeval *tv; +{ + garray_T ga; + int first = TRUE; + char_u *tofree; + char_u numbuf[NUMBUFLEN]; + dictitem *item; + char_u *s; + + 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(&ga, (char_u *)", "); + + 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(&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. * "numbuf" is used for a number. @@ -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) - { - 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 - } - *r++ = '#'; + *r++ = '\''; + for (p = str; *p != NUL; ) + { + if (*p == '\'') + *r++ = '\''; + MB_COPY_CHAR(p, 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, ar 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 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, ar * 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 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 */ @@ -8241,6 +8798,17 @@ f_join(argvars, rettv) } /* + * "keys()" function + */ + static void +f_keys(argvars, rettv) + typeval *argvars; + typeval *rettv; +{ + dict_list(argvars, rettv, 0); +} + +/* * "last_buffer_nr()" function. */ /*ARGSUSED*/ @@ -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]); @@ -10930,6 +11575,17 @@ f_type(argvars, rettv) } /* + * "values(dict)" function + */ + static void +f_values(argvars, rettv) + typeval *argvars; + typeval *rettv; +{ + dict_list(argvars, rettv, 1); +} + +/* * "virtcol(string)" function */ static void @@ -11325,7 +11981,7 @@ find_name_end(arg, expr_start, expr_end, 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, 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, strin 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; diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -905,6 +905,9 @@ do_filter(line1, line2, eap, cmd, do_in, 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, 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; diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c --- a/src/gui_gtk_x11.c +++ b/src/gui_gtk_x11.c @@ -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));