Thanks ====== 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. 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 --------------- 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. Thankfully, this restriction has been removed and so, in general, numbered macros are obsolete. However, vile's help file and other related documents still heavily use this format. Consequently, familiarity with this outdated syntax is a must. 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). Note: due to vile's use of the underscore ('_') as a command terminator, this character should _not_ be used as a word separator within "unique-name". Instead, use either a hyphen ('-') or mixed case alphabetics. 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 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, in/out, internally referenced by $1 and $2. 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 color-scheme fcolor mcolor mini-hilite popup-choices record-format recordseparator video-attrs visual-matches vtflash 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 are 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 \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. 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. $bflags [READ ONLY] Status flags for current buffer, as shown in [Buffer List]. a autobuffer caused this to be created 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). $buffer-hook Name of procedure to run when switching to a buffer. $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: perl -> editor includes perl interpreter oleauto -> editor supports OLE automation. termcap -> editor reads TERMCAP db for screen info. terminfo -> editor reads TERMINFO db for screen info. 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. $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. $disinp Boolean value which will prevent vile from echoing ?????? $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. $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) $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. $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. $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. $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. $match [READ ONLY] After a successful search, contains the text that matched the search pattern. $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", "vms", "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 [READ ONLY] current "path-like" word, under the cursor. $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. $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: "2", "25", "80x25", "4", "43", "80x43", "5", "50", "80x50", "80x14", "80x28", "40x12", "40x21", "40x25", "40x28", "40x50" $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. $title The current window title (X11, win32 versions only). $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. $tpause On a PC, the amount of time to pause for showmatch mode parenthesis matching. $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 [READ ONLY] 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". × "N1" "N2" Multiply "N1" by "N2". ÷ "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". >key Get a single raw keystroke from the user. >sequence 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: ¬ "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"? &less "N1" "N2" Is "N1" numerically less than "N2"? &greater "N1" "N2" Is "N1" numerically greater than "N2"? &sequal "S1" "S2" Is "S1" the same string as "S2"? &sless "S1" "S2" Is "S1" lexically less than "S2"? &sgreater "S1" "S2" Is "S1" lexically greater or 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? &filter "M" Does the given majormode have a built-in highlighting filter? &error "S" Was the string set with the ERROR token? For example, a &query that is aborted will return an ERROR result. Miscellaneous functions -- These all return string values: &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. &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. ®ister "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". &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. ~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 ¬ $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 ¬ $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 ~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, 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 to suppress display updates until the macro completes. ~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 include 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. ============================ vile.rc ======================== 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" set force-console set w32pipes ~elseif &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 ========================= end vile.rc ======================= ----------------------------------- $Header: /usr/build/vile/vile/doc/RCS/macros.doc,v 1.72 2001/05/20 21:14:03 tom Exp $ -----------------------------------