In this chapter we study in detail several of the functions used in GNU Emacs. This is called a "walk-through". These functions are used as examples of Lisp code, but are not imaginary examples; with the exception of the first, simplified function definition, these functions show the actual code used in GNU Emacs. You can learn a great deal from these definitions. The functions described here are all related to buffers. Later, we will study other functions.
goto-char
,
point-min
, and push-mark
.
beginning-of-buffer
.
save-excursion
and
insert-buffer-substring
.
In this walk-through, I will describe each new function as we come to it, sometimes in detail and sometimes briefly. If you are interested, you can get the full documentation of any Emacs Lisp function at any time by typing C-h f and then the name of the function (and then RET). Similarly, you can get the full documentation for a variable by typing C-h v and then the name of the variable (and then RET).
Also, if you want to see a function in its original source file, you
can use the find-tags
function to jump to it. Type M-.
(i.e., type the META and the period key at the same time, or
else type the ESC key and then type the period key), and then,
at the prompt, type in the name of the function whose source code you
want to see, such as mark-whole-buffer
, and then type
RET. Emacs will switch buffers and display the source code for
the function on your screen. To switch back to this buffer, type
C-x b RET.
Depending on how the initial default values of your copy of Emacs are
set, you may also need to specify a `tags table', which is a file
called `TAGS'. The one you will most likely want to specify is
in the `emacs/src' directory; thus you would use the
M-x visit-tags-table
command and specify a pathname such as
`/usr/local/lib/emacs/19.23/src/TAGS'.
See section `Tag Tables' in The GNU Emacs Manual.
Also, see section Create Your Own `TAGS' File,
for how to create your own.
After you become more familiar with Emacs Lisp, you will find that you will
frequently use find-tags
to navigate your way around source code;
and you will create your own `TAGS' tables.
Incidentally, the files that contain Lisp code are conventionally called libraries. The metaphor is derived from that of a specialized library, such as a law library or an engineering library, rather than a general library. Each library, or file, contains functions that relate to a particular topic or activity, such as `abbrev.el' for handling abbreviations and other typing shortcuts, and `help.el' for on-line help. (Sometimes several libraries provide code for a single activity, as the various `rmail...' files provide code for reading electronic mail.) In The GNU Emacs Manual, you will see sentences such as "The C-h p command lets you search the standard Emacs Lisp libraries by topic keywords."
beginning-of-buffer
Definition
The beginning-of-buffer
command is a good function to start with
since you are likely to be familiar with it and it is easy to
understand. Used as an interactive command, beginning-of-buffer
moves the cursor to the beginning of the buffer, leaving the mark at the
previous position. It is generally bound to M-<.
In this section, we will discuss a shortened version of the function
that shows how it is most frequently used. This shortened function
works as written, but it does not contain the code for a complex option.
In another section, we will describe the entire function.
(See section Complete Definition of beginning-of-buffer
.)
Before looking at the code, let's consider what the function definition has to contain: it must include an expression that makes the function interactive so it can be called by typing M-x beginning-of-buffer or by typing a keychord such as C-<; it must include code to leave a mark at the original position in the buffer; and it must include code to move the cursor to the beginning of the buffer.
Here is the complete text of the shortened version of the function:
(defun simplified-beginning-of-buffer () "Move point to the beginning of the buffer; leave mark at previous position." (interactive) (push-mark) (goto-char (point-min)))
Like all function definitions, this definition has five parts following
the special form defun
:
simplified-beginning-of-buffer
.
()
,
In this function definition, the argument list is empty; this means that this function does not require any arguments. (When we look at the definition for the complete function, we will see that it may be passed an optional argument.)
The interactive expression tells Emacs that the function is intended to
be used interactively. In this case, interactive
does not have
an argument because simplified-beginning-of-buffer
does not
require one.
The body of the function consists of the two lines:
(push-mark) (goto-char (point-min))
The first of these lines is the expression, (push-mark)
. When
this expression is evaluated by the Lisp interpreter, it sets a mark at
the current position of the cursor, wherever that may be. The position
of this mark is saved in the mark ring.
The next line is (goto-char (point-min))
. This expression
jumps the cursor to the minimum point in the buffer, that is, to the
beginning of the buffer (or to the beginning of the accessible portion
of the buffer if it is narrowed. See section Narrowing and Widening.)
The push-mark
command sets a mark at the place where the cursor
was located before it was moved to the beginning of the buffer by the
(goto-char (point-min))
expression. Consequently, you can, if
you wish, go back to where you were originally by typing C-x C-x.
That is all there is to the function definition!
When you are reading code such as this and come upon an unfamiliar
function, such as goto-char
, you can find out what it does by
using the describe-function
command. To use this command, type
C-h f and then type in the name of the function and press
RET. The describe-function
command will print the
function's documentation string in a `*Help*' window. For
example, the documentation for goto-char
is:
One arg, a number. Set point to that number. Beginning of buffer is position (point-min), end is (point-max).
(The prompt for describe-function
will offer you the symbol
preceding the cursor, so you can save typing by positioning the cursor
right after the function and then typing C-h f RET.)
The end-of-buffer
function definition is written in the same way as
the beginning-of-buffer
definition except that the body of the
function contains the expression (goto-char (point-max))
in place
of (goto-char (point-min))
.
mark-whole-buffer
The mark-whole-buffer
function is no harder to understand than the
simplified-beginning-of-buffer
function. In this case, however,
we will look at the complete function, not a shortened version.
The mark-whole-buffer
function is not as commonly used as the
beginning-of-buffer
function, but is useful nonetheless: it
marks a whole buffer as a region by putting point at the beginning and
a mark at the end of the buffer. It is generally bound to C-x
h.
The code for the complete function looks like this:
(defun mark-whole-buffer () "Put point at beginning and mark at end of buffer." (interactive) (push-mark (point)) (push-mark (point-max)) (goto-char (point-min)))
Like all other functions, the mark-whole-buffer
function fits
into the template for a function definition. The template looks like
this:
(defun name-of-function (argument-list) "documentation..." (interactive-expression...) body...)
Here is how the function works: the name of the function is
mark-whole-buffer
; it is followed by an empty argument list,
`()', which means that the function does not require arguments.
The documentation comes next.
The next line is an (interactive)
expression that tells Emacs
that the function will be used interactively. These details are similar
to the simplified-beginning-of-buffer
function described in the
previous section.
mark-whole-buffer
The body of the mark-whole-buffer
function consists of three
lines of code:
(push-mark (point)) (push-mark (point-max)) (goto-char (point-min))
The first of these lines is the expression, (push-mark (point))
.
This line does exactly the same job as the first line of the body of
the simplified-beginning-of-buffer
function, which is written
(push-mark)
. In both cases, the Lisp interpreter sets a mark
at the current position of the cursor.
I don't know why the expression in mark-whole-buffer
is written
(push-mark (point))
and the expression in
beginning-of-buffer
is written (push-mark)
. Perhaps
whoever wrote the code did not know that the argument for
push-mark
is optional and that if push-mark
is not
passed an argument, the function automatically sets mark at the
location of point by default. Or perhaps the expression was written
so as to parallel the structure of the next line. In any case, the
line causes Emacs to determine the position of point and set a mark
there.
The next line of mark-whole-buffer
is (push-mark
(point-max))
. This expression sets a mark at the point in the buffer
that has the highest number. This will be the end of the buffer (or,
if the buffer is narrowed, the end of the accessible portion of the
buffer. See section Narrowing and Widening, for
more about narrowing.) After this mark has been set, the previous
mark, the one set at point, is no longer set, but Emacs remembers its
position, just as all other recent marks are always remembered. This
means that you can, if you wish, go back to that position by typing
C-u C-SPC twice.
Finally, the last line of the function is (goto-char
(point-min)))
. This is written exactly the same way as it is written
in beginning-of-buffer
. The expression moves the cursor to
the minimum point in the buffer, that is, to the beginning of the buffer
(or to the beginning of the accessible portion of the buffer). As a
result of this, point is placed at the beginning of the buffer and mark
is set at the end of the buffer. The whole buffer is, therefore, the
region.
append-to-buffer
The append-to-buffer
command is very nearly as simple as the
mark-whole-buffer
command. What it does is copy the region (that
is, the part of the buffer between point and mark) from the current
buffer to a specified buffer.
The append-to-buffer
command uses the
insert-buffer-substring
function to copy the region.
insert-buffer-substring
is described by its name: it takes a
string of characters from part of a buffer, a "substring", and
inserts them into another buffer. Most of append-to-buffer
is
concerned with setting up the conditions for
insert-buffer-substring
to work: the code must specify both the
buffer to which the text will go and the region that will be copied.
Here is the complete text of the function:
(defun append-to-buffer (buffer start end) "Append to specified buffer the text of the region. It is inserted into that buffer before its point. When calling from a program, give three arguments: a buffer or the name of one, and two character numbers specifying the portion of the current buffer to be copied." (interactive "BAppend to buffer: \nr") (let ((oldbuf (current-buffer))) (save-excursion (set-buffer (get-buffer-create buffer)) (insert-buffer-substring oldbuf start end))))
The function can be understood by looking at it as a series of filled-in templates.
The outermost template is for the function definition. In this case, it looks like this (with several slots filled in):
(defun append-to-buffer (buffer start end) "documentation..." (interactive "BAppend to buffer: \nr") body...)
The first line of the function includes its name and three arguments.
The arguments are the buffer
to which the text will be copied, and
the start
and end
of the region in the current buffer that
will be copied.
The next part of the function is the documentation, which is clear and complete.
let
expression.
save-excursion
works.
append-to-buffer
Interactive Expression
Since the append-to-buffer
function will be used interactively,
the function must have an interactive
expression. (For a
review of interactive
, see section Make a Function Interactive.) The expression reads as follows:
(interactive "BAppend to buffer: \nr")
This expression has an argument inside of quotation marks and that argument has two parts, separated by `\n'.
The first part is `BAppend to buffer: '. Here, the `B'
tells Emacs to ask for the name of the buffer that will be passed to the
function. Emacs will ask for the name by prompting the user in the
minibuffer, using the string following the `B', which is the string
`Append to buffer: '. Emacs then binds the variable buffer
in the function's argument list to the specified buffer.
The newline, `\n', separates the first part of the argument from
the second part. It is followed by an `r' that tells Emacs to bind
the two arguments that follow the symbol buffer
in the function's
argument list (that is, start
and end
) to the values of
point and mark.
append-to-buffer
The body of the append-to-buffer
function begins with let
.
As we have seen before (see section let
), the purpose of a
let
expression is to create and give initial values to one or
more variables that will only be used within the body of the
let
. This means that such a variable will not be confused with
any variable of the same name outside the let
expression.
We can see how the let
expression fits into the function as a
whole by showing a template for append-to-buffer
with the
let
expression in outline:
(defun append-to-buffer (buffer start end) "documentation..." (interactive "BAppend to buffer: \nr") (let ((variable value)) body...)
The let
expression has three elements:
let
;
(variable value)
;
let
expression.
In the append-to-buffer
function, the varlist looks like this:
(oldbuf (current-buffer))
In this part of the let
expression, the one variable,
oldbuf
, is bound to the value returned by the
(current-buffer)
expression. The variable, oldbuf
, is
used to keep track of the buffer in which you are working.
The element or elements of a varlist are surrounded by a set of
parentheses so the Lisp interpreter can distinguish the varlist from
the body of the let
. As a consequence, the two-element list
within the varlist is surrounded by circumscribing set of parentheses.
The line looks like this:
(let ((oldbuf (current-buffer))) ... )
The two parentheses before oldbuf
might surprise you if you did
not realize that the first parenthesis before oldbuf
marks the
boundary of the varlist and the second parenthesis marks the beginning
of the two-element list, (oldbuf (current-buffer))
.
save-excursion
in append-to-buffer
The body of the let
expression in append-to-buffer
consists of a save-excursion
expression.
The save-excursion
function saves the locations of point and
mark, and restores them to those positions after the expressions in the
body of the save-excursion
complete execution. In addition,
save-excursion
keeps track of the original buffer, and
restores it. This is how save-excursion
is used in
append-to-buffer
.
Incidentally, it is worth noting here that a Lisp function is normally
formatted so that everything that is enclosed in a multi-line spread is
indented more to the right than the first symbol. In this function
definition, the let
is indented more than the defun
, and
the save-excursion
is indented more than the let
, like
this:
(defun ... ... ... (let... (save-excursion ...
This formatting convention makes it easy to see that the two lines in
the body of the save-excursion
are enclosed by the parentheses
associated with save-excursion
, just as the
save-excursion
itself is enclosed by the parentheses associated
with the let
:
(let ((oldbuf (current-buffer))) (save-excursion (set-buffer (get-buffer-create buffer)) (insert-buffer-substring oldbuf start end))))
The use of the save-excursion
function can be viewed as a process
of filling in the slots of a template:
(save-excursion first-expression-in-body second-expression-in-body ... last-expression-in-body)
In this function, the body of the save-excursion
contains only
two expressions. The body looks like this:
(set-buffer (get-buffer-create buffer)) (insert-buffer-substring oldbuf start end)
When the append-to-buffer
function is evaluated, the two
expressions in the body of the save-excursion
are evaluated in
sequence. The value of the last expression is returned as the value of
the save-excursion
function; the other expression is evaluated
only for its side effects.
The first line in the body of the save-excursion
uses the
set-buffer
function to change the current buffer to the one
specified in the first argument to append-to-buffer
. (Changing
the buffer is the side effect; as we have said before, in Lisp, a side
effect is often the primary thing we want.) The second line does the
primary work of the function.
The set-buffer
function changes Emacs' attention to the buffer to
which the text will be copied and from which save-excursion
will
return. The line looks like this:
(set-buffer (get-buffer-create buffer))
The innermost expression of this list is (get-buffer-create
buffer)
. This expression uses the get-buffer-create
function,
which either gets the named buffer, or if it does not exist, creates one
with the given name. This means you can use append-to-buffer
to
put text into a buffer that did not previously exist.
get-buffer-create
also keeps set-buffer
from getting an
unnecessary error: set-buffer
needs a buffer to go to; if you
were to specify a buffer that does not exist, Emacs would baulk.
Since get-buffer-create
will create a buffer if none exists,
set-buffer
is always provided with a buffer.
The last line of append-to-buffer
does the work of appending
the text:
(insert-buffer-substring oldbuf start end)
The insert-buffer-substring
function copies a string from
the buffer specified as its first argument and inserts the string into
the present buffer. In this case, the argument to
insert-buffer-substring
is the value of the variable created and
bound by the let
, namely the value of oldbuf
, which was
the current buffer when you gave the append-to-buffer
command.
After insert-buffer-substring
has done its work,
save-excursion
will restore the action to the original buffer and
append-to-buffer
will have done its job.
Written in skeletal form, the workings of the body look like this:
(let (bind-oldbuf
-to-value-of-current-buffer
) (save-excursion ; Keep track of buffer. change-buffer insert-substring-from-oldbuf
-into-buffer) change-back-to-original-buffer-when-finished let-the-local-meaning-of-oldbuf
-disappear-when-finished
In summary, append-to-buffer
works as follows: it saves the value
of the current buffer in the variable called oldbuf
. It gets the
new buffer, creating one if need be, and switches Emacs to it. Using
the value of oldbuf
, it inserts the region of text from the old
buffer into the new buffer; and then using save-excursion
, it
brings you back to your original buffer.
In looking at append-to-buffer
, you have explored a fairly
complex function. It shows how to use let
and
save-excursion
, and how to change to and come back from another
buffer. Many functions definitions use let
,
save-excursion
, and set-buffer
this way.
Here is a brief summary of the various functions discussed in this chapter.
describe-function
describe-variable
find-tag
save-excursion
save-excursion
have been evaluated. Also, remember
the current buffer and return to it.
push-mark
goto-char
(point-min)
.
insert-buffer-substring
mark-whole-buffer
set-buffer
get-buffer-create
get-buffer
get-buffer
function returns nil
if the named
buffer does not exist.
simplified-end-of-buffer
function definition;
then test it to see whether it works.
if
and get-buffer
to write a function that prints a
message telling you whether a buffer exists.
find-tag
, find the source for the copy-to-buffer
function.