To extend jed, it is necessary to become familiar with the S-Lang
programming language. S-Lang not a standalone programming language like
C, Pascal, etc. Rather it is meant to be embedded into a C program. The
S-Lang programming language itself provides only arithmetic, looping, and
branching constructs. In addition, it defines a few other primitive
operations on its data structures. It is up to the application to define
other built-in operations tailored to the application. That is what has
been done for the jed editor. See the document slang.txt
for
S-Lang basics as well as the jed Programmer’s Manual for functions jed has
added to the language. In any case, look at the *.sl
files for
explicit examples.
For the most part, the average user will simply want to rebind some keys and change some variables (e.g., tab width). Here I discuss setting keys and the predefined global variables.
Defining a key to invoke a certain function is accomplished using the
setkey
function. This function takes two arguments: the function to
be executed and the key binding. For example, suppose that you want to
bind the key Ctrl-A to cause the cursor to go to the beginning of
the current line. The jed function that causes this is bol
(See
the jed Programmer’s Manual for a complete list of functions). Putting
the line:
setkey ("bol", "^A");
in the startup file jed.rc
(.jedrc
) file will perform the
binding. Here ^A
consists of the two characters ^
and
A which jed will interpret as the single character Ctrl-A
.
For more examples, see either of the S-Lang files emacs.sl
or
edt.sl
.
The first argument to the setkey
function may be any S-Langexpression. Well, almost any. The only restriction is that the newline
character cannot appear in the expression. For example, the line
setkey ("bol();skip_white ();", "^A");
defines the Ctrl-A
key such that when it is pressed, the editing
point will move the beginning of the line and then skip whitespace up to
the first non-whitespace character on the line.
In addition to being able to define keys to execute functions, it is also possible to define a key to directly insert a string of characters. For example, suppose that you want to define a key to insert the string int main(int argc, char **argv) whenever you press the key Esc m. This may be accomplished as follows:
setkey (" int main(int argc, char **argv)", "\em");
Notice two things. First of all, the key sequence Esc m has
been written as "\em"
where \e
will be interpreted by jed as
Esc. The other salient feature is that the first argument to
setkey
, the “function” argument, begins with a space. This tells
jed that it is not be interpreted as the name of a function; rather, the
characters following the space are to be inserted into the buffer.
Omitting the space character would cause jed to execute a function called
int main(int argc, char **argv) which would fail and generate an
error.
Finally, it is possible to define a key to execute a series of keystrokes similar to a keyboard macro. This is done by prefixing the “function” name with the @ character. This instructs jed to interpret the characters following the @ character as characters entered from the keyboard and execute any function that they are bound to. For example, consider the following key definition which will generate a C language comment to comment out the current line of text. In C, this may be achieved by inserting symbol "/*" at the beginning of the line and inserting "*/" at the end of the line. Hence, the sequence is clear (Emacs keybindings):
"\001"
.\005
.To bind this sequence of steps to the key sequence Esc ;, simply use
setkey("@\001/*\005*/", "\e;");
Again, the prefix @ lets jed know that the remaining characters will
carry out the functions they are currently bound to. Also pay particular
attention to the way Ctrl-A and Ctrl-E have been written. Do
not attempt to use the ^
to represent “Ctrl”. It does not
have the same meaning in the first argument to the setkey
function
as it does in the second argument. To have control characters in the
first argument, you must enter them as \
xyz where xyz is
a three digit decimal number coinciding with the ASCII value of the
character. In this notation, the Esc character could have been
written as \027
. See the S-Lang Programmer’s Reference Manual for
further discussion of this notation.
The setkey
function sets a key in the global
keymap from
which all others are derived. It is also possible to use the function
local_setkey
which operates only upon the current keymap which may
or may not be the global
map.
jed includes some predefined variables which the user may change. By convention, predefined variables are in uppercase. The variables which effect all modes include:
BLINK
(1) if non-zero, blink matching parenthesis.
TAB_DEFAULT
(8) sets default tab setting for newly created buffers
to specified number of columns.
TAB
Value of tab setting for current buffer.
ADD_NEWLINE
(1) adds newline to end of file if needed when writing it out to the
disk.
META_CHAR
(-1) prefix for chars with high bit set (see section on eight bit
clean issues for details)
DISPLAY_EIGHT_BIT
see section on eight bit clean issues.
COLOR
(23) IBMPC background color (see jed.rc
for meaning)
LINENUMBERS
(0) if 1, show current line number on status line
WANT_EOB
(0) if 1, [EOB] denotes end of buffer.
TERM_CANNOT_INSERT
(0) if 1, do not put the terminal in insert mode when writing to the
screen.
IGNORE_BEEP
(0) do not beep the terminal when signalling errors
In addition to the above, there are variables which affect only certain modes. See the section on modes for details.
A hook is a user defined function that jed calls under certain conditions
which allow the user to modify default actions. For example, when jed
starts up it looks for the existence of a user defined function
command_line_hook
. If this function exists, jed calls the
function. What the function does is completely arbitrary and is left to
the discretion of the user. The startup file, site.sl
, defines
such a function which reads in the files listed on the command line. It is
also this function which loads the jed.rc
startup file. Unlike the
other hooks, this one must be present in the file site.sl
since it
is the only file loaded before calling the hook.
After the startup files are loaded, jed calls the hook
jed_startup_hook
immediately before entering the main editor loop.
This hook is useful to modify certain data structures which may not have
existed when the startup files were loaded.
In addition to the above hooks, jed currently also looks for:
suspend_hook
function to be executed before suspending
resume_hook
function that gets carried out after suspension
exit_hook
gets executed before exiting jed
mode_hook
sets buffer mode based on filename extension
find_file_hook
called before file is read into a buffer. It currently
checks for presence of autosave file and warns user if
it is more recent than file.
See site.sl
for explicit examples of the above hooks.
Another useful hook is is_paragraph_separator
. This hook is called
when jed searches for the beginning or end of a paragraph. This search
is performed by all paragraph formatting functions as well as the forward
and backward paragraph movement commands. As jed performs the search,
it moves from one line to another testing the line to see if it separates
a paragraph. The function of the hook is to make this decision and return
zero if the line does not separate paragraphs or return one if it does.
The default value of this hook may be written in S-Lang as
define is_paragraph_separator () { bol (); if (looking_at ("\\")) return 1; if (looking_at ("%")) return 1; skip_white(); eolp (); }
A related hook called after a paragraph is formatted is
format_paragraph_hook
. This hook is only called if either
format_paragraph
or narrow_paragraph
is called with a prefix
digit argument. For example, format_paragraph
is bound to
Esc q. Simply pressing this key sequence will call
format_paragraph
but format_paragraph_hook
will not be called.
However, pressing Esc 1 followed by Esc q will
result in a call to format_paragraph_hook
. Currently, this hook
simply justifies the paragraph. That is, it fills each line in the
paragraph such that the line ends at the right margin, which is defined by
the WRAP
variable.
This section assumes some knowledge about S-Lang and is designed to
explain how to debug S-Lang routines quickly. For information about
S-Lang, read slang.txt
.
There are two ways of loading a file of S-Lang code into jed. The
most common way is through the function evalfile
. If an error
occurs while loading a file, jed will give some indication of where the
problem lies by displaying the line number and the offending bit of
S-Lang code in the minibuffer. In practice though, this can be quite
inefficient. The evalfile
function is primarily designed to load
debugged and tested S-Lang code.
The best way to develop and test S-Lang code with jed is to use the
function evalbuffer
. Simply load the piece of code into jed as an
ordinary file, press Esc X and enter the function
evalbuffer
If the piece of code in the buffer has any syntax
errors, jed will put the cursor on the error. This is the best way to
spot compile time errors such as syntax errors. However, this will not
catch runtime errors.
When a runtime error occurs, jed will put the cursor on the top level
function where the original call was made and NOT the actual location of
the function. To aid in determining where an error occurs, jed can be
made to give a symbolic traceback. As the S-Lang runtime stack unwinds,
S-Lang will simply print the name of function at that particular level.
If the function includes local variables, their values will be dumped as
well. Hence, it is easy to quickly narrow the location of an error down
to function where the error occurs. By default, the traceback is
disabled. The traceback is enabled by setting the S-Lang variable
_traceback
to a non-zero value. It is simpliest to just press
Ctrl-X Esc and enter _traceback = 1
at the S-Lang
prompt. This is one of those times where one needs access to the
S-Lang> prompt and not the M-x prompt. For example, consider
the following piece of code:
define fun_two () {forever {}} % loops forever define fun_one () {fun_two ()} % calls fun_two-- never returns
Simply enter the above into an empty jed *scratch*
buffer, then
press Ctrl-X Esc and enter:
_traceback = 1; () = evalbuffer (); fun_one ();
This will turn on tracebacks, evaluate the buffer and call the
function fun_one
. jed will then be put into an infinite loop which
can only be stopped by pressing the abort character which by default is
Ctrl-G. Doing so, will produce the traceback messages
S-Lang Traceback: fun_two S-Lang Traceback: fun_one
in addition to the error message User Break!. Of course, this technique only narrows down the source of an error to a particular function. To proceed further, it may necessary to put “print” statements at suitable places in the function. There are several ways to do this:
insert
function to insert the contents of a variable
into the current buffer.error
function to abort the function and display the
value of a variable in the minibuffer.message
function to display the value of a variable in
the minibuffer. Unlike error
, the message
function does
not abort the execution of the function.
Since each of these functions require a string argument, it is usually
best to call the string
function first for the conversion followed
by the output function. This has to be done anyway if it is desired to
get the contents of an integer variable. Although the second approach is
prehaps the most useful in practice, it is somtimes appropriate to use a
combination of these techniques.
Finally, to print the entire stack, one can use the print_stack
function. This function dumps the S-Lang runtime stack into the
*traceback* buffer.
Since S-Lang is an interpreted language, judicious application of the above techniques should lead very quickly to the source of any errors.