Go to the first, previous, next, last section, table of contents.

A Few More Complex Functions

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.

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 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 of save-excursion.
    change-buffer
      (erase-buffer)
      (save-excursion     ; Second use of save-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.)

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.

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...)

The Interactive Expression in insert-buffer

In insert-buffer, the argument to the interactive declaration has two parts, an asterisk, `*', and `bInsert buffer: '.

A Read-only 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.

`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 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.

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:

(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).

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 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))

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:

(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.

Complete Definition of 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.

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 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.

What happens in a large buffer

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.

What happens in a small buffer

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.

The Complete 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.

Review

Here is a brief summary of some of the topics covered in this chapter.

or
Evaluate each argument in sequence, and return the value of the first argument that is not 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
Evaluate each argument in sequence, and if any are 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
A keyword used to indicate that an argument to a function definition is optional; this means that the function can be evaluated without the argument, if desired.
prefix-numeric-value
Convert the `raw prefix argument' produced by (interactive "P") to a numeric value.
forward-line
Move point forward to the beginning of the next line, or if the argument is greater than one, forward that many lines. If it can't move as far forward as it is supposed to, 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
Delete the entire contents of the current buffer.
bufferp
Return 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.