Defining Your Own Macros

This section is intended for the advanced user who wishes to add functions to the supplied macro file /usr/lib/ed.macros or define a set of new macros for a particular editing task at hand. Before trying your hand at new macros you should have read both the Tutorial Guide and Reference Manual in detail. Macros are basically very simple to write, but only if you have a good grasp of the many editor commands described in the reference manual.

What is a Macro

A macro is just the simple replacement of an input key with a string of characters. The replacement string contains the keys you would have to type to perform the required function manually. As an example, let's look at the definition of the Ins key. Each time it is typed, it toggles option character insert (oi). You could perform this task manually by going to the command line and typing the command

oi~

which will toggle the option. You would then want to go back to the text area if that's where you came from. If you were already on the command line with a partially typed command then the situation is slightly more complex. You can't type in a new command on the command line without deleting what is currently there. To overcome this difficulty, the editor provides a very special character {command_char} which causes all input following it, up to the next newline to be collected (without echo) in a hidden command buffer which is then executed. By prefixing all commands with this character you can execute commands regardless of whether you are on the command line or in the text area. The value of the command character is hexadecimal FF and may be entered directly from your key board by holding down the Alt key and typing the Backspace key normally used for character delete. You can now toggle insert mode at any time by typing the string

{command_char}oi~<newline>

where {command char} is an Alt-Backspace and <newline> is either a carriage return or a linefeed. To turn this into a macro for the Ins key you would translate the value generated by the Ins key into the above string.

t \ab \ffoi~\0a
   |    |    |
   |    |    +--  Linefeed character.
   |    +-------  Command character.
   +------------  Hexadecimal value generated by
                  the Ins key.

If you leave off the command char then the string

oi~

followed by a newline would be entered at the active cursor. Likewise, if you leave off the newline, then the command will be collected, but you will have to supply the newline yourself by typing a carriage return.

Most macros are this simple. However, care should always be taken to ensure reasonable behavior when a requested operation may fail. An example of this is the down arrow key on the keypad. It is designed to move you down one line. This could be performed by executing the command

    .+1

which works fine until you try to move past the last line in your buffer where you will be greeted by an unpleasant error message which will quickly annoy any user. The simple solution is to limit the line address to remain within the buffer using the | operator. The resulting macro definition would be:

\ff.+1|\0a

which behaves in a friendlier fashion. As one final point, to prevent your screen from being re-centered when you attempt to leave the screen, you might add a Zap Cursor Lock command resulting in

\ff.+1|zcl\0a

Lets look at one more simple example illustrating the importance of planning your macro's behavior in all possible situations. You want the PgDn key to display the next page of your buffer. Your text area is 23 lines so you might simply attempt to move forward 23 lines via a

.+23

This has two problems

  1. You may not have another 23 lines in the buffer.
  2. If your current line was not on line 1, then you would miss part of the next page. You should be jumping forward relative to the address of the top line of the current screen. The PgDn is currently defined as
    
    
        t \aa \ff@+23|\0a
           |       | |
           |       | +----  Limit address to lie within buffer.
           |       +------  Address of top line on the screen.
           +--------------  Hexadecimal value generated by the
                            PgDn key.

Multi-line Macros

Most of your macros will consist of one or more editor commands entered as a string which makes up a single command line. There are cases, however, where you will find that you need to enter a multi-line command. You may do this by simply embedding <newline> characters in your translate string. The macro

    t \84 \ffu1 *s/register//\0a \ffu1 *s/short/int/\0a
       |   |                  |   |
       |   |                  |   +-  Start of line two.
       |   |                  +-----  End of line one.
       |   +------------------------  Start of line one.
       +----------------------------  Hexadecimal value
                                      generated by F4 key.

defines a macro which will remove all occurrences of the string “register” and change all occurrences of the string “short” to the string “int”. The Until one (u1) will prevent an error from being printed if the substitute fails to find a match. By placing them on two lines we are guaranteed to perform the second substitute even if the first one fails. Remember, an error (even inside an Until) terminates execution of the current line.

Macros Containing Branches

The most common use of multi-line macros involves the Branch (b) command. This command was included in the editor to allow you to perform conditional execution of editor commands within macros. A simple example is the large + key on your keypad which performs one of two tasks. If you are in the text area it simply places you on the command line. If, however, you are already on the command line, it simulates your entering a carriage return to execute any command, then places you back on the command line. Hitting carriage return would normally execute any command then put you back in the text area. This key is defined as.

    t \a7 \ffoc?b2f\0a\0a\ffoc+\0a
       |     |         |    |
       |     |         |    +--  Return to command mode.
       |     |         +-------  Literal newline to execute
       |     |                   command line.
       |     +-----------------  Query and set condition
       |                         register if on command line.
       +-----------------------  Hexadecimal value generated
                                 by the large + key.

This macro is interesting for two reasons:

  1. It makes use of the Branch (b) command to conditionally execute editor commands. If you are in the text area it will skip two <newline> characters and only execute the \ffoc+\0a which will place you in command mode.
  2. It mixes literal text (which is entered at the current cursor) with command text (which is preceded by a \ff and is collected in an execute buffer). If you were already on the command line it will not branch, and therefore enter a <newline> on the command line which will cause it to be executed. You will then fall into the code which sets option command and goes to the command line.

Defining complex macros which contain several branches and several lines quickly becomes confusing when displayed as a single line with embedded <newline> character escapes. The macro for the F2 key illustrates this.

    t \82 \ffon?b3t\0a\ffi\0a\ffb4\0a\ffon-zch1\0a\ffb2t\0a\ffd\0a

This macro can be better understood when displayed as the multi-line sequence

t \82 \ffon?b3t
Check if option newline is on or off.
\ffi
If it is off, then do an insert command
\ffb4
and skip the rest of the macro.
\ffon-zch1
Else go to the beginning of the line and skip
\ffb2t
to the end of the macro if line isn't empty.
\ffd
Delete the empty line.

It should be remembered that the Zap Cursor Horizontal command (zch) will set the condition register false if you move to a position which does not contain a character. If there is no character in the first column then the line must be empty.

There is a macro which will allow you to type in a macro in your text buffer in the multi-line form above and convert it into a single string on the command line which you may type carriage return to enter. This macro is defined in your /usr/lib/ed.macros file, but has been commented out by a double quote. You may remove this quote and then execute the file

x /usr/lib/ed.macros

to define this macro as your Alt-m key. If you type the Alt-m key in the text area, it will compress your buffer into a single line and move it to the command line for execution. If you are on the command line, then it will take a macro which you have displayed via the

    t ? \hh

command and place it in your text buffer in a multi-line format.

For interest this macro is defined as:

t \84 \ffoc?b7f*da
\ffon-om+0zk1
\ffu1 s/\\\\\\\\/\\80/
\ffu1 s/\\\\0a/\\0a/
\ffu1 *s/\\80/\\\\\\\\/
\ff0zce255 1zch1b5
\ffom+                            -Start of code in the case
\ffu1 *s/$/\\\\0a/                 where you are in the text area.
\ffu40 1j
\ff1zk0zch1oc+

This macro uses Until (u) commands to prevent errors should a substitute fail.

In the case where you are on the command line, it deletes your buffer and places an empty line in it via the Append command. It then copies the command line to the empty line and splits the line at each \0a. Note that it is not tricked by a \\0a sequence.

The reverse process appends each line with a \0a sequence, joins all lines, then copies it to the command line. It leaves the joined line in your text buffer allowing you to save it with a write or write append command.

Suggestions

At this point you should examine the macros defined in the file /usr/lib/ed.macros to gain further insight into the writing of new macros. You may examine them by reading the file, or by displaying each key's translation one at a time via the t ? \hh command and then using the F4 key to show it as a nice multi-line sequence. If you do not know the hexadecimal value of a key, then you may enter it's literal value by preceding it with the macro disable key (- key on the keypad). For example, you could type either

t ? \81

or

t ? <MINUS key><F1 key>

The possibilities for macros are endless, but you are warned that creating them is very much a black art...