EEL TutorialThis section will take you step by step through the process of creating a new command using EEL. You will learn how to use the EEL compiler, a few control structures and data types, and a few primitive operations. Most important, this section will teach you the mechanics of writing extensions in EEL.
As our example, we will write a simplified version of the insert-file command called simple-insert-file. It will ask for the name of a file, and insert the contents of the file before point in the current buffer. We will write it a few lines at a time, each time having the command do more until the whole command works. When you write EEL routines, you may find this the way to go. This method allows you to debug small sections of code.
Start Epsilon in a directory where you want to create the files for this tutorial. Using the find-file command (Ctrl-X Ctrl-F), create a file with the name "learn.e".
To write an extension, you: write the source code, compile the source code, load the compiled code, then run the command.
First, we write the source code. Type the following into the buffer and save it:
Comments go between /* and */.
The file "eel.h" defines some system-wide constants, and a few global variables. Always include it at the beginning of your extension files.
says to define a command
with the name
Each command or subroutine begins with a sequence of local variable declarations. Our command has one, the line
which declares an array of characters called
The next statement
calls the built-in subroutine get_file( ). This primitive takes three parameters: a character array to store the user's typed-in file name, a string with which to prompt the user, and a value to offer as a default. In this case, the Epsilon will prompt the user with the text between the double quotes (with a colon stuck on the end). We call a sequence of characters between double quotes a string constant.
When the user invokes this command, the prompt string appears in the echo area. Epsilon then waits for the user to enter a string, which it copies to the character array. While typing in the file name, the user may use Epsilon's file name completion and querying facility. This routine returns when the user hits the <Enter> key.
The next statement,
prints in the echo area what file name the user typed in. The
primitive say( ) takes one or more arguments. The first argument
acts as a template, specifying what to print out. The "%s" in the
above format string says to interpret the next argument as a
character array (or a string), and to print that instead of the
"%s". In this case, for the second argument we provided
For example, say the user types the file name "foo.bar", followed
by <Enter>. The character array
in the echo area.
One way to get this command into Epsilon is to run the EEL compiler to compile the source code into a form Epsilon can interpret, called a bytecode file. EEL source files end in ".e", and the compiler generates a file of compiled binary object code that ends in ".b". After you do that, you can load the .b file using the load-bytes command.
But an easier way that combines these steps is to use Epsilon's compile-buffer command on Alt-F3. This command invokes the EEL compiler, as if you typed
where filename is the name of the file you want to compile, and then (if there are no errors) loads the resulting bytecode file. You should get the message "learn.b compiled and loaded." in the echo area.
Now that you've compiled and loaded learn.b, Epsilon knows about a
command named simple-insert-file. Epsilon translates the
underscores of command names to hyphens, so as to avoid conflicts
with the arithmetic minus sign in the source text. So the name
Go ahead and invoke the command simple-insert-file. The prompt
appears in the echo area. Type in a file name now. You can use all Epsilon's completion and querying facilities. If you press "?", you will get a list of all the files. If you type "foo?", you will get a list of all the files that start with "foo". <Esc> and <Space> completion work. You can abort the command with Ctrl-G.
After you type a file name, this version of the command simply displays what you typed in the echo area.
Let's continue with the simple-insert-file command. We will create an empty temporary buffer, read the file into that buffer, transfer the characters to our buffer, then delete the temporary buffer. Also, let's get rid of the line that displays what you just typed. Make the file learn.e look like this:
The value of the variable bufname changes each time
the current buffer changes. For this reason, we refer
to such variables as buffer-specific variables.
At any given time, bufname contains the
name of the current buffer. So this initialization
in effect stores the name of the current buffer in the local
After the get_file( ) call, we create a new empty buffer
named "tempbuf" with the statement "
Now we can read the file in:
if an error occurred while reading the file. Otherwise, we move on to the next statement. The primitive error( ) takes the same arguments that say( ) takes. It prints out the message in the echo area, aborts the command, and drops any characters you may have typed ahead.
Now we have the text of the file we want to insert in a buffer named
calls the primitive xfer( ), which transfers characters
from one buffer to another. The first argument specifies the name of the
buffer to transfer characters to. The second and third arguments
give the region of the current buffer to transfer. In this
case, we want to transfer characters to
The last two statements return us to our original buffer and delete the temporary buffer.
The final version of this command adds several more details.
On the first line, we've added
causes Epsilon to leave the region set
around the inserted text. The xfer( ) will insert its text
between mark and point. We've added the line
We now save the error code that file_read( ) returns so we can delete the temporary buffer in the event of an error. We also use the file_error( ) primitive rather than error( ) because the former will translate system error codes to text.
Finally, we added the line
to provide a default if you should execute the command
more than once. Because this definition occurs outside of a function
definition, the variable persists even after the command finishes.
Variables defined within a function definition (local variables) go
away when the function finishes. We copy the file name to