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 an asterisk 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: With
set-buffer
,get-buffer-create
. - insert-buffer: Read-only, and with
or
. - beginning-of-buffer: Shows
goto-char
,point-min
, andpush-mark
. - Second Buffer Related Review
- optional Exercise
5.1 The Definition of 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 all the previous text in the second buffer.
The body of copy-to-buffer
looks like this,
1 | ... |
The copy-to-buffer
function has a simpler interactive
expression than append-to-buffer
.
The definition then says
1 | (with-current-buffer (get-buffer-create buffer) ... |
First, look at the earliest inner expression; that is evaluated first. That expression starts with get-buffer-create buffer
. The function tells the computer to use the buffer with the name specified as the one to which you are copying, or if such a buffer does not exist, to create it. Then, the with-current-buffer
function evaluates its body with that buffer temporarily current.
(This demonstrates another way to shift the computer’s attention but not the user’s. The append-to-buffer
function showed how to do the same with save-excursion
and set-buffer
. with-current-buffer
is a newer, and arguably easier, mechanism.)
The barf-if-buffer-read-only
function sends you an error message saying the buffer is read-only if you cannot modify it.
The next line has the erase-buffer
function as its sole contents. That function erases the buffer.
Finally, the last two lines contain the save-excursion
expression with insert-buffer-substring
as its body. The insert-buffer-substring
expression copies the text from the buffer you are in (and you have not seen the computer shift its attention, so you don’t know that that buffer is now called oldbuf
).
Incidentally, this is what is meant by “replacement”. To replace text, Emacs erases the previous text and then inserts new text.
In outline, the body of copy-to-buffer
looks like this:
1 | (let (bind-oldbuf-to-value-of-current-buffer) |
5.2 The Definition of 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.
Here is a discussion based on the original code. The code was simplified in 2003 and is harder to understand.
(See New Body for insert-buffer
, to see a discussion of the new body.)
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.
- insert-buffer code
- insert-buffer interactive: When you can read, but not write.
- insert-buffer body: The body has an
or
and alet
. - if & or: Using an
if
instead of anor
. - Insert or: How the
or
expression works. - Insert let: Two
save-excursion
expressions. - New insert-buffer
The Code for insert-buffer
Here is the earlier code:
1 | (defun insert-buffer (buffer) |
As with other function definitions, you can use a template to see an outline of the function:
1 | (defun insert-buffer (buffer) |
5.2.1 The Interactive Expression in insert-buffer
In insert-buffer
, the argument to the interactive
declaration has two parts, an asterisk, ‘*’, and ‘bInsert buffer: ’.
- Read-only buffer: When a buffer cannot be modified.
- b for interactive: An existing buffer or else its name.
A Read-only Buffer
The asterisk is for the situation when the current buffer is a read-only buffer—a buffer that cannot be modified. If insert-buffer
is called when the current buffer 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.
‘b’ in an Interactive Expression
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 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.
The new and simplified code generates a list for interactive
. It uses the barf-if-buffer-read-only
and read-buffer
functions with which we are already familiar and the progn
special form with which we are not. (It will be described later.)
5.2.2 The Body of the 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:
1 | (defun insert-buffer (buffer) |
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.
5.2.3 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:
1 | (if (not (holding-on-to-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:
1 | (if (not (bufferp buffer)) ; if-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 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:
1 | (not (bufferp buffer)) |
not
is a function that 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.
Using this test, the if
expression works as follows: when the value of the variable buffer
is actually a buffer rather than 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).
5.2.4 The 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 to 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:
1 | (or (bufferp 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 to 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:
1 | (or (holding-on-to-guest) (find-and-take-arm-of-guest)) |
5.2.5 The 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:
1 | (save-excursion |
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. The first argument of setq
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:
1 | (save-excursion |
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 is relocated to its original place.
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-save-excursion
.
The whole let
expression looks like this:
1 | (let (start end 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.
5.2.6 New Body for insert-buffer
The body in the GNU Emacs 22 version is more confusing than the original.
It consists of two expressions,
1 | (push-mark |
except, and this is what confuses novices, very important work is done inside the push-mark
expression.
The get-buffer
function returns a buffer with the name provided. You will note that the function is not called get-buffer-create
; it does not create a buffer if one does not already exist. The buffer returned by get-buffer
, an existing buffer, is passed to insert-buffer-substring
, which inserts the whole of the buffer (since you did not specify anything else).
The location into which the buffer is inserted is recorded by push-mark
. Then the function returns nil
, the value of its last command. Put another way, the insert-buffer
function exists only to produce a side effect, inserting another buffer, not to return any value.
5.3 Complete Definition of beginning-of-buffer
The basic structure of the beginning-of-buffer
function has already been discussed. (See 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 (in truth, the beginning of the accessible portion 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.
- Optional Arguments
- beginning-of-buffer opt arg: Example with optional argument.
- beginning-of-buffer complete
5.3.1 Optional Arguments
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 particular keyword is 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
, no value need 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:
1 | (defun beginning-of-buffer (&optional arg) |
In outline, the whole function looks like this:
1 | (defun beginning-of-buffer (&optional arg) |
The function is similar to the simplified-beginning-of-buffer
function 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 that is not a cons cell.
(Since I do not explain a cons cell for many more chapters, please consider ignoring the function consp
. See How Lists are Implemented, and Cons Cell and List Types.)
The "P"
in the interactive
expression tells Emacs to pass a prefix argument, if there is one, to the function in raw form. A prefix argument is made by typing the 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 a cons cell with a 4. A lowercase "p"
in the interactive
expression causes the function to convert a prefix arg to a number.)
The true-or-false-test of the if
expression looks complex, but it is not: it checks whether arg
has a value that is not nil
and whether it is a cons cell. (That is what consp
does; it checks whether its argument is a cons cell.) If arg
has a value that is not nil
(and is not a cons cell), which will be the case if beginning-of-buffer
is called with a numeric 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.