4.4.3 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 location of point, and restores it to that position 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 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 …)
    (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-bodylast-expression-in-body)

In this function, the body of the save-excursion contains only one expression, the let* expression. You know about a let function. The let* function is different. It enables Emacs to set each variable in its varlist in sequence, one after another; such that variables in the latter part of the varlist can make use of the values to which Emacs set variables earlier in the varlist.

Looking at the let* expression in append-to-buffer:

(let* ((append-to (get-buffer-create buffer))
       (windows (get-buffer-window-list append-to t t))
       point)
  BODY...)

we see that append-to is bound to the value returned by the (get-buffer-create buffer). On the next line, append-to is used as an argument to get-buffer-window-list; this would not be possible with the let expression. Note that point is automatically bound to nil, the same way as it would be done in the let statement.

Now let’s focus on the functions set-buffer and insert-buffer-substring in the body of the let* expression.

In the old days, the set-buffer expression was simply

(set-buffer (get-buffer-create buffer))

but now it is

(set-buffer append-to)

This is because append-to was bound to (get-buffer-create buffer) earlier on in the let* expression.

The append-to-buffer function definition inserts text from the buffer in which you are currently to a named buffer. It happens that insert-buffer-substring does just the reverse—it copies text from another buffer to the current buffer—that is why the append-to-buffer definition starts out with a let that binds the local symbol oldbuf to the value returned by current-buffer.

The insert-buffer-substring expression looks like this:

(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’s attention 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 function definitions use let, save-excursion, and set-buffer this way.