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.
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.
<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
store-procedure <unique-name> ["help-string"]
<language element>
...
<language element>
~endm
where:
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:
: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.
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 register 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
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.
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.
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.
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.
Lines ending with '\' are joined before interpretation.
The length of a variable name may not exceed 255 (NLINE-1) bytes of storage. Most other strings are allocated dynamically.
Like many simple languages, 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.
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 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 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. You can also refer to the value of a mode by prefixing its name with a $ character. However:
Here are the state variables:
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 |
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 ("").
set-variable $search "new pattern to look for"
setv %index "1"
setv %index2="2"
force-empty-lines
command uses this value
to decide whether to add or delete blank lines to make all
line-gaps the same size.The default (false) allows vile to search forward until it gets a match which may not necessarily include "dot".
The default (false) behavior in vile matches from the current editing position onward.
The number is the character offset (starting at zero) of the match. It is set to -1 if there is no match.
The number is the character length of the match. It is set to -1 if there is no match.
$term-encoding
.$sres | screen size |
---|---|
"2" | "25" |
"4" | "43" |
"5" | "50" |
"6" | "60" |
Values for VMS: | |
"WIDE" | "NORMAL" |
$startup-path
.-u
or -U
option, since those
buffers may be initialized before the initialization script is
processed.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 (a '<' followed by a buffer name) return the current line of the specified buffer, automatically setting the position to the next line.
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 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.
Arithmetic functions —
These all return numeric values:
String manipulation functions —
These two return numeric values:
The rest return strings:
Boolean/logical functions —
These all return TRUE or FALSE:
Miscellaneous functions —
These all return string values:
&env &indirect %foo
will return the home directory pathname.
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.
end – suffix of the filename
full – absolute path
head – directory
root – filename without suffix
short – relative path
tail – filename
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.
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.
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.
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.
Rather than suppress all screen updates, you may suppress any messages that are written as the command progresses.
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
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 ¬ $status
~goto foundtop
...
...
*foundtop
write-message "At top of buffer"
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
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 ¬ $status
~break
~endif
set-variable %howmany &add %howmany "1"
~endwhile
~force next-buffer
~if ¬ $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
This causes immediate exit of the current macro, back to the calling macro, or to user control, as appropriate.
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."
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*$"
For example,
~trace on
activates tracing,
~trace off
deactivates it, and
~trace
prints a message telling if tracing is active.
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.
"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
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
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:
"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
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
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 ¬ &equal 0 &sindex $cfgopts "perl"
perl "use hgrep"
perl "use dirlist"
~endif
~if ¬ &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 ¬ $status
~break
~endif
goto-bol
~force 2 down-line ;skip to next para
~if ¬ $status
~break
~endif
~endwhile
~endm
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.