In this chapter, we build on what we have learned in previous
chapters by looking at more complex functions. The
copy-to-buffer function illustrates use of two
save-excursion expressions in one definition, while the
insert-buffer function illustrates use of * in an
interactive expression, use of or, and the important
distinction between a name and the object to which the name refers.
copy-to-buffer
After understanding how append-to-buffer works, it is easy to
understand copy-to-buffer. This function copies text into a
buffer, but instead of adding to the second buffer, it replaces the
previous text in the second buffer. The code for the
copy-to-buffer function is almost the same as the code for
append-to-buffer, except that erase-buffer and a second
save-excursion are used. (See section The Definition of append-to-buffer, for the description of
append-to-buffer.)
The body of copy-to-buffer looks like this
...
(interactive "BCopy to buffer: \nr")
(let ((oldbuf (current-buffer)))
(save-excursion
(set-buffer (get-buffer-create buffer))
(erase-buffer)
(save-excursion
(insert-buffer-substring oldbuf start end)))))
This code is similar to the code in append-to-buffer: it is
only after changing to the buffer to which the text will be copied
that the definition for this function diverges from the definition for
append-to-buffer: the copy-to-buffer function erases the
buffer's former contents. (This is what is meant by `replacement'; to
replace text, Emacs erases the previous text and then inserts new
text.) After erasing the previous contents of the buffer,
save-excursion is used for a second time and the new text is
inserted.
Why is save-excursion used twice? Consider again what the
function does.
In outline, the body of copy-to-buffer looks like this:
(let (bind-oldbuf-to-value-of-current-buffer) (save-excursion ; First use ofsave-excursion. change-buffer (erase-buffer) (save-excursion ; Second use ofsave-excursion. insert-substring-from-oldbuf-into-buffer)))
The first use of save-excursion returns Emacs to the buffer from
which the text is being copied. That is clear, and is just like its use
in append-to-buffer. Why the second use? The reason is that
insert-buffer-substring always leaves point at the end of
the region being inserted. The second save-excursion causes
Emacs to leave point at the beginning of the text being inserted. In
most circumstances, users prefer to find point at the beginning of
inserted text. (Of course, the copy-to-buffer function returns
the user to the original buffer when done--but if the user then
switches to the copied-to buffer, point will go to the beginning of the
text. Thus, this use of a second save-excursion is a little
nicety.)
insert-buffer
insert-buffer is yet another buffer-related function. This
command copies another buffer into the current buffer. It is the
reverse of append-to-buffer or copy-to-buffer, since they
copy a region of text from the current buffer to another buffer.
In addition, this code illustrates the use of interactive with a
buffer that might be read-only and the important distinction
between the name of an object and the object actually referred to. Here
is the code:
(defun insert-buffer (buffer)
"Insert after point the contents of BUFFER.
Puts mark after the inserted text.
BUFFER may be a buffer or a buffer name."
(interactive "*bInsert buffer: ")
(or (bufferp buffer)
(setq buffer (get-buffer buffer)))
(let (start end newmark)
(save-excursion
(save-excursion
(set-buffer buffer)
(setq start (point-min) end (point-max)))
(insert-buffer-substring buffer start end)
(setq newmark (point)))
(push-mark newmark)))
As with other function definitions, you can use a template to see an outline of the function:
(defun insert-buffer (buffer) "documentation..." (interactive "*bInsert buffer: ") body...)
insert-buffer
In insert-buffer, the argument to the interactive
declaration has two parts, an asterisk, `*', and `bInsert
buffer: '.
The asterisk is for the situation when the buffer is a read-only
buffer--a buffer that cannot be modified. If insert-buffer is
called on a buffer that is read-only, a message to this effect is
printed in the echo area and the terminal may beep or blink at you;
you will not be permitted to insert anything into current buffer. The
asterisk does not need to be followed by a newline to separate it from
the next argument.
The next argument in the interactive expression starts with a lower
case `b'. (This is different from the code for
append-to-buffer, which uses an upper-case `B'.
See section The Definition of append-to-buffer.)
The lower-case `b' tells the Lisp interpreter that the argument
for insert-buffer should be an existing buffer or else its
name. (The upper-case `B' option provides for the possibility
that the buffer does not exist.) Emacs will prompt you for the name
of the buffer, offering you a default buffer, with name completion
enabled. If the buffer does not exist, you receive a message that
says "No match"; your terminal may beep at you as well.
insert-buffer Function
The body of the insert-buffer function has two major parts: an
or expression and a let expression. The purpose of the
or expression is to ensure that the argument buffer is
bound to a buffer and not just the name of a buffer. The body of the
let expression contains the code which copies the other buffer
into the current buffer.
In outline, the two expressions fit into the insert-buffer
function like this:
(defun insert-buffer (buffer)
"documentation..."
(interactive "*bInsert buffer: ")
(or ...
...
(let (varlist)
body-of-let... )
To understand how the or expression ensures that the argument
buffer is bound to a buffer and not to the name of a buffer, it
is first necessary to understand the or function.
Before doing this, let me rewrite this part of the function using
if so that you can see what is done in a manner that will be familiar.
insert-buffer With an if Instead of an or
The job to be done is to make sure the value of buffer is a
buffer itself and not the name of a buffer. If the value is the name,
then the buffer itself must be got.
You can imagine yourself at a conference where an usher is wandering around holding a list with your name on it and looking for you: the usher is "bound" to your name, not to you; but when the usher finds you and takes your arm, the usher becomes "bound" to you.
In Lisp, you might describe this situation like this:
(if (not (holding-on-to-guest))
(find-and-take-arm-of-guest))
We want to do the same thing with a buffer--if we do not have the buffer itself, we want to get it.
Using a predicate called bufferp that tells us whether we have a
buffer (rather than its name), we can write the code like this:
(if (not (bufferp buffer)) ; if-part
(setq buffer (get-buffer buffer))) ; then-part
Here, the true-or-false-test of the if expression is
(not (bufferp buffer)); and the then-part is the expression
(setq buffer (get-buffer buffer)).
In the test, the function bufferp returns true if its argument is
a buffer--but false if its argument is the name of the buffer. (The
last character of the function name bufferp is the character
`p'; as we saw earlier, such use of `p' is a convention that
indicates that the function is a predicate, which is a term that means
that the function will determine whether some property is true or false.
See section Using the Wrong Type Object as an Argument.)
The function not precedes the expression (bufferp buffer),
so the true-or-false-test looks like this:
(not (bufferp buffer))
not is a function the returns true if its argument is false and
false if its argument is true. So if (bufferp buffer) returns
true, the not expression returns false and vice-versa: what is
"not true" is false and what is "not false" is true.
Using this test, the if expression works as follows: when the
value of the variable buffer is actually a buffer rather then
its name, the true-or-false-test returns false and the if
expression does not evaluate the then-part. This is fine, since we do
not need to do anything to the variable buffer if it really is
a buffer.
On the other hand, when the value of buffer is not a buffer
itself, but the name of a buffer, the true-or-false-test returns true
and the then-part of the expression is evaluated. In this case, the
then-part is (setq buffer (get-buffer buffer)). This
expression uses the get-buffer function to return an actual
buffer itself, given its name. The setq then sets the variable
buffer to the value of the buffer itself, replacing its previous
value (which was the name of the buffer).
or in the Body
The purpose of the or expression in the insert-buffer
function is to ensure that the argument buffer is bound to a
buffer and not just the name of a buffer. The previous section shows
how the job could have been done using an if expression.
However, the insert-buffer function actually uses or.
To understand this, it is necessary to understand how or works.
An or function can have any number of arguments. It evaluates
each argument in turn and returns the value of the first of its
arguments that is not nil. Also, and this is a crucial feature
of or, it does not evaluate any subsequent arguments after
returning the first non-nil value.
The or expression looks like this:
(or (bufferp buffer)
(setq buffer (get-buffer buffer)))
The first argument to or is the expression (bufferp buffer).
This expression returns true (a non-nil value) if the buffer is
actually a buffer, and not just the name of a buffer. In the or
expression, if this is the case, the or expression returns this
true value and does not evaluate the next expression--and this is fine
with us, since we do not want to do anything to the value of
buffer if it really is a buffer.
On the other hand, if the value of (bufferp buffer) is nil,
which it will be if the value of buffer is the name of a buffer,
the Lisp interpreter evaluates the next element of the or
expression. This is the expression (setq buffer (get-buffer
buffer)). This expression returns a non-nil value, which
is the value to which it sets the variable buffer---and this
value is a buffer itself, not the name of a buffer.
The result of all this is that the symbol buffer is always
bound to a buffer itself rather than the name of a buffer. All this
is necessary because the set-buffer function in a following
line only works with a buffer itself, not with the name to a buffer.
Incidentally, using or, the situation with the usher would be
written like this:
(or (holding-on-to-guest) (find-and-take-arm-of-guest))
let Expression in insert-buffer
After ensuring that the variable buffer refers to a buffer itself
and not just to the name of a buffer, the insert-buffer function
continues with a let expression. This specifies three local
variables, start, end, and newmark and binds them
to the initial value nil. These variables are used inside the
remainder of the let and temporarily hide any other occurrence of
variables of the same name in Emacs until the end of the let.
The body of the let contains two save-excursion
expressions. First, we will look at the inner save-excursion
expression in detail. The expression looks like this:
(save-excursion (set-buffer buffer) (setq start (point-min) end (point-max)))
The expression (set-buffer buffer) changes Emacs's attention from
the current buffer to the one from which the text will copied. In that
buffer, the variables start and end are set to the
beginning and end of the buffer, using the commands point-min and
point-max. Note that we have here an illustration of how
setq is able to set two variables in the same expression.
setq's first argument is set to the value of its second and its
third argument is set to the value of its fourth.
After the body of the inner save-excursion is evaluated, the
save-excursion restores the original buffer, but start and
end remain set to the values of the beginning and end of the
buffer from which the text will be copied.
The outer save-excursion expression looks like this:
(save-excursion (inner-save-excursion-expression (go-to-new-buffer-and-set-start-and-end) (insert-buffer-substring buffer start end) (setq newmark (point)))
The insert-buffer-substring function copies the text
into the current buffer from the region indicated by
start and end in buffer. Since the whole of the
second buffer lies between start and end, the whole of
the second buffer is copied into the buffer you are editing. Next,
the value of point, which will be at the end of the inserted text, is
recorded in the variable newmark.
After the body of the outer save-excursion is evaluated, point
and mark are relocated to their original places.
However, it is convenient to locate a mark at the end of the newly
inserted text and locate point at its beginning. The newmark
variable records the end of the inserted text. In the last line of
the let expression, the (push-mark newmark) expression
function sets a mark to this location. (The previous location of the
mark is still accessible; it is recorded on the mark ring and you can
go back to it with C-u C-SPC.) Meanwhile, point is
located at the beginning of the inserted text, which is where it was
before you called the insert function.
The whole let expression looks like this:
(let (start end newmark)
(save-excursion
(save-excursion
(set-buffer buffer)
(setq start (point-min) end (point-max)))
(insert-buffer-substring buffer start end)
(setq newmark (point)))
(push-mark newmark))
Like the append-to-buffer function, the insert-buffer
function uses let, save-excursion, and
set-buffer. In addition, the function illustrates one way to
use or. All these functions are building blocks that we will
find and use again and again.
beginning-of-buffer
The basic structure of the beginning-of-buffer function has
already been discussed. (See section A Simplified beginning-of-buffer Definition.)
This section describes the complex part of the definition.
As previously described, when invoked without an argument,
beginning-of-buffer moves the cursor to the beginning of the
buffer, leaving the mark at the previous position. However, when the
command is invoked with a number between one and ten, the function
considers that number to be a fraction of the length of the buffer,
measured in tenths, and Emacs moves the cursor that fraction of the way
from the beginning of the buffer. Thus, you can either call this
function with the key command M-<, which will move the cursor to
the beginning of the buffer, or with a key command such as C-u 7
M-< which will move the cursor to a point 70% of the way through the
buffer. If a number bigger than ten is used for the argument, it moves
to the end of the buffer.
The beginning-of-buffer function can be called with or without an
argument. The use of the argument is optional.
Unless told otherwise, Lisp expects that a function with an argument in its function definition will be called with a value for that argument. If that does not happen, you get an error and a message that says `Wrong number of arguments'.
However, optional arguments are a feature of Lisp: a keyword may
be used to tell the Lisp interpreter that an argument is optional.
The keyword is &optional. (The `&' in front of
`optional' is part of the keyword.) In a function definition, if
an argument follows the keyword &optional, a value does not
need to be passed to that argument when the function is called.
The first line of the function definition of beginning-of-buffer
therefore looks like this:
(defun beginning-of-buffer (&optional arg)
In outline, the whole function looks like this:
(defun beginning-of-buffer (&optional arg)
"documentation..."
(interactive "P")
(push-mark)
(goto-char
(if-there-is-an-argument
figure-out-where-to-go
else-go-to
(point-min))))
The function is similar to simplified-beginning-of-buffer except
that the interactive expression has "P" as an argument and
the goto-char function is followed by an if-then-else expression
that figures out where to put the cursor if there is an argument.
The "P" in the interactive expression tells Emacs to pass
a prefix argument, if there is one, to the function. A prefix argument
is made by typing the META key followed by a number, or by typing
C-u and then a number (if you don't type a number, C-u
defaults to 4).
The true-or-false-test of the if expression is simple: it is
simply the argument arg. If arg has a value that is not
nil, which will be the case if beginning-of-buffer is
called with an argument, then this true-or-false-test will return true
and the then-part of the if expression will be evaluated. On the
other hand, if beginning-of-buffer is not called with an
argument, the value of arg will be nil and the else-part
of the if expression will be evaluated. The else-part is simply
point-min, and when this is the outcome, the whole
goto-char expression is (goto-char (point-min)), which is
how we saw the beginning-of-buffer function in its simplified
form.
beginning-of-buffer with an Argument
When beginning-of-buffer is called with an argument, an
expression is evaluated which calculates what value to pass to
goto-char. This expression is rather complicated at first sight.
It includes an inner if expression and much arithmetic. It looks
like this:
(if (> (buffer-size) 10000)
;; Avoid overflow for large buffer sizes!
(* (prefix-numeric-value arg) (/ (buffer-size) 10))
(/
(+ 10
(*
(buffer-size) (prefix-numeric-value arg))) 10))
Like other complex-looking expressions, this one can be distangled by looking at it as parts of a template, in this case, the template for an if-then-else expression. When in skeletal form, the expression looks like this:
(if (buffer-is-large
divide-buffer-size-by-10-and-multiply-by-arg
else-use-alternate-calculation
The true-or-false-test of this inner if expression checks the
size of the buffer. The reason for this is that version 18 Emacs Lisp
uses numbers that are no bigger than eight million or so (bigger numbers
are not needed) and in the computation that follows, Emacs might try to
use over-large numbers if the buffer were large. The term `overflow',
mentioned in the comment, means numbers that are over large.
There are two cases: if the buffer is large and if it is not.
In beginning-of-buffer, the inner if expression tests
whether the size of the buffer is greater than 10,000 characters. To do
this, it uses the > function and the buffer-size function.
The line looks like this:
(if (> (buffer-size) 10000)
When the buffer is large, the then-part of the if expression is
evaluated. It reads like this (after formatting for easy reading):
(* (prefix-numeric-value arg) (/ (buffer-size) 10))
This expression is a multiplication, with two arguments to the function
*.
The first argument is (prefix-numeric-value arg). When
"P" is used as the argument for interactive, the value
passed to the function as its argument is passed a "raw prefix
argument", and not a number. (It is a number in a list.) To perform
the arithmetic, a conversion is necessary, and
prefix-numeric-value does the job.
The second argument is (/ (buffer-size) 10). This expression
divides the numeric value of the buffer by ten. This produces a number
that tells how many characters make up one tenth of the buffer size.
(In Lisp, / is used for division, just as * is
used for multiplication.)
In the multiplication expression as a whole, this amount is multiplied by the value of the prefix argument--the multiplication looks like this:
(* numeric-value-of-prefix-arg number-of-characters-in-one-tenth-of-the-buffer)
If, for example, the prefix argument is `7', the one-tenth value will be multiplied by 7 to give a position 70% of the way through the buffer.
The result of all this is that if the buffer is large, the
goto-char expression reads like this:
(goto-char (* (prefix-numeric-value arg)
(/ (buffer-size) 10)))
This puts the cursor where we want it.
If the buffer contains fewer than 10,000 characters, a slightly different computation is performed. You might think this is not necessary, since the first computation could do the job. However, in a small buffer, the first method may not put the cursor on exactly the desired line; the second method does a better job.
The code looks like this:
(/ (+ 10 (* (buffer-size) (prefix-numeric-value arg))) 10))
This is code in which you figure out what happens by discovering how the functions are embedded in parentheses. It is easier to read if you reformat it with each expression indented more deeply than its enclosing expression:
(/
(+ 10
(*
(buffer-size)
(prefix-numeric-value arg)))
10))
Looking at parentheses, we see that the innermost operation is
(prefix-numeric-value arg), which converts the raw argument to a
number. This number is multiplied by the buffer size in the following
expression:
(* (buffer-size) (prefix-numeric-value arg)
This multiplication creates a number that may be larger than the size of the buffer--seven times larger if the argument is 7, for example. Ten is then added to this number and finally the large number is divided by ten to provide a value that is one character larger than the percentage position in the buffer.
The number that results from all this is passed to goto-char and
the cursor is moved to that point.
beginning-of-buffer
Here is the complete text of the beginning-of-buffer function:
(defun beginning-of-buffer (&optional arg)
"Move point to the beginning of the buffer;
leave mark at previous position.
With arg N, put point N/10 of the way
from the true beginning.
Don't use this in Lisp programs!
\(goto-char (point-min)) is faster
and does not set the mark."
(interactive "P")
(push-mark)
(goto-char
(if arg
(if (> (buffer-size) 10000)
;; Avoid overflow for large buffer sizes!
(* (prefix-numeric-value arg)
(/ (buffer-size) 10))
(/ (+ 10 (* (buffer-size)
(prefix-numeric-value arg)))
10))
(point-min)))
(if arg (forward-line 1)))
Except for two small points, the previous discussion shows how this function works. The first point deals with a detail in the documentation string, and the second point concerns the last line of the function.
In the documentation string, there is reference to an expression:
\(goto-char (point-min))
A `\' is used before the first parenthesis of this expression. This `\' tells the Lisp interpreter that the expression should be printed as shown in the documentation rather than evaluated as a symbolic expression, which is what it looks like.
Finally, the last line of the beginning-of-buffer command says to
move point to the beginning of the next line if the command is
invoked with an argument:
(if arg (forward-line 1)))
This puts the cursor at the beginning of the first line after the appropriate tenths position in the buffer. This is a flourish that means that the cursor is always located at least the requested tenths of the way through the buffer, which is a nicety that is, perhaps, not necessary, but which, if it did not occur, would be sure to draw complaints.
Here is a brief summary of some of the topics covered in this chapter.
or
nil; if none return a value that is not
nil, return nil. In brief, return the first true value
of the arguments; return a true value if one or any of the
other are true.
and
nil, return
nil; if none are nil, return the value of the last
argument. In brief, return a true value only if all the arguments are
true; return a true value if one and each of the others is
true.
&optional
prefix-numeric-value
(interactive
"P") to a numeric value.
forward-line
forward-line goes forward as far as
it can and then returns a count of the number of additional lines it was
supposed to move but couldn't.
erase-buffer
bufferp
t if its argument is a buffer; otherwise return nil.
&optional Argument Exercise
Write an interactive function with an optional argument that tests
whether its argument, a number, is greater or less than the value of
fill-column, and tells you which, in a message. However, if you
do not pass an argument to the function, use 56 as a default value.
Go to the first, previous, next, last section, table of contents.