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.
set-buffer
, get-buffer-create
.
or
.
goto-char
,
point-min
, and push-mark
.
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...)
or
and a let
.
if
instead of an or
.
or
expression works.
save-excursion
expressions.
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 an 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.