Elvis can evaluate expressions involving numbers, strings, and boolean values, using a C-like syntax. These are used in several EX commands, one VI command, and a couple of other situations.
There are two syntaxes. The normal syntax is extremely similar to C, and is used in circumstances where you probably would never use a literal value, such as for the :if command. The simpler syntax makes literal values easier to enter, while still making the full power of the expression evaluator available if you need it.
The normal syntax is intended to resemble the syntax of the C programming language very closely. You can't define your own functions or use flow-control constructs though; you can only use expressions. In traditional C documentation, these would be called "rvalues." Basically that means you can use literal values, option names, operators, parentheses, and some built-in functions.
\b
, \E
(uppercase, representing the Esc character), \f
,
\n
, \r
, and \t
.
Also, you can use \\
for a literal backslash character, or
\"
for a literal double-quote character within a string.
/
regexp/
to pass the regular expression
text.
Note that the regular expression is passed as a string;
it is equivalent to "/
regexp"
.
The following examples produce exactly identical results.
:calc "8" 8 :calc 8 8 :calc 010 8 :calc 0x8 8 :calc '\b' 8
You can also use option names in Elvis the same way you would use variable names in C.
:calc list false :calc scroll 12 :calc display normal
There is also a pseudo-option named "_" (the underscore character). Its value is the content of the current line.
:calc _ Its value is the content of the current line.
Additionally, a dollar sign followed by the name of an environment variable is replaced by the value of that environment variable. If there is no such environment variable, then Elvis will act as though it exists and has a null value... unless there is an option with that name, in which case the option's value is used. (In other words, "$" is optional before option names, but mandatory before environment variable names.)
:calc $TERM xterm :calc $NOSUCHVARIABLE :calc $autoindent false :calc $_ Its value is the content of the current line.
In some circumstances, you can use a dollar sign followed by a digit to access special arguments. This is used in error messages and also in the values of a few options, as described in section 13.6. These special arguments can only be supplied by Elvis' internal code, and it only supplies them in a few special circumstances so you can't use them in :calculate, for example.
Elvis' style of concatenation is smart about whitespace. If neither argument is an empty string, and the left argument doesn't end with whitespace, and the right argument doesn't start with whitespace, then Elvis will insert a single space character between them. Otherwise it concatenates them without adding or removing any characters.
If you don't want Elvis to add whitespace, use the ; operator instead.
If the first argument to * is a string instead of a number, then the second value is used as the number of times to repeat the first argument.
:calc "[]" * 6 [][][][][][]
Also, the / operator can be used to combine a directory name and a file name, to form an absolute pathname. Here are some examples showing how this works in DOS:
:set dir home directory=C:\temp home=C:\ :calc dir/"tempfile" C:\temp\tempfile :calc home/"elvis.rc" C:\elvis.rc
:calc "[" ; "port" << 6 ; "]" [port ] :calc "[" ; "starboard" >> 6 ; "]" [rboard]
:
and third argument are optional; if omitted,
then Elvis mentally sticks :""
on the end of the expression.
:
is used without a ?
, its behavior is
totally different.
It concatenates its arguments, with an OS-specific path delimiter between them.
On Unix systems, the delimiter is ":" but on most other systems it is ";".
This is useful when constructing values for path options such as
includepath or
tags.
:let includepath = includepath:home/"include"
quote()
and unquote()
functions.
(no operator)
.
:calc 1+2 3*4 1+2 32 32 32 3 :calc (1+2)(3*4) 3 12 :calc 1+2;3*4 312
All of these functions accept a single string as an argument. Omitting the argument is equivalent to passing an empty string (""). Some functions are described as taking two arguments; actually, they take one argument and divide it at the first comma character.
The built-in functions are:
basename("foo/bar.c")
' would return "bar".
(If you want "bar.c", use the dirfile() function.)
char(65)
' returns "A".
Item | Returned data |
---|---|
"line" | Current line number. |
"column" | Current column number. |
"word" | The word at the cursor location. If the cursor isn't on a word, then this returns an empty string. (Note: To get the contents of the current line, use the line() function.) |
"wspell" | The spell-checker word at the cursor location. This differs from the normal "word" in that the spell checker allows apostrophes to appear in words if they have a letter on both sides. |
/regexp/ | Text matching the regexp and containing the cursor.
For example, current(/\S*/) returns the current
whitespace-delimited word.
Note that/ regexp/ is equivalent to
"/ regexp" .
|
"tag" | If the show option contains the keyword
"tag", then
this returns the name of the tag that is defined at the cursor location, or
the nearest one before it.
If the show option doesn't contain "tag", or the
cursor is located above the first tag defined in this file,
then current("tag") will return an empty string.
|
"mode" | Current key parsing mode. This returns the same string that the showmode option displays, except that this function converts it to all lowercase, and strips out whitespace. The usual return values are "command", "input", and "replace". If the window isn't editing its main buffer (i.e., if you're entering an ex command line, regular expression, or filter command) then this function will return an empty string. |
"next" | Next file. This returns the name of the file that the :next command would load, or an empty string if you're at the end of the args list. |
"previous" | Previous file. This returns the name of the file that the :previous command would load, or an empty string if you're at the start of the args list. |
"face" | The face name (as used in the :color command) at the cursor location. This value is only updated when the window is updated. |
"region" | The current region's face name. |
"rcomment" | The current region's comment. |
"selection" | Visible selection type. This returns one of "character", "rectangle", or "line" to indicate the type of visible selection which is currently marked in the window, or an empty string if no visible selection is marked. |
"tagstack" | If the window's tag stack is empty, this returns "". Otherwise it returns the name of the buffer to which :pop would move the cursor. |
"background" | If the normal background color is known, then this returns "light" or "dark"
to describe it.
If the background is unknown (which can only happen with the
termcap user interface), it returns "".
This function is different from the background
option -- |
Output | What it means |
---|---|
"invalid" | The argument is malformed; it could not possibly be a valid file name. |
"badpath" | The argument is a pathname, and one or more of the directories named in that pathname either doesn't exist or is something other than a directory. |
"notfile" | The argument is the name of something other than a file or directory; for example, it may be a named socket or block device. |
"directory" | The argument is the name of a directory (folder). |
"new" | There is no file, directory, or anything else with the given name. |
"unreadable" | The file exists but you don't have permission to read it. |
"readonly" | The file exists and you can read it, but you don't have permission to write to it. |
"readwrite" | The file exists and you can read or write it. |
Otherwise, when passed only a face name, it returns the attributes of that face as set via the :color command. When invoked with "set" as a second argument, it returns true or false to indicate whether the colors were explicitly set, or are merely the defaults. When invoked with "fg", "bg", or "like" as a second argument, it parses the face's description and returns that particular attribute.
Note that the foreground may be a single color, or a list of colors delimited by the word "or". Also, in "x11" at least, the background may be a single color, an XPM image file name, or both for a tinted image.
feature()
returns true
for all
supported display modes, network protocols, and certain other words;
it returns false
for anything else.
Specifically, Elvis will return true for
any of the following which are supported by Elvis' configuration:
Name | The tested feature |
---|---|
"alias" | :alias command |
"array" | subscripts in expressions |
"autocmd" | :autocmd and related commands |
"backtick" | use of `program` in ex command lines |
"browse" | :browse command |
"complete" | name completion via the <Tab> key |
"equaltilde" | The :let command supports "=~ excmds"
|
"fold" | :fold and :unfold commands |
"ftp" | ftp network protocol |
"g" | Commands with a g prefix |
"hex" | hex display mode |
"hlobject" | hlobject and hllayers options |
"hlsearch" | hlsearch option |
"html" | html display mode |
"http" | http network protocol |
"image" | background images and themes (X11 and Windows) |
"incsearch" | incsearch option |
"listchars" | The listchars option. |
"lpr" | :lpr command and printing options |
"make" | :make and related commands |
"man" | man display mode |
"mapdb" | map debugger, via maptrace and maplog options. |
"mkexrc" | :mkexrc command |
"normal" | Vim's :normal command |
"proto" | add new protocols via aliases |
"ram" | -f ram command-line option (mostly for MS-DOS) |
"region" | :region and related commands |
"showtag" | The "tag" keyword in the show option |
"smartargs" | smartargs option |
"spell" | spell checker |
"split" | :split and related commands |
"stdin" | Use Elvis as filter by giving "-" as file name |
"syntax" | syntax display mode |
"tex" | tex display mode |
"textobj" | text objects for use with vi operators |
"v" | The v, V, and ^V commands |
"xft" | antialiased fonts (X11 only) |
As new features are added to future versions of Elvis,
I expect to add them to feature()
's list.
Note that the buffer is not necessarily associated with any file. In particular, this function does not load a file's contents into the buffer.
:set t="/* regexp */" :set r="*^$/.[" :eval /(quote(r, t))/... will search for the next instance of the literal string "/* regexp */". The '/' and '*' characters won't be treated as metacharacters in the regular expression, because the quote() function inserts backslashes before them.
Quite often, the character list will simply be the magicchar option. This works perfectly for quoting plain text to be used in a regular expression, with the magic option set.
:calc rand(6) 2 :calc rand(6) 5 :calc rand("north", "south", "east", "west") north :calc rand("north,south,east,west") east
(If you want to choose from a list with some other type of delimiter, you can use an array subscript of "?".)
The following example will fetch the computer's name, by running the Unix "uname" program:
:let n = shell("uname -n")
spell()
or spelltag()
returns "".
Otherwise, it returns a space-delimited list of possible corrected spellings.
If no corrected spellings are found, then it returns "?".
The spelltag()
function looks up words via the tags dictionary,
while spell()
uses both the tags and natural-language dictionaries.
These only find words that have been loaded into RAM, however.
This includes all tags so the spelltag()
function should work well,
but Elvis only loads natural-language words the first time it displays
them on the screen.
This will often prevent spell()
from finding the corrected word.
You can use the :wordfile command
or spellautoload option to load
all of the natural-language words into RAM, so spell()
will work
better.
True
then subscripts should work.
Subscripts offer a way to extract parts of a string. By default it divides the string into words. By placing index numbers in square brackets at the end of a string expression, you can extract the indexed words.
:let a="this is a test" :calc a[1] this :calc a[2 3 4] is a test
Note that subscripts start 1, not 0 as in C. Positive subscripts count from the left edge of the string, and negative subscripts count from the right edge. Zero returns the number of items in the string.
:calc a[-1] test :calc a[0] 4
Using "?" in the subscript chooses an element randomly.
:calc a["?"] is
You aren't limited to a single index number. You can use a space-delimited list of elements to extract. The ".." operator is handy for this. An empty list extracts an empty string.
:calc a[1..3] this is a :calc a[2..] is a test :calc a[] :calc a[4 3 2 1] test a is this :calc a["?????"] this a test a is :calc a["?."] a test
Although the subscripting works on space-delimited words by default, it can also use any single-character delimiter. Simply pass the delimiter inside the square brackets, before the index values.
:calc "/var/tmp/myfile"["/", 2] var :calc "/var/tmp/myfile"["/", -1] myfile
Notice that the text before the first instance of the delimiter (a zero-length string in the above examples) counts as item #1, so "var" is actually the second item in that string.
You can index by individual characters. This is denoted by giving an empty string as the delimiter.
:calc a["",1..6] This i :calc a["",7..] s a test
The :let command also allows you to use subscripts to change the value of part of an option's value.
:let a[2] = "has been" :calc a this has been a test :calc a[0] 5
There are some limitations on what subscripts you can use for assignments. You can't change the length by assigning to subscript [0] for example. The replaced text must be contiguous, not scattered around the string. You also can't change an element which doesn't exist yet.
:let a[0] = 3 invalid subscript for assignment :let a[2 4] = "foo" invalid subscript for assignment :let a[6] = "dude!" invalid subscript for assignment
Remember, this may look like you're using arrays but it is really just string processing. If you want to add a new item to a string, use a concatenation operator.
:let a = a "dude!" :calc a this has been a test dude! :calc a[0] 6 :let a["", 3 4] = "at" :calc a that has been a test dude!
The subscript string can be a field name instead of a list of subscript numbers. Field names can be any alphanumeric strings that start with a letter. When you do this, Elvis will look for a comma-delimited item which begins with the field name followed by a ':' character. The remainder of the item is then used as the value.
:let n = "first:1,last:2" :calc n["first"] 1 :let n["first"] = "Steve" :calc n first:Steve,last:2
For field names that are constant strings, you can also use .
name
notation.
:calc n.first Steve :let n.last = "Kirkendall" :calc n first:Steve,last:Kirkendall
In a command of the form
":let
option.name=
value",
if the option doesn't already contain a field with the given
name, then Elvis will append it.
It is also worth noting that the listchars option uses this format. Consequently, you can use named subscripts to access its fields.
:let listchars.esc=""
There are some tricks you can use to simplify the way you specify a delimiter. First, since any empty expression returns an empty string, you can leave off the "" (but retain the comma) when indexing by characters.
:calc a[,1..4] that
Next, many punctuation characters are used as arithmetic operators, and many arithmetic operators will concatenate strings with a copy of the operator between them, if either of their arguments isn't a number. This means that you can sometimes avoid quoting operators.
:calc "*-courier-medium-r-*-18-*"[-,-2] 18 :calc "Portland|Oregon|97201"[|,1] Portland
Some other operators do interesting and useful things.
For example, the /
operator concatenates strings using the
operating system's directory delimiter, and the :
operator
concatenates them with the path delimiter.
This means that unquoted /
and :
can be used
to pick apart file names and directory paths in an OS-independent manner.
:calc filename C:\tmp\filename.txt :calc filename[/,-1] filename.txt :calc $PATH C:\windows;C:\windows\command;C:\msdev\bin :calc $PATH[:,1] C:\windows
Here is an incomplete table summarizing some of these idioms:
.--------.-------------------------------------------. | Idiom | What it returns | |--------|-------------------------------------------| | a[i] | The i'th word | | a[,i] | The i'th character | | a[,,i] | The i'th comma-delimited item | | a[/,i] | The i'th part of a filename | | a[:,i] | The i'th element in a directory path list | ^--------^-------------------------------------------^
True
,
then Elvis also supports sets.
Sets are represented as comma-delimited lists of names or
name:value pairs.
This format is very similar to the way Elvis uses field names as subscripts, and that's why sets are supported only when arrays are supported. This similarity means you can test whether a given element is in a set by using an expression of the form set.fieldname, like this:
:let a="red:255,green:150,blue:0,allocated,private" :calc a.red 255 :calc a.alpha :calc a.allocated True
Note that elements that are present but have no value will return the value of the true option, but missing elements will simply return "" (not false, although "" is treated as a false value in Boolean contexts such as an :if command). Elements with values will return their value.
The &, |, and ^ operators will perform set intersection, union, and difference, respectively, when passed non-numeric arguments. Specifically...
:calc 2345 & 0xff 41 :calc "a,b,c" & "a,c,e" a,c :calc "foo:1,bar:true" & "foo" foo :calc "foo" & "foo:1,bar:true" foo:1
:calc "a,b,c" ^ "b" a,c :calc "a:1,b:2,c:3" ^ "b:don't care" a:1,c:3 :set listchars? listchars=trail:_,tab:>- :let listchars ^= "trail" :set listchars? listchars=tab:>-
:calc "a,b,c" | "a,c,e" a,b,c,e :calc "a:1,b:2,c:3" | "b:two,d:four" a:1,b:two,c:3,d:four :calc listchars? listchars=tab:>- :let listchars |= "trail:_" :calc listchars? listchars=tab:>-,trail:_
If both argument sets are sorted, then the resulting set from any of the operators will also be sorted. The :theme alias uses the following code to build a sorted list of available themes. Since the set of themes starts out empty (which is inherently sorted), and we only add single elements at a time (which are also inherently sorted), this means the list of themes will be sorted.
local a="" i d f for i (1..elvispath[:,0]) do { let d=elvispath[:,i] for f in (d/"themes"/"*.ex") do { if basename(f) != "*" then let a |= basename(f) } } if a == "" then error no themes else calc a
The real magic happens in the ":let a |= basename(f)
" command,
which adds an item to the set, while avoiding duplicates and keeping the set
sorted.
The :eval command uses the simpler syntax, and the :echo command displays its arguments. These commands can be used together to experiment with the simpler syntax, the same way we used :calculate to experiment with the normal syntax.
:eval echo TERM=$TERM TERM=xterm :eval echo home=(home) home=/home/steve :eval echo 2+2=(2+2) 2+2=4
The :if command evaluates its argument using the normal syntax. If the resulting value is any Boolean true value then a flag is set; otherwise the flag is reset. After that, you can use :then and :else commands to conditionally execute some commands, depending on the state of that flag.
The :while command defines the expression that is to be tested by a later :do command. The :for command, on the other hand, can evaluate its argument immediately and use the result as a list of values to be used by :do.
The :eval command evaluates its arguments using the simpler syntax. The resulting string value is then interpreted as an EX command line. This gives you a way to use the expression evaluator with commands which otherwise wouldn't evaluate expressions.
The :let command allows you to change the
values of options.
Its syntax is ":let option=expression
",
where expression is any expression using the normal syntax.
You can use this to change the value of any unlocked option,
similarly to :set.
:set i=14 :calc i 14 :let i=i+1 :set i? i=15 :eval set i=(i*2) :calc i 30 :let elvispath="." :let list="false" :let sidescroll=0x10
Note that the = operator only works this way when used with the v command for marking characters. If you visibly mark lines, or use the traditional =movement syntax, then Elvis will send the selected lines though the external filter program named in the equalprg option.
The # command doesn't use expressions, but it does perform some simple math.
Similarly, the keywordprg option uses the simpler syntax with $1 being replaced by the name of a tag to be looked up, and $2 by the current file name.
The tagprg option, if set, replaces $1 with the arguments of the :tag command -- generally the name of the tag to be looked up, although the user is free to type in anything.
The full power of the expression evaluator is available; you can use it to do more than just expand environment variable names. For example, you could store the name of a file in one of the user options, and then later use that option name in parentheses wherever a filename was expected.
:set f=myfile.txt :w (f) wrote myfile.txt, ...
If you use this trick, remember that it only works when Elvis is expecting a file name. It won't work when invoking external programs, because Elvis doesn't know which program arguments are supposed to be file names. Elvis always passes program arguments literally.
Recall that when a backslash character is followed by an alphanumeric character, both the backslash and the alphanumeric character become part of the resulting value. This was done mostly for the benefit of file names. If the backslash was always dropped then MS-DOS users would have a heck of a time entering pathnames of files! By making the backslash a little smarter, we avoid that problem.
:eval echo c:\tmp \(notice the backslashes\) c:\tmp (notice the backslashes)
To simplify the task of writing portable ex scripts, the behavior of the / operator has been extended. When one or both of its arguments are strings, it concatenates them as a directory name and a file name, yielding a full pathname.
:(=x)%p
" is equivalent to ":(foo)%p
".
Both commands would print the contents of the "foo" buffer.
This feature was added because using :eval
to evaluate buffer
names was cumbersome.
The parentheses have special meaning in both expressions and ex addresses,
and :eval
affects parentheses and backslashes throughout the line,
not just in the buffer name.
The following example demonstrates the two techniques, deleting any backspace
sequences in the buffer whose name is stored in option x.
Clearly, the ":(=x)
" version is smaller and easier to understand.
:eval \((x)\)%s/.\\b\\\(.\\\)/\\1/g :(=x)%s/.\b\(.\)/\1/g