# Programmed Macros in vile

vile presents a couple of forms of what are commonly called "macros". This document presents information on those written in the builtin "macro language". (The other form of macro is a "keyboard macro", a simple stored sequence of vile keystrokes, which can be replayed on command.) Macros written in the macro language can be bound to keys, run from files or buffers, given names (in which case they're known as "procedures"), and in the last case, those names may be directly introduced into vile's command set.

The language can execute any valid vile command, using one of it's "named" forms. I.e. the command that would be executed would be "down-line", "forward-line", or "next-line", but it would not work to write the macro to use 'j'.

vile commands can be linked together in repetitive or conditional ways using various builtin directives (e.g. "if", "else", "while", "break", etc), and intermediate results can be stored in string-valued temporary variables. Other forms of variable can be used to reference parts of vile's current state, e.g. the current line number. Finally, there is a set of functions that can act on variables, to concatenate them, compare them, increment them, change their representation, etc.

Each of these language aspects will be described in turn, but first the execution framework must be explained.

## Creating, executing, storing macros

In the simplest case, valid macro language constructs are placed in a file or buffer and subsequently executed with one of these editor commands:

command | applies to | example
--- | --- | ---
execute-buffer | buffer | execute-buffer cfgcmds
execute-file | disk file | execute-file ~/.projcfg
source | disk file | source c:/utils/proj.cfg

The most common example of this usage is vile's startup file, which is "sourced" during the editor's invocation. Typically, the startup file configures the user's preferences and looks something like this:

set ai
set ts=4
set flash
<etc.>

A startup/configuration file might also use macro language directives to conditionally configure the editor. For example, if xvile executes this startup file fragment:

~if &sequal $progname "xvile"
set-variable $title $cbufname
~endif

then the editor's X window titlebar changes. However, "standard" vile (i.e., non-gui vile) ignores this fragment and thus, a single startup file can be used to configure both the gui and non-gui versions of the editor.

vile also provides constructs that encapsulate macro language elements as numbered and named programs. These programs represent the entity that most programmers identify as a "true" macro. And in fact, the remainder of this document will simply assume that the word "macro" refers to one of aforementioned program types.

### Numbered macros (anachronism)

The numbered macro syntax looks like so:

<number> store-macro
<language element>
...
<language element>
~endm

A numbered macro is executed using this command:

execute-macro-<number>

To bind a keystroke to this macro, use this command:

bind-key execute-macro-<number> <keystroke>

Here's an actual example:

30 store-macro
write-message "this is a test macro"
~endm
bind-key execute-macro-30 #h

Now, whenever "#h" is pressed, a message is written on the editor's message line.

Although this syntax serves a purpose, it's obvious that numbered programs don't lend themselves to easy recall (quick, what does macro 22 do?). But this format was an integral part of vile for many years, simply because named macros could not be bound to keystrokes. This restriction has been removed, rendering this feature essentially obsolete. The only advantage of numbered macros over named macros is that the former do not share the same namespace as vile's commands. This attribute can be advantageous when creating macros recalled solely via key bindings.

For completeness sake, it should be mentioned that numbered macros are allocated from a fixed pool (default is 40 macros). This fixed pool can be increased via the following configuration option:

    	--with-exec-macros=N    specify count of numbered macros

### Named macros

A named macro, aka "stored procedure", uses this syntax:

store-procedure <unique-name> ["help-string"]
<language element>
...
<language element>
~endm

where:

unique-name
  is an alpha-numeric identifier that does not conflict with the name of any existing editor command (the show-commands command generates a list of all existing commands).
help-string
  is an optional description of the macro. This string is displayed in the listing created by show-commands.

A stored procedure is executed by simply referencing its name. To bind a keystroke to this macro, use this command:

    	bind-key <unique-name> <keystroke>

Here's the stored procedure equivalent of macro number 30 above:

store-procedure write-msg-tst "displays test message"
write-message "this is a test macro"
~endm
bind-key write-msg-tst #h

Two mechanisms now exist for executing this macro:

- press "#h" within the editor, or
- simply use the name "write-msg-tst" as if it were any other built-in editor command. This means that "write-msg-tst" can be invoked from another macro, from a startup/configuration file, or from vile's command line, like so:

  :write-msg-tst

Named macros may have parameters. Like Bourne shell, the parameters are denoted '$' followed by a number, e.g., $1 for the first parameter.

- $# gives the number of parameters.
- $* gives a blank-separated list of all parameters.
- $@ gives a blank-separated list of all parameters, quoted.
- $0 is the name of the current procedure, not expanded in the lists.

The individual parameters are evaluated when the macro is invoked, and may consist of expressions. They are stored as strings.

The macro interpreter uses a template in the definition to define the types of parameters which are accepted. For each parameter, a keyword, optionally followed by the prompt string is required. Keywords (which may be abbreviated) include

    	bool
    	buffer
    	directory
    	enum		(see below)
    	file
    	integer
    	majormode
    	mode
    	string
    	variable

Unless overridden, the prompt for each parameter is named after the keyword. Override the prompt by an assignment, e.g.,

store-procedure Filter f="Input" f="Output"

to begin a macro 'Filter' with two parameters, Input and Output, internally referenced by $1 and $2.

Here is a simple macro which accepts two parameters and uses them to position the cursor on a given line and zero-based character offset.

; macro for wingrep and similar applications that can pass
; both line- and column-number to external tools.
; usage: "winvile +WinGrep $L $C $F"
store-procedure WinGrep i="Line" i="Offset"
~local %col
setv %col &sub $2 1
$1 goto-line
beginning-of-line
~if &ge %col 1
%col forward-character-to-eol
~endif
~endm

The 'enum' parameter type is special; it requires a second keyword which denotes the symbol table which is used for name-completion. The table name (which cannot be abbreviated) follows the 'enum' after a colon (:), e.g.,

store-procedure Scheme e:fcolor="Foreground"

The 'enum' tables correspond to the enumerated modes:

    	*bool
    	backup-style
    	bcolor
    	byteorder-mark
    	ccolor
    	color-scheme
    	cursor-tokens
    	fcolor
    	file-encoding
    	for-buffers
    	mcolor
    	mini-hilite
    	popup-choices
    	qualifiers
    	reader-policy
    	record-attrs (VMS only)
    	record-format (VMS only)
    	recordseparator
    	showformat
    	video-attrs
    	visual-matches
    	vtflash

### Returning values

Any macro can return a value to a calling script. This is done using special variables:

$return
  is a symbol that a macro can set to any string.
$_
  is copied from $return when completing a macro. If no string was assigned to $return within the macro, $_ will contain a readable form of the exit status. These are the standard values used within vile:

      TRUE
      	FALSE
      	ABORT
      	SORTOFTRUE

  $_ may also contain the special symbol ERROR if the macro could not run, e.g., due to too much recursion, or if the exit status was none of the standard values.

### Storing macros

In general, macros are stored in the editor's startup file. Prolific macro authors may instead opt to sprinkle their macros across one or more external text files and source those file(s) from the startup file.

This concludes the discussion of the macro language execution framework.

The remainder of this document describes individual language constructs. The presentation is bottom-up (i.e., reference format), so individual sections may be read in any order.

## Comments

A semi-colon (;) or double-quote (") denotes a comment that extends from the delimiter to end of line. The semi-colon is inherited from MicroEMACS, the double-quote is for vi compatibility.

Note 1: The double-quote also delimits string arguments, but the command parser correctly distinguishes the various use cases.

Note 2: Inline comments (comment text that follows a command) are permitted except when used in conjunction with commands that take optional arguments. Here follow two examples of unacceptable usage:

winopen ; invoke win32 common open dialog
write-file ; flush curr buffer to disk

In the first case, the winopen command attempts to browse ';' as a directory. In the second case, write-file flushes the current buffer to disk using ';' as the filename.

## Misc macro syntax features

Lines ending with '\' are joined before interpretation.

### Limits

The length of a variable name may not exceed 255 (NLINE-1) bytes of storage. Most other strings are allocated dynamically.

### Strings

Like many simple language, the macro language operates exclusively on strings. That is to say, variables are always of type "string", and need not be declared in advance.

Strings may be surrounded by double quotes. As in C-like languages, a few special characters may be represented using an "escape" notation, using a backslash and another character to represent the "special character:"

Escape code | Actual character | Example
--- | --- | ---
\n | newline character | (control-J)
\r | carriage return | (control-M)
\\ | backslash | (itself: '\')
\b | backspace | (control-H)
\f | formfeed | (control-L)
\t | tab | (control-I)
\a | bell | (control-G)
\s | space | (ASCII SPACE)
\" | quote | (the '"' character)
\xNN | the character in hex (i.e. 0xNN) |
\NNN | the character in octal (i.e. 0NNN) |
\C | (any char) | itself

It is permissible to omit the double quotes surrounding a string if the parser will not confuse it with another element of the macro language, and if it contains no whitespace, but it's probably better practice to use the quotes all the time, to reinforce the idea that all values are strings.

You may also use strings surrounded by single quotes. The single quotes override double quotes and backslashes, making it simpler to enter regular expressions. Double a single quote to insert one into a string.

## Variables

As noted above, variables hold strings. These strings may represent words, text, numerical values, logical values, etc, depending on the context in which they are used. There are several distinct classes of variables, distinguished syntactically by the character preceding their name.

Class | Example
--- | ---
Temporary variable | %foo
State variable | $curcol
Buffer variable | <main.c
Interactive variable | @"Enter a filename: "
Mode variable | $autoindent

All temporary variables, and some state variables, may be assigned to, using the "set-variable" command, or "setv" for short:

set-variable $search "new pattern to look for"
setv %index "1"
setv %index2="2"

An assignment may use either an equals (=) sign, or whitespace to delimit the left/right sides of the assignment, as shown.

## Temporary variables

Temporary variables are used in macros to hold intermediate values. They are only temporary in that they aren't a "fixed" part of vile — but they _are_ persistent across invocations of one or more macros. (That is, they have global scope.) Temporary variables are prefixed with the % character, and their names may be constructed from any printing character.

## State Variables

State variables allow a macro to refer to and change some aspects of vile's behavior. State variables are prefixed with a $ character, and are always referred to in lowercase. Not all state variables are settable — some are read-only, and are so marked in the table below.

$abufname
  [READ ONLY] Name of the "other" buffer, the one most recently visited. This is what you would get if you typed '#' at a prompt. (E.g. ":e #")
$autocolor-hook
  name of the hook to run when attempting to do automatic syntax coloring.
$bchars
  [READ ONLY] Number of characters in current buffer.
$bchanged
  [READ ONLY] Number of characters in current buffer.
$bflags
  [READ ONLY] Status flags for current buffer, as shown in [Buffer List].

  Flag | Description
  --- | ---
  a | autobuffer caused this to be created
  d | directory listing
  i | invisible, e.g., tags
  m | modified
  s | scratch, will be removed when popped down
  u | unread

$blines
  [READ ONLY] Number of lines in current buffer.
$brightness
  RGB levels for gray, normal, bright in the 0-255 range (winvile version only).
$buf-encoding
  [READ ONLY] Shows the character encoding for the current buffer. The values are the same as for the file-encoding mode, but are always resolved, i.e., no "auto" or "locale".
$buf-fname-expr
  [READ ONLY] Combines bufname-expr and pathname-expr modes for the current buffer.
$buffer-hook
  Name of procedure to run when switching to a buffer.
$bufname
  current buffer-name under the cursor. This is computed from the bufname-expr and pathname-expr patterns.
$bwindows
  [READ ONLY] Number of windows open on current buffer.
$cbufname
  The current buffer's "buffername". (As opposed to the name of the file it may contain.)
$cd-hook
  Name of procedure to run when changing directories.
$cdpath
  editor's copy of the $CDPATH env var (read/write)
$cfgopts
  [READ ONLY] Comma-delimited list of "interesting" compiled options. Currently tracked options include:

  Option | Description
  --- | ---
  athena | xvile built with Athena widgets
  curses | editor uses curses terminal driver
  locale | editor uses system's LC_CTYPE locale
  motif | xvile built with Motif libraries
  nextaw | xvile built with Athena widgets (NeXtaw)
  noshell | shell commands are disabled
  oleauto | editor supports OLE automation.
  openlook | xvile built with OpenLook libraries
  perl | editor includes perl interpreter
  termcap | editor reads TERMCAP db for screen info.
  terminfo | editor reads TERMINFO db for screen info.
  xaw | xvile built with Athena widgets (Xaw)
  xaw3d | xvile built with Athena widgets (Xaw3D)

  If none of the above options are in effect, $cfgopts will be empty ("").

$cfilname
  Full pathname of the current buffer, unless it is a special vile buffer, in which case it will return the empty string.
$char
  Decimal representation of the ASCII character at the cursor location.
$cmd-count
  [READ ONLY] Counts repetition of the current macro, from 1 up to the given repeat count. If not in a macro, this is zero.
$cmd-encoding
  Character set to use for the minibuffer, i.e., the buffer used for commands and messages. The default (auto) tells vile to set it to match the character set of the current buffer.
$cmd-motion
  [READ ONLY] Returns the motion (keyword) associated with the current macro if it was defined using store-operator. Use with $cmd-count to compose the complete motion, e.g.,

  set-variable $search "new pattern to look for"
  setv %index "1"
  setv %index2="2"

$cryptkey
  [WRITE ONLY] encryption key.
$curchar
  The index of the cursor in the buffer, in characters (counting from 1).
$curcol
  The cursor's column number (counting from 1).
$curline
  The cursor's line number (counting from 1).
$cwd
  Current working directory.
$cwline
  The relative line number of the cursor in the current window.
$debug
  Boolean value which enables runtime tracing of macro execution. This can be set in the ~trace command (described in detail below) as well as via set-variable.
$directory
  Location of temp files. This is unused, but its initial value is set from the user's TMP environment variable.
$discmd
  Boolean value which will prevent vile from printing some status messages. It is automatically set to FALSE while sourcing a file, or while executing a macro (after prompting for missing parameters). Set it to TRUE to allow nested macros to show a prompt-string for missing parameters.
$disinp
  Boolean value which will prevent vile from displaying command line input characters.
$encoding
  [READ ONLY] The character encoding, e.g., ISO-8859-1.
$end-of-cmd
  Boolean which is true if user entered the cmd with a carriage return (Some commands may expect to receive additional arguments if their name is terminated with a space character rather than a carriage return.)
$error-buffer
  Buffer name associated with vile's error-buffer feature. This variable commonly appears in a ~local statement in macros that temporarily reassign error-buffer (e.g., the ShowManpage macro in macros/manpage.rc).
$error-expr
  Regular expression expanded from [Error Expressions] which matched the most recent find-next-error command.
$error-match
  Text from the current buffer which which was matched in the most recent find-next-error command.
$error-tabstop
  Tabstop value to use when computing the column that a "%C" pattern will produce. If zero or negative, use the current buffer's tabstop, otherwise use the given value. The default is 8.
$exec-path
  [READ ONLY] Where to find vile.
$exec-suffix
  [READ ONLY] suffix, if any, for execable programs. Use this in portable macros, with &lookup, to get the actual program path.
$exit-hook
  Name of procedure to run when quitting.
$favorites
  [READ ONLY] Path to favorites folder (win32 only)
$fchanged
  Boolean, true if file for current buffer is modified.
$fence-limit
  iteration limit for complex fences, in seconds.
$filename-expr
  actual pattern for %F in [Error Expressions]. Note that vile wraps this in "\(" and "\+\)", so the 1-or-more applies to the last subexpression in %F. We use this side-effect in the win32 port, for example '\([a-zA-Z]:\)\?[^ \t:]' to make the last range repeat 1-or-more times.
$filename-ic
  [READ ONLY] Boolean indicating if the host system's filenames are matched ignoring their case.
$filter-list
  [READ ONLY] list of builtin-filters.
$findpath
  editor's copy of the $VILE_FINDPATH environment var (read/write). Available on win32 and unix hosts. For more details, refer to that section of the help file entitled "Working in a project hierarchy".
$find-cmd
  [READ ONLY] last shell command spawned via the capture command's builtin "find" interface. Available on win32 and unix hosts.
$font
  Name of the current font (in xvile/winvile only).
$forward-search
  Boolean value indicating search direction.
$helpfile
  Filename referred to when help is requested (usually vile.hlp).
$iconname
  With xvile, contains current icon name.
$identifier
  The current "identifier-like" word under the cursor. This is computed from the identifier-expr pattern. Also see the "tagword" mode.
$kbd-macro
  [READ ONLY] This is the contents of the keyboard macro buffer.
$kill
  [READ ONLY] This is vile's "unnamed" yank register, which contains the result of the last line-oriented yank or delete.
$lastkey
  [READ ONLY] Character most recently entered at the keyboard.
$latin1-expr
  pattern to match locales using ISO-8859-1 in case they are not installed. Normally vile looks for a narrow locale to correspond with the wide (UTF-8 encoding) locale. If locale data is incomplete, vile can still support ISO-8859-1 encoding, but checks to ensure that the locale is compatible. Some systems lack a full set of locale data. This pattern helps it decide if the (missing) narrow locale would use ISO-8859-1 encoding.
$lcols
  [READ ONLY] The length (in columns) of the current line.
$libdir-path
  This value will be used to augment the user's PATH environment variable when running filters, so that vile-specific filters needn't clutter general purpose bin directories.
$line
  Contains the text of the current line of the current buffer starting with the cursor position.
$llength
  [READ ONLY] The length (in characters, not columns) of the current line.
$locale
  [READ ONLY] The character locale, e.g., en_US.
$majormode
  [READ ONLY] Current majormode, if any.
$majormode-hook
  name of the hook to run when attempting to determine the majormode for the current buffer. If this is not defined, vile uses the prefixes and suffixes values to decide.
$match
  [READ ONLY] After a successful search, contains the text that matched the search pattern.
$menu-file
  The name of the menu file (e.g. .vilemenu)
$mode
  [READ ONLY] "insert", "command", or "overwrite" mode.
$modeline-format
  Format of mode lines. See "Mode line customization" in the vile help file.
$modified
  [READ ONLY] is current buffer modified or not?
$ncolors
  Number of displayed colors, must be power of two.
$ntildes
  Percent of window filled by ~ chars, at end of buffer.
$ocwd
  [READ ONLY] Previous current directory.
$os
  [READ ONLY] "Operating system" for which was vile was built. Currently "unix" (if no more-specific name is derived from the configure script), "dos", "os/2", "vms" and "win32".
$pagelen
  Length of the vile screen.
$pagewid
  Width of the vile screen.
$palette
  Some versions of vile implement screen coloring. The variable consists of digits which control the current color set, usually one digit per color.
$patchlevel
  [READ ONLY] current patch-level (empty for release).
$pathlist-separator
  separates directory names in lists such as $PATH.
$pathname
  current "path-like" word, under the cursor. This is computed from the pathname-expr pattern. If the current buffer is a directory, use the entire line, overriding the regular expression.
$pathname-separator
  separates levels of directory names in a path. Usually this is '/', for Unix.
$pending
  [READ ONLY] Boolean which is true when the user has "typed ahead", i.e. there are waiting keystrokes.
$pid
  [READ ONLY] vile's process-id.
$position-format
  Format of ^G command. See "Mode line customization" in the vile help file.
$progname
  [READ ONLY] The string "vile", "xvile", or "winvile" as appropriate.
$prompt
  The string ": ", used in command-line prompts.
$qidentifier
  the name of the current "qualified-identifier-like" word under the cursor, useful for C++ programmers.
$read-hook
  Name of procedure to run after a file is read.
$replace
  The current replacement strings, used in substitutions.
$search
  The current search pattern.
$seed
  The seed for the internal random number generator.
$shell
  Name of the shell program for spawned commands. For Unix, this corresponds to $SHELL, while DOS, OS/2 and related systems use $COMSPEC.
$sres
  Current screen size on a DOS PC (meaningless on a Win32 host). Values:

  $sres | screen size
  --- | ---
  "2" | "25"
  "4" | "43"
  "5" | "50"
  "6" | "60"
  Values for VMS:
  "WIDE" | "NORMAL"

  Other drivers ignore the value.
$startup-file
  The name of the startup file (e.g. .vilerc)
$startup-path
  Where to find the startup file
$status
  [READ ONLY] Boolean representing success of most recent command. Since a failed command will usually cause an entire macro to fail, the ~force directive is often used to suppress a command's failure. $status preserves its exit status.
$term-encoding
  [READ ONLY] Shows the character encoding which the terminal/display driver can handle. The values are the same as for the file-encoding mode. If it can display UTF-8 (terminfo/termcap) or UTF-16 (winvile), and the terminal is initialized to match, vile shows the corresponding "utf-8" or "utf-16" value.
$title
  The current window title (X11, win32 versions only).
$title-encoding
  Tell whether to send title-change escape sequences to xterm in ISO-8859-1 (8bit) or UTF-8 (utf-8). A "locale" setting is provided, which tells vile to use UTF-8 if the locale encoding uses UTF-8, and ISO-8859-1 otherwise. ISO-8859-1 is standard; some terminal emulators require UTF-8. Even with this, other programs such as GNU screen interfere with setting titles with non-ASCII content.
$title-format
  The format for the window title (X11, win32 versions). If this variable is not set, the title is the program name and the current buffer, separated by a dash, e.g., %{$progname} - %{$cbufname} Use the swap-title mode to control the order of those strings. If $title-format is set, swap-title has no effect. See "Mode line customization" in the vile help file.
$version
  [READ ONLY] Contains vile's version string.
$with-prefix
  [READ ONLY] String which is set by "~with" directive in macros. If no prefix was set, this returns ERROR.
$wlines
  Height of current window.
$word
  The "word" at the cursor location.
$write-hook
  Name of procedure to run before a file is written
$xdisplay
  The value to set $DISPLAY when running $xshell.
$xshell
  Name of the terminal program for spawned xvile commands. The default is "xterm", but may also be set by the environment variable $XSHELL.
$xshell-flags
  Command-line flags after $xshell, normally "-e" The $XSHELLFLAGS environment variable determines the default value.

## Mode variables

You may set and use the values of the editor modes (i.e., universal modes, buffer-only modes or window-only modes) as if they were state variables (e.g., "setv $errorbells=true"). The global values of the editor modes are not visible to the expression evaluator.

Realistically, this feature is little used, since vile's set/setl commands, as well as the &global/&local functions, serve the same purpose.

## Buffer variables

Buffer variables (a '<' followed by a buffer name) return the current line of the specified buffer, automatically setting the position to the next line.

## Interactive variables

Interactive variables are not actually "variables" at all — they're really more like functions that return a string, entered by the user in response to a prompt. The prompt is the name of the "variable".

They are so similar to a query function that there is function which serves this exact purpose, and which should be used in preference. Thus, one might have previously written:

set-variable %file @"What file?"

Instead, one should now write:

set-variable %file &query "What file?"

## Functions

Functions always return strings. Functions can take 0, 1, 2, or 3 arguments. Function names are always preceded by the & character, and can usually be shortened to just three characters, though there is little reason to do so.

Tasks that are usually implemented as "operators" in other languages are implemented as functions in vile's macro language. Thus, for example, arithmetic division which is usually written as "6 / 2" is written as "&div 6 2". (I believe this is sometimes called "prefix" notation, as opposed to the normal operator "infix" notation, or the "postfix" notation used on a stack-oriented calculator, i.e. "6 2 /".)

Depending on the function, arguments may be expected to represent generic strings, numeric values, or logical (boolean) values.

- Numeric arguments can be any of the following:
  - hexadecimal values (digits beginning with leading "0x")
  - octal values (digits beginning with leading "0")
  - decimal values (other strings of digits)
  - character constants (single character in single quotes: 'C')
  - any other string will be interpreted as 0.
- Boolean (or "logical") arguments will be interpreted as follows (without regard to upper/lowercase):
  - Logically "true" values:
    "true", "t", "yes", "y", "on", and non-zero numerics.
  - Logically "false" values:
    "false", "f", "no", "n", "off", and zero-valued numerics

Arithmetic functions —

These all return numeric values:

&add "N1" "N2"
  Add "N1" and "N2".
&sub "N1" "N2"
  Subtract "N2" from "N1".
&times "N1" "N2"
  Multiply "N1" by "N2".
&divide "N1" "N2"
  Divide the "N1" by "N2"
&mod "N1" "N2"
  Divide the "N1" by "N2", return remainder.
&negate "N"
  Return -(N).
&ascii "S"
  Return the ASCII code of the first character in "S"
&random "N"
&rnd "N"
  Random number between 1 and N
&abs "N"
  Absolute value of "N"
&ftime "N"
  The modification time of the file "N"
&stime
  The system time.

String manipulation functions —

These two return numeric values:

&length "S"
  Returns length of string "S".
&sindex "S1" "S2"
  Returns index of "S2" within "S1", or 0.

The rest return strings:

&bind "S"
  Return the function name bound to the key sequence "S".
&cat "S1" "S2"
  Concatenate S1 and string "S".
&chr "N"
  Converts numeric "N" to an ASCII character.
&cclass "S"
  Character class (see "show-printable")
&env "S"
  Return the value of the user's environment variable named "S".
&gtkey
  Get a single raw keystroke from the user.
&gtmotion "N"
  Get a key sequence from user which must be bound to a motion. The parameter is one of the binding names:
  - "command"
  - "default"
  - "insert"
  - "select"
  If successful, the name of the corresponding motion function is returned.
&gtsequence
  Get a complete vile key sequence from user.
&left "S" "N"
  Extract first "N" characters from "S"
&lower "S"
  Return lowercase version of "S".
&right "S" "N"
  Extract chars from position "N" onward.
&middle "S" "N1" "N2"
  Extract "N2" chars at position "N1".
&upper "S"
  Return uppercase version of "S".
&trim "S"
  Remove whitespace at either end of "S", reduce multiple spaces within "S" to just one space each.

Boolean/logical functions —

These all return TRUE or FALSE:

&not "B"
  Return inverse of boolean "B".
&and "B1" "B2"
  Return logical AND of "B1" and "B2".
&or "B1" "B2"
  Return logical OR of "B1" and "B2".
&equal "N1" "N2"
  Is "N1" numerically equal to "N2"?
&geq "N1" "N2"
  Is "N1" numerically not less than "N2"?
&greater "N1" "N2"
  Is "N1" numerically greater than "N2"?
&gt "N1" "N2"
  (same as &greater)
&isa "C" "N"
  Is "N" a member of class "C". Classes include: buffer, color, mode, submode, Majormode.
&leq "N1" "N2"
  Is "N1" numerically not greater than "N2"?
&lessthan "N1" "N2"
  Is "N1" numerically less than "N2"?
&lt "N1" "N2"
  (same as &lessthan)
&neq "N1" "N2"
  Is "N1" numerically not equal to "N2"?
&sequal "S1" "S2"
  Is "S1" the same string as "S2"?
&sgeq "S1" "S2"
  Is "S1" lexically not less than "S2"?
&sgreater "S1" "S2"
  Is "S1" lexically greater than "S2"?
&sgt "S1" "S2"
  (same as &slessthan)
&sleq "S1" "S2"
  Is "S1" lexically not greater than "S2"?
&slessthan "S1" "S2"
  Is "S1" lexically less than "S2"?
&slt "S1" "S2"
  (same as &slessthan)
&sneq "S1" "S2"
  Is "S1" lexically not equal to "S2"?
&readable "S"
&rd "S"
  Is the named file "S" readable?
&writable "S"
  Is the named file "S" writable?
&execable "S"
  Is the named file "S" exec'able?
&bchanged "S"
  Is given buffer "S" modified?
&fchanged "S"
  Is file for given buffer "S" modified?
&cmatch "R" "V"
  Does the given regular expression "R" match the value "V" (ignoring case)?
&error "S"
  Was the string set with the ERROR token? For example, a &query that is aborted will return an ERROR result.
&match "R" "V"
  Does the given regular expression "R" match the value "V"?
&filter "M"
  Does the given majormode have a built-in highlighting filter?
&stoken "T" "D" "S"
  Is token "T" in string "S", given delimiters "D"?

Miscellaneous functions —

These all return string values:

&classof "N"
  Retrieves the class(es) to which the given name may return. Usually this is a single name, e.g., one of those checked by &isa. If multiple matches are found, the result contains each classname separated by a space.
&default "MODENAME"
  Retrieves initial/default value for the given mode or state variable.
&global "MODENAME"
  Retrieves universal/global mode setting.
&indirect "S"
  Evaluate value of "S" as a macro language variable itself. Thus if %foo has value "HOME", then

  &env &indirect %foo

  will return the home directory pathname.
&local "MODENAME"
  Retrieves local mode setting (for current buffer).
&lookup "N" "P"
  The "N" keyword tells which field to use looking for the file "P":

  bin – look in vile's directory
  current – look in the current directory
  home – look in user's $HOME directory
  libdir – look along $libdir-path
  path – look along user's $PATH
  startup – look along $startup-path

  as well as associated access tests:

  execable - test if file is exec'able
  readable - test if file is readable
  writable - test if file is writable

  The search order is fixed: current, home, bin, startup, path, libdir. Note that the directory lists may overlap.

&mclass "M"
  Retrieve the class to which the given mode belongs. This is different from &mclass since it distinguishes the modes Return values include: universal buffer window submode Majormode.
&qpasswd "S"
  Present "S" to the user and return their response. Each typed character is echoed as '*'. The response is not recallable via the editor's history mechanism.
&query "S"
  Present "S" to the user, and return their typed response.
&date "F" "T"
  If strftime() is found, format the time "T" using the "F" format. Otherwise, use ctime() to format the time. Times are numbers (see &ftime and &stime).
&dquery "S" "D"
  Present "S" to the user, and return their typed response. If "D" is given, use that as the default response. Otherwise use the previous response as the default.
&path "N" "P"
  The "N" keyword tells which field to extract from the pathname "P":

  end – suffix of the filename
  full – absolute path
  head – directory
  root – filename without suffix
  short – relative path
  tail – filename

&pcat "D" "F"
  Concatenate directory and filename, handing syntax and ensuring that if the filename is absolute, that the directory is ignored.
&pquote "P"
  Quote the pathname if it contains characters such as space that cannot be passed to the shell without special handling.
&register "S"
  Return contents of register "S". Only the first character of "S" is used for the name. Note that the contents may be more than one line.
&token "N" "D" "S"
  Select N'th token of string "S", given delimiters "D".
&translate "F" "T" "S"
  Translate from "F" to "T" each character of string "S". The "F" and "T" are strings of equal length; characters from "S" found in "F" are translated to the character in "T" at the same index.
&word "N" "S"
  Select N'th word of string "S", blank separated.

## Directives

The macro language has the capability for controlling flow and repetition through conditional, branching, and looping instructions. Complex text processing or user input tasks can be constructed in this way. The keywords that introduce this control are called "directives". They are always prefixed with the ~ character, and they are always in all lowercase.

### ~endm

The "store-procedure" and "store-macro" commands both indicate the start of the body of a macro routine. ~endm indicates the end of that routine.

### ~force

To prevent a failed command from terminating the macro which invokes it, the ~force directive can be used to "hide" a bad return code. For instance, the "up-line" command might fail if executed at the top of a buffer. "~force up-line" will suppress the failure. The $status variable can be used to determine whether the command succeeded or not.

### ~hidden

You can suppress not only the check for success or failure of a macro as in ~force, but also the screen refresh, making macros run more rapidly. For example

30 store-macro
write-message "[Attaching C/C++ attributes...]"
~local $curcol $curline
~hidden goto-beginning-of-file
~hidden attribute-from-filter end-of-file "vile-c-filt"
write-message "[Attaching C/C++ attributes...done ]"
~endm
bind-key execute-macro-30 ^X-q

causes the screen updates from moving the current position to the beginning of the file and then filtering (which moves the position to the end-of-file) to be suppressed. The screen will be updated after completion of the macro, after the current position has been restored from the values saved with the ~local directive.

### ~quiet

Rather than suppress all screen updates, you may suppress any messages that are written as the command progresses.

### ~if, ~elseif, ~else, and ~endif

These control execution of macro commands in the expected manner. The ~if directive is followed by a string which is evaluated for truth or falsehood according to the rules outlines for boolean variables, above. The following fragment demonstrates the use of this family of directives:

beginning-of-line
; test for '#'
~if &equ $char 35
set-variable %comment-type "shell comment"
; test for ';'
~elseif &equ $char 59
set-variable %comment-type "vile macro language comment"
~else
write-message "Not an expected comment type"
~return
~endif
write-message &cat "The current line is a " %comment-type

### ~goto

What would a decent programming language be without a "goto"? The ~goto directive is followed by the name of a label. Labels may appear anywhere in the current macro definition, and are themselves preceded with a * character.

~force up-line
if &not $status
~goto foundtop
...
...
*foundtop
write-message "At top of buffer"

### ~while and ~endwhile

The block of statements bracketed by ~while and ~endwhile are executed repeatedly, until the condition being tested by ~while becomes false.

; how many occurrences of a given pattern in a buffer?
set nowrapscan
set-variable %lookfor somepattern
; we'll count one too many
set-variable %howmany "-1"
set-variable %continue yes
~while %continue
~force search-forward %lookfor
set-variable %continue $status
set-variable %howmany &add %howmany "1"
~endwhile
write-message &cat &cat %howmany " appearances of " %lookfor

### ~break

The ~break directive allows early termination of an enclosing while-loop. Extending the above example:

; count the occurrences of a pattern in all buffers
set nowrapscan
set noautobuffer
rewind
set-variable %lookfor pgf
set-variable %howmany "0"
set-variable %buffers "1"
set-variable %cont yes
~while true
goto-beginning-of-file
~while true
~force search-forward %lookfor
~if &not $status
~break
~endif
set-variable %howmany &add %howmany "1"
~endwhile
~force next-buffer
~if &not $status
~break
~endif
set-variable %buffers &add %buffers "1"
~endwhile
set-variable %msg %lookfor
set-variable %msg &cat " appeared "
set-variable %msg &cat %howmany
set-variable %msg &cat %msg " times in "
set-variable %msg &cat %msg %buffers
set-variable %msg &cat %msg " buffers."
write-message %msg

### ~return

This causes immediate exit of the current macro, back to the calling macro, or to user control, as appropriate.

### ~local

The ~local directive causes the variables which are listed to be saved at that point (once if the directive is within a loop), and automatically restored at the end of the current macro. If the directive specifies a temporary variable which was not defined before, it will be deleted rather than restored.

For example:

~local $curcol $curline

will restore the cursor position. The order is important in this example, because vile restores the variables in the reverse order of the ~local declaration. If $curline is set, $curcol will be reset to the first column as a side effect. So we specify that $curcol is restored last.

~local can save/restore the state of mode variables [1], user variables and the state variables shown with show-variables. Note that setting certain variables, such as the cursor position, will have side effects, i.e., modifying the display. If these are distracting, use ~hidden or ~quiet to suppress display updates until the macro completes.

[1] Subject to the limitations described above for "Mode variables". Namely, "global values of the editor modes are not visible to the expression evaluator."

### ~with, ~elsewith and ~endwith

Tokens following the ~with directive will be prepended to succeeding lines of macro until the next ~endwith directive, or the end of the current macro. This is useful for simplifying majormode directives, which are repetitive. Use ~elsewith as a convenience for fences; otherwise it functions just as ~with does.

For example, use

define-mode txt
~with define-submode txt
suf "\\.txt$"
comment-prefix "^\\s*/\\?--"
comments "^\\s*/\\?--\\s+/\\?\\s*$"
~endwith

rather than

define-mode txt
define-submode txt suf "\\.txt$"
define-submode txt comment-prefix "^\\s*/\\?--"
define-submode txt comments "^\\s*/\\?--\\s+/\\?\\s*$"

### ~trace

No program is complete without a few bugs. Use vile's builtin macro tracing to see what the macros really do. The ~trace command sets the $debug variable, which controls whether vile appends to the [Trace] buffer a copy of each line executed, the local variables saved/restored and intermediate states of expression evaluation.

For example,

~trace on

activates tracing,

~trace off

deactivates it, and

~trace

prints a message telling if tracing is active.

## Editor commands

The "show-commands" command lists _all_ available editor commands. This is, admittedly, a large list and generally grows with successive releases of the editor. Fortunately, most editor commands include short help strings that describe their purpose. To winnow the list to a particular area of interest, use the "apropos" command (e.g., "apropos append"). To determine the command bound to a specific key, use "describe-key". The format of the apropos, describe-key, and show-commands listing is as follows:

    	command-name             optional-key-binding(s)
    	optional-command-name-aliases
    	(help-string)

Commands fall into three broad categories: simple, motion, operator.

### Simple commands

A simple command neither acts on a region nor does it explicitly move the cursor through a region (the "region" concept is explained in the "Motion commands" section below). An example of a simple command is "find-tag", and here's the listing returned by show-commands:

    	"find-tag"               ^]
    	  or            "ta"
    	  or            "tag"
    	  ( look up the given (or under-cursor) name as a "tag" )

From the perspective of writing a macro, it can be seen that find-tag has two aliases, either of which may be substituted for the "find-tag" name within a macro definition. Notice that the help string mentions a "name" argument and sure enough, if you type ":find-tag" within the editor, you'll be prompted for a "Tag name". This gives us enough information to write a contrived macro that finds a fixed tag name:

store-procedure tryit
tag "filterregion"
~endm

Note also that some help strings include a "CNT" keyword, which indicates that the command name may be preceded by an integer count that repeats the command action that many times (default CNT value is 1). For example, here's the "join-lines" listing:

    	"join-lines"     J
    	  ( join CNT lines together with the current one )

And here's a macro that joins 4 lines:

store-procedure join4
4 join-lines
~endm

### Motion commands

Motions move the cursor and, consequently, may be used to define a region. This latter property is an important aspect of an "operator command". The "show-motions" command lists the editor's motion commands.

Within a macro, the following general syntax invokes a motion:

    	[count] region-spec

The optional "count" specifies the number of affected region-specs (default value is 1). An example motion is "back-line", and here is its show-commands listing:

    	"back-line"              k         #-A
    	  or    "previous-line"
    	  or    "up-arrow"
    	  or    "up-line"
    	  (motion: move up CNT lines )

Note that the help string is prefixed with the word "motion", which unambiguously identifies the nature of this command. Given the above information, we can write a contrived macro to move the cursor up three lines:

store-procedure upthree
3 back-line
~endm

### Operator commands

Operators manipulate regions. The "show-operators" command lists the editor's operator commands. By convention, most operator names end with "-til" (short for "until").

Within a macro, the following general syntax invokes an operator:

    	[count] operator-name region-spec [args...]

where:

region-spec
  may be replaced with any motion command or the special word "lines" (the latter is a synonym for a single buffer line).
count
  optionally specifies the number of region-specs affected by operator-name (default value is 1).
args
  denotes optional string arguments(s) required by some operators (e.g., "filter-til").

An example operator is "flip-til", and here's its show-commands info:

    	"flip-til"               ^A-~
    	  or    "~"
    	  (operator:  exchange upper and lowercase on characters in the
    	  region) (may follow global command)

A salient point to note within the help string is the "operator" keyword, which unambiguously identifies the purpose of this command. Given the above information, we can write a macro to flip the case of the current paragraph.

store-procedure flippara
up-paragraph ; move to beginning of para
flip-til down-paragraph ; flip case of entire para
~endm

One might be tempted to bind this macro to a key using this syntax:

bind-key flippara g

and then attempt to use a numerical argument to control the number of affected paragraphs. I.E., type "3g" to flip three paragraphs. But this actually invokes "flippara" three times in a row, which (due to the sequential up- and down-paragraph motions), flips the case of the _same_ paragraph three times. However, we can workaround that obstacle with the use of an interactive variable:

store-procedure flippara
setv %dflt 1
setv %quest @&cat &cat "Flip how many para [" %dflt "]? "
~if &sequal %quest ""
setv %quest %dflt
~endif
up-paragraph
%quest flip-til down-paragraph
~endm

## Debugging macros

vile's popup-msgs mode pops up the [Messages] buffer to show text written to the message line. Closing the [Messages] buffer window clears its content until the next message is written. This mode is most useful when debugging macros, since many messages may appear, each overwriting a previous one.

Let's use this macro fragment for illustration:

~if &greater $blines 0
; buffer has at least one line of data, proceed
~else
; this is unexpected!
~endif

Suppose the macro is taking the unexpected code path in one of several buffers, but you don't know which. To trace the path, modify the macro like so:

~if &greater $blines 0
; buffer has at least one line of data, proceed
~else
; this is unexpected!
setv %msg &cat "Error: Buffer " &cat $cbufname " empty"
write-message %msg
~endif

Next, enable popup-msgs (i.e., set popup-msgs) and then start the macro. When the "write-message" command is executed, the [Messages] buffer pops up and displays the string written by the unexpected code path.

Disable popup-msgs using this command:

:set nopopup-msgs

## Example startup file

The startup file included below illustrates several of the language constructs described in this document. This example is crafted for the win32 environment, but its syntax and usage are applicable to any host OS supported by vile.

set ai aw ts=4 sw=4 flash
bind-key next-window ^N
bind-key previous-window ^P
~if &sequal $progname "winvile"
set-variable $font "r_ansi,8"
~endif
~if &equal 0 &sindex &lower $shell "command.com"
set w32pipes
~else
set now32pipes
~endif
~if &not &equal 0 &sindex $cfgopts "perl"
perl "use hgrep"
perl "use dirlist"
~endif
~if &not &equal 0 &sindex $cfgopts "oleauto"
set redirect-keys=&cat &global redirect-keys ",MULTIPLY:A:S"
~endif
; modify ^A-i and ^A-o so that they don't wrap inserted text.
store-procedure save-wrap-state
setv %wm=$wrapmargin
setv %ww=$wrapwords
setl nowrapwords wm=0
~endm
store-procedure restore-wrap-state
setl wrapmargin=%wm
~if %ww
setl wrapwords
~else
setl nowrapwords
~endif
~endm
store-procedure insert-chars-noai-nowrap
save-wrap-state
insert-chars-no-autoindent
restore-wrap-state
~endm
bind-key insert-chars-noai-nowrap ^A-i
store-procedure open-line-below-noai-nowrap
save-wrap-state
open-line-below-no-autoindent
restore-wrap-state
~endm
bind-key open-line-below-noai-nowrap ^A-o
;Rather than composing documents in a word processor, it's much
;more efficient to use vile. But pasting vile-formatted text into,
;say, MS Word is a pain in the neck because each paragraph needs
;to be reformatted. Example:
;
; vile txt
; ========
; para 1 line1,
; line 2,
; line 3,
; line 4
;
; para 2 line 1,
; line 2,
; line 3,
; line 4
;
;If "vile txt" is copied and pasted into Word, it looks awful because
;the lines of the paragraphs do not flow together (i.e., the new lines
;terminating each vile paragraph serve as a "hard" paragraph break).
;
;'Twould be nice if vile could join each paragraph so that "vile txt"
;above looked like this:
;
; vile txt
; ========
; para 1 line1, line 2, line 3, line 4
;
; para 2 line 1, line 2, line 3, line 4
;
;Then, when this version is pasted into Word, all paragraphs are
;automatically reformatted. Here's a macro that adds this feature:
store-procedure join-all-para
goto-beginning-of-file
write-message "[joining all paragraphs...]"
~while true
~force join-lines-til down-paragraph
~if &not $status
~break
~endif
goto-bol
~force 2 down-line ;skip to next para
~if &not $status
~break
~endif
~endwhile
~endm

## Credits

This document, and the macro language it describes, owes a debt of thanks to Dan Lawrence and his MicroEMACS text editor. Many of the features described herein first appeared in this form in MicroEMACS.