summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2005-01-14 21:53:12 +0000
committerBram Moolenaar <Bram@vim.org>2005-01-14 21:53:12 +0000
commit8c711458a6affdad1cbae635694ff0a014850a27 (patch)
tree1645457d30b263a5eb31ea79bd62230a1cb4caa0
parentd8b0273231d69a2a6c24844c1acc938330acf035 (diff)
downloadvim-8c711458a6affdad1cbae635694ff0a014850a27.zip
updated for version 7.0038
-rw-r--r--runtime/doc/todo.txt42
-rw-r--r--runtime/doc/version7.txt21
-rw-r--r--runtime/keymap/polish-slash_cp1250.vim6
-rwxr-xr-xruntime/tools/tcltags159
-rw-r--r--src/eval.c1369
-rw-r--r--src/ex_cmds.c4
-rw-r--r--src/gui_gtk_x11.c2
7 files changed, 1085 insertions, 518 deletions
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index 0add158ce..13f4ad1c4 100644
--- 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 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 ):
diff --git a/runtime/doc/version7.txt b/runtime/doc/version7.txt
index ccf9b037f..9fa874746 100644
--- 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 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:
diff --git a/runtime/keymap/polish-slash_cp1250.vim b/runtime/keymap/polish-slash_cp1250.vim
index 3aa3202bf..df1f67a21 100644
--- 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 <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
index 952415279..000000000
--- 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 <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
diff --git a/src/eval.c b/src/eval.c
index 54215198d..100bc80a9 100644
--- 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 **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;
@@ -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, 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 == '.')
{
- range = TRUE;
+ /*
+ * 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! */
+ 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;
-
-#ifdef FEAT_MBYTE
- /* For a multi-byte character copy the bytes after the first one. */
- if (has_mbyte)
- {
- int l = (*mb_ptr2len_check)(p);
+ MB_COPY_CHAR(p, name);
- 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
@@ -3691,72 +3892,30 @@ get_lit_string_tv(arg, rettv, evaluate)
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);
@@ -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)
+ *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
*/
@@ -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, 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;
diff --git a/src/ex_cmds.c b/src/ex_cmds.c
index 7bcdaf2e9..14f758013 100644
--- a/src/ex_cmds.c
+++ b/src/ex_cmds.c
@@ -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;
diff --git a/src/gui_gtk_x11.c b/src/gui_gtk_x11.c
index 0ff685aea..518a4b1f8 100644
--- 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));